问题
So I know that on the official documentation it says
Please note that mouse events do not propagate to widgets inside of the scroll container.
But what I'm trying to do is exactly that: How can I make mouse events propagate through this widget? Is there a way?
What I'm trying to do is make a "to-do app" built into awesome. But for that I actually need to have items (which will be the tasks I want to get done)
So I want to have a menu with widgets inside it that are rows, so that I can use it to scroll with the mouse wheel (or the keyboard) up and down, but still be able to select stuff. I think I can do the scrolling with the mouse through setting the widget's buttons
and tweaking with the scroll
container's :pause
, :continue
, etc. methods but I can't do the clicking part.
Therefore, my question is: how can I accomplish this? even if it's not through using the scroll
container, how can I make this work?
To get an idea of what I want, here's what I made so far:
回答1:
Okay, so you want the complicated version that will clip its contained widget to its size and also handle input events. The following is the complicated and slow version:
local inner_widget = screen[1].mytaglist
local inner_width, inner_height = 200, 40
-- No idea how to pick a good width and height for the wibox.
local w = wibox{ x = 100, y = 100, width = 100, height = 20, visible = true }
local own_widget = wibox.widget.base.make_widget()
w:set_widget(own_widget)
local offset_x, offset_y = -20, 0
local own_context = { screen = screen[1], dpi = 92 } -- We have to invent something here... :-(
local hierarchy
hierarchy = wibox.hierarchy.new(own_context, inner_widget, inner_width, inner_height, function()
own_widget:emit_signal("widget::redraw_needed")
end, function()
hierarchy:update(own_context, inner_widget, inner_width, inner_height)
own_widget:emit_signal("widget::redraw_needed")
end, nil)
function own_widget:draw(context, cr, width, height)
-- This does the scrolling
cr:translate(offset_x, offset_y)
-- Then just draw the inner stuff directly
hierarchy:draw(own_context, cr)
end
-- Start a timer to simulate scrolling: Once per second we move things slightly
gears.timer.start_new(1, function()
offset_x = - offset_x
own_widget:emit_signal("widget::redraw_needed")
return true
end)
-- Finally, make input events work
local function button_signal(name)
-- This function is basically copy&paste from find_widgets() in
-- wibox.drawable
local function traverse_hierarchy_tree(h, x, y, ...)
local m = h:get_matrix_from_device()
-- Is (x,y) inside of this hierarchy or any child (aka the draw extents)?
-- If not, we can stop searching.
local x1, y1 = m:transform_point(x, y)
local x2, y2, w2, h2 = h:get_draw_extents()
if x1 < x2 or x1 >= x2 + w2 then
return
end
if y1 < y2 or y1 >= y2 + h2 then
return
end
-- Is (x,y) inside of this widget?
-- If yes, we have to emit the signal on the widget.
local width, height = h:get_size()
if x1 >= 0 and y1 >= 0 and x1 <= width and y1 <= height then
h:get_widget():emit_signal(name, x1, y1, ...)
end
-- Continue searching in all children.
for _, child in ipairs(h:get_children()) do
traverse_hierarchy_tree(child, x, y, ...)
end
end
own_widget:connect_signal(name, function(_, x, y, ...)
-- Translate to "local" coordinates
x = x - offset_x
y = y - offset_y
-- Figure out which widgets were hit and emit the signal on them
traverse_hierarchy_tree(hierarchy, x, y, ...)
end)
end
button_signal("button::press")
button_signal("button::release")
Instead of letting AwesomeWM handle everything and just placing another widget in :layout
, this code does more itself. Namely, it directly manages a widget tree (called a "hierarchy" in AwesomeWM) and draws it itself. Half of this code is then responsible for handling button events: When one comes in, this code forwards it to the right widget, taking the current scrolling into account.
Note that this redraws everything that is shown by this custom widget whenever anything changes. I guess for your scrolling problem this is necessary anyway, because "everything changes" when you scroll a bit. However, when AwesomeWM draws some widgets itself, it tries to only redraw the part that actually changed. For example, if the clock updates because the time changed, only the clock will be redrawn. This code here instead redraws everything always.
(And yes, I know that this code is quite spaghetti-y and bad, but it should help you figure out the necessary ingredients.)
回答2:
Does the following example help you? It creates a new widget that draws some other widget with some hardcoded width/height and offset. A timer is used to animate things a bit.
-- No idea how to pick a good width and height for the wibox.
local w = wibox{ x = 100, y = 100, width = 100, height = 20, visible = true }
local own_widget = wibox.widget.base.make_widget()
local offset_x, offset_y = -20, 0
function own_widget:layout(context, width, height)
-- No idea how to pick good widths and heights for the inner widget.
return { wibox.widget.base.place_widget_at(screen[1].mytaglist, offset_x, offset_y, 200, 40) }
end
gears.timer.start_new(1, function()
if offset_x < 0 then
offset_x = 20
else
offset_x = -20
end
own_widget:emit_signal("widget::layout_changed")
return true
end)
w:set_widget(own_widget)
来源:https://stackoverflow.com/questions/55936636/how-to-make-mouse-events-propagate-to-widgets-in-scroll-containers