Skip to content

Commit b3347db

Browse files
committed
add scroll-spy stuff
1 parent 2ac36b6 commit b3347db

File tree

5 files changed

+73
-5
lines changed

5 files changed

+73
-5
lines changed

examples/arrows.v

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ struct ArrowSymbol {
2424

2525
fn main() {
2626
mut window := gui.window(
27-
title: 'Arrow Symbols'
28-
state: &ArrowsApp{}
29-
width: 1000
30-
height: 800
31-
on_init: fn (mut w gui.Window) {
27+
title: 'Arrow Symbols'
28+
state: &ArrowsApp{}
29+
width: 1000
30+
height: 800
31+
on_init: fn (mut w gui.Window) {
3232
mut app := w.state[ArrowsApp]()
3333
app.arrows = get_arrows()
3434
// Prepare table data and groups
@@ -202,6 +202,9 @@ fn grid_view(mut w gui.Window) gui.View {
202202

203203
return gui.column(
204204
id: 'grid-scroll'
205+
on_scroll: fn (_ &gui.Layout, mut w gui.Window) {
206+
update_active_scroll_group(mut w)
207+
}
205208
id_scroll: 1
206209
sizing: gui.fill_fill
207210
padding: gui.padding_large
@@ -267,6 +270,9 @@ fn list_view(mut w gui.Window) gui.View {
267270

268271
return gui.column(
269272
id: 'list-scroll'
273+
on_scroll: fn (_ &gui.Layout, mut w gui.Window) {
274+
update_active_scroll_group(mut w)
275+
}
270276
id_scroll: 1
271277
sizing: gui.fill_fill
272278
padding: gui.padding(0, 0, 15, 15)
@@ -1030,3 +1036,38 @@ fn get_arrows() []ArrowSymbol {
10301036

10311037
return arrows
10321038
}
1039+
1040+
fn update_active_scroll_group(mut w gui.Window) {
1041+
mut app := w.state[ArrowsApp]()
1042+
// ID of the main content scrollview
1043+
scroll_id := if app.view_mode == 'list' { 'list-scroll' } else { 'grid-scroll' }
1044+
1045+
// We need to find the scroll container to get its Y position
1046+
container := w.find_layout_by_id(scroll_id) or { return }
1047+
container_y := container.shape.y + container.shape.padding.top
1048+
1049+
mut active_group := ''
1050+
// Iterate through groups in reverse order to find the first one that is above the fold?
1051+
// No, we want the one that is closest to the top but not too far down.
1052+
// Actually, standard spy logic: last header that has (y <= container_y)
1053+
1054+
for group in app.all_groups {
1055+
if group_layout := w.find_layout_by_id('group-${group}') {
1056+
// Check if the group header is at or above the top of the container
1057+
// We give it a little threshold (e.g. 10px) so it selects as soon as it's near top
1058+
if group_layout.shape.y <= container_y + 10 {
1059+
active_group = group
1060+
} else {
1061+
// Since groups are ordered, once we find one below the fold, we can stop
1062+
// The previous one (stored in active_group) is the correct one.
1063+
break
1064+
}
1065+
}
1066+
}
1067+
1068+
if active_group != '' && active_group != app.selected_group {
1069+
app.selected_group = active_group
1070+
// State changed, ensure window updates next frame
1071+
w.update_window()
1072+
}
1073+
}

shape.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub mut:
4646
on_mouse_move fn (&Layout, mut Event, mut Window) = unsafe { nil }
4747
on_mouse_up fn (&Layout, mut Event, mut Window) = unsafe { nil }
4848
on_mouse_scroll fn (&Layout, mut Event, mut Window) = unsafe { nil }
49+
on_scroll fn (&Layout, mut Window) = unsafe { nil }
4950
amend_layout fn (mut Layout, mut Window) = unsafe { nil }
5051
on_hover fn (mut Layout, mut Event, mut Window) = unsafe { nil }
5152
h_align HorizontalAlign

view_container.v

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ fn (mut cv ContainerView) generate_layout(mut _ Window) Layout {
9595
}
9696
on_mouse_up: cv.on_mouse_up
9797
on_hover: cv.on_hover
98+
on_scroll: cv.on_scroll
9899
amend_layout: cv.amend_layout
99100
}
100101
}
@@ -170,6 +171,7 @@ pub:
170171
on_keydown fn (&Layout, mut Event, mut Window) = unsafe { nil }
171172
on_mouse_move fn (&Layout, mut Event, mut Window) = unsafe { nil }
172173
on_mouse_up fn (&Layout, mut Event, mut Window) = unsafe { nil }
174+
on_scroll fn (&Layout, mut Window) = unsafe { nil }
173175
amend_layout fn (mut Layout, mut Window) = unsafe { nil }
174176
on_hover fn (mut Layout, mut Event, mut Window) = unsafe { nil }
175177
width f32
@@ -302,6 +304,7 @@ fn container(cfg ContainerCfg) View {
302304
on_mouse_move: cfg.on_mouse_move
303305
on_mouse_up: cfg.on_mouse_up
304306
on_hover: cfg.on_hover
307+
on_scroll: cfg.on_scroll
305308
amend_layout: cfg.amend_layout
306309
content: content
307310
}

view_scrollbar.v

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,19 @@ fn (cfg &ScrollbarCfg) mouse_move_locked(layout &Layout, mut e Event, mut w Wind
159159
&& e.mouse_x <= (ly.shape.x + ly.shape.width + extend) {
160160
offset := offset_mouse_change_x(ly, e.mouse_dx, cfg.id_track, w)
161161
w.view_state.scroll_x[cfg.id_track] = offset
162+
if ly.shape.on_scroll != unsafe { nil } {
163+
ly.shape.on_scroll(ly, mut w)
164+
}
162165
}
163166
}
164167
else {
165168
if e.mouse_y >= (ly.shape.y - extend)
166169
&& e.mouse_y <= (ly.shape.y + ly.shape.height + extend) {
167170
offset := offset_mouse_change_y(ly, e.mouse_dy, cfg.id_track, w)
168171
w.view_state.scroll_y[cfg.id_track] = offset
172+
if ly.shape.on_scroll != unsafe { nil } {
173+
ly.shape.on_scroll(ly, mut w)
174+
}
169175
}
170176
}
171177
}
@@ -323,6 +329,9 @@ fn offset_from_mouse_x(layout &Layout, mouse_x f32, id_scroll u32, mut w Window)
323329
percent = 1
324330
}
325331
w.view_state.scroll_x[id_scroll] = -percent * (total_width - sb.shape.width)
332+
if sb.shape.on_scroll != unsafe { nil } {
333+
sb.shape.on_scroll(sb, mut w)
334+
}
326335
}
327336
}
328337

@@ -346,6 +355,9 @@ fn offset_from_mouse_y(layout &Layout, mouse_y f32, id_scroll u32, mut w Window)
346355
percent = 1
347356
}
348357
w.view_state.scroll_y[id_scroll] = -percent * (total_height - sb.shape.height)
358+
if sb.shape.on_scroll != unsafe { nil } {
359+
sb.shape.on_scroll(sb, mut w)
360+
}
349361
}
350362
}
351363

@@ -365,6 +377,9 @@ fn scroll_horizontal(layout &Layout, delta f32, mut w Window) bool {
365377
max_offset := f32_min(0, layout.shape.width - layout.shape.padding.width() - content_width(layout))
366378
offset_x := w.view_state.scroll_x[v_id] + delta * gui_theme.scroll_multiplier
367379
w.view_state.scroll_x[v_id] = f32_clamp(offset_x, max_offset, 0)
380+
if layout.shape.on_scroll != unsafe { nil } {
381+
layout.shape.on_scroll(layout, mut w)
382+
}
368383
return true
369384
}
370385
return false
@@ -386,6 +401,9 @@ fn scroll_vertical(layout &Layout, delta f32, mut w Window) bool {
386401
max_offset := f32_min(0, layout.shape.height - layout.shape.padding.height() - content_height(layout))
387402
offset_y := w.view_state.scroll_y[v_id] + delta * gui_theme.scroll_multiplier
388403
w.view_state.scroll_y[v_id] = f32_clamp(offset_y, max_offset, 0)
404+
if layout.shape.on_scroll != unsafe { nil } {
405+
layout.shape.on_scroll(layout, mut w)
406+
}
389407
return true
390408
}
391409
return false

xtra_window.v

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ fn (mut window Window) update_window_size() {
179179
window.window_size = window.ui.window_size()
180180
}
181181

182+
// find_layout_by_id searches the layout tree for a layout with the given ID.
183+
pub fn (window &Window) find_layout_by_id(id string) ?Layout {
184+
return window.layout.find_by_id(id)
185+
}
186+
182187
// scroll_to_view scrolls the parent scroll container to make the view with the given id visible.
183188
pub fn (mut w Window) scroll_to_view(id string) {
184189
target := w.layout.find_by_id(id) or { return }

0 commit comments

Comments
 (0)