From e0143f49f03ac1baf945cbd998acaf953c6b1143 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 9 Nov 2025 16:24:40 +0300 Subject: [PATCH] Text modifiers now apply to text that will be inserted later --- src/DrawingProgram/TextBoxEditTool.cpp | 97 +++++++++++++++++--------- src/DrawingProgram/TextBoxEditTool.hpp | 8 ++- src/InputManager.cpp | 30 ++++---- src/InputManager.hpp | 5 +- src/RichTextBox/RichTextBox.cpp | 18 +++-- src/RichTextBox/RichTextBox.hpp | 6 +- 6 files changed, 106 insertions(+), 58 deletions(-) diff --git a/src/DrawingProgram/TextBoxEditTool.cpp b/src/DrawingProgram/TextBoxEditTool.cpp index aea25b1..235e0ef 100644 --- a/src/DrawingProgram/TextBoxEditTool.cpp +++ b/src/DrawingProgram/TextBoxEditTool.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -24,40 +25,50 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr& comp) { t.gui.push_id("edit tool text"); t.gui.text_label_centered("Edit Text"); - newFontName = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::FONT_FAMILIES])->get_families().back().c_str(); - t.gui.left_to_right_line_layout([&]() { t.gui.text_label("Font"); - if(t.gui.font_picker("font picker", &newFontName)) - a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, std::make_shared(std::vector{SkString{newFontName.c_str(), newFontName.size()}})); + if(t.gui.font_picker("font picker", &newFontName)) { + currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES] = std::make_shared(std::vector{SkString{newFontName.c_str(), newFontName.size()}}); + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES]); + } }); t.gui.left_to_right_line_centered_layout([&]() { - bool isBold = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::WEIGHT])->get_weight() == SkFontStyle::Weight::kBold_Weight; - if(t.gui.svg_icon_button("Bold button", "data/icons/RemixIcon/bold.svg", isBold)) - a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, std::make_shared(isBold ? SkFontStyle::Weight::kNormal_Weight : SkFontStyle::Weight::kBold_Weight)); + if(t.gui.svg_icon_button("Bold button", "data/icons/RemixIcon/bold.svg", newIsBold)) { + newIsBold = !newIsBold; + currentMods[TextStyleModifier::ModifierType::WEIGHT] = std::make_shared(newIsBold ? SkFontStyle::Weight::kBold_Weight : SkFontStyle::Weight::kNormal_Weight); + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::WEIGHT]); + } - bool isItalic = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::SLANT])->get_slant() == SkFontStyle::Slant::kItalic_Slant; - if(t.gui.svg_icon_button("Italic button", "data/icons/RemixIcon/italic.svg", isItalic)) - a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, std::make_shared(isItalic ? SkFontStyle::Slant::kUpright_Slant : SkFontStyle::Slant::kItalic_Slant)); + if(t.gui.svg_icon_button("Italic button", "data/icons/RemixIcon/italic.svg", newIsItalic)) { + newIsItalic = !newIsItalic; + currentMods[TextStyleModifier::ModifierType::SLANT] = std::make_shared(newIsItalic ? SkFontStyle::Slant::kItalic_Slant : SkFontStyle::Slant::kUpright_Slant); + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::SLANT]); + } - uint8_t currentDecorationValue = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::DECORATION])->get_decoration_value(); - bool isUnderlined = currentDecorationValue & skia::textlayout::TextDecoration::kUnderline; - if(t.gui.svg_icon_button("Underline button", "data/icons/RemixIcon/underline.svg", isUnderlined)) - a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, std::make_shared(isUnderlined ? (currentDecorationValue & ~static_cast(skia::textlayout::TextDecoration::kUnderline)) : currentDecorationValue | static_cast(skia::textlayout::TextDecoration::kUnderline))); - - bool isLinethrough = currentDecorationValue & skia::textlayout::TextDecoration::kLineThrough; - if(t.gui.svg_icon_button("Strikethrough button", "data/icons/RemixIcon/strikethrough.svg", isLinethrough)) - a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, std::make_shared(isLinethrough ? (currentDecorationValue & ~static_cast(skia::textlayout::TextDecoration::kLineThrough)) : currentDecorationValue | static_cast(skia::textlayout::TextDecoration::kLineThrough))); - - bool isOverline = currentDecorationValue & skia::textlayout::TextDecoration::kOverline; - if(t.gui.svg_icon_button("Overline button", "data/icons/RemixIcon/overline.svg", isOverline)) - a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, std::make_shared(isOverline ? (currentDecorationValue & ~static_cast(skia::textlayout::TextDecoration::kOverline)) : currentDecorationValue | static_cast(skia::textlayout::TextDecoration::kOverline))); + bool decorationEdited = false; + if(t.gui.svg_icon_button("Underline button", "data/icons/RemixIcon/underline.svg", newIsUnderlined)) { + newIsUnderlined = !newIsUnderlined; + decorationEdited = true; + } + if(t.gui.svg_icon_button("Strikethrough button", "data/icons/RemixIcon/strikethrough.svg", newIsLinethrough)) { + newIsLinethrough = !newIsLinethrough; + decorationEdited = true; + } + if(t.gui.svg_icon_button("Overline button", "data/icons/RemixIcon/overline.svg", newIsOverline)) { + newIsOverline = !newIsOverline; + decorationEdited = true; + } + if(decorationEdited) { + currentMods[TextStyleModifier::ModifierType::DECORATION] = std::make_shared(get_new_decoration_value()); + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::DECORATION]); + } }); - newFontSize = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::SIZE])->get_size(); - if(t.gui.slider_scalar_field("Font Size Slider", "Font Size", &newFontSize, 3, 100)) - a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, std::make_shared(newFontSize)); + if(t.gui.slider_scalar_field("Font Size Slider", "Font Size", &newFontSize, 3, 100)) { + currentMods[TextStyleModifier::ModifierType::SIZE] = std::make_shared(newFontSize); + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::SIZE]); + } t.gui.left_to_right_line_layout([&]() { CLAY({.layout = {.sizing = {.width = CLAY_SIZING_FIXED(40), .height = CLAY_SIZING_FIXED(40)}}}) { @@ -67,15 +78,26 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr& comp) { } t.gui.text_label("Text Color"); }); - if((&newTextColor == t.colorRight) && t.isUpdatingColorRight) - a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, std::make_shared(newTextColor)); - newTextColor = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::COLOR])->get_color(); + if((&newTextColor == t.colorRight) && t.isUpdatingColorRight) { + currentMods[TextStyleModifier::ModifierType::COLOR] = std::make_shared(newTextColor); + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::COLOR]); + } + t.gui.pop_id(); - if(a->textBox->inputChangedTextBox) + if(a->textBox->inputChangedTextBox) { set_mods_at_selection(a); + newFontName = std::static_pointer_cast(currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES])->get_families().back().c_str(); + newFontSize = std::static_pointer_cast(currentMods[TextStyleModifier::ModifierType::SIZE])->get_size(); + newTextColor = std::static_pointer_cast(currentMods[TextStyleModifier::ModifierType::COLOR])->get_color(); + newIsBold = std::static_pointer_cast(currentMods[TextStyleModifier::ModifierType::WEIGHT])->get_weight() == SkFontStyle::Weight::kBold_Weight; + newIsItalic = std::static_pointer_cast(currentMods[TextStyleModifier::ModifierType::SLANT])->get_slant() == SkFontStyle::Slant::kItalic_Slant; + newIsUnderlined = std::static_pointer_cast(currentMods[TextStyleModifier::ModifierType::DECORATION])->get_decoration_value() & skia::textlayout::TextDecoration::kUnderline; + newIsLinethrough = std::static_pointer_cast(currentMods[TextStyleModifier::ModifierType::DECORATION])->get_decoration_value() & skia::textlayout::TextDecoration::kLineThrough; + newIsOverline = std::static_pointer_cast(currentMods[TextStyleModifier::ModifierType::DECORATION])->get_decoration_value() & skia::textlayout::TextDecoration::kOverline; + } // NOTE: There should be a system that periodically sends updates even if no changes are made, since unreliable channels can drop update data bool oldInputChangedTextBox = a->textBox->inputChangedTextBox; @@ -83,13 +105,24 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr& comp) { return oldInputChangedTextBox; } +uint8_t TextBoxEditTool::get_new_decoration_value() { + uint8_t toRet = skia::textlayout::TextDecoration::kNoDecoration; + if(newIsUnderlined) + toRet |= skia::textlayout::TextDecoration::kUnderline; + if(newIsLinethrough) + toRet |= skia::textlayout::TextDecoration::kLineThrough; + if(newIsOverline) + toRet |= skia::textlayout::TextDecoration::kOverline; + return toRet; +} + void TextBoxEditTool::set_mods_at_selection(const std::shared_ptr& a) { RichTextBox::TextPosition start = std::min(a->cursor->selectionBeginPos, a->cursor->selectionEndPos); RichTextBox::TextPosition end = std::max(a->cursor->selectionBeginPos, a->cursor->selectionEndPos); if(start == end) - modsAtStartOfSelection = a->textBox->get_mods_used_at_pos(a->textBox->move(RichTextBox::Movement::LEFT, start)); + currentMods = a->textBox->get_mods_used_at_pos(a->textBox->move(RichTextBox::Movement::LEFT, start)); else - modsAtStartOfSelection = a->textBox->get_mods_used_at_pos(start); + currentMods = a->textBox->get_mods_used_at_pos(start); } void TextBoxEditTool::commit_edit_updates(const std::shared_ptr& comp, std::any& prevData) { @@ -159,7 +192,7 @@ bool TextBoxEditTool::edit_update(const std::shared_ptr& 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); + 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); return true; diff --git a/src/DrawingProgram/TextBoxEditTool.hpp b/src/DrawingProgram/TextBoxEditTool.hpp index 4e3ac92..39b62df 100644 --- a/src/DrawingProgram/TextBoxEditTool.hpp +++ b/src/DrawingProgram/TextBoxEditTool.hpp @@ -22,10 +22,16 @@ class TextBoxEditTool : public DrawingProgramEditToolBase { uint32_t newFontSize = 15; Vector4f newTextColor{1.0f, 1.0f, 1.0f, 1.0f}; + bool newIsBold = false; + bool newIsItalic = false; + bool newIsUnderlined = false; + bool newIsLinethrough = false; + bool newIsOverline = false; std::string newFontName; + uint8_t get_new_decoration_value(); void set_mods_at_selection(const std::shared_ptr& a); - TextStyleModifier::ModifierMap modsAtStartOfSelection; + TextStyleModifier::ModifierMap currentMods; std::vector sortedFontList; diff --git a/src/InputManager.cpp b/src/InputManager.cpp index 8576177..5aef68d 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -77,15 +77,16 @@ void InputManager::text_input_silence_everything() { text.newInput.clear(); } -void InputManager::Text::set_rich_text_box_input(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor) { +void InputManager::Text::set_rich_text_box_input(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, const std::optional& nModMap) { set_accepting_input(); newTextBox = nTextBox; newCursor = nCursor; + newModMap = nModMap; } void InputManager::Text::add_text_to_textbox(const std::string& inputText) { if(textBox) - textBox->process_text_input(*cursor, inputText); + textBox->process_text_input(*cursor, inputText, modMap); } void InputManager::Text::set_accepting_input() { @@ -218,42 +219,42 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) { case SDLK_UP: set_key_down(e, KEY_TEXT_UP); if(text.textBox && key(KEY_TEXT_UP).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::UP, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::UP, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_DOWN: set_key_down(e, KEY_TEXT_DOWN); if(text.textBox && key(KEY_TEXT_DOWN).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::DOWN, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::DOWN, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_LEFT: set_key_down(e, KEY_TEXT_LEFT); if(text.textBox && key(KEY_TEXT_LEFT).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::LEFT, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::LEFT, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_RIGHT: set_key_down(e, KEY_TEXT_RIGHT); if(text.textBox && key(KEY_TEXT_RIGHT).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::RIGHT, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::RIGHT, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_BACKSPACE: set_key_down(e, KEY_TEXT_BACKSPACE); if(text.textBox && key(KEY_TEXT_BACKSPACE).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::BACKSPACE, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::BACKSPACE, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_DELETE: set_key_down(e, KEY_TEXT_DELETE); if(text.textBox && key(KEY_TEXT_DELETE).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::DELETE, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::DELETE, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_HOME: set_key_down(e, KEY_TEXT_HOME); if(text.textBox && key(KEY_TEXT_HOME).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::HOME, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::HOME, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_END: set_key_down(e, KEY_TEXT_END); if(text.textBox && key(KEY_TEXT_END).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::END, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::END, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_LSHIFT: set_key_down(e, KEY_TEXT_SHIFT); @@ -287,24 +288,24 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) { if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL)) set_key_down(e, KEY_TEXT_PASTE); if(text.textBox && key(KEY_TEXT_PASTE).repeat) - text.textBox->process_text_input(*text.cursor, get_clipboard_str()); + text.textBox->process_text_input(*text.cursor, get_clipboard_str(), text.modMap); break; case SDLK_A: if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL)) set_key_down(e, KEY_TEXT_SELECTALL); if(text.textBox && key(KEY_TEXT_SELECTALL).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::SELECT_ALL, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::SELECT_ALL, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; case SDLK_RETURN: { set_key_down(e, KEY_TEXT_ENTER); if(text.textBox && key(KEY_TEXT_ENTER).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::ENTER, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::ENTER, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; } case SDLK_TAB: { set_key_down(e, KEY_TEXT_TAB); if(text.textBox && key(KEY_TEXT_TAB).repeat) - text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::TAB, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held); + text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::TAB, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap); break; } case SDLK_ESCAPE: @@ -417,6 +418,7 @@ void InputManager::frame_reset(const Vector2i& windowSize) { text.acceptingInputNew = false; text.cursor = text.newCursor; text.textBox = text.newTextBox; + text.modMap = text.newModMap; text.newCursor = nullptr; text.newTextBox = nullptr; diff --git a/src/InputManager.hpp b/src/InputManager.hpp index 9e44f5b..15139d8 100644 --- a/src/InputManager.hpp +++ b/src/InputManager.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include "RichTextBox/TextStyleModifier.hpp" #include "TimePoint.hpp" #include #include @@ -87,15 +88,17 @@ struct InputManager { void set_accepting_input(); bool get_accepting_input(); - void set_rich_text_box_input(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor); + void set_rich_text_box_input(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, const std::optional& nModMap = std::nullopt); void add_text_to_textbox(const std::string& inputText); private: std::shared_ptr textBox; std::shared_ptr cursor; + std::optional modMap; std::shared_ptr newTextBox; std::shared_ptr newCursor; + std::optional newModMap; bool acceptingInput = false; bool acceptingInputNew = false; diff --git a/src/RichTextBox/RichTextBox.cpp b/src/RichTextBox/RichTextBox.cpp index 9a5c1d1..1170cb6 100644 --- a/src/RichTextBox/RichTextBox.cpp +++ b/src/RichTextBox/RichTextBox.cpp @@ -61,7 +61,7 @@ RichTextBox::RichTextBox() { paragraphs.emplace_back(); } -void RichTextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift) { +void RichTextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift, const std::optional& inputModMap) { bool moved = false; switch(in) { @@ -102,10 +102,10 @@ void RichTextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool sh cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.pos, move(ctrl ? Movement::RIGHT_WORD : Movement::RIGHT, cur.pos)); break; case InputKey::ENTER: - process_text_input(cur, "\n"); + process_text_input(cur, "\n", inputModMap); break; case InputKey::TAB: - process_text_input(cur, "\t"); + process_text_input(cur, "\t", inputModMap); break; case InputKey::SELECT_ALL: cur.selectionBeginPos = move(Movement::HOME, cur.pos); @@ -152,11 +152,11 @@ std::string RichTextBox::process_cut(Cursor& cur) { return toRet; } -void RichTextBox::process_text_input(Cursor& cur, const std::string& in) { +void RichTextBox::process_text_input(Cursor& cur, const std::string& in, const std::optional& inputModMap) { if(!in.empty()) { if(cur.selectionBeginPos != cur.selectionEndPos) cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos); - cur.selectionEndPos = cur.selectionBeginPos = cur.pos = insert(cur.pos, in); + cur.selectionEndPos = cur.selectionBeginPos = cur.pos = insert(cur.pos, in, inputModMap); cur.previousX = std::nullopt; inputChangedTextBox = true; } @@ -704,8 +704,8 @@ void RichTextBox::set_tab_space_width(unsigned newTabWidth) { tabWidth = newTabWidth; } -RichTextBox::TextPosition RichTextBox::insert(TextPosition pos, std::string_view textToInsert) { - pos = move(Movement::NOWHERE, pos); +RichTextBox::TextPosition RichTextBox::insert(TextPosition pos, std::string_view textToInsert, const std::optional& inputModMap) { + TextPosition oldPos = pos = move(Movement::NOWHERE, pos); for(char c : textToInsert) { if(c == '\n') { @@ -745,6 +745,10 @@ RichTextBox::TextPosition RichTextBox::insert(TextPosition pos, std::string_view if(!textToInsert.empty()) { remove_duplicate_text_style_mods(); + if(inputModMap.has_value()) { + for(const auto& [modType, modifier] : inputModMap.value()) + set_text_style_modifier_between(oldPos, pos, modifier); + } inputChangedTextBox = true; needsRebuild = true; } diff --git a/src/RichTextBox/RichTextBox.hpp b/src/RichTextBox/RichTextBox.hpp index 5b87fc1..00f76b6 100644 --- a/src/RichTextBox/RichTextBox.hpp +++ b/src/RichTextBox/RichTextBox.hpp @@ -80,7 +80,7 @@ class RichTextBox { TextPosition move(Movement movement, TextPosition pos, std::optional* previousX = nullptr, bool flipDependingOnTextDirection = false); void set_width(float newWidth); - TextPosition insert(TextPosition pos, std::string_view textToInsert); + TextPosition insert(TextPosition pos, std::string_view textToInsert, const std::optional& inputModMap = std::nullopt); TextPosition remove(TextPosition p1, TextPosition p2); void set_font_collection(const sk_sp& fC); void paint(SkCanvas* canvas, const PaintOpts& paintOpts); @@ -111,13 +111,13 @@ class RichTextBox { 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_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift); + void process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift, const std::optional& inputModMap = std::nullopt); std::string process_copy(Cursor& cur); std::string process_cut(Cursor& cur); std::string get_text_between(TextPosition p1, TextPosition p2); std::string get_string(); void set_tab_space_width(unsigned newTabWidth); - void process_text_input(Cursor& cur, const std::string& in); + void process_text_input(Cursor& cur, const std::string& in, const std::optional& inputModMap = std::nullopt); private: static size_t count_grapheme(const std::string& text);