如何使鼠标事件传播到“滚动”容器中的小部件?
How to make mouse events propagate to widgets in `scroll` containers?
所以我知道官方文档上说的
Please note that mouse events do not propagate to widgets inside of the scroll container.
但我想做的正是:如何让鼠标事件通过这个小部件传播?有办法吗?
我想做的是将 "to-do app" 内置到 awesome 中。但是为此我实际上需要有物品(这将是我想要完成的任务)
所以我想要一个菜单,里面有行的小部件,这样我就可以用它来上下滚动鼠标滚轮(或键盘),但仍然能够 select 东西。我想我可以通过设置小部件的 buttons
和调整 scroll
容器的 :pause
、:continue
等方法来用鼠标滚动,但我做不到点击部分。
因此,我的问题是:我怎样才能做到这一点?即使不是通过使用 scroll
容器,我怎样才能让它工作?
为了了解我想要什么,这是我目前所做的:
下面的例子对您有帮助吗?它创建了一个新的小部件,该小部件使用一些硬编码 width/height 和偏移量绘制其他小部件。计时器用于让事情有点动画。
-- 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)
好的,所以您需要复杂的版本,它将其包含的小部件裁剪到其大小并处理输入事件。以下是复杂而缓慢的版本:
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
中放置另一个小部件,这段代码自己做的更多。即,它直接管理一个 widget 树(在 AwesomeWM 中称为 "hierarchy")并自行绘制。然后这段代码的一半负责处理按钮事件:当一个按钮出现时,这段代码将它转发给正确的小部件,同时考虑当前的滚动。
请注意,只要有任何变化,这就会重新绘制此自定义小部件显示的所有内容。我想对于你的滚动问题,无论如何这是必要的,因为 "everything changes" 当你滚动一点时。但是,当 AwesomeWM 自己绘制一些小部件时,它会尝试仅重绘实际更改的部分。例如,如果时钟因时间更改而更新,则只会重绘时钟。这里的代码总是重绘所有内容。
(是的,我知道这段代码相当 spaghetti-y 并且很糟糕,但它应该可以帮助您找出必要的成分。)
所以我知道官方文档上说的
Please note that mouse events do not propagate to widgets inside of the scroll container.
但我想做的正是:如何让鼠标事件通过这个小部件传播?有办法吗?
我想做的是将 "to-do app" 内置到 awesome 中。但是为此我实际上需要有物品(这将是我想要完成的任务)
所以我想要一个菜单,里面有行的小部件,这样我就可以用它来上下滚动鼠标滚轮(或键盘),但仍然能够 select 东西。我想我可以通过设置小部件的 buttons
和调整 scroll
容器的 :pause
、:continue
等方法来用鼠标滚动,但我做不到点击部分。
因此,我的问题是:我怎样才能做到这一点?即使不是通过使用 scroll
容器,我怎样才能让它工作?
为了了解我想要什么,这是我目前所做的:
下面的例子对您有帮助吗?它创建了一个新的小部件,该小部件使用一些硬编码 width/height 和偏移量绘制其他小部件。计时器用于让事情有点动画。
-- 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)
好的,所以您需要复杂的版本,它将其包含的小部件裁剪到其大小并处理输入事件。以下是复杂而缓慢的版本:
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
中放置另一个小部件,这段代码自己做的更多。即,它直接管理一个 widget 树(在 AwesomeWM 中称为 "hierarchy")并自行绘制。然后这段代码的一半负责处理按钮事件:当一个按钮出现时,这段代码将它转发给正确的小部件,同时考虑当前的滚动。
请注意,只要有任何变化,这就会重新绘制此自定义小部件显示的所有内容。我想对于你的滚动问题,无论如何这是必要的,因为 "everything changes" 当你滚动一点时。但是,当 AwesomeWM 自己绘制一些小部件时,它会尝试仅重绘实际更改的部分。例如,如果时钟因时间更改而更新,则只会重绘时钟。这里的代码总是重绘所有内容。
(是的,我知道这段代码相当 spaghetti-y 并且很糟糕,但它应该可以帮助您找出必要的成分。)