Skip to content

Commit a163c6d

Browse files
committed
Add detailed documentation for cursor navigation and text manipulation functions
- Enhanced comments across `delete`, `insert`, `redo`, and cursor-related utility functions for improved clarity. - Addressed cursor offset handling during vertical navigation to ensure computation consistency. - Improved logic for backspace and delete operations, including state management and selection clearing. - Streamlined handling for cursor column alignment in wrapped text during vertical movement.
1 parent 13e37f8 commit a163c6d

File tree

4 files changed

+83
-22
lines changed

4 files changed

+83
-22
lines changed

view_input.v

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,14 @@ fn (cfg &InputCfg) on_char(layout &Layout, mut event Event, mut w Window) {
288288
}
289289
}
290290

291+
// delete removes text from the input field based on the cursor position or
292+
// selected text range. If text is selected, it deletes the entire selection.
293+
// Otherwise, it deletes the character before the cursor (backspace) when
294+
// is_delete is false, or the character after the cursor (delete key) when
295+
// is_delete is true. The function saves the current state to the undo stack
296+
// before performing the deletion, updates the cursor position, and clears any
297+
// text selection. Returns the modified text string, or none if the operation
298+
// cannot be performed (e.g., cursor at start with backspace).
291299
fn (cfg &InputCfg) delete(mut w Window, is_delete bool) ?string {
292300
mut text := cfg.text.runes()
293301
input_state := w.view_state.input_state[cfg.id_focus]
@@ -329,14 +337,23 @@ fn (cfg &InputCfg) delete(mut w Window, is_delete bool) ?string {
329337
select_end: input_state.select_end
330338
})
331339
w.view_state.input_state[cfg.id_focus] = InputState{
332-
cursor_pos: cursor_pos
333-
select_beg: 0
334-
select_end: 0
335-
undo: undo
340+
cursor_pos: cursor_pos
341+
select_beg: 0
342+
select_end: 0
343+
undo: undo
344+
cursor_offset: -1 // view_text.v-on_key_down-up/down handler tests for < 0
336345
}
337346
return text.string()
338347
}
339348

349+
// insert adds text to the input field at the current cursor position or
350+
// replaces the currently selected text. For single-line fixed-width inputs,
351+
// it validates that the inserted text fits within the available width. If
352+
// text is selected, the selection is replaced with the inserted text. The
353+
// function saves the current state to the undo stack before performing the
354+
// insertion, updates the cursor position to after the inserted text, and
355+
// clears any text selection. Returns the modified text string, or an error
356+
// if the cursor position is out of range.
340357
fn (cfg &InputCfg) insert(s string, mut w Window) !string {
341358
// clamp max chars to width of box when single line fixed.
342359
if cfg.mode == .single_line && cfg.sizing.width == .fixed {
@@ -376,12 +393,12 @@ fn (cfg &InputCfg) insert(s string, mut w Window) !string {
376393
select_beg: input_state.select_beg
377394
select_end: input_state.select_end
378395
})
379-
offset := offset_from_cursor_position()
380396
w.view_state.input_state[cfg.id_focus] = InputState{
381-
cursor_pos: cursor_pos
382-
select_beg: 0
383-
select_end: 0
384-
undo: undo
397+
cursor_pos: cursor_pos
398+
select_beg: 0
399+
select_end: 0
400+
undo: undo
401+
cursor_offset: -1 // view_text.v-on_key_down-up/down handler tests for < 0
385402
}
386403
return text.string()
387404
}
@@ -464,6 +481,12 @@ pub fn (cfg &InputCfg) undo(mut w Window) string {
464481
return memento.text
465482
}
466483

484+
// redo re-applies a previously undone operation by popping the last state from
485+
// the redo stack and pushing the current state onto the undo stack. Returns
486+
// the text content from the restored state. If the redo stack is empty, returns
487+
// the current text unchanged. The function restores cursor position, selection
488+
// range, and text content from the saved memento, effectively reversing an undo
489+
// operation.
467490
pub fn (cfg &InputCfg) redo(mut w Window) string {
468491
input_state := w.view_state.input_state[cfg.id_focus]
469492
mut redo := input_state.redo

view_text.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut window Window) {
340340
return
341341
}
342342

