Skip to content

Commit 85f1842

Browse files
committed
up cursor nav in view_text. Column oriented, not pixel offfset oriented for now
1 parent 81eb31c commit 85f1842

File tree

3 files changed

+64
-28
lines changed

3 files changed

+64
-28
lines changed

view_input.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ pub:
3131
select_end u32
3232
undo datatypes.Stack[InputMemento]
3333
redo datatypes.Stack[InputMemento]
34-
// cursor_offset is used to maintain the horizontal offset of the cursor
34+
// cursor_column is used to maintain the horizontal offset of the cursor
3535
// when traversing vertically through text. It is reset when a non-vertical
3636
// navigation operation occurs.
37-
cursor_offset f32
37+
cursor_column int
3838
}
3939

4040
// InputMemento is admittedly a naive implementation of undo/redo operations.

view_text.v

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,14 @@ fn (tv &TextView) on_click(layout &Layout, mut e Event, mut w Window) {
156156
)
157157
// Set cursor position and reset text selection
158158
cursor_pos := tv.mouse_cursor_pos(layout.shape, e, mut w)
159+
cursor_column := get_cursor_column(layout.shape.text_lines, cursor_pos)
159160
input_state := w.view_state.input_state[layout.shape.id_focus]
160161
w.view_state.input_state[layout.shape.id_focus] = InputState{
161162
...input_state
162-
cursor_pos: cursor_pos
163-
select_beg: 0
164-
select_end: 0
163+
cursor_pos: cursor_pos
164+
select_beg: 0
165+
select_end: 0
166+
cursor_column: cursor_column
165167
}
166168
e.is_handled = true
167169
}
@@ -297,13 +299,14 @@ fn (tv &TextView) mouse_cursor_pos(shape &Shape, e &Event, mut w Window) int {
297299
// on_key_down handles keyboard input for navigation and text selection.
298300
// It supports standard navigation keys (arrows, home, end) and modifiers
299301
// (Alt, Ctrl, Shift) for word/line jumping and selection extension.
300-
fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut w Window) {
301-
if w.is_focus(layout.shape.id_focus) {
302+
fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut window Window) {
303+
if window.is_focus(layout.shape.id_focus) {
302304
if tv.placeholder_active {
303305
return
304306
}
305-
mut current_input_state := w.view_state.input_state[layout.shape.id_focus]
307+
mut current_input_state := window.view_state.input_state[layout.shape.id_focus]
306308
mut new_cursor_pos := current_input_state.cursor_pos
309+
mut new_cursor_column := current_input_state.cursor_column
307310
text_lines := layout.shape.text_lines
308311

309312
// Handle navigation with modifiers
@@ -327,8 +330,8 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut w Window) {
327330
match e.key_code {
328331
.left { new_cursor_pos = cursor_left(new_cursor_pos) }
329332
.right { new_cursor_pos = cursor_right(text_lines, new_cursor_pos) }
330-
.up { new_cursor_pos = cursor_up(text_lines, new_cursor_pos) }
331-
.down { new_cursor_pos = cursor_down(text_lines, new_cursor_pos) }
333+
.up { new_cursor_pos = cursor_up(text_lines, new_cursor_pos, new_cursor_column) }
334+
.down { new_cursor_pos = cursor_down(text_lines, new_cursor_pos, new_cursor_column) }
332335
.home { new_cursor_pos = cursor_home() }
333336
.end { new_cursor_pos = cursor_end(text_lines) }
334337
else { return }
@@ -337,10 +340,14 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut w Window) {
337340
return
338341
}
339342

343+
if e.key_code != .up && e.key_code != .down {
344+
new_cursor_column = get_cursor_column(text_lines, new_cursor_pos)
345+
}
346+
340347
// input_cursor_on_sticky allows the cursor to stay on during cursor movements.
341348
// See `blinky_cursor_animation()`
342349
if new_cursor_pos != current_input_state.cursor_pos {
343-
w.view_state.cursor_on_sticky = true
350+
window.view_state.cursor_on_sticky = true
344351
}
345352

346353
// ================================
@@ -390,15 +397,16 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut w Window) {
390397
}
391398

392399
// Update input state with new cursor position and selection
393-
w.view_state.input_state[layout.shape.id_focus] = InputState{
400+
window.view_state.input_state[layout.shape.id_focus] = InputState{
394401
...current_input_state
395-
cursor_pos: new_cursor_pos
396-
select_beg: new_select_beg
397-
select_end: new_select_end
402+
cursor_pos: new_cursor_pos
403+
select_beg: new_select_beg
404+
select_end: new_select_end
405+
cursor_column: new_cursor_column
398406
}
399407

400408
// Ensure the new cursor position is visible
401-
scroll_cursor_into_view(new_cursor_pos, layout, e, mut w)
409+
scroll_cursor_into_view(new_cursor_pos, layout, e, mut window)
402410
e.is_handled = true
403411
}
404412
}
@@ -501,7 +509,7 @@ pub fn (tv &TextView) select_all(shape &Shape, mut w Window) {
501509
cursor_pos: len
502510
select_beg: 0
503511
select_end: u32(len)
504-
cursor_offset: 0
512+
cursor_column: len
505513
}
506514
}
507515

