From 5eee348d3f2c0b1001f5368b78122712a02b1570 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 22 Dec 2021 20:38:22 +0100 Subject: [PATCH 1/3] feat(w.w.slider): Add signals for the dragging state When trying to hook up the slider to an external value, one would usually have two data streams: - `property::value` -> set external value - external value changed -> `.value = new_value` The problem is that without manual intervention, these two streams form a loop, as setting `.value` also emits `property::value`. The new set of signals is disconnected from the `value` property and its signal and allows for more fine grained inspection of the dragging state. Signed-off-by: Lucas Schwiderski --- lib/wibox/widget/slider.lua | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/wibox/widget/slider.lua b/lib/wibox/widget/slider.lua index 690ee7fad6..acd8e6d636 100644 --- a/lib/wibox/widget/slider.lua +++ b/lib/wibox/widget/slider.lua @@ -274,6 +274,28 @@ local slider = {mt={}} -- @beautiful beautiful.slider_bar_active_color -- @param color +--- Emitted when the user starts dragging the handle. +-- +-- @signal drag_start +-- @tparam number value The current value + +--- Emitted for every mouse move while the handle is being dragged. +-- +-- This signal is only emitted by the user dragging the handle. It is therefore +-- preferrable over `property::value`, as it cannot create a loop when trying to +-- hook up the slider as representation of an external value (e.g. system +-- volume). +-- +-- @signal drag +-- @tparam number value The current value + +--- Emitted when the user stops dragging the handle. +-- +-- This signal is emitted when the user releases the mouse button after dragging +-- the handle. +-- +-- @signal drag_end +-- @tparam number value The current value local properties = { -- Handle @@ -334,6 +356,15 @@ function slider:set_value(value) end end +--- Returns `true` while the user is dragging the handle. +-- +-- @property is_dragging +-- @propertydefault Depends on the current dragging state. +-- @treturn boolean +function slider:get_is_dragging() + return self._private.is_dragging +end + local function get_extremums(self) local min = self._private.minimum or properties.minimum local max = self._private.maximum or properties.maximum @@ -535,6 +566,9 @@ local function mouse_press(self, x, y, button_id, _, geo) move_handle(self, width, x, y) + self._private.is_dragging = true + self:emit_signal("drag_start", self.value) + -- Calculate a matrix transforming from screen coordinates into widget coordinates local wgeo = geo.drawable.drawable:geometry() local matrix = matrix_from_device:translate(-wgeo.x, -wgeo.y) @@ -545,11 +579,14 @@ local function mouse_press(self, x, y, button_id, _, geo) capi.mousegrabber.run(function(mouse) if not mouse.buttons[1] then + self._private.is_dragging = false + self:emit_signal("drag_end", self.value) return false end -- Calculate the point relative to the widget move_handle(self, width, matrix:transform_point(mouse.x, mouse.y)) + self:emit_signal("drag", self.value) return true end,handle_cursor) From fc9c3a5786ee87b563d4e1cf872eedc99c5de700 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Dec 2021 18:50:38 +0100 Subject: [PATCH 2/3] test(w.w.slider): Add integration tests Signed-off-by: Lucas Schwiderski --- tests/test-wibox-widget-slider.lua | 89 ++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/test-wibox-widget-slider.lua diff --git a/tests/test-wibox-widget-slider.lua b/tests/test-wibox-widget-slider.lua new file mode 100644 index 0000000000..239835339b --- /dev/null +++ b/tests/test-wibox-widget-slider.lua @@ -0,0 +1,89 @@ +local runner = require("_runner") +local wibox = require("wibox") +local gears = { + shape = require("gears.shape") +} + +local steps = {} + +-- The test runner doesn't support it (yet), but it would be nice to have named +-- steps. +local function step(_, func) + table.insert(steps, func) +end + +local w +local slider + +local on_mouse_press = nil +local on_drag_start = nil +local on_drag = nil +local on_drag_end = nil +local on_property_value = nil + +step("create slider widget", function() + slider = wibox.widget.slider { + minimum = 0, + maximum = 100, + bar_shape = gears.shape.rounded_rect, + bar_height = 3, + bar_color = "#ffffff", + handle_color = "#0000ff", + handle_shape = gears.shape.circle, + handle_border_color = "#ffffff", + handle_border_width = 1, + } + + slider:connect_signal("button::press", function(...) on_mouse_press = true end) + slider:connect_signal("drag_start", function(...) on_drag_start = true end) + slider:connect_signal("drag", function(...) on_drag = true end) + slider:connect_signal("drag_end", function(...) on_drag_end = true end) + slider:connect_signal("property::value", function(...) on_property_value = true end) + + w = wibox { + ontop = true, + x = 0, + y = 0, + width = 250, + height = 50, + visible = true, + widget = slider, + } + + return true +end) + +step("start dragging", function() + -- Coordinates to hit the slider's handle + mouse.coords({ x = 1, y = 24 }) + + root.fake_input("button_press", 1) + awesome.sync() + + return on_drag_start +end) + +step("drag handle", function() + mouse.coords({ x = 50, y = 24 }) + return true +end) + +step("stop dragging", function() + root.fake_input("button_release", 1) + awesome.sync() + + return on_drag_end +end) + +step("check signals", function() + assert(on_mouse_press, "Signal `button::press` was not called") + assert(on_drag_start, "Signal `drag_start` was not called") + assert(on_property_value, "Signal `property::value` was not called") + assert(on_drag, "Signal `drag` was not called") + assert(on_drag_end, "Signal `drag_end` was not called") + return true +end) + +runner.run_steps(steps) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From eaff8aaddc3fce1c42ca8e0fa1b786ac7ccf2152 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 29 Jun 2022 11:30:13 +0200 Subject: [PATCH 3/3] test(w.w.slider): Fix integration test In CI, the wibox doesn't always seem to be in the same location. So before the actual test steps, we need to find it first. --- tests/test-wibox-widget-slider.lua | 64 ++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/tests/test-wibox-widget-slider.lua b/tests/test-wibox-widget-slider.lua index 239835339b..790bee44ce 100644 --- a/tests/test-wibox-widget-slider.lua +++ b/tests/test-wibox-widget-slider.lua @@ -12,6 +12,12 @@ local function step(_, func) table.insert(steps, func) end +-- Apparently in CI the wibox won't always be at the coordinates we specify. +-- So we'll have to click around blindly until we find our widget, +-- then use those coordinates as offset for the actual test. +local offset_x = nil +local offset_y = nil + local w local slider @@ -34,11 +40,22 @@ step("create slider widget", function() handle_border_width = 1, } - slider:connect_signal("button::press", function(...) on_mouse_press = true end) - slider:connect_signal("drag_start", function(...) on_drag_start = true end) - slider:connect_signal("drag", function(...) on_drag = true end) - slider:connect_signal("drag_end", function(...) on_drag_end = true end) - slider:connect_signal("property::value", function(...) on_property_value = true end) + slider:connect_signal("button::press", function() + on_mouse_press = true + + if offset_x ~= nil then + return + end + + local coords = mouse.coords() + offset_x = coords.x + offset_y = coords.y + print(coords.x, coords.y) + end) + slider:connect_signal("drag_start", function() on_drag_start = true end) + slider:connect_signal("drag", function() on_drag = true end) + slider:connect_signal("drag_end", function() on_drag_end = true end) + slider:connect_signal("property::value", function() on_property_value = true end) w = wibox { ontop = true, @@ -50,29 +67,34 @@ step("create slider widget", function() widget = slider, } + -- Mute luacheck warning + assert(w.ontop) + return true end) -step("start dragging", function() - -- Coordinates to hit the slider's handle - mouse.coords({ x = 1, y = 24 }) +for x = 0, 2000, 5 do + for y = 0, 2000, 5 do + step("find widget", function() + if offset_x ~= nil then + return true + end - root.fake_input("button_press", 1) - awesome.sync() - - return on_drag_start -end) + mouse.coords({ x = x, y = y }) + root.fake_input("button_press", 1) + root.fake_input("button_release", 1) -step("drag handle", function() - mouse.coords({ x = 50, y = 24 }) - return true -end) + return true + end) + end +end -step("stop dragging", function() +step("test dragging", function() + mouse.coords({ x = offset_x, y = offset_y }) + root.fake_input("button_press", 1) + mouse.coords({ x = offset_x + 100, offset_y }) root.fake_input("button_release", 1) - awesome.sync() - - return on_drag_end + return true end) step("check signals", function()