From 0d62f77aaa87f4c77442ecdf939c13affe28f738 Mon Sep 17 00:00:00 2001 From: Lenni0451 <20379977+Lenni0451@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:53:29 +0200 Subject: [PATCH 1/5] Fixed crash when using delete at the end of the text --- src/textbox_textview.v | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/textbox_textview.v b/src/textbox_textview.v index 22da224b..1f333dba 100644 --- a/src/textbox_textview.v +++ b/src/textbox_textview.v @@ -340,6 +340,9 @@ fn (mut tv TextView) insert(s string) { fn (mut tv TextView) delete_cur_char() { mut ustr := tv.text.runes() + if tv.cursor_pos >= ustr.len { + return + } ustr.delete(tv.cursor_pos) unsafe { *tv.text = ustr.string() From e93a43b8c1b049dcbd6a00601de505c22c59b9fd Mon Sep 17 00:00:00 2001 From: Lenni0451 <20379977+Lenni0451@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:00:18 +0200 Subject: [PATCH 2/5] Support delete entire words while pressing ctrl --- src/textbox_textview.v | 108 +++++++++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/src/textbox_textview.v b/src/textbox_textview.v index 1f333dba..072c2ac4 100644 --- a/src/textbox_textview.v +++ b/src/textbox_textview.v @@ -1,11 +1,13 @@ module ui import gx +import math // import time // import encoding.utf8 const textview_margin = 10 const wordwrap_border = 20 +const word_separator = ' \n\t\v\f\r' // position (cursor_pos, sel_start, sel_end) set in the runes world pub struct TextView { @@ -338,12 +340,46 @@ fn (mut tv TextView) insert(s string) { tv.update_lines() } -fn (mut tv TextView) delete_cur_char() { +// get the index of the word at the cursor position +// The start index is the index of the first character of the word +// The end index is after the last character of the word and may be out of array bounds +fn (mut tv TextView) get_word_bounds() (int, int) { mut ustr := tv.text.runes() - if tv.cursor_pos >= ustr.len { + if ustr.len == 0 { + return 0, 0 + } + mut start := tv.cursor_pos + mut end := tv.cursor_pos + + // find the start of the word + start_search: for start > 0 { + for sc in word_separator { + if ustr[start - 1] == sc { + break start_search + } + } + start-- + } + + // find the end of the word + end_search: for end < ustr.len { + for sc in word_separator { + if ustr[end] == sc { + break end_search + } + } + end++ + } + return start, end +} + +fn (mut tv TextView) delete_cur(count int) { + mut ustr := tv.text.runes() + total := math.min(count, ustr.len - tv.cursor_pos) + if total == 0 { return } - ustr.delete(tv.cursor_pos) + ustr.delete_many(tv.cursor_pos, total) unsafe { *tv.text = ustr.string() } @@ -351,14 +387,14 @@ fn (mut tv TextView) delete_cur_char() { tv.update_lines() } -fn (mut tv TextView) delete_prev_char() { - if tv.cursor_pos <= 0 { - tv.cursor_pos = 0 +fn (mut tv TextView) delete_prev(count int) { + mut ustr := tv.text.runes() + total := math.min(count, tv.cursor_pos) + if total == 0 { return } - mut ustr := tv.text.runes() - tv.cursor_pos-- - ustr.delete(tv.cursor_pos) + tv.cursor_pos -= total + ustr.delete_many(tv.cursor_pos, total) unsafe { *tv.text = ustr.string() } @@ -656,27 +692,50 @@ fn (mut tv TextView) key_down(e &KeyEvent) { if tv.is_sel_active() { tv.delete_selection() } else if e.mods in [.super, .ctrl] { - // Delete until previous whitespace - // mut i := tv.tlv.cursor_pos_i - // for { - // if i > 0 { - // i-- - // } - // if text[i].is_space() || i == 0 { - // // unsafe { *tb.text = u[..i) + u.right(tb.cursor_pos_i]} - // break - // } - // } - // tb.cursor_pos_i = i + // Delete until previous separator + word_start, _ := if ctrl_key(e.mods) { + tv.get_word_bounds() + } else { + 0, 0 + } + if word_start == tv.cursor_pos { + // Delete just one character (probably a separator) + tv.delete_prev(1) + } else { + tv.delete_prev(tv.cursor_pos - word_start) + } } else { // Delete just one character - tv.delete_prev_char() + tv.delete_prev(1) } tv.cursor_allways_visible() } .delete { tv.tb.ui.show_cursor = true - tv.delete_cur_char() + // println('backspace cursor_pos=($tv.tlv.cursor_pos_i, $tv.tlv.cursor_pos_j) len=${(*tv.text).len} \n <${*tv.text}>') + if *tv.text == '' { + return + } + // Delete the entire selection + if tv.is_sel_active() { + tv.delete_selection() + } else if e.mods in [.super, .ctrl] { + // Delete until previous separator + _, word_end := if ctrl_key(e.mods) { + tv.get_word_bounds() + } else { + 0, 0 + } + if word_end == tv.cursor_pos { + // Delete just one character (probably a separator) + tv.delete_cur(1) + } else { + tv.delete_cur(word_end - tv.cursor_pos) + } + } else { + // Delete just one character + tv.delete_cur(1) + } tv.cursor_allways_visible() } .left, .right, .up, .down { @@ -727,8 +786,7 @@ pub fn (mut tv TextView) do_indent(shift bool) { if shift { if tv.tlv.lines[j][..2] == ' ' { tv.sel_end -= 2 - tv.delete_cur_char() - tv.delete_cur_char() + tv.delete_cur(2) } } else { tv.sel_end += 2 From 232ffbdc3063cfc100d5898b2ce2d249a98e1d69 Mon Sep 17 00:00:00 2001 From: Lenni0451 <20379977+Lenni0451@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:02:00 +0200 Subject: [PATCH 3/5] Removed unused method --- src/textbox_textview.v | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/textbox_textview.v b/src/textbox_textview.v index 072c2ac4..acb71b21 100644 --- a/src/textbox_textview.v +++ b/src/textbox_textview.v @@ -402,34 +402,6 @@ fn (mut tv TextView) delete_prev(count int) { tv.update_lines() } -fn (mut tv TextView) delete_prev_word() { - if tv.cursor_pos <= 0 { - tv.cursor_pos = 0 - return - } - mut ustr := tv.text.runes() - // Delete until previous whitespace - // TODO!!!! - // mut i := tv.cursor_pos - // for { - // if i > 0 { - // i-- - // } - // if text[i].is_space() || i == 0 { - // // unsafe { *tb.text = u[..i) + u.right(tb.cursor_pos_i]} - // break - // } - // } - // tb.cursor_pos_i = i - tv.cursor_pos-- - ustr.delete(tv.cursor_pos) - unsafe { - *tv.text = ustr.string() - } - tv.refresh_visible_lines() - tv.update_lines() -} - fn (mut tv TextView) delete_selection() { // tv.info() if tv.sel_start > tv.sel_end { From 138f1f0d41a188643af876d98e030966685c74ed Mon Sep 17 00:00:00 2001 From: Lenni0451 <20379977+Lenni0451@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:18:36 +0200 Subject: [PATCH 4/5] Add quick selection of words when holding ctrl and shift --- src/textbox_textview.v | 43 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/textbox_textview.v b/src/textbox_textview.v index acb71b21..3bd0762c 100644 --- a/src/textbox_textview.v +++ b/src/textbox_textview.v @@ -710,10 +710,47 @@ fn (mut tv TextView) key_down(e &KeyEvent) { } tv.cursor_allways_visible() } - .left, .right, .up, .down { + .left, .right { + ustr := tv.text.runes() + word_start, word_end := if shift_key(e.mods) || ctrl_key(e.mods) { + tv.get_word_bounds() + } else { + // If shift and ctrl are not pressed, calculating the word bounds is not necessary + 0, 0 + } + + move_amount := if e.key == .left { + if ctrl_key(e.mods) && word_start < tv.cursor_pos { + -(tv.cursor_pos - word_start) + } else { + -1 + } + } else { + if ctrl_key(e.mods) && word_end > tv.cursor_pos { + word_end - tv.cursor_pos + } else { + 1 + } + } + move_target := math.min(math.max(tv.cursor_pos + move_amount, 0), ustr.len) + + if shift_key(e.mods) { + if !tv.is_sel_active() { + tv.tb.sel_active = true + tv.sel_start = tv.cursor_pos + tv.tb.ui.show_cursor = false + } + tv.cursor_pos = move_target + tv.sel_end = tv.cursor_pos + tv.sync_text_lines() + } else { + tv.cancel_selection() + tv.tb.ui.show_cursor = true + tv.cursor_pos = move_target + } + } + .up, .down { dir := match e.key { - .left { Side.left } - .right { Side.right } .up { Side.top } else { Side.bottom } } From 3bf4d6bc28a5474e697f67e7d3f7b98dbf11b4e4 Mon Sep 17 00:00:00 2001 From: Lenni0451 <20379977+Lenni0451@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:10:59 +0200 Subject: [PATCH 5/5] Fix formatting --- src/textbox_textview.v | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/textbox_textview.v b/src/textbox_textview.v index 3bd0762c..a94225ed 100644 --- a/src/textbox_textview.v +++ b/src/textbox_textview.v @@ -353,7 +353,7 @@ fn (mut tv TextView) get_word_bounds() (int, int) { // find the start of the word start_search: for start > 0 { - for sc in word_separator { + for sc in ui.word_separator { if ustr[start - 1] == sc { break start_search } @@ -363,7 +363,7 @@ fn (mut tv TextView) get_word_bounds() (int, int) { // find the end of the word end_search: for end < ustr.len { - for sc in word_separator { + for sc in ui.word_separator { if ustr[end] == sc { break end_search }