【问题标题】:How to make mouse events propagate to widgets in `scroll` containers?如何使鼠标事件传播到“滚动”容器中的小部件?
【发布时间】:2019-05-09 13:06:01
【问题描述】:

所以我知道官方文档上写着

请注意,鼠标事件不会传播到滚动容器内的小部件。

但我想要做的正是:如何让鼠标事件通过这个小部件传播?有什么办法吗?

我想做的是制作一个内置的“待办事项”应用程序。但为此我实际上需要有物品(这将是我想要完成的任务)

所以我想有一个菜单,里面有行的小部件,这样我就可以用它来上下滚动鼠标滚轮(或键盘),但仍然可以选择东西。我想我可以通过设置小部件的buttons 并使用scroll 容器的:pause:continue 等方法进行调整,但我不能做点击部分。

因此,我的问题是:我怎样才能做到这一点?即使不是通过使用scroll 容器,我怎样才能做到这一点?

为了了解我想要什么,这是我到目前为止所做的:

【问题讨论】:

  • 您是否正在创建自己的 wibox,在其中绘制“滚动的东西”(仅此而已)?如果是这样,那会稍微简化一些事情。

标签: awesome-wm


【解决方案1】:

好的,所以您想要一个复杂的版本,它将其包含的小部件剪辑到它的大小并处理输入事件。下面是复杂慢的版本:

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")

这段代码不是让 AwesomeWM 处理所有事情,而是在 :layout 中放置另一个小部件,而是自己做更多事情。也就是说,它直接管理一个小部件树(在 AwesomeWM 中称为“层次结构”)并自己绘制它。然后,该代码的一半负责处理按钮事件:当有按钮事件进入时,该代码会将其转发到正确的小部件,同时考虑当前的滚动。

请注意,每当发生任何变化时,都会重绘此自定义小部件显示的所有内容。我想对于您的滚动问题,无论如何这都是必要的,因为当您滚动一点时,“一切都会改变”。但是,当 AwesomeWM 自己绘制一些小部件时,它会尝试仅重绘实际更改的部分。例如,如果时钟因时间更改而更新,则仅重绘时钟。此处的代码总是重绘所有内容。

(是的,我知道这段代码很意大利面而且很糟糕,但它应该可以帮助你找出必要的成分。)

【讨论】:

  • 所以我尝试了一些随机匆忙制作的小部件,它完美地工作!但后来我试图将其合并到滚动布局中,一切都崩溃了。我将输入处理代码放在:draw 方法中,因为这是我可以访问在scroll 布局的代码中已经创建的层次结构的范围。但随后真正奇怪的错误开始发生,就像我单击一个小部件并且回调会被调用很多次。而且我让小部件动画的次数越多,调用回调的次数就越多。然后我意识到这是因为......
  • ...事实上,我将输入处理代码放在:draw 方法中,以便在小部件重绘时执行代码,在我的例子中是每秒 60 次。所以这一切都很好,但现在我要开始尝试找到合适的范围来放入该代码,然后我会回来告诉你这是怎么回事。但是非常感谢您展示了我如何自己转发输入事件!
【解决方案2】:

以下示例对您有帮助吗?它创建了一个新的小部件,该小部件使用一些硬编码的宽度/高度和偏移量绘制其他一些小部件。计时器用于为事物制作一些动画。

-- 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)

【讨论】:

  • 感谢您回答我的问题!但与此同时,我感到无聊并决定编写自己的布局来充当菜单。我仍在努力,最大的障碍是我必须使用 Awesome 中更晦涩、文档更少的部分,例如实现 :layout:fit。但是,是的,您的回答似乎确实是解决我的问题的核心,但是我的情况有点复杂。我还有更多想要绘制的小部件,而不仅仅是 wibox 中的一个小部件。我必须学习更多关于实现一些“移动”行为的知识,然后我会回来的。
  • 对于您的“多个小部件”:嗯...不,wibox 只能显示一个小部件。大多数时候,使用一种布局,然后绘制其他小部件。你也可以在这里使用这样的布局。换一种说法:只需使用与其他方式相同的小部件
  • 对于你问题的另一部分:当:fit 被调用时,它基本上意味着“我可以为你提供多少空间,你想要多少空间?”,所以你返回两个数字(宽度和高度)。 :layout 是“我给你分配了这么多空间,请告诉我你的孩子小部件在哪里”。它返回一个表格,可以像我通过place_widget_at 填写的答案一样填写。如果这些功能的结果发生变化(例如,您的小部件变宽或您想移动您的子小部件之一),您必须发出 widget::layout_changed 信号,就像我的回答一样。
  • 哦,这消除了过去几天的一些困惑!非常感谢!
  • 关于您的困惑是什么以及如何避免其他人的任何提示?还是你没有找到awesomewm.org/apidoc/documentation/04-new-widgets.md.html
猜你喜欢
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-14
  • 1970-01-01
  • 1970-01-01
  • 2020-10-24
相关资源
最近更新 更多