Add select by words and select by paragraphs

This commit is contained in:
Yousef Khadadeh 2025-11-27 16:12:32 +03:00
parent da7f8af582
commit ae096b524e
3 changed files with 57 additions and 11 deletions

View file

@ -259,7 +259,7 @@ void TextBoxEditTool::edit_start(EditTool& editTool, const std::shared_ptr<DrawC
auto& textbox = a->textBox;
cur = std::make_shared<TextBox::Cursor>();
Vector2f textSelectPos = a->get_mouse_pos(drawP);
textbox->process_mouse_left_button(*cur, textSelectPos, true, false, false);
textbox->process_mouse_left_button(*cur, textSelectPos, 1, false, false);
prevData = get_all_data(a);
a->d.editing = true;
a->textBox->inputChangedTextBox = true;
@ -287,7 +287,7 @@ bool TextBoxEditTool::edit_update(const std::shared_ptr<DrawComponent>& comp) {
bool collidesWithBox = a->collides_with_cam_coords(drawP.world.drawData.cam.c, mousePointCollection);
input.text.set_rich_text_box_input(a->textBox, a->cursor, currentMods);
a->textBox->process_mouse_left_button(*a->cursor, a->get_mouse_pos(drawP), drawP.controls.leftClick && collidesWithBox, drawP.controls.leftClickHeld, input.key(InputManager::KEY_GENERIC_LSHIFT).held);
a->textBox->process_mouse_left_button(*a->cursor, a->get_mouse_pos(drawP), (drawP.controls.leftClick && collidesWithBox) ? drawP.world.main.input.mouse.leftClicks : 0, drawP.controls.leftClickHeld, input.key(InputManager::KEY_GENERIC_LSHIFT).held);
return true;
}

View file

@ -173,20 +173,58 @@ void TextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift,
inputChangedTextBox = true;
}
void TextBox::process_mouse_left_button(Cursor& cur, const Vector2f& pos, bool clicked, bool held, bool shift) {
if(clicked || held) {
void TextBox::process_mouse_left_button(Cursor& cur, const Vector2f& pos, int clickCount, bool held, bool shift) {
if(!held)
lastClicksWhileHeld = clickCount;
else
lastClicksWhileHeld = std::max(lastClicksWhileHeld, clickCount);
if(lastClicksWhileHeld || held) {
rebuild();
Cursor oldCursor = cur;
cur.pos = get_text_pos_closest_to_point(pos);
if(lastClicksCursorPos != cur.pos && clickCount) {
lastClicksWhileHeld = std::min(1, lastClicksWhileHeld);
lastClicksCursorPos = cur.pos;
}
cur.selectionBeginPos = cur.pos;
if(clicked && !shift)
cur.selectionEndPos = cur.selectionBeginPos;
if(lastClicksWhileHeld && !shift) {
if(!cur.selectionEndPosBeforeHeld.has_value()) {
cur.selectionEndPos = cur.selectionBeginPos;
cur.selectionEndPosBeforeHeld = cur.selectionEndPos;
}
if(lastClicksWhileHeld == 3) {
cur.selectionEndPos = cur.selectionEndPosBeforeHeld.value();
if(cur.selectionBeginPos < cur.selectionEndPos) {
cur.selectionBeginPos.fTextByteIndex = 0;
cur.selectionEndPos.fTextByteIndex = paragraphs[cur.selectionEndPos.fParagraphIndex].text.size();
}
else {
cur.selectionBeginPos.fTextByteIndex = paragraphs[cur.selectionBeginPos.fParagraphIndex].text.size();
cur.selectionEndPos.fTextByteIndex = 0;
}
}
else if(lastClicksWhileHeld == 2) {
cur.selectionEndPos = cur.selectionEndPosBeforeHeld.value();
if(cur.selectionBeginPos < cur.selectionEndPos) {
cur.selectionBeginPos = move(Movement::LEFT_WORD_TIGHT, cur.selectionBeginPos);
cur.selectionEndPos = move(Movement::RIGHT_WORD_TIGHT, cur.selectionEndPos);
}
else {
cur.selectionBeginPos = move(Movement::RIGHT_WORD_TIGHT, cur.selectionBeginPos);
cur.selectionEndPos = move(Movement::LEFT_WORD_TIGHT, cur.selectionEndPos);
}
}
}
cur.previousX = std::nullopt;
inputChangedTextBox |= (oldCursor != cur);
}
else
cur.selectionEndPosBeforeHeld = std::nullopt;
}
std::pair<std::string, TextData> TextBox::process_copy(Cursor& cur) {
@ -716,7 +754,8 @@ TextPosition TextBox::move(Movement movement, TextPosition pos, std::optional<fl
}
break;
}
case Movement::LEFT_WORD: {
case Movement::LEFT_WORD:
case Movement::LEFT_WORD_TIGHT: {
if(pos == TextPosition{0, 0})
break;
@ -737,12 +776,13 @@ TextPosition TextBox::move(Movement movement, TextPosition pos, std::optional<fl
const char* tPtr = fullText.c_str() + nextByteIndex;
SkUnichar u = SkUTF::NextUTF8(&tPtr, fullText.c_str() + fullText.size());
if(!SkUnicodes::ICU::Make()->isWhitespace(u))
if(!SkUnicodes::ICU::Make()->isWhitespace(u) || movement == Movement::LEFT_WORD_TIGHT)
byteIndexToRet = nextByteIndex;
}
break;
}
case Movement::RIGHT_WORD: {
case Movement::RIGHT_WORD:
case Movement::RIGHT_WORD_TIGHT: {
TextPosition endPos = move(Movement::END, {0, 0});
if(pos == endPos)
break;
@ -761,7 +801,7 @@ TextPosition TextBox::move(Movement movement, TextPosition pos, std::optional<fl
return get_text_pos_from_byte_pos(fullText, p);
const char* tPtr = fullText.c_str() + p;
SkUnichar u = SkUTF::NextUTF8(&tPtr, fullText.c_str() + fullText.size());
if(!SkUnicodes::ICU::Make()->isWhitespace(u))
if(!SkUnicodes::ICU::Make()->isWhitespace(u) || movement == Movement::RIGHT_WORD_TIGHT)
return get_text_pos_from_byte_pos(fullText, p);
}

View file

@ -71,7 +71,9 @@ class TextBox {
UP,
DOWN,
LEFT_WORD,
LEFT_WORD_TIGHT,
RIGHT_WORD,
RIGHT_WORD_TIGHT,
HOME,
END
};
@ -84,6 +86,7 @@ class TextBox {
a(pos, selectionBeginPos, selectionEndPos);
}
std::optional<float> previousX;
std::optional<TextPosition> selectionEndPosBeforeHeld;
bool operator==(const Cursor& o) const;
bool operator!=(const Cursor& o) const;
};
@ -135,7 +138,7 @@ class TextBox {
void clear_text();
void set_string(const std::string& str);
void process_mouse_left_button(Cursor& cur, const Vector2f& pos, bool clicked, bool held, bool shift);
void process_mouse_left_button(Cursor& cur, const Vector2f& pos, int clickCount, bool held, bool shift);
void process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift, const std::optional<TextStyleModifier::ModifierMap>& inputModMap = std::nullopt);
std::pair<std::string, TextData> process_copy(Cursor& cur);
std::pair<std::string, TextData> process_cut(Cursor& cur);
@ -183,6 +186,9 @@ class TextBox {
unsigned tabWidth = 4;
std::shared_ptr<FontData> fontData;
int lastClicksWhileHeld = 0;
TextPosition lastClicksCursorPos{0, 0};
struct ParagraphData {
std::string text;
std::unique_ptr<skia::textlayout::Paragraph> p;