@@ -515,6 +523,6 @@ pub fn (tv &TextView) unselect_all(mut w Window) {
515523
cursor_pos: 0
516524
select_beg: 0
517525
select_end: 0
518-
cursor_offset: 0
526+
cursor_column: 0
519527
}
520528
}

xtra_text_cursor.v

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,25 @@ fn cursor_right(strs []string, pos int) int {
88
return int_min(count_chars(strs), pos + 1)
99
}
1010

11-
fn cursor_up(strs []string, pos int) int {
11+
// cursor_up moves the cursor position up one line in wrapped text, attempting
12+
// to maintain the same column position. If the previous line is shorter than
13+
// the current column, the cursor moves to the end of that line.
14+
fn cursor_up(strs []string, cursor_pos int, cursor_column int) int {
1215
mut idx := 0
1316
mut offset := 0
1417
lengths := strs.map(utf8_str_visible_length(it))
1518

1619
// find which line to from
1720
for i, len in lengths {
1821
idx = i
19-
if offset + len > pos {
22+
if offset + len > cursor_pos {
2023
break
2124
}
2225
offset += len
2326
}
2427

2528
// move to previous line
2629
if idx > 0 {
27-
col := pos - offset
2830
p_idx := idx - 1
2931
p_len := lengths[p_idx]
3032
mut p_start := 0
@@ -33,28 +35,33 @@ fn cursor_up(strs []string, pos int) int {
3335
p_start += len
3436
}
3537
}
36-
return if col >= p_len { p_start + p_len - 1 } else { p_start + col }
38+
39+
new_cursor_position := match true {
40+
cursor_column <= p_len { p_start + cursor_column }
41+
else { p_start + p_len - 1 }
42+
}
43+
44+
return new_cursor_position
3745
}
38-
return pos
46+
return cursor_pos
3947
}
4048

41-
fn cursor_down(strs []string, pos int) int {
49+
fn cursor_down(strs []string, cursor_position int, cursor_column int) int {
4250
mut idx := 0
4351
mut offset := 0
4452
lengths := strs.map(utf8_str_visible_length(it))
4553

4654
// find which line to from
4755
for i, len in lengths {
4856
idx = i
49-
if offset + len > pos {
57+
if offset + len > cursor_position {
5058
break
5159
}
5260
offset += len
5361
}
5462

5563
// move to next line
5664
if idx < strs.len - 1 {
57-
col := pos - offset
5865
n_idx := idx + 1
5966
n_len := lengths[n_idx]
6067
mut n_start := 0
@@ -63,9 +70,15 @@ fn cursor_down(strs []string, pos int) int {
6370
n_start += len
6471
}
6572
}
66-
return if col >= n_len { n_start + n_len - 1 } else { n_start + col }
73+
74+
new_cursor_position := match true {
75+
cursor_column <= n_len { n_start + cursor_column }
76+
else { n_start + n_len - 1 }
77+
}
78+
79+
return new_cursor_position
6780
}
68-
return pos
81+
return cursor_position
6982
}
7083

7184
fn cursor_home() int {
@@ -233,3 +246,18 @@ fn cursor_start_of_paragraph(strs []string, pos int) int {
233246

234247
return int_max(i + len, 0)
235248
}
249+
250+
fn get_cursor_column(strs []string, cursor_pos int) int {
251+
mut len := 0
252+
for str in strs {
253+
str_len := utf8_str_visible_length(str)
254+
if len + str_len < cursor_pos {
255+
len += str_len
256+
continue
257+
}
258+
break
259+
}
260+
cursor_column := cursor_pos - len
261+
assert cursor_column >= 0
262+
return cursor_column
263+
}

0 commit comments

Comments
 (0)