diff --git a/docs/changelog.txt b/docs/changelog.txt index 3c6ede53a4..05c030023c 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -133,6 +133,7 @@ Template for new versions: ## Lua - Added ``dfhack.maps.forEachTile`` to scan a cuboid of tiles with a declarative filter and apply actions (count, set tiletype or per-tiletype replacements, set designation/occupancy fields, spawn constructions, Lua callback) in a single native call +- ``widgets.Slider`` and ``widgets.RangeSlider``: fix sliders allowing values outside their number of stops (``num_stops``) when clicked or dragged with the mouse - Fixed ``ipairs()`` on enum attribute tables (e.g. ``df.item_type.attrs``) never terminating (issue #1860) - Added ``dfhack.maps.getTileBlockCoord``, ``dfhack.maps.getBlockOrigin``, and ``dfhack.maps.getTileBlockOffset`` to convert between tile and block coordinates 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 450954b9c1..84ef12427a 100644 --- a/library/lua/gui/widgets/slide_core.lua +++ b/library/lua/gui/widgets/slide_core.lua @@ -3,46 +3,50 @@ 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 _SliderCore: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 @@ -96,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() @@ -160,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 65ce28a5c7..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, slide_core) +Slider = defclass(Slider, _SliderCore) 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(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(self.get_idx_fn() + 1) + self.on_change(self:clamp_idx(self.get_idx_fn() + 1)) end return true end