From 1e9f89ec2b568e9285a9997425208a9016a6d17d Mon Sep 17 00:00:00 2001 From: Halavus Nenuli Date: Sat, 14 Mar 2026 00:13:53 +0100 Subject: [PATCH 1/3] clamp Slider indices when stepping with clicks Clicking left and right only did -1 & +1 without checking the validity of the result. --- docs/changelog.txt | 1 + library/lua/gui/widgets/slider.lua | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index bf2ca7d998..29374ef840 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -59,6 +59,7 @@ Template for new versions: ## New Features ## Fixes +- GUI: Fix sliders allowing values outside their number of stops (num_stops) when incremented with mouse clicks. ## Misc Improvements diff --git a/library/lua/gui/widgets/slider.lua b/library/lua/gui/widgets/slider.lua index 65ce28a5c7..3fe7ff9b2f 100644 --- a/library/lua/gui/widgets/slider.lua +++ b/library/lua/gui/widgets/slider.lua @@ -41,7 +41,7 @@ function Slider:onInput(keys) local left_pos = width_per_idx*(left_idx-1) local right_pos = width_per_idx*(right_idx-1) + 4 if x < left_pos then - self.on_change(self.get_idx_fn() - 1) + self.on_change(math.max(1, self.get_idx_fn() - 1)) elseif x < left_pos+3 then self.is_dragging_target = 'left' self.is_dragging_idx = x - left_pos @@ -52,7 +52,7 @@ function Slider:onInput(keys) self.is_dragging_target = 'right' self.is_dragging_idx = x - right_pos else - self.on_change(self.get_idx_fn() + 1) + self.on_change(math.min(self.num_stops, self.get_idx_fn() + 1)) end return true end From 0d56b232f49545866f758c22da88440f94ca5b53 Mon Sep 17 00:00:00 2001 From: Halavus Nenuli Date: Sat, 14 Mar 2026 01:08:36 +0100 Subject: [PATCH 2/3] clamp Slider indices when dragging --- docs/changelog.txt | 2 +- library/lua/gui/widgets/slide_core.lua | 5 +++++ library/lua/gui/widgets/slider.lua | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 29374ef840..f4c2c6bd17 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -59,7 +59,7 @@ Template for new versions: ## New Features ## Fixes -- GUI: Fix sliders allowing values outside their number of stops (num_stops) when incremented with mouse clicks. +- GUI: Fix sliders allowing values outside their number of stops (num_stops) when incremented with the mouse. ## Misc Improvements diff --git a/library/lua/gui/widgets/slide_core.lua b/library/lua/gui/widgets/slide_core.lua index 450954b9c1..655ff6a69e 100644 --- a/library/lua/gui/widgets/slide_core.lua +++ b/library/lua/gui/widgets/slide_core.lua @@ -43,6 +43,10 @@ function slide_core:get_min_stops() return self.is_single and 1 or 2 end +function slide_core:clamp_idx(idx) + return math.max(1, math.min(self.num_stops, idx)) +end + local function do_drag(self, width_per_idx) local x = self.frame_body:localXY(dfhack.screen.getMousePos()) local cur_pos = x - self.is_dragging_idx @@ -50,6 +54,7 @@ local function do_drag(self, width_per_idx) cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos) local offset = self.is_dragging_target == 'right' and -2 or 1 local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1 + new_idx = self:clamp_idx(new_idx) local new_left_idx, new_right_idx if self.is_dragging_target == 'right' then new_right_idx = new_idx diff --git a/library/lua/gui/widgets/slider.lua b/library/lua/gui/widgets/slider.lua index 3fe7ff9b2f..e0aa78fc68 100644 --- a/library/lua/gui/widgets/slider.lua +++ b/library/lua/gui/widgets/slider.lua @@ -19,7 +19,7 @@ local core = require('gui.widgets.slide_core') ---@field super widgets.Widget ---@field ATTRS widgets.Slider.attrs|fun(attributes: widgets.Slider.attrs.partial) ---@overload fun(init_table: widgets.Slider.initTable): self -Slider = defclass(Slider, slide_core) +Slider = defclass(Slider, core) Slider.ATTRS{ get_idx_fn=DEFAULT_NIL, on_change=DEFAULT_NIL, @@ -41,7 +41,7 @@ function Slider:onInput(keys) local left_pos = width_per_idx*(left_idx-1) local right_pos = width_per_idx*(right_idx-1) + 4 if x < left_pos then - self.on_change(math.max(1, self.get_idx_fn() - 1)) + self.on_change(self:clamp_idx(self.get_idx_fn() - 1)) elseif x < left_pos+3 then self.is_dragging_target = 'left' self.is_dragging_idx = x - left_pos @@ -52,7 +52,7 @@ function Slider:onInput(keys) self.is_dragging_target = 'right' self.is_dragging_idx = x - right_pos else - self.on_change(math.min(self.num_stops, self.get_idx_fn() + 1)) + self.on_change(self:clamp_idx(self.get_idx_fn() + 1)) end return true end From 37b9890c0d6c08caaba9a283f4e36c9fdc117823 Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 22 Sep 2026 09:07:23 -0500 Subject: [PATCH 3/3] clamp RangeSlider click-stepping; rename slide_core class to _SliderCore Applies the same num_stops clamping to RangeSlider's click-to-step paths that Slider already had, and renames the internal slide_core class to _SliderCore per naming conventions (CamelCase, leading underscore marks it as not intended for direct instantiation). --- docs/changelog.txt | 2 +- library/lua/gui/widgets/range_slider.lua | 12 +++++----- library/lua/gui/widgets/slide_core.lua | 30 ++++++++++++------------ library/lua/gui/widgets/slider.lua | 8 +++---- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 186ef99048..f877736638 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -59,7 +59,7 @@ Template for new versions: ## New Features ## Fixes -- GUI: Fix sliders allowing values outside their number of stops (num_stops) when incremented with the mouse. +- ``widgets.Slider`` and ``widgets.RangeSlider``: fix sliders allowing values outside their number of stops (``num_stops``) when clicked or dragged with the mouse - `autolabor`: Fix running 1 tick less frequently than intended. - `buildingplan`: fixed non-clickable pressure plates's triggers (issue #5736) - `getplants`: added protective code to avoid misoperation when a plant has an invalid material (which should never happen, but...) diff --git a/library/lua/gui/widgets/range_slider.lua b/library/lua/gui/widgets/range_slider.lua index 9b44e0fd54..0f53906c8c 100644 --- a/library/lua/gui/widgets/range_slider.lua +++ b/library/lua/gui/widgets/range_slider.lua @@ -1,4 +1,4 @@ -local core = require('gui.widgets.slide_core') +local _SliderCore = require('gui.widgets.slide_core') -------------------------------- -- RangeSlider @@ -16,11 +16,11 @@ local core = require('gui.widgets.slide_core') ---@class widgets.RangeSlider.initTable: widgets.RangeSlider.attrs ---@field num_stops integer ----@class widgets.RangeSlider: widgets.Widget, widgets.RangeSlider.attrs ----@field super widgets.Widget +---@class widgets.RangeSlider: widgets._SliderCore, widgets.RangeSlider.attrs +---@field super widgets._SliderCore ---@field ATTRS widgets.RangeSlider.attrs|fun(attributes: widgets.RangeSlider.attrs.partial) ---@overload fun(init_table: widgets.RangeSlider.initTable): self -RangeSlider = defclass(RangeSlider, slide_core) +RangeSlider = defclass(RangeSlider, _SliderCore) RangeSlider.ATTRS{ get_left_idx_fn=DEFAULT_NIL, get_right_idx_fn=DEFAULT_NIL, @@ -44,7 +44,7 @@ function RangeSlider:onInput(keys) local left_pos = width_per_idx*(left_idx-1) local right_pos = width_per_idx*(right_idx-1) + 4 if x < left_pos then - self.on_left_change(self.get_left_idx_fn() - 1) + self.on_left_change(self:clamp_idx(self.get_left_idx_fn() - 1)) elseif x < left_pos+3 then self.is_dragging_target = 'left' self.is_dragging_idx = x - left_pos @@ -55,7 +55,7 @@ function RangeSlider:onInput(keys) self.is_dragging_target = 'right' self.is_dragging_idx = x - right_pos else - self.on_right_change(self.get_right_idx_fn() + 1) + self.on_right_change(self:clamp_idx(self.get_right_idx_fn() + 1)) end return true end diff --git a/library/lua/gui/widgets/slide_core.lua b/library/lua/gui/widgets/slide_core.lua index 655ff6a69e..84ef12427a 100644 --- a/library/lua/gui/widgets/slide_core.lua +++ b/library/lua/gui/widgets/slide_core.lua @@ -3,47 +3,47 @@ local Widget = require('gui.widgets.widget') local to_pen = dfhack.pen.parse -------------------------------- --- slide_core +-- _SliderCore -------------------------------- ----@class widgets.slide_core.attrs: widgets.Widget.attrs +---@class widgets._SliderCore.attrs: widgets.Widget.attrs ---@field num_stops integer ---@field is_single boolean ---@field w integer ----@class widgets.slide_core.attrs.partial: widgets.slide_core.attrs +---@class widgets._SliderCore.attrs.partial: widgets._SliderCore.attrs ----@class widgets.slide_core.initTable: widgets.slide_core.attrs +---@class widgets._SliderCore.initTable: widgets._SliderCore.attrs ---@field num_stops integer ----@class widgets.slide_core: widgets.Widget, widgets.slide_core.attrs +---@class widgets._SliderCore: widgets.Widget, widgets._SliderCore.attrs ---@field super widgets.Widget ----@field ATTRS widgets.slide_core.attrs|fun(attributes: widgets.slide_core.attrs.partial) ----@overload fun(init_table: widgets.slide_core.initTable): self -slide_core = defclass(slide_core, Widget) -slide_core.ATTRS{ +---@field ATTRS widgets._SliderCore.attrs|fun(attributes: widgets._SliderCore.attrs.partial) +---@overload fun(init_table: widgets._SliderCore.initTable): self +_SliderCore = defclass(_SliderCore, Widget) +_SliderCore.ATTRS{ num_stops=DEFAULT_NIL, is_single=DEFAULT_NIL, w=DEFAULT_NIL } -function slide_core:preinit(init_table) +function _SliderCore:preinit(init_table) init_table.frame = init_table.frame or {} init_table.frame.h = init_table.frame.h or 1 end -function slide_core:init() +function _SliderCore:init() local min_stops = self:get_min_stops() if self.num_stops < min_stops then error(('too few stops, expected at least %s'):format(min_stops)) end self.is_dragging_target = nil -- 'left', 'right', or 'both' self.is_dragging_idx = nil -- offset from leftmost dragged tile end -function slide_core:get_min_stops() +function _SliderCore:get_min_stops() return self.is_single and 1 or 2 end -function slide_core:clamp_idx(idx) +function _SliderCore:clamp_idx(idx) return math.max(1, math.min(self.num_stops, idx)) end @@ -101,7 +101,7 @@ local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW} local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW} local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW} -function slide_core:onRenderBody(dc, rect) +function _SliderCore:onRenderBody(dc, rect) local left_idx, right_idx if self.get_idx_fn ~= nil then left_idx = self.get_idx_fn() @@ -165,4 +165,4 @@ function slide_core:onRenderBody(dc, rect) end end -return slide_core +return _SliderCore diff --git a/library/lua/gui/widgets/slider.lua b/library/lua/gui/widgets/slider.lua index e0aa78fc68..480f4787f8 100644 --- a/library/lua/gui/widgets/slider.lua +++ b/library/lua/gui/widgets/slider.lua @@ -1,4 +1,4 @@ -local core = require('gui.widgets.slide_core') +local _SliderCore = require('gui.widgets.slide_core') -------------------------------- -- Slider @@ -15,11 +15,11 @@ local core = require('gui.widgets.slide_core') ---@class widgets.Slider.initTable: widgets.Slider.attrs ---@field num_stops integer ----@class widgets.Slider: widgets.Widget, widgets.Slider.attrs ----@field super widgets.Widget +---@class widgets.Slider: widgets._SliderCore, widgets.Slider.attrs +---@field super widgets._SliderCore ---@field ATTRS widgets.Slider.attrs|fun(attributes: widgets.Slider.attrs.partial) ---@overload fun(init_table: widgets.Slider.initTable): self -Slider = defclass(Slider, core) +Slider = defclass(Slider, _SliderCore) Slider.ATTRS{ get_idx_fn=DEFAULT_NIL, on_change=DEFAULT_NIL,