From bc8bd93e3b43ae9d1274e1e97029a0d88d229076 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Wed, 5 Nov 2025 14:55:22 +0300 Subject: [PATCH] Added font size modifier to edit GUI --- src/DrawComponents/DrawTextBox.cpp | 2 +- src/DrawingProgram/TextBoxEditTool.cpp | 33 +++++++++++++------------- src/DrawingProgram/TextBoxEditTool.hpp | 2 ++ src/GUIStuff/Elements/NumberSlider.hpp | 22 ++++++++++------- src/GUIStuff/Elements/TextBox.hpp | 24 ++++++++++++------- src/GUIStuff/GUIManager.cpp | 6 +++-- src/GUIStuff/GUIManager.hpp | 31 ++++++++++++++---------- src/RichTextBox/RichTextBox.cpp | 16 ++++++------- 8 files changed, 80 insertions(+), 56 deletions(-) diff --git a/src/DrawComponents/DrawTextBox.cpp b/src/DrawComponents/DrawTextBox.cpp index d62bc34..1a53de0 100644 --- a/src/DrawComponents/DrawTextBox.cpp +++ b/src/DrawComponents/DrawTextBox.cpp @@ -21,7 +21,7 @@ DrawTextBox::DrawTextBox(): textBox->set_initial_text_style_modifier(colorMod); auto textSizeMod = std::make_shared(); - textSizeMod->size = 16.0f; + textSizeMod->size = 18.0f; textBox->set_initial_text_style_modifier(textSizeMod); auto fontFamilyMod = std::make_shared(); diff --git a/src/DrawingProgram/TextBoxEditTool.cpp b/src/DrawingProgram/TextBoxEditTool.cpp index 4007167..30cda9e 100644 --- a/src/DrawingProgram/TextBoxEditTool.cpp +++ b/src/DrawingProgram/TextBoxEditTool.cpp @@ -31,21 +31,13 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr& comp) { italicMod->set_slant(isItalic ? SkFontStyle::Slant::kUpright_Slant : SkFontStyle::Slant::kItalic_Slant); a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, italicMod); } - if(t.gui.text_button_wide("Red button", "Red")) { - auto redMod = std::make_shared(); - 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); + newFontSize = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::SIZE])->size; + if(t.gui.slider_scalar_field("Font Size Slider", "Font Size", &newFontSize, 3, 100)) { + auto sizeMod = std::make_shared(); + sizeMod->size = newFontSize; + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, sizeMod); } + //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)) @@ -56,7 +48,7 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr& comp) { t.gui.pop_id(); if(a->textBox->inputChangedTextBox) - modsAtStartOfSelection = a->textBox->get_mods_used_at_pos(std::min(a->cursor->selectionBeginPos, a->cursor->selectionEndPos)); + set_mods_at_selection(a); // 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; @@ -64,6 +56,15 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr& comp) { return oldInputChangedTextBox; } +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)); + else + modsAtStartOfSelection = a->textBox->get_mods_used_at_pos(start); +} + void TextBoxEditTool::commit_edit_updates(const std::shared_ptr& comp, std::any& prevData) { std::shared_ptr a = std::static_pointer_cast(comp); @@ -112,7 +113,7 @@ void TextBoxEditTool::edit_start(EditTool& editTool, const std::shared_ptrcommit_update(drawP); a->client_send_update(drawP, false); - modsAtStartOfSelection = a->textBox->get_mods_used_at_pos(std::min(a->cursor->selectionBeginPos, a->cursor->selectionEndPos)); + set_mods_at_selection(a); editTool.add_point_handle({&a->d.p1, nullptr, &a->d.p2}); editTool.add_point_handle({&a->d.p2, &a->d.p1, nullptr}); diff --git a/src/DrawingProgram/TextBoxEditTool.hpp b/src/DrawingProgram/TextBoxEditTool.hpp index 7d2f6bf..1925707 100644 --- a/src/DrawingProgram/TextBoxEditTool.hpp +++ b/src/DrawingProgram/TextBoxEditTool.hpp @@ -19,7 +19,9 @@ class TextBoxEditTool : public DrawingProgramEditToolBase { DrawTextBox::Data textboxData; RichTextBox::RichTextData richText; }; + uint32_t newFontSize; + void set_mods_at_selection(const std::shared_ptr& a); TextStyleModifier::ModifierMap modsAtStartOfSelection; TextBoxEditToolAllData get_all_data(const std::shared_ptr& a); diff --git a/src/GUIStuff/Elements/NumberSlider.hpp b/src/GUIStuff/Elements/NumberSlider.hpp index a70d1aa..2f45e94 100644 --- a/src/GUIStuff/Elements/NumberSlider.hpp +++ b/src/GUIStuff/Elements/NumberSlider.hpp @@ -5,8 +5,9 @@ namespace GUIStuff { template class NumberSlider : public Element { public: - void update(UpdateInputData& io, T* dataPtr, T minNew, T maxNew, const std::function& elemUpdate) { - data = dataPtr; + bool update(UpdateInputData& io, T* data, T minNew, T maxNew, const std::function& elemUpdate) { + bool isUpdating = false; + min = minNew; max = maxNew; @@ -19,17 +20,22 @@ template class NumberSlider : public Element { selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld); if(selection.held && data) { float fracPosOnSlider = (io.mouse.pos.x() - bb.min.x()) / bb.width(); - *data = std::clamp(std::lerp(min, max, fracPosOnSlider), min, max); + *data = static_cast(std::clamp(std::lerp(min, max, fracPosOnSlider), min, max)); // Clamp as double then cast so that unsigned types dont wrap on clamp + isUpdating = true; } if(elemUpdate) elemUpdate(); } + + if(data) + cachedData = *data; + else + cachedData = 0.0; + + return isUpdating; } virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) override { - if(!data) - return; - bb = get_bb(command); canvas->save(); @@ -47,7 +53,7 @@ template class NumberSlider : public Element { const float yChange = bb.height() * 0.5f - holderRadius * 0.5f; - float holderPos = lerp_time(*data, max, min) * bb.width(); + float holderPos = lerp_time(cachedData, max, min) * bb.width(); SkRect barFull = SkRect::MakeXYWH(0.0f, yChange, holderPos, holderRadius); SkRect barEmpty = SkRect::MakeXYWH(holderPos, yChange, bb.width() - holderPos, holderRadius); @@ -80,9 +86,9 @@ template class NumberSlider : public Element { SCollision::AABB bb; SelectionHelper selection; - T* data = nullptr; T min = 0.0; T max = 1.0; + T cachedData = 0.0; float hoverAnimation = 0.0; float holdAnimation = 0.0; diff --git a/src/GUIStuff/Elements/TextBox.hpp b/src/GUIStuff/Elements/TextBox.hpp index 37226eb..aca75ee 100644 --- a/src/GUIStuff/Elements/TextBox.hpp +++ b/src/GUIStuff/Elements/TextBox.hpp @@ -8,7 +8,9 @@ namespace GUIStuff { template class TextBox : public Element { public: - void update(UpdateInputData& io, T* newData, const std::function(const std::string&)>& newFromStr, const std::function newToStr, bool newSingleLine, bool updateEveryEdit, const std::function& elemUpdate) { + bool update(UpdateInputData& io, T* newData, const std::function(const std::string&)>& newFromStr, const std::function newToStr, bool newSingleLine, bool updateEveryEdit, const std::function& elemUpdate) { + bool isUpdating = false; + if(!textbox || !cur) force_update_textbox(io, false); @@ -30,17 +32,23 @@ template class TextBox : public Element { if(data && selection.selected) { io.richTextBoxEdit(textbox, cur); textbox->process_mouse_left_button(*cur, io.mouse.pos - bb.min, io.mouse.leftClick, io.mouse.leftHeld, io.key.leftShift); - update_on_edit(updateEveryEdit); + if(updateEveryEdit || io.key.enter) { + update_on_edit(); + isUpdating = true; + } } if(data && selection.justUnselected) { std::optional dataToAssign = fromStr(textbox->get_string()); if(dataToAssign) *data = dataToAssign.value(); force_update_textbox(io, true); + isUpdating = true; } if(elemUpdate) elemUpdate(selection); } + + return isUpdating; } virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) override { @@ -98,13 +106,11 @@ template class TextBox : public Element { } } - void update_on_edit(bool updateEveryEdit) { - if(updateEveryEdit) { - std::optional dataToAssign = fromStr(textbox->get_string()); - if(dataToAssign) { - *data = dataToAssign.value(); - oldData = *data; - } + void update_on_edit() { + std::optional dataToAssign = fromStr(textbox->get_string()); + if(dataToAssign) { + *data = dataToAssign.value(); + oldData = *data; } } diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index b6ff55f..31de512 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -496,9 +496,10 @@ bool GUIManager::text_button_left_transparent(const std::string& id, const std:: return toRet; } -void GUIManager::input_scalar(const std::string& id, uint8_t* val, uint8_t min, uint8_t max, int decimalPrecision, const std::function& elemUpdate) { +bool GUIManager::input_scalar(const std::string& id, uint8_t* val, uint8_t min, uint8_t max, int decimalPrecision, const std::function& elemUpdate) { + bool isUpdating = false; push_id(id); - insert_element>()->update(*io, val, + isUpdating = insert_element>()->update(*io, val, [&](const std::string& a) { if(a.empty()) return min; @@ -514,6 +515,7 @@ void GUIManager::input_scalar(const std::string& id, uint8_t* val, uint8_t min, return std::to_string(static_cast(a)); }, true, false, elemUpdate); pop_id(); + return isUpdating; } void GUIManager::input_path(const std::string& id, std::filesystem::path* val, std::filesystem::file_type fileTypeRestriction, const std::function& elemUpdate) { diff --git a/src/GUIStuff/GUIManager.hpp b/src/GUIStuff/GUIManager.hpp index c197782..73a2a9d 100644 --- a/src/GUIStuff/GUIManager.hpp +++ b/src/GUIStuff/GUIManager.hpp @@ -127,14 +127,16 @@ class GUIManager { return toRet; } - template void input_generic(const std::string& id, T* val, const std::function(const std::string&)>& fromStr, const std::function& toStr, bool singleLine, bool updateEveryEdit, const std::function& elemUpdate = nullptr) { + template bool input_generic(const std::string& id, T* val, const std::function(const std::string&)>& fromStr, const std::function& toStr, bool singleLine, bool updateEveryEdit, const std::function& elemUpdate = nullptr) { + bool isUpdating = false; push_id(id); - insert_element>()->update(*io, val, fromStr, toStr, singleLine, updateEveryEdit, elemUpdate); + isUpdating = insert_element>()->update(*io, val, fromStr, toStr, singleLine, updateEveryEdit, elemUpdate); pop_id(); + return isUpdating; } - template void input_scalar(const std::string& id, T* val, T min, T max, int decimalPrecision = 0, const std::function& elemUpdate = nullptr) { - input_generic(id, val, + template bool input_scalar(const std::string& id, T* val, T min, T max, int decimalPrecision = 0, const std::function& elemUpdate = nullptr) { + return input_generic(id, val, [&](const std::string& a) { if(a.empty()) return min; @@ -158,7 +160,7 @@ class GUIManager { }, true, false, elemUpdate); } - void input_scalar(const std::string& id, uint8_t* val, uint8_t min, uint8_t max, int decimalPrecision, const std::function& elemUpdate); + bool input_scalar(const std::string& id, uint8_t* val, uint8_t min, uint8_t max, int decimalPrecision, const std::function& elemUpdate); template void input_scalar_fields(const std::string& id, const std::string& name, TContainer* val, size_t elemCount, T min, T max, int decimalPrecision = 0, const std::function& elemUpdate = nullptr) { push_id(id); @@ -213,27 +215,32 @@ class GUIManager { pop_id(); } - template void slider_scalar(const std::string& id, T* val, T min, T max, const std::function& elemUpdate = nullptr) { + template bool slider_scalar(const std::string& id, T* val, T min, T max, const std::function& elemUpdate = nullptr) { push_id(id); - insert_element>()->update(*io, val, min, max, elemUpdate); + bool isUpdating = insert_element>()->update(*io, val, min, max, elemUpdate); pop_id(); + return isUpdating; } - template void input_scalar_field(const std::string& id, const std::string& name, T* val, T min, T max, int decimalPrecision = 0, const std::function& elemUpdate = nullptr) { + template bool input_scalar_field(const std::string& id, const std::string& name, T* val, T min, T max, int decimalPrecision = 0, const std::function& elemUpdate = nullptr) { + bool isUpdating = false; left_to_right_line_layout([&]() { text_label(name); - input_scalar(id, val, min, max, decimalPrecision, elemUpdate); + isUpdating = input_scalar(id, val, min, max, decimalPrecision, elemUpdate); }); + return isUpdating; } - template void slider_scalar_field(const std::string& id, const std::string& name, T* val, T min, T max, int decimalPrecision = 0, const std::function& elemUpdate = nullptr) { + template bool slider_scalar_field(const std::string& id, const std::string& name, T* val, T min, T max, int decimalPrecision = 0, const std::function& elemUpdate = nullptr) { + bool isUpdating = false; push_id(id); left_to_right_line_layout([&]() { text_label(name); - input_scalar("0", val, min, max, decimalPrecision, nullptr); + isUpdating |= input_scalar("0", val, min, max, decimalPrecision, nullptr); }); - slider_scalar("1", val, min, max, elemUpdate); + isUpdating |= slider_scalar("1", val, min, max, elemUpdate); pop_id(); + return isUpdating; } template bool big_color_button(const std::string& id, T* val, bool isSelected = false, const std::function& elemUpdate = nullptr) { diff --git a/src/RichTextBox/RichTextBox.cpp b/src/RichTextBox/RichTextBox.cpp index bcdb4d7..017e136 100644 --- a/src/RichTextBox/RichTextBox.cpp +++ b/src/RichTextBox/RichTextBox.cpp @@ -675,9 +675,9 @@ void RichTextBox::rebuild() { heightOffset += pData.p->getHeight(); } - std::cout << "size: " << tStyleMods.size() << std::endl; - for(auto& s : tStyleMods) - std::cout << s.pos.fParagraphIndex << " " << s.pos.fTextByteIndex << std::endl; + //std::cout << "size: " << tStyleMods.size() << std::endl; + //for(auto& s : tStyleMods) + // std::cout << s.pos.fParagraphIndex << " " << s.pos.fTextByteIndex << std::endl; needsRebuild = false; } @@ -816,12 +816,12 @@ SkRect RichTextBox::get_cursor_rect(TextPosition pos) { height = pData.p->getHeight(); } else { - if(pos.fTextByteIndex == pData.text.size()) { + if(pos.fTextByteIndex == 0) { skia::textlayout::Paragraph::GlyphClusterInfo glyphInfo; - bool exists = pData.p->getGlyphClusterAt(prev_grapheme(pData.text, pos.fTextByteIndex), &glyphInfo); + bool exists = pData.p->getGlyphClusterAt(pos.fTextByteIndex, &glyphInfo); if(exists) { bool isLtr = glyphInfo.fGlyphClusterPosition == skia::textlayout::TextDirection::kLtr; - topPoint = (isLtr ? glyphInfo.fBounds.TR() : glyphInfo.fBounds.TL()) + SkPoint{0.0f, pData.heightOffset}; + topPoint = (isLtr ? glyphInfo.fBounds.TL() : glyphInfo.fBounds.TR()) + SkPoint{0.0f, pData.heightOffset}; height = glyphInfo.fBounds.height(); } else { @@ -831,10 +831,10 @@ SkRect RichTextBox::get_cursor_rect(TextPosition pos) { } else { skia::textlayout::Paragraph::GlyphClusterInfo glyphInfo; - bool exists = pData.p->getGlyphClusterAt(pos.fTextByteIndex, &glyphInfo); + bool exists = pData.p->getGlyphClusterAt(prev_grapheme(pData.text, pos.fTextByteIndex), &glyphInfo); if(exists) { bool isLtr = glyphInfo.fGlyphClusterPosition == skia::textlayout::TextDirection::kLtr; - topPoint = (isLtr ? glyphInfo.fBounds.TL() : glyphInfo.fBounds.TR()) + SkPoint{0.0f, pData.heightOffset}; + topPoint = (isLtr ? glyphInfo.fBounds.TR() : glyphInfo.fBounds.TL()) + SkPoint{0.0f, pData.heightOffset}; height = glyphInfo.fBounds.height(); } else {