Skip to content

Commit 81eb31c

Browse files
committed
Refactor cursor handling and modifier logic
- Simplified modifier combinations by replacing `has_all` usage with direct equality checks (e.g., `.ctrl_shift`). - Renamed cursor-related methods for clarity and alignment with functionality (`cursor_start_of_word`, `cursor_end_of_line`, etc.). - Consolidated cursor blink logic by replacing `input_cursor_on_sticky` with `cursor_on_sticky`. - Removed redundant word and line position functions and replaced them with more streamlined implementations. - Updated test cases to reflect method name changes and ensure functionality accuracy. - Added `test_count_chars` to validate character counting logic in arrays.
1 parent 8c1a597 commit 81eb31c

File tree

10 files changed

+354
-318
lines changed

10 files changed

+354
-318
lines changed

event.v

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -129,27 +129,28 @@ pub enum MouseCursor as u8 {
129129
}
130130

131131
pub enum Modifier as u32 {
132-
none = 0
133-
shift = 1 //(1<<0)
134-
ctrl = 2 //(1<<1)
135-
alt = 4 //(1<<2)
136-
super = 8 //(1<<3)
137-
lmb = 0x100
138-
rmb = 0x200
139-
mmb = 0x400
132+
none = 0
133+
shift = 1 //(1<<0)
134+
ctrl = 2 //(1<<1)
135+
alt = 4 //(1<<2)
136+
super = 8 //(1<<3)
137+
lmb = 0x100
138+
rmb = 0x200
139+
mmb = 0x400
140+
ctrl_shift = 2 | 1
141+
ctrl_alt = 2 | 4
142+
ctrl_alt_shift = 2 | 4 | 1
143+
ctrl_super = 2 | 8
144+
alt_shift = 4 | 1
145+
alt_super = 4 | 8
146+
super_shift = 8 | 1
140147
}
141148

142149
// has checks if the current modifier bitmask contains the specified modifier.
143-
// (same as has_all() but reads better when testing only one modifier)
144150
pub fn (m Modifier) has(modifier Modifier) bool {
145151
return u32(m) & u32(modifier) > 0 || m == modifier // .none case
146152
}
147153

148-
// has_all checks if the current modifier bitmask contains all of the specified modifiers.
149-
pub fn (m Modifier) has_all(modifiers ...Modifier) bool {
150-
return modifiers.all(u32(m) & u32(it) > 0 || m == it) // .none case
151-
}
152-
153154
// has_any checks if the current modifier bitmask contains at least one of the specified modifiers.
154155
pub fn (m Modifier) has_any(modifiers ...Modifier) bool {
155156
return modifiers.any(u32(m) & u32(it) > 0 || m == it) // .none case

event_test.v

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,6 @@ fn test_modifier_has() {
1818
}
1919
}
2020

21-
fn test_modifier_has_all() {
22-
assert Modifier.none.has_all(Modifier.none)
23-
assert !Modifier.none.has_all(Modifier.shift)
24-
assert !Modifier.none.has_all(Modifier.ctrl, Modifier.shift)
25-
assert Modifier.shift.has_all(Modifier.shift)
26-
assert !Modifier.shift.has_all(Modifier.ctrl)
27-
28-
unsafe {
29-
assert Modifier(u32(Modifier.shift) | u32(Modifier.ctrl)).has_all(.shift, .ctrl)
30-
assert !Modifier(u32(Modifier.alt) | u32(Modifier.ctrl)).has_all(.shift, .ctrl)
31-
}
32-
}
33-
3421
fn test_modifier_has_any() {
3522
assert Modifier.none.has_any(.none)
3623
assert !Modifier.none.has_any(.shift)

examples/doc_viewer.v

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ pub mut:
2424

2525
fn main() {
2626
mut window := gui.window(
27-
state: &DocViewerApp{}
28-
width: 950
29-
height: 850
30-
title: 'Doc Viewer'
31-
on_init: fn (mut w gui.Window) {
27+
state: &DocViewerApp{}
28+
width: 950
29+
height: 850
30+
title: 'Doc Viewer'
31+
cursor_blink: true
32+
on_init: fn (mut w gui.Window) {
3233
w.update_view(main_view)
3334
}
3435
)

view_input.v

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,12 @@ fn (cfg &InputCfg) on_char(layout &Layout, mut event Event, mut w Window) {
225225
c := event.char_code
226226
if cfg.on_text_changed != unsafe { nil } {
227227
mut text := cfg.text
228-
if event.modifiers.has_all(.ctrl, .shift) {
228+
if event.modifiers == .ctrl_shift {
229229
match c {
230230
ctrl_z { text = cfg.redo(mut w) }
231231
else {}
232232
}
233-
} else if event.modifiers.has_all(.super, .shift) {
233+
} else if event.modifiers == .super_shift {
234234
match c {
235235
cmd_z { text = cfg.redo(mut w) }
236236
else {}
@@ -466,11 +466,6 @@ pub fn (cfg &InputCfg) undo(mut w Window) string {
466466
return memento.text
467467
}
468468

469-
// redo reapplies a previously undone operation by popping the last operation
470-
// from the redo stack and pushing the current state onto the undo stack.
471-
// Returns the text content from the restored state. If the redo stack is
472-
// empty, returns the current text unchanged. The function restores cursor
473-
// position, selection range, and text content from the saved memento.
474469
pub fn (cfg &InputCfg) redo(mut w Window) string {
475470
input_state := w.view_state.input_state[cfg.id_focus]
476471
mut redo := input_state.redo

view_state.v

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@ import sokol.sapp
66
// store view states. This is gui's solution.
77
struct ViewState {
88
mut:
9-
input_state map[u32]InputState // [id_focus] -> InputState
10-
input_date_state map[string]bool // [id] -> visible
11-
scroll_x map[u32]f32 // [id_scroll] -> scroll offset x
12-
scroll_y map[u32]f32 // [id_scroll] -> scroll offset y
13-
text_widths map[u32]f32 // [text + hash(text_style)] -> text width
14-
mouse_cursor sapp.MouseCursor // arrow, finger, ibeam, etc.
15-
menu_state map[u32]string // [id_menubar] -> id of menu
16-
menu_key_nav bool // true, menu navigated by keyboard
17-
image_map map[string]int // [file name] -> context.cache image id
18-
select_state map[string]bool // [id select] -> open/close state
19-
tree_state map[string]map[string]bool // [tree id] -> [node id ] -> open/closed
20-
date_picker_state map[string]DatePickerState // [id date_picker -> DatePickerState
21-
mouse_lock MouseLockCfg // mouse down/move/up methods to call when locked
22-
id_focus u32 // current view that has focus
23-
input_cursor_on_sticky bool // keeps the cursor visible during cursor movement
24-
input_cursor_on bool = true // used by cursor blink animation
9+
input_state map[u32]InputState // [id_focus] -> InputState
10+
input_date_state map[string]bool // [id] -> visible
11+
scroll_x map[u32]f32 // [id_scroll] -> scroll offset x
12+
scroll_y map[u32]f32 // [id_scroll] -> scroll offset y
13+
text_widths map[u32]f32 // [text + hash(text_style)] -> text width
14+
mouse_cursor sapp.MouseCursor // arrow, finger, ibeam, etc.
15+
menu_state map[u32]string // [id_menubar] -> id of menu
16+
menu_key_nav bool // true, menu navigated by keyboard
17+
image_map map[string]int // [file name] -> context.cache image id
18+
select_state map[string]bool // [id select] -> open/close state
19+
tree_state map[string]map[string]bool // [tree id] -> [node id ] -> open/closed
20+
date_picker_state map[string]DatePickerState // [id date_picker -> DatePickerState
21+
mouse_lock MouseLockCfg // mouse down/move/up methods to call when locked
22+
id_focus u32 // current view that has focus
23+
cursor_on_sticky bool // keeps the cursor visible during cursor movement
24+
input_cursor_on bool = true // used by cursor blink animation
2525
}
2626

2727
fn (mut vs ViewState) clear(mut w Window) {

view_text.v

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -307,49 +307,48 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut w Window) {
307307
text_lines := layout.shape.text_lines
308308

309309
// Handle navigation with modifiers
310-
if e.modifiers == .alt || e.modifiers.has_all(.alt, .shift) {
310+
if e.modifiers == .alt || e.modifiers == .alt_shift {
311311
// Alt: Jump by word or paragraph
312312
match e.key_code {
313-
.left { new_cursor_pos = start_of_word_pos(text_lines, new_cursor_pos) }
314-
.right { new_cursor_pos = end_of_word_pos(text_lines, new_cursor_pos) }
315-
.up { new_cursor_pos = start_of_paragraph(text_lines, new_cursor_pos) }
313+
.left { new_cursor_pos = cursor_start_of_word(text_lines, new_cursor_pos) }
314+
.right { new_cursor_pos = cursor_end_of_word(text_lines, new_cursor_pos) }
315+
.up { new_cursor_pos = cursor_start_of_paragraph(text_lines, new_cursor_pos) }
316316
else { return }
317317
}
318-
} else if e.modifiers == .ctrl || e.modifiers.has_all(.ctrl, .shift) {
318+
} else if e.modifiers == .ctrl || e.modifiers == .ctrl_shift {
319319
// Ctrl: Jump to start/end of line
320320
match e.key_code {
321-
.left { new_cursor_pos = start_of_line_pos(text_lines, new_cursor_pos) }
322-
.right { new_cursor_pos = end_of_line_pos(text_lines, new_cursor_pos) }
321+
.left { new_cursor_pos = cursor_start_of_line(text_lines, new_cursor_pos) }
322+
.right { new_cursor_pos = cursor_end_of_line(text_lines, new_cursor_pos) }
323323
else { return }
324324
}
325325
} else if e.modifiers.has_any(.none, .shift) {
326326
// Standard navigation: char by char, prev/next line, home/end of text
327327
match e.key_code {
328-
.left { new_cursor_pos = int_max(0, new_cursor_pos - 1) }
329-
.right { new_cursor_pos = int_min(count_chars(text_lines), new_cursor_pos + 1) }
328+
.left { new_cursor_pos = cursor_left(new_cursor_pos) }
329+
.right { new_cursor_pos = cursor_right(text_lines, new_cursor_pos) }
330330
.up { new_cursor_pos = cursor_up(text_lines, new_cursor_pos) }
331331
.down { new_cursor_pos = cursor_down(text_lines, new_cursor_pos) }
332-
.home { new_cursor_pos = 0 }
333-
.end { new_cursor_pos = utf8_str_visible_length(tv.text) }
332+
.home { new_cursor_pos = cursor_home() }
333+
.end { new_cursor_pos = cursor_end(text_lines) }
334334
else { return }
335335
}
336336
} else if e.modifiers == Modifier.super {
337337
return
338338
}
339339

340-
// Sticky allows the cursor to stay on during cursor movements.
340+
// input_cursor_on_sticky allows the cursor to stay on during cursor movements.
341341
// See `blinky_cursor_animation()`
342342
if new_cursor_pos != current_input_state.cursor_pos {
343-
w.view_state.input_cursor_on_sticky = true
343+
w.view_state.cursor_on_sticky = true
344344
}
345345

346-
e.is_handled = true
347-
mut new_select_beg := u32(0)
348-
mut new_select_end := u32(0)
349-
350346
// ================================
351347
// shift => Extend/shrink selection
352348
// ================================
349+
mut new_select_beg := u32(0)
350+
mut new_select_end := u32(0)
351+
353352
if e.modifiers.has(.shift) {
354353
old_cursor_pos := current_input_state.cursor_pos
355354
new_select_beg = current_input_state.select_beg
@@ -400,6 +399,7 @@ fn (tv &TextView) on_key_down(layout &Layout, mut e Event, mut w Window) {
400399

401400
// Ensure the new cursor position is visible
402401
scroll_cursor_into_view(new_cursor_pos, layout, e, mut w)
402+
e.is_handled = true
403403
}
404404
}
405405

@@ -498,9 +498,10 @@ pub fn (tv &TextView) select_all(shape &Shape, mut w Window) {
498498
len := utf8_str_visible_length(tv.text)
499499
w.view_state.input_state[tv.id_focus] = InputState{
500500
...input_state
501-
cursor_pos: len
502-
select_beg: 0
503-
select_end: u32(len)
501+
cursor_pos: len
502+
select_beg: 0
503+
select_end: u32(len)
504+
cursor_offset: 0
504505
}
505506
}
506507

@@ -511,8 +512,9 @@ pub fn (tv &TextView) unselect_all(mut w Window) {
511512
input_state := w.view_state.input_state[tv.id_focus]
512513
w.view_state.input_state[tv.id_focus] = InputState{
513514
...input_state
514-
cursor_pos: 0
515-
select_beg: 0
516-
select_end: 0
515+
cursor_pos: 0
516+
select_beg: 0
517+
select_end: 0
518+
cursor_offset: 0
517519
}
518520
}

0 commit comments

Comments
 (0)