How to make mouse events propagate to widgets in `scroll` containers?

泄露秘密 提交于 2019-12-13 03:53:45

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!