Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new slider widget to devel/hello-world #1363

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

realSquidCoder
Copy link
Contributor

@realSquidCoder realSquidCoder commented Jan 1, 2025

Created devel/helloSlider.lua (a prototype for a new single-slider widget)

Created helloSlider.lua (a prototype for a new single-slider widget)
@realSquidCoder realSquidCoder marked this pull request as ready for review January 1, 2025 17:38
Comment on lines 5 to 130
init_table.frame = init_table.frame or {}
init_table.frame.h = init_table.frame.h or 1
end

function Slider:init()
if self.num_stops < 2 then error('too few Slider stops') end
self.is_dragging_target = nil -- 'left', 'right', or 'both'
self.is_dragging_idx = nil -- offset from leftmost dragged tile
end

local function Slider_get_width_per_idx(self)
return math.max(3, (self.frame_body.width-7) // (self.num_stops-1))
end

function Slider:onInput(keys)
if not keys._MOUSE_L then return false end
local x = self:getMousePos()
if not x then return false end
local left_idx = self.get_idx_fn()
local width_per_idx = Slider_get_width_per_idx(self)
local left_pos = width_per_idx*(left_idx-1)
local right_pos = width_per_idx*(left_idx-1) + 4
if x < left_pos then
self.on_change(self.get_idx_fn() - 1)
else
self.is_dragging_target = 'both'
self.is_dragging_idx = x - right_pos
end
return true
end

local function Slider_do_drag(self, width_per_idx)
local x = self.frame_body:localXY(dfhack.screen.getMousePos())
local cur_pos = x - self.is_dragging_idx
cur_pos = math.max(0, cur_pos)
cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos)
local offset = 1
local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1
if self.is_dragging_target == 'both' then
if new_idx > self.num_stops then
return
end
end
if new_idx and new_idx ~= self.get_idx_fn() then
self.on_change(new_idx)
end
end

local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK}
local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK}
local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK}
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 Slider:onRenderBody(dc, rect)
local left_idx = self.get_idx_fn()
local width_per_idx = Slider_get_width_per_idx(self)
-- draw track
dc:seek(1,0)
dc:char(nil, SLIDER_LEFT_END)
dc:char(nil, SLIDER_TRACK)
for stop_idx=1,self.num_stops-1 do
local track_stop_pen = SLIDER_TRACK_STOP_SELECTED
local track_pen = SLIDER_TRACK_SELECTED
if left_idx ~= stop_idx then
track_stop_pen = SLIDER_TRACK_STOP
track_pen = SLIDER_TRACK
elseif left_idx == stop_idx then
track_pen = SLIDER_TRACK
end
dc:char(nil, track_stop_pen)
for i=2,width_per_idx do
dc:char(nil, track_pen)
end
end
if left_idx >= self.num_stops then
dc:char(nil, SLIDER_TRACK_STOP_SELECTED)
else
dc:char(nil, SLIDER_TRACK_STOP)
end
dc:char(nil, SLIDER_TRACK)
dc:char(nil, SLIDER_RIGHT_END)
-- draw tab
dc:seek(width_per_idx*(left_idx-1)+2)
dc:char(nil, SLIDER_TAB_LEFT)
dc:char(nil, SLIDER_TAB_CENTER)
dc:char(nil, SLIDER_TAB_RIGHT)
-- manage dragging
if self.is_dragging_target then
Slider_do_drag(self, width_per_idx)
end
if df.global.enabler.mouse_lbut_down == 0 then
self.is_dragging_target = nil
self.is_dragging_idx = nil
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 158 to 188
local LEVEL_OPTIONS = {
{label='Low', value=1},
{label='Medium', value=2},
{label='High', value=3},
{label='Pro', value=4},
{label='Insane', value=5},
}

self:addviews{
widgets.CycleHotkeyLabel{
view_id='level',
frame={l=1, t=0, w=16},
label='Level:',
label_below=true,
key_back='CUSTOM_SHIFT_C',
key='CUSTOM_SHIFT_V',
options=LEVEL_OPTIONS,
initial_option=LEVEL_OPTIONS[1].value,
on_change=function(val)
self.subviews.level:setOption(val)
end,
},
Slider{
frame={l=1, t=3},
num_stops=#LEVEL_OPTIONS,
get_idx_fn=function()
return self.subviews.level:getOptionValue()
end,
on_change=function(idx) self.subviews.level:setOption(idx) end,
},
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code should be added to https://github.com/DFHack/scripts/blob/master/devel/hello-world.lua, with frame sizes adjusted to accommodate.

Move Slider example into the hello world
move the Slider widget away to its proper place (DFHack Repo).
@realSquidCoder realSquidCoder changed the title Add prototype for a new slider widget with only one slider. Added new slider widget to devel/hello-world Jan 20, 2025
@@ -32,6 +42,30 @@ function HelloWorldWindow:init()
frame={w=10, h=5},
frame_style=gui.INTERIOR_FRAME,
},
widgets.Divider{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please set the properties to make it a flat divider instead of a connected divider:

                frame={h=1},
                frame_style_l=false,
                frame_style_r=false,

},
widgets.CycleHotkeyLabel{
view_id='level',
frame={l=0, t=3, w=16},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setting t properties in frames is not useful for autoarranged widgets

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

however, since 2 lines is way too many to put between the CycleHotkeyLabel and the Slider widget, maybe create a containing Panel that does not have autolayout properties.

view_id='level',
frame={l=0, t=3, w=16},
label='Level:',
label_below=true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for a non-range slider, I think label_below would only waste space

Comment on lines +57 to +59
on_change=function(val)
self.subviews.level:setOption(val)
end,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this widget should not be setting its own state in its own callback

@myk002
Copy link
Member

myk002 commented Jan 20, 2025

also needs a changelog entry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Review In Progress
Development

Successfully merging this pull request may close these issues.

2 participants