Как заставить события мыши распространяться на виджеты в контейнерах `scroll`?

Итак, я знаю, что в официальной документации говорится

Обратите внимание, что события мыши не распространяются на виджеты внутри контейнера прокрутки.

Но я пытаюсь сделать именно это: как я могу заставить события мыши распространяться через этот виджет? Есть ли способ?

То, что я пытаюсь сделать, это сделать «приложение для дел», встроенное в awesome. Но для этого мне на самом деле нужно иметь предметы (которые будут задачами, которые я хочу выполнить)

Итак, я хочу иметь меню с виджетами внутри, которые представляют собой строки, чтобы я мог использовать его для прокрутки колесиком мыши (или клавиатуры) вверх и вниз, но при этом иметь возможность выбирать вещи. Я думаю, что могу выполнить прокрутку с помощью мыши, установив buttons виджета и настроив методы :pause, :continue контейнера scroll и т. д., но я не могу выполнить часть щелчка.

Поэтому мой вопрос: как я могу это сделать? даже если это не с помощью контейнера scroll, как я могу заставить это работать?

Чтобы получить представление о том, что я хочу, вот что я сделал до сих пор:

ухххх меню


person LawsDontApplyToPigs    schedule 01.05.2019    source источник
comment
Вы создаете свой собственный wibox, в котором вы рисуете свои прокручиваемые вещи (и ничего больше)? Если да, то это немного упростило бы ситуацию.   -  person Uli Schlachter    schedule 05.05.2019


Ответы (2)


Итак, вам нужна сложная версия, которая обрезает содержащийся в ней виджет до его размера, а также обрабатывает события ввода. Ниже приведена сложная и медленная версия:

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 сам рисует некоторые виджеты, он пытается перерисовать только ту часть, которая действительно изменилась. Например, если часы обновляются из-за изменения времени, будут перерисованы только часы. Этот код здесь вместо этого всегда перерисовывает все.

(И да, я знаю, что этот код довольно сомнительный и плохой, но он должен помочь вам определить необходимые ингредиенты.)

person Uli Schlachter    schedule 09.05.2019
comment
Итак, я попробовал это с каким-то случайным наспех сделанным виджетом, и это сработало отлично! Но потом я попытался включить это в макет прокрутки, и все сломалось. Я поместил этот код обработки ввода в метод :draw, так как это область, в которой я мог получить доступ к иерархии, которая уже была создана в коде макета scroll. Но затем начали происходить действительно странные ошибки, например, я нажимал на один виджет, и обратный вызов вызывался огромное количество раз. И чем больше я позволяю виджету анимироваться, тем больше раз будет вызываться обратный вызов. Потом я понял, что это произошло потому, что... - person LawsDontApplyToPigs; 09.05.2019
comment
... того факта, что я поместил этот код обработки ввода в метод :draw, чтобы этот код выполнялся всякий раз, когда виджет будет перерисовываться, что в моем случае составляет 60 раз в секунду. Так что все хорошо, но теперь я пойду и попытаюсь найти правильную область для размещения этого кода, а затем вернусь, чтобы сообщить вам, как это прошло. Но большое спасибо за демонстрацию того, как я могу пересылать входные события самостоятельно! - person LawsDontApplyToPigs; 09.05.2019

Поможет ли вам следующий пример? Он создает новый виджет, который рисует какой-то другой виджет с жестко заданной шириной/высотой и смещением. Таймер используется, чтобы немного оживить вещи.

-- 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)
person Uli Schlachter    schedule 05.05.2019
comment
Спасибо за ответ на мой вопрос! Но тем временем мне стало скучно, и я решил написать свой собственный макет, который действовал бы как меню. Я все еще работаю над этим, и самым большим препятствием является тот факт, что мне приходится использовать более неясные, менее документированные части awesome, такие как реализация :layout, :fit. Но да, ваш ответ, похоже, является основой того, что решит мою проблему, однако моя ситуация немного сложнее. У меня есть еще виджеты, которые я еще хочу нарисовать, а не только один виджет в вайбоксе. Мне нужно немного больше узнать о реализации некоторого движущегося поведения, и тогда я вернусь. - person LawsDontApplyToPigs; 06.05.2019
comment
Для вашего более чем одного виджета: Ну... нет, wibox может отображать только один виджет. В большинстве случаев используется макет, который затем рисует другие виджеты. Вы также можете просто использовать такой макет здесь. Другими словами: просто используйте тот же виджет, который вы использовали бы в противном случае. - person Uli Schlachter; 06.05.2019
comment
И что касается другой части вашего вопроса: когда вызывается :fit, это в основном означает, сколько места я могу вам предложить, сколько вы хотите?, поэтому вы возвращаете два числа (ширина и высота). И :layout я отвела вам столько места, скажите, пожалуйста, где находятся ваши дочерние виджеты. Он возвращает таблицу, которую можно заполнить, как в моем ответе, через place_widget_at. Если результат этих функций изменяется (например, ваш виджет становится шире или вы хотите переместить один из ваших дочерних виджетов), вы должны выдать сигнал widget::layout_changed, как в моем ответе. - person Uli Schlachter; 06.05.2019
comment
О, это проясняет некоторую путаницу последних нескольких дней! Большое спасибо! - person LawsDontApplyToPigs; 07.05.2019
comment
Какие-нибудь намеки на то, в чем заключалась ваша путаница и как ее можно было избежать для других? Или вы просто не нашли awesomewm.org/apidoc/documentation/04- новый-widgets.md.html? - person Uli Schlachter; 08.05.2019
comment
Я нашел это, просто некоторые вещи показались мне очень расплывчатыми. Например, то, как работают сигналы, для меня все еще остается черной магией. Единственное, что я знаю, это то, что вы можете делать такие вещи, как mywidget:connect_signal("button::release", epic_function), и что вы можете испускать их из виджетов, чтобы определенные методы вызывались снова, например, somewidget:emit_signal("widget::layout_changed") будет вызывать метод :layout из somewidget (я думаю?? Я не совсем уверен даже об этом.Я собирался задать вопрос о сигналах) - person LawsDontApplyToPigs; 08.05.2019
comment
Кроме того, много упоминаний о context и cr (это cr, я думаю, является контекстом Каира, а другое - местом, где будет происходить рисунок ((я думаю))). То же и с этими. Я понятия не имею, что это такое. Я попытался найти учебники в cairo для этих привязок lua, которые отлично используются (материал lgi). Но найти что-либо было очень сложно. Я только что просмотрел официальную документацию cairo, пытаясь понять, что делают некоторые методы. Но даже это было с переменным успехом, так как большая часть документации написана на C, а я не знаю C. - person LawsDontApplyToPigs; 08.05.2019
comment
И, наконец, я думаю, что некоторые примеры будут иметь большое значение. Как я уже сказал, все казалось неясным. Затем я реализовал некоторые вещи сам, а затем произошли странные вещи, такие как тот факт, что я реализовал :fit, вернув что-то глупое, например return 30, 30, а затем ничего не изменилось. Или тот факт, что я реализовал метод :layout, а затем убрал метод :draw, а потом все равно что-то нарисовалось. Так что меня удивил тот факт, что даже другой метод, кроме :draw, мог рисовать (как-то). Такие мелочи, где, если бы у меня были примеры и иллюстрации, было бы намного легче понять. - person LawsDontApplyToPigs; 08.05.2019
comment
Я планирую отправить в документацию несколько исправлений, чтобы облегчить понимание тех частей, с которыми я столкнулся с трудностями, например, написания примеров. Также я обнаружил некоторые ошибки, которые я исправил, но я планирую отправить их, как только закончу настройку своей установки. Но помимо этого, большое спасибо за то, что помогли мне понять все это, это было действительно полезно. - person LawsDontApplyToPigs; 08.05.2019