343-
if e.key_code != .up && e.key_code != .down {
343+
if (e.key_code != .up && e.key_code != .down) || input_state.cursor_offset < 0 {
344344
offset = offset_from_cursor_position(layout.shape, position, window)
345345
}
346346

xtra_text.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,8 @@ pub fn to_clipboard(s ?string) bool {
411411
return false
412412
}
413413

414+
// count_chars returns the total number of visible characters across all
415+
// strings in the array, used for cursor positioning in wrapped text.
414416
fn count_chars(strs []string) int {
415417
mut count := 0
416418
for str in strs {

xtra_text_cursor.v

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
module gui
22

3+
// cursor_left moves the cursor position one character to the left in the text.
4+
// It decrements the position by one, but ensures the result never goes below
5+
// zero, effectively preventing the cursor from moving before the start of the
6+
// text. Returns the new cursor position.
37
fn cursor_left(pos int) int {
48
return int_max(0, pos - 1)
59
}
610

11+
// cursor_right moves the cursor position one character to the right in wrapped
12+
// text. It increments the position by one, but ensures the result never exceeds
13+
// the total character count of all text lines combined, effectively preventing
14+
// the cursor from moving beyond the end of the text. Returns the new cursor
15+
// position.
716
fn cursor_right(strs []string, pos int) int {
817
return int_min(count_chars(strs), pos + 1)
918
}
1019

1120
// cursor_up moves the cursor position up one line in wrapped text, attempting
12-
// to maintain the same horizontal pixel offset. It locates the current line,
13-
// moves to the previous line, and uses the cursor_offset to find the corresponding
14-
// column position. If the previous line is shorter than the calculated column,
15-
// the cursor moves to the end of that line. Returns the original position if
16-
// already at the first line.
21+
// to maintain the same horizontal pixel offset. It locates the current line by
22+
// iterating through the wrapped text lines, moves to the previous line, and
23+
// uses the cursor_offset to find the corresponding column position. If the
24+
// cursor_offset is invalid (less than zero, typically -1), it is recomputed
25+
// from the current cursor position. If the previous line is shorter than the
26+
// calculated column, the cursor moves to the end of that line. Returns the
27+
// original position if already at the first line.
1728
fn cursor_up(shape Shape, cursor_pos int, cursor_offset f32, window &Window) int {
1829
mut idx := 0
1930
mut offset := 0
@@ -39,7 +50,15 @@ fn cursor_up(shape Shape, cursor_pos int, cursor_offset f32, window &Window) int
3950
}
4051
}
4152

42-
cursor_column := cursor_position_from_offset(shape.text_lines[p_idx], cursor_offset,
53+
// An offset of less than zero (usually -1) indicates that the offset was
54+
// invalidated, likely because of an edit operation like insert/delete.
55+
// Compute a new offset based on the current cursor position
56+
c_offset := match cursor_offset >= 0 {
57+
true { cursor_offset }
58+
else { offset_from_cursor_position(shape, cursor_pos, window) }
59+
}
60+
61+
cursor_column := cursor_position_from_offset(shape.text_lines[p_idx], c_offset,
4362
shape.text_style, window)
4463

4564
new_cursor_position := match true {
@@ -53,11 +72,14 @@ fn cursor_up(shape Shape, cursor_pos int, cursor_offset f32, window &Window) int
5372
}
5473

5574
// cursor_down moves the cursor position down one line in wrapped text, attempting
56-
// to maintain the same horizontal pixel offset. It locates the current line,
57-
// moves to the next line, and uses the cursor_offset to find the corresponding
58-
// column position. If the next line is shorter than the calculated column,
59-
// the cursor moves to the end of that line. Returns the original position if
60-
// already at the last line.
75+
// to maintain the same horizontal pixel offset. It locates the current line by
76+
// iterating through the wrapped text lines, moves to the next line, and uses
77+
// the cursor_offset to find the corresponding column position. If the cursor_offset
78+
// is invalid (less than zero, typically -1), it is recomputed from the current
79+
// cursor position. If the next line is shorter than the calculated column, the
80+
// cursor position is adjusted: for lines ending with a newline, it moves to the
81+
// position before the newline; for the last line, it moves to the end of the line.
82+
// Returns the original position if already at the last line.
6183
fn cursor_down(shape Shape, cursor_pos int, cursor_offset f32, window &Window) int {
6284
mut idx := 0
6385
mut offset := 0
@@ -84,7 +106,15 @@ fn cursor_down(shape Shape, cursor_pos int, cursor_offset f32, window &Window) i
84106
}
85107
}
86108

87-
cursor_column := cursor_position_from_offset(n_text, cursor_offset, shape.text_style,
109+
// An offset of less than zero (usually -1) indicates that the offset was
110+
// invalidated, likely because of an edit operation like insert/delete.
111+
// Compute a new offset based on the current cursor position
112+
c_offset := match cursor_offset >= 0 {
113+
true { cursor_offset }
114+
else { offset_from_cursor_position(shape, cursor_pos, window) }
115+
}
116+
117+
cursor_column := cursor_position_from_offset(n_text, c_offset, shape.text_style,
88118
window)
89119

90120
new_cursor_position := match true {
@@ -101,10 +131,16 @@ fn cursor_down(shape Shape, cursor_pos int, cursor_offset f32, window &Window) i
101131
return cursor_pos
102132
}
103133

134+
// cursor_home moves the cursor to the beginning of the text by returning
135+
// position 0. This is equivalent to the "Home" key behavior, placing the
136+
// cursor at the start of the entire text content.
104137
fn cursor_home() int {
105138
return 0
106139
}
107140

141+
// cursor_end moves the cursor to the end of the text by returning the total
142+
// character count across all wrapped text lines. This is equivalent to the
143+
// "End" key behavior, placing the cursor at the end of the entire text content.
108144
fn cursor_end(strs []string) int {
109145
return count_chars(strs)
110146
}

0 commit comments

Comments
 (0)