From 029b6c896626efd32474f34087aa45be4c85bb53 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Tue, 4 Nov 2025 19:04:09 +0300 Subject: [PATCH] Text styles can stack --- src/DrawingProgram/TextBoxEditTool.cpp | 10 ++++++ src/RichTextBox/RichTextBox.cpp | 45 +++++++++++++++++++++++--- src/RichTextBox/RichTextBox.hpp | 2 ++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/DrawingProgram/TextBoxEditTool.cpp b/src/DrawingProgram/TextBoxEditTool.cpp index 42160a2..6a7d9f1 100644 --- a/src/DrawingProgram/TextBoxEditTool.cpp +++ b/src/DrawingProgram/TextBoxEditTool.cpp @@ -32,6 +32,16 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr& comp) { redMod->color = {1.0f, 0.0f, 0.0f, 1.0f}; a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, redMod); } + if(t.gui.text_button_wide("Green button", "Green")) { + auto redMod = std::make_shared(); + redMod->color = {0.0f, 1.0f, 0.0f, 1.0f}; + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, redMod); + } + if(t.gui.text_button_wide("Blue button", "Blue")) { + auto redMod = std::make_shared(); + redMod->color = {0.0f, 0.0f, 1.0f, 1.0f}; + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, redMod); + } //t.gui.left_to_right_line_layout([&]() { // CLAY({.layout = {.sizing = {.width = CLAY_SIZING_FIXED(40), .height = CLAY_SIZING_FIXED(40)}}}) { // if(t.gui.color_button("Text Color", &a->d.textColor, &a->d.textColor == t.colorRight)) diff --git a/src/RichTextBox/RichTextBox.cpp b/src/RichTextBox/RichTextBox.cpp index 3869c7c..9f9af0b 100644 --- a/src/RichTextBox/RichTextBox.cpp +++ b/src/RichTextBox/RichTextBox.cpp @@ -37,6 +37,14 @@ bool RichTextBox::TextPosition::operator>(const RichTextBox::TextPosition& o) co return !(*this < o) && !(*this == o); } +bool RichTextBox::TextPosition::operator>=(const RichTextBox::TextPosition& o) const { + return (*this > o) || (*this == o); +} + +bool RichTextBox::TextPosition::operator<=(const RichTextBox::TextPosition& o) const { + return (*this < o) || (*this == o); +} + RichTextBox::RichTextBox() { paragraphs.emplace_back(); } @@ -317,7 +325,33 @@ void RichTextBox::set_text_style_modifier_between(TextPosition p1, TextPosition toPlace.start = std::min(p1, p2); toPlace.end = std::max(p1, p2); toPlace.modifier = modifier; + std::erase_if(tStyleModifiers, [&toPlace](TextStyleRangeModifier& tStyleMod) { + if(tStyleMod.modifier->equivalent(*toPlace.modifier)) { + // The equivalent modifiers collide, so merge them into one modifier + if((tStyleMod.start >= toPlace.start && tStyleMod.start <= toPlace.end) || (tStyleMod.end >= toPlace.start && tStyleMod.end <= toPlace.end) || + (toPlace.start >= tStyleMod.start && toPlace.start <= tStyleMod.end) || (toPlace.end >= tStyleMod.start && toPlace.end <= tStyleMod.end)) { + toPlace.start = std::min(toPlace.start, tStyleMod.start); + toPlace.end = std::max(toPlace.end, tStyleMod.end); + return true; + } + } + return false; + }); + std::erase_if(tStyleModifiers, [&toPlace](TextStyleRangeModifier& tStyleMod) { + if(tStyleMod.modifier->get_type() == toPlace.modifier->get_type()) { + bool tStyleModStartInToPlace = (tStyleMod.start >= toPlace.start && tStyleMod.start <= toPlace.end); + bool tStyleModEndInToPlace = (tStyleMod.end >= toPlace.start && tStyleMod.end <= toPlace.end); + if(tStyleModStartInToPlace && tStyleModEndInToPlace) + return true; + else if(tStyleModStartInToPlace) + tStyleMod.start = toPlace.end; + else if(tStyleModEndInToPlace) + tStyleMod.end = toPlace.start; + } + return false; + }); tStyleModifiers.emplace_back(toPlace); + fit_text_style_ranges_in_text(); needsRebuild = true; } } @@ -535,7 +569,7 @@ void RichTextBox::rebuild() { } auto nextStackCommandIt = modifierStackCommands.begin(); - std::unordered_map>> modifierStacks; + std::unordered_map>> modifierStacks; // Not really a stack, but we use the last element as the modifier skia::textlayout::TextStyle tStyle = initialTStyle; @@ -566,17 +600,18 @@ void RichTextBox::rebuild() { // Modify stack with new data for(const std::shared_ptr& modifier : nextStackCommand.second) { auto& modStack = modifierStacks[modifier->get_type()]; - if(modStack.empty() || modStack.top() != modifier) - modStack.emplace(modifier); + auto foundInStackIt = std::find(modStack.begin(), modStack.end(), modifier); + if(foundInStackIt == modStack.end()) + modStack.emplace_back(modifier); else - modStack.pop(); + modStack.erase(foundInStackIt); } // Modify style with new stacks tStyle = initialTStyle; for(auto& [modType, modifier] : modifierStacks) { if(!modifier.empty()) - modifier.top()->modify_text_style(tStyle); + modifier.back()->modify_text_style(tStyle); } ++nextStackCommandIt; diff --git a/src/RichTextBox/RichTextBox.hpp b/src/RichTextBox/RichTextBox.hpp index 2248b82..5963e17 100644 --- a/src/RichTextBox/RichTextBox.hpp +++ b/src/RichTextBox/RichTextBox.hpp @@ -34,6 +34,8 @@ class RichTextBox { bool operator==(const TextPosition& o) const = default; bool operator<(const TextPosition& o) const; bool operator>(const TextPosition& o) const; + bool operator>=(const RichTextBox::TextPosition& o) const; + bool operator<=(const RichTextBox::TextPosition& o) const; }; struct TextStyleRangeModifier {