@@ -241,48 +241,56 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut window Window) {
241241 return
242242 }
243243 mut input_state := window.view_state.input_state[layout.shape.id_focus]
244- mut position := input_state.cursor_pos
244+ mut pos := input_state.cursor_pos
245245 mut offset := input_state.cursor_offset
246246 text_lines := layout.shape.text_lines
247247
248248 // Handle navigation with modifiers
249249 if e.modifiers == .alt || e.modifiers == .alt_shift {
250250 // Alt: Jump by word or paragraph
251251 match e.key_code {
252- .left { position = cursor_start_of_word (text_lines, position ) }
253- .right { position = cursor_end_of_word (text_lines, position ) }
254- .up { position = cursor_start_of_paragraph (text_lines, position ) }
252+ .left { pos = cursor_start_of_word (text_lines, pos ) }
253+ .right { pos = cursor_end_of_word (text_lines, pos ) }
254+ .up { pos = cursor_start_of_paragraph (text_lines, pos ) }
255255 else { return }
256256 }
257257 } else if e.modifiers == .ctrl || e.modifiers == .ctrl_shift {
258258 // Ctrl: Jump to start/end of line
259259 match e.key_code {
260- .left { position = cursor_start_of_line (text_lines, position ) }
261- .right { position = cursor_end_of_line (text_lines, position ) }
260+ .left { pos = cursor_start_of_line (text_lines, pos ) }
261+ .right { pos = cursor_end_of_line (text_lines, pos ) }
262262 else { return }
263263 }
264264 } else if e.modifiers.has_any (.none , .shift) {
265265 // Standard navigation: char by char, prev/next line, home/end of text
266+ mut lpp := 0 // lines per page
267+ layout_scroll := find_layout_by_id_scroll (window.layout, layout.shape.id_scroll_container)
268+ if layout_scroll != none {
269+ layout_scroll_height := layout_scroll.shape.height - layout_scroll.shape.padding.height ()
270+ lpp = int (layout_scroll_height / line_height (layout.shape))
271+ }
266272 match e.key_code {
267- .left { position = cursor_left (position) }
268- .right { position = cursor_right (text_lines, position) }
269- .up { position = cursor_up (layout.shape, position, offset, window) }
270- .down { position = cursor_down (layout.shape, position, offset, window) }
271- .home { position = cursor_home () }
272- .end { position = cursor_end (text_lines) }
273+ .left { pos = cursor_left (pos) }
274+ .right { pos = cursor_right (text_lines, pos) }
275+ .up { pos = cursor_up (layout.shape, pos, offset, 1 , window) }
276+ .down { pos = cursor_down (layout.shape, pos, offset, 1 , window) }
277+ .page_up { pos = cursor_up (layout.shape, pos, offset, lpp, window) }
278+ .page_down { pos = cursor_down (layout.shape, pos, offset, lpp, window) }
279+ .home { pos = cursor_home () }
280+ .end { pos = cursor_end (text_lines) }
273281 else { return }
274282 }
275283 } else if e.modifiers == Modifier.super {
276284 return
277285 }
278286
279287 if (e.key_code != .up && e.key_code != .down) || input_state.cursor_offset < 0 {
280- offset = offset_from_cursor_position (layout.shape, position , window)
288+ offset = offset_from_cursor_position (layout.shape, pos , window)
281289 }
282290
283291 // input_cursor_on_sticky allows the cursor to stay on during cursor movements.
284292 // See `blinky_cursor_animation()`
285- if position != input_state.cursor_pos {
293+ if pos != input_state.cursor_pos {
286294 window.view_state.cursor_on_sticky = true
287295 }
288296
@@ -305,16 +313,16 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut window Window) {
305313
306314 // Move the selection boundary that was at the old cursor position.
307315 if old_cursor_pos == int (select_beg) {
308- select_beg = u32 (position )
316+ select_beg = u32 (pos )
309317 } else if old_cursor_pos == int (select_end) {
310- select_end = u32 (position )
318+ select_end = u32 (pos )
311319 } else {
312320 // If the old cursor was not at a boundary (e.g., from a click),
313321 // move the boundary closest to the new cursor position.
314- if math.abs (position - int (select_beg)) < math.abs (position - int (select_end)) {
315- select_beg = u32 (position )
322+ if math.abs (pos - int (select_beg)) < math.abs (pos - int (select_end)) {
323+ select_beg = u32 (pos )
316324 } else {
317- select_end = u32 (position )
325+ select_end = u32 (pos )
318326 }
319327 }
320328 // Ensure beg is always less than or equal to end
@@ -324,24 +332,24 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut window Window) {
324332 } else if input_state.select_beg != input_state.select_end && e.modifiers == .none {
325333 // If a selection exists and a non-shift movement key is pressed,
326334 // collapse the selection to the beginning or end of the selection.
327- position = match e.key_code {
335+ pos = match e.key_code {
328336 .left, .home { int (input_state.select_beg) }
329337 .right, .end { int (input_state.select_end) }
330- else { position }
338+ else { pos }
331339 }
332340 }
333341
334342 // Update input state with new cursor position and selection
335343 window.view_state.input_state[layout.shape.id_focus] = InputState{
336344 ...input_state
337- cursor_pos: position
345+ cursor_pos: pos
338346 select_beg: select_beg
339347 select_end: select_end
340348 cursor_offset: offset
341349 }
342350
343351 // Ensure the new cursor position is visible
344- scroll_cursor_into_view (position , layout, mut window)
352+ scroll_cursor_into_view (pos , layout, mut window)
345353 e.is_handled = true
346354 }
347355}
0 commit comments