Skip to content

Commit c29e413

Browse files
committed
Improve documentation and event handling for view_range_slider.v
- Added detailed module and function-level comments explaining the range slider's configuration options, behavior, and usage. - Refined event handling logic for mouse interactions, including click and drag mechanisms. - Enhanced `on_hover_slide` and `amend_layout` methods for better readability and functionality. - Applied minor adjustments to improve clarity and ensure consistency across comments.
1 parent a176745 commit c29e413

File tree

1 file changed

+95
-36
lines changed

1 file changed

+95
-36
lines changed

view_range_slider.v

Lines changed: 95 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
module gui
22

3+
// view_range_slider.v implements a range slider UI component that allows users
4+
// to select a value from a continuous range by dragging a thumb along a track.
5+
// The component supports both horizontal and vertical orientations, customizable
6+
// styling, keyboard navigation, mouse wheel input, and configurable value ranges.
7+
//
38
import math
49

10+
// RangeSliderCfg defines the configuration options for the range slider component.
11+
// It includes visual styling properties like colors and dimensions, behavioral
12+
// settings like value range and step size, and callbacks for handling user input.
513
@[heap; minify]
614
pub struct RangeSliderCfg {
715
pub:
@@ -34,6 +42,21 @@ pub:
3442
invisible bool
3543
}
3644

45+
// range_slider creates and returns a range slider View component based on the provided configuration.
46+
// The range slider allows users to select a numeric value within a specified range by dragging
47+
// a thumb along a track or using keyboard/mouse wheel input.
48+
//
49+
// Parameters:
50+
// cfg RangeSliderCfg - Configuration struct containing all customization options including:
51+
// - Visual styling (colors, dimensions, etc.)
52+
// - Value range (min/max)
53+
// - Step size
54+
// - Callbacks for input handling
55+
// - Layout options
56+
//
57+
// Returns:
58+
// View - A fully configured range slider View component
59+
//
3760
pub fn range_slider(cfg RangeSliderCfg) View {
3861
if cfg.min >= cfg.max {
3962
panic('range_slider.min must be less than range_slider.max')
@@ -54,6 +77,7 @@ pub fn range_slider(cfg RangeSliderCfg) View {
5477
h_align: .center
5578
v_align: .middle
5679
axis: if cfg.vertical { .top_to_bottom } else { .left_to_right }
80+
on_click: cfg.on_mouse_down
5781
amend_layout: cfg.amend_layout_slide
5882
on_hover: cfg.on_hover_slide
5983
on_keydown: cfg.on_keydown
@@ -80,9 +104,7 @@ pub fn range_slider(cfg RangeSliderCfg) View {
80104
fill: cfg.fill
81105
color: cfg.color_border
82106
padding: cfg.padding_border
83-
on_click: cfg.on_mouse_down
84107
amend_layout: cfg.amend_layout_thumb
85-
on_hover: cfg.on_hover_thumb
86108
content: [
87109
circle(
88110
name: 'range_slider thumb'
@@ -100,8 +122,28 @@ pub fn range_slider(cfg RangeSliderCfg) View {
100122
)
101123
}
102124

125+
// amend_layout_slide adjusts the layout of the range slider components based on the
126+
// current value and configuration.
127+
//
128+
// The slider consists of three main visual elements:
129+
// 1. A border container that wraps everything
130+
// 2. An interior container with the main track
131+
// 3. A "filled" portion showing the selected value (left bar)
132+
//
133+
// For vertical sliders:
134+
// - Adjusts the height of the left bar based on current value percentage
135+
// - Centers the track horizontally relative to the thumb
136+
// - Applies padding and sizing to maintain proper visual alignment
137+
//
138+
// For horizontal sliders:
139+
// - Adjusts the width of the left bar based on current value percentage
140+
// - Centers the track vertically relative to the thumb
141+
// - Applies padding and sizing to maintain proper visual alignment
142+
//
143+
// Parameters:
144+
// layout Layout - The root layout node for the range slider
145+
// w Window - Window context for focus state handling
103146
fn (cfg &RangeSliderCfg) amend_layout_slide(mut layout Layout, mut w Window) {
104-
layout.shape.on_click = cfg.on_click
105147
layout.shape.on_mouse_scroll = cfg.on_mouse_scroll
106148

107149
// set positions of left/right or top/bottom rectangles
@@ -148,36 +190,72 @@ fn (cfg &RangeSliderCfg) amend_layout_slide(mut layout Layout, mut w Window) {
148190
}
149191
}
150192

151-
fn (cfg &RangeSliderCfg) on_hover_slide(mut layout Layout, mut e Event, mut _ Window) {
152-
layout.children[0].shape.color = cfg.color_hover
193+
fn (cfg &RangeSliderCfg) on_hover_slide(mut layout Layout, mut e Event, mut w Window) {
194+
w.set_mouse_cursor_pointing_hand()
195+
layout.shape.color = cfg.color_hover
153196
if e.mouse_button == .left {
154197
layout.children[0].shape.color = cfg.color_click
155198
}
156199
}
157200

201+
// amend_layout_thumb positions the slider's thumb element based on the current value.
202+
// This function is called as an amend layout callback after the main layout is composed
203+
// because the thumb position depends on the final dimensions of the slider track.
204+
//
205+
// The thumb position is calculated as follows:
206+
// 1. Converts the current value to a percentage within the min/max range
207+
// 2. For vertical sliders:
208+
// - Maps percentage to y-coordinate along the track height
209+
// - Centers thumb horizontally using padding
210+
// 3. For horizontal sliders:
211+
// - Maps percentage to x-coordinate along the track width
212+
// - Centers thumb vertically using padding
213+
//
214+
// Parameters:
215+
// layout Layout - The thumb element's layout node to position
216+
// _ Window - Window context (unused)
158217
fn (cfg &RangeSliderCfg) amend_layout_thumb(mut layout Layout, mut _ Window) {
159218
// set the thumb position
160219
value := f32_clamp(cfg.value, cfg.min, cfg.max)
161220
percent := math.abs(value / (cfg.max - cfg.min))
221+
radius := cfg.thumb_size / 2
162222
if cfg.vertical {
163223
height := layout.parent.shape.height
164224
y := f32_min(height * percent, height)
165-
layout.shape.y = layout.parent.shape.y + y - cfg.padding_border.height()
225+
layout.shape.y = layout.parent.shape.y + y - cfg.padding_border.height() - radius
166226
layout.children[0].shape.y = layout.shape.y + cfg.padding_border.top
167227
} else {
168228
width := layout.parent.shape.width
169229
x := f32_min(width * percent, width)
170-
layout.shape.x = layout.parent.shape.x + x - cfg.padding_border.width()
230+
layout.shape.x = layout.parent.shape.x + x - cfg.padding_border.width() - radius
171231
layout.children[0].shape.x = layout.shape.x + cfg.padding_border.top
172232
}
173233
}
174234

175-
fn (_ &RangeSliderCfg) on_hover_thumb(mut _ Layout, mut _ Event, mut w Window) {
176-
w.set_mouse_cursor_pointing_hand()
177-
}
235+
// on_mouse_down handles the initial mouse click on the range slider.
236+
// When clicked, it:
237+
// 1. Creates a copy of the event with unadjusted coordinates since mouse_move
238+
// handler expects absolute screen coordinates rather than layout-relative ones
239+
// 2. Calls mouse_move to immediately update slider position to click location
240+
// 3. Sets up mouse lock to track drag movements by registering mouse_move as the
241+
// drag handler and configuring mouse release to unlock
242+
//
243+
// The coordinate adjustment is needed because regular events get layout-relative
244+
// coordinates while MouseLock events use absolute screen coordinates. Since the
245+
// same mouse_move handler is used for both initial click and dragging, we need
246+
// to convert to absolute coordinates.
247+
fn (cfg &RangeSliderCfg) on_mouse_down(layout &Layout, mut e Event, mut w Window) {
248+
mut ev := &Event{
249+
...e
250+
touches: e.touches // runtime mem error otherwise
251+
mouse_x: e.mouse_x + layout.shape.x
252+
mouse_y: e.mouse_y + layout.shape.y
253+
}
254+
cfg.mouse_move(layout, mut ev, mut w)
178255

179-
fn (cfg &RangeSliderCfg) on_mouse_down(_ &Layout, mut e Event, mut w Window) {
256+
// Lock the mouse to the range slider until the mouse button is released
180257
w.mouse_lock(MouseLockCfg{
258+
// event mouse coordinates are not adjusted here
181259
mouse_move: cfg.mouse_move
182260
mouse_up: fn (_ &Layout, mut _ Event, mut w Window) {
183261
w.mouse_unlock()
@@ -186,16 +264,17 @@ fn (cfg &RangeSliderCfg) on_mouse_down(_ &Layout, mut e Event, mut w Window) {
186264
e.is_handled = true
187265
}
188266

189-
// mouse_move pass cfg by value more reliable here
190-
fn (cfg RangeSliderCfg) mouse_move(layout &Layout, mut e Event, mut w Window) {
267+
// mouse_move expects the events mouse coordinates to MOT be adjusted (see on_mouse_down)
268+
fn (cfg &RangeSliderCfg) mouse_move(layout &Layout, mut e Event, mut w Window) {
191269
id := cfg.id
192270

193271
if cfg.on_change != unsafe { nil } {
194-
if node_circle := layout.find_layout(fn [id] (n Layout) bool {
272+
range_slider := layout.find_layout(fn [id] (n Layout) bool {
195273
return n.shape.id == id
196274
})
197-
{
198-
shape := node_circle.parent.shape
275+
if range_slider != none {
276+
w.set_mouse_cursor_pointing_hand()
277+
shape := range_slider.shape
199278
if cfg.vertical {
200279
height := shape.height
201280
percent := f32_clamp((e.mouse_y - shape.y) / height, 0, 1)
@@ -221,26 +300,6 @@ fn (cfg RangeSliderCfg) mouse_move(layout &Layout, mut e Event, mut w Window) {
221300
}
222301
}
223302

224-
fn (cfg &RangeSliderCfg) on_click(layout &Layout, mut e Event, mut w Window) {
225-
if cfg.on_change != unsafe { nil } {
226-
forgiveness := 10
227-
len := if cfg.vertical { layout.shape.height } else { layout.shape.width }
228-
mouse := if cfg.vertical { e.mouse_y } else { e.mouse_x }
229-
pos := if cfg.vertical { layout.shape.y } else { layout.shape.x }
230-
percent := match true {
231-
mouse <= pos + forgiveness { 0 }
232-
mouse >= pos + len - forgiveness { 1 }
233-
else { f32_clamp((mouse - pos) / len, 0, 1) }
234-
}
235-
val := (cfg.max - cfg.min) * percent
236-
mut value := f32_clamp(val, cfg.min, cfg.max)
237-
if cfg.round_value {
238-
value = f32(math.round(f64(value)))
239-
}
240-
cfg.on_change(value, mut e, mut w)
241-
}
242-
}
243-
244303
fn (cfg &RangeSliderCfg) on_keydown(_ &Layout, mut e Event, mut w Window) {
245304
if cfg.on_change != unsafe { nil } && e.modifiers == .none {
246305
mut value := cfg.value

0 commit comments

Comments
 (0)