From be51e3723194f57d700138ad1d91d98c4a7f8466 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Mon, 20 Oct 2025 23:37:36 +0200 Subject: [PATCH] home and end --- lib/ugui.c3l/src/input.c3 | 10 +++--- lib/ugui.c3l/src/textedit.c3 | 63 +++++++++++++++++++++++++++++------- src/main.c3 | 6 ++-- 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/lib/ugui.c3l/src/input.c3 b/lib/ugui.c3l/src/input.c3 index 0721d36..5a134c8 100644 --- a/lib/ugui.c3l/src/input.c3 +++ b/lib/ugui.c3l/src/input.c3 @@ -44,11 +44,13 @@ bitstruct ModKeys : uint { bool scroll : 11; bool bkspc : 12; bool del : 13; + bool home : 14; + bool end : 15; // arrow keys - bool up : 14; - bool down : 15; - bool left : 16; - bool right : 17; + bool up : 16; + bool down : 17; + bool left : 18; + bool right : 19; } const ModKeys KMOD_CTRL = {.lctrl = true, .rctrl = true}; diff --git a/lib/ugui.c3l/src/textedit.c3 b/lib/ugui.c3l/src/textedit.c3 index 00937b1..b90ba16 100644 --- a/lib/ugui.c3l/src/textedit.c3 +++ b/lib/ugui.c3l/src/textedit.c3 @@ -10,8 +10,10 @@ struct TextEdit { } fn String TextEdit.to_string(&te) => (String)te.buffer[:te.chars]; -fn String TextEdit.until_cursor(&te) => (String)te.buffer[:te.cursor]; -fn String TextEdit.from_cursor(&te) => (String)te.buffer[te.cursor..]; +fn String TextEdit.until_cursor(&te) => (String)te.buffer[..te.cursor]; +fn String TextEdit.from_cursor(&te) => (String)te.buffer[te.cursor..te.chars]; +fn String TextEdit.until(&te, usz off) => (String)te.buffer[..min(te.chars, off)]; +fn String TextEdit.from(&te, usz off) => (String)te.buffer[off..te.chars]; // implement text editing operations on the buffer // returns true if the buffer is full @@ -22,17 +24,19 @@ fn bool Ctx.text_edit(&ctx, TextEdit* te) te.insert_utf8(in); + bool select = !!(mod & KMOD_SHIFT); + bool ctrl = !!(mod & KMOD_CTRL); // handle backspace and delete if (mod.bkspc) { - if (mod & KMOD_CTRL) { + if (ctrl) { te.remove_word(false); } else { te.remove_character(false); } } if (mod.del) { - if (mod & KMOD_CTRL) { + if (ctrl) { te.remove_word(true); } else { te.remove_character(true); @@ -41,23 +45,29 @@ fn bool Ctx.text_edit(&ctx, TextEdit* te) // handle arrow keys if (mod.left) { - if (mod & KMOD_CTRL) { - te.move_cursor_word(false, !!(mod & KMOD_SHIFT)); + if (ctrl) { + te.move_cursor_word(false, select); } else { - te.move_cursor(false, !!(mod & KMOD_SHIFT)); + te.move_cursor(false, select); } } if (mod.right) { - if (mod & KMOD_CTRL) { - te.move_cursor_word(true, !!(mod & KMOD_SHIFT)); + if (ctrl) { + te.move_cursor_word(true, select); } else { - te.move_cursor(true, !!(mod & KMOD_SHIFT)); + te.move_cursor(true, select); } } + if (mod.home) { + te.set_cursor(te.search_lines(false).first, select); + } + + if (mod.end) { + te.set_cursor(te.search_lines(true).first, select); + } + // TODO: up, down - // TODO: mod.home - // TODO: mod.end return te.chars < te.buffer.len; } @@ -66,6 +76,9 @@ module ugui::textedit::te; import std::core::string; import std::ascii; +import std::collections::pair; + +alias OffPair = pair::Pair{usz, usz}; // returns the offset of the next codepoint in the buffer from the cursor fn usz TextEdit.next_char_off(&te) @@ -160,6 +173,32 @@ fn void TextEdit.move_cursor_word(&te, bool forward, bool select) if (prev_cur == te.cursor) te.move_cursor(forward, select); } +// get the offset of the current line start and the previous line start from the cursor +fn OffPair TextEdit.search_lines(&te, bool forward) +{ + // look for the current line start + OffPair p; + + if (forward) { + // if searching forwards look for the end of the current line and the end of the next line + p.first = te.cursor + (te.from_cursor().index_of_char('\n') ?? (te.chars - te.cursor)); + if (p.first < te.chars) { + p.second = p.first + 1 + (te.from(p.first + 1).index_of_char('\n') ?? (te.chars - p.first - 1)); + } else { + p.second = te.chars; + } + } else { + // if searching backwards, look for the start of the current and previous line + if (te.cursor > 0) { + p.first = te.until(te.cursor-1).rindex_of_char('\n') ?? 0; + if (p.first != 0 && te.chars) p.first++; + } + p.second = te.until(p.first).rindex_of_char('\n') ?? 0; + } + + return p; +} + fn void TextEdit.delete_selection(&te) { if (te.sel_len == 0) return; diff --git a/src/main.c3 b/src/main.c3 index e911781..431f74d 100644 --- a/src/main.c3 +++ b/src/main.c3 @@ -155,6 +155,8 @@ fn int main(String[] args) mod.lshift = e.key.key == K_LSHIFT ? !!(e.type == EVENT_KEY_DOWN) : mod.lshift; mod.bkspc = e.key.key == K_BACKSPACE ? !!(e.type == EVENT_KEY_DOWN) : mod.bkspc; mod.del = e.key.key == K_DELETE ? !!(e.type == EVENT_KEY_DOWN) : mod.del; + mod.home = e.key.key == K_HOME ? !!(e.type == EVENT_KEY_DOWN) : mod.home; + mod.end = e.key.key == K_END ? !!(e.type == EVENT_KEY_DOWN) : mod.end; mod.up = e.key.key == K_UP ? !!(e.type == EVENT_KEY_DOWN) : mod.up; mod.down = e.key.key == K_DOWN ? !!(e.type == EVENT_KEY_DOWN) : mod.down; mod.left = e.key.key == K_LEFT ? !!(e.type == EVENT_KEY_DOWN) : mod.left; @@ -398,7 +400,7 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te) ui.button("Quattro")!!; ui.div_end()!!; } - + // ui input/output ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) { // center everything on the screen ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT) { @@ -469,7 +471,7 @@ fn void popup(ugui::Ctx* ui) { static bool toggle; static ugui::Point pos; - + if (toggle) { toggle = ui.popup_begin(pos, ugui::@fit(), ugui::@fit(), COLUMN)!!; ui.button("POP")!!;