From 47939c052724259d2b000fa3f620cbb06ebc8c61 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 14 Aug 2025 02:24:48 +0300 Subject: [PATCH] feat: textbox indentation manipulations using tab, shift+tab --- src/graphics/ui/elements/TextBox.cpp | 78 +++++++++++++++++++++++++++- src/graphics/ui/elements/TextBox.hpp | 2 + 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 0ed75882..34e2784a 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -810,6 +810,82 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) { } } +static int calc_indent(int linestart, std::wstring_view input) { + int indent = 0; + while (linestart + indent < input.length() && + input[linestart + indent] == L' ') + indent++; + return indent; +} + +void TextBox::onTab(bool shiftPressed) { + std::wstring indentStr = L" "; + + if (!shiftPressed && getSelectionLength() == 0) { + paste(indentStr); + return; + } + if (getSelectionLength() == 0) { + selectionStart = caret; + selectionEnd = caret; + selectionOrigin = caret; + } + + int lineA = getLineAt(selectionStart); + int lineB = getLineAt(selectionEnd); + int caretLine = getLineAt(caret); + + size_t lineAStart = getLinePos(lineA); + size_t lineBStart = getLinePos(lineB); + size_t caretLineStart = getLinePos(caretLine); + size_t caretIndent = calc_indent(caretLineStart, input); + size_t aIndent = calc_indent(lineAStart, input); + size_t bIndent = calc_indent(lineBStart, input); + + int lastSelectionStart = selectionStart; + int lastSelectionEnd = selectionEnd; + size_t lastCaret = caret; + + resetSelection(); + + for (int line = lineA; line <= lineB; line++) { + size_t linestart = getLinePos(line); + int indent = calc_indent(linestart, input); + + if (shiftPressed) { + if (indent >= indentStr.length()) { + setCaret(linestart); + select(linestart, linestart + indentStr.length()); + eraseSelected(); + } + } else { + setCaret(linestart); + paste(indentStr); + } + refreshLabel(); // todo: replace with textbox cache + } + + int linestart = getLinePos(caretLine); + int linestartA = getLinePos(lineA); + int linestartB = getLinePos(lineB); + int la = lastSelectionStart - lineAStart; + int lb = lastSelectionEnd - lineBStart; + if (shiftPressed) { + setCaret(lastCaret - caretLineStart + linestart - std::min(caretIndent, indentStr.length())); + selectionStart = la + linestartA - std::min(std::min(la, aIndent), indentStr.length()); + selectionEnd = lb + linestartB - std::min(std::min(lb, bIndent), indentStr.length()); + } else { + setCaret(lastCaret - caretLineStart + linestart + indentStr.length()); + selectionStart = la + linestartA + indentStr.length(); + selectionEnd = lb + linestartB + indentStr.length(); + } + if (selectionOrigin == lastSelectionStart) { + selectionOrigin = selectionStart; + } else { + selectionOrigin = selectionEnd; + } +} + void TextBox::refreshSyntax() { if (!syntax.empty()) { const auto& processor = gui.getEditor().getSyntaxProcessor(); @@ -868,7 +944,7 @@ void TextBox::performEditingKeyboardEvents(Keycode key) { } } } else if (key == Keycode::TAB) { - paste(L" "); + onTab(shiftPressed); } else if (key == Keycode::LEFT) { stepLeft(shiftPressed, breakSelection); } else if (key == Keycode::RIGHT) { diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index 321d71eb..9bbfac39 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -71,6 +71,8 @@ namespace gui { void stepDefaultDown(bool shiftPressed, bool breakSelection); void stepDefaultUp(bool shiftPressed, bool breakSelection); + void onTab(bool shiftPressed); + size_t normalizeIndex(int index); int calcIndexAt(int x, int y) const;