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

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

Please note that mouse events do not propagate to widgets inside of the scroll container.

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

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

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

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

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

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

Вы создаете свой собственный wibox, в котором вы рисуете свои "прокручиваемые вещи" (и ничего больше)? Если да, то это немного упростило бы ситуацию.

Uli Schlachter 05.05.2019 12:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
463
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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. Но да, ваш ответ, похоже, является основой того, что решит мою проблему, однако моя ситуация немного сложнее. У меня есть еще виджеты, которые я еще хочу нарисовать, а не только один виджет в вайбоксе. Мне нужно немного больше узнать о реализации некоторого «движущегося» поведения, и тогда я вернусь.

LawsDontApplyToPigs 06.05.2019 14:49

Для вашего «более одного виджета»: Ну… нет, wibox может отображать только один виджет. В большинстве случаев используется макет, который затем рисует другие виджеты. Вы также можете просто использовать такой макет здесь. Другими словами: просто используйте тот же виджет, который вы использовали бы в противном случае.

Uli Schlachter 06.05.2019 21:46

И что касается другой части вашего вопроса: когда вызывается :fit, это в основном означает «вот сколько места я могу вам предложить, сколько вы хотите?», поэтому вы возвращаете два числа (ширина и высота). А :layout — это «Я выделил вам столько места, пожалуйста, скажите мне, где находятся ваши дочерние виджеты». Он возвращает таблицу, которую можно заполнить, как в моем ответе, через place_widget_at. Если результат этих функций изменяется (например, ваш виджет становится шире или вы хотите переместить один из ваших дочерних виджетов), вы должны выдать сигнал widget::layout_changed, как в моем ответе.

Uli Schlachter 06.05.2019 21:47

О, это проясняет некоторую путаницу последних нескольких дней! Большое спасибо!

LawsDontApplyToPigs 07.05.2019 07:14

Какие-нибудь намеки на то, в чем заключалась ваша путаница и как ее можно было избежать для других? Или вы просто не нашли awesomewm.org/apidoc/documentation/04-new-widgets.md.html?

Uli Schlachter 08.05.2019 18:16

Я нашел это, просто некоторые вещи показались мне очень расплывчатыми. Например, то, как работают сигналы, для меня все еще остается черной магией. Единственное, что я знаю, это то, что вы можете делать такие вещи, как mywidget:connect_signal("button::release", epic_function), и что вы можете испускать их из виджетов, чтобы определенные методы вызывались снова, например, somewidget:emit_signal("widget::layout_changed") вызовет :layout метод somewidget (я думаю?? Я не совсем уверен даже об этом.Я собирался задать вопрос о сигналах)

LawsDontApplyToPigs 08.05.2019 18:43

Кроме того, много упоминаний о context и cr (это cr я думаю, контекст Каира, а другое место, где будет происходить рисунок ((я думаю))). То же и с этими. Я понятия не имею, что это такое. Я попытался найти учебники в cairo для этих привязок lua, которые отлично используются (материал lgi). Но найти что-либо было очень сложно. Я только что просмотрел официальную документацию cairo, пытаясь понять, что делают некоторые методы. Но даже это было с переменным успехом, так как большая часть документации написана на C, а я не знаю C.

LawsDontApplyToPigs 08.05.2019 18:49

И, наконец, я думаю, что некоторые примеры будут иметь большое значение. Как я уже сказал, все казалось неясным. Затем я реализовал вещи сам, а затем произошли странные вещи, такие как тот факт, что я реализовал :fit, вернув что-то глупое, например return 30, 30, а затем ничего не изменилось. Или тот факт, что я реализовал метод :layout, а затем убрал метод :draw, а потом все равно что-то нарисовалось. Поэтому меня удивил тот факт, что даже другой метод, кроме :draw, мог рисовать (каким-то образом). Такие мелочи, где, если бы у меня были примеры и иллюстрации, было бы намного легче понять.

LawsDontApplyToPigs 08.05.2019 19:01

Я планирую отправить в документацию несколько исправлений, чтобы облегчить понимание тех частей, с которыми я столкнулся, например, написания примеров. Также я обнаружил некоторые ошибки, которые я исправил, но я планирую отправить их, как только закончу настройку своей установки. Но помимо этого, большое спасибо за то, что помогли мне понять все это, это было действительно полезно.

LawsDontApplyToPigs 08.05.2019 19:07
Ответ принят как подходящий

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

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

LawsDontApplyToPigs 09.05.2019 21:39

... того факта, что я поместил этот код обработки ввода в метод :draw, чтобы этот код выполнялся всякий раз, когда виджет будет перерисовываться, что в моем случае составляет 60 раз в секунду. Так что все хорошо, но теперь я пойду и попытаюсь найти правильную область для размещения этого кода, а затем вернусь, чтобы сообщить вам, как это прошло. Но большое спасибо за демонстрацию того, как я могу пересылать входные события самостоятельно!

LawsDontApplyToPigs 09.05.2019 21:42

Другие вопросы по теме