From 695393392f9a2719b249e0f95ff514d95e45e4de Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 5 Apr 2026 11:02:48 +0300 Subject: [PATCH] Font picker works --- .../Tools/EditTools/TextBoxEditTool.cpp | 24 +- .../ElementHelpers/ScrollAreaHelpers.cpp | 2 +- .../ElementHelpers/TextBoxHelpers.cpp | 24 +- .../ElementHelpers/TextBoxHelpers.hpp | 10 +- src/GUIStuff/Elements/DropDown.hpp | 8 +- src/GUIStuff/Elements/FontPicker.cpp | 367 ++++++++++-------- src/GUIStuff/Elements/FontPicker.hpp | 22 +- src/GUIStuff/Elements/ScrollArea.cpp | 143 +++---- src/GUIStuff/Elements/ScrollArea.hpp | 2 +- src/GUIStuff/Elements/TextParagraph.cpp | 4 +- src/GUIStuff/Elements/TextParagraph.hpp | 4 +- 11 files changed, 337 insertions(+), 273 deletions(-) diff --git a/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp b/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp index 00c88f2..63dcaf0 100644 --- a/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp +++ b/src/DrawingProgram/Tools/EditTools/TextBoxEditTool.cpp @@ -19,6 +19,7 @@ #include "../../../GUIStuff/ElementHelpers/ButtonHelpers.hpp" #include "../../../GUIStuff/ElementHelpers/NumberSliderHelpers.hpp" #include "../../../GUIStuff/Elements/PositionAdjustingPopupMenu.hpp" +#include "../../../GUIStuff/Elements/FontPicker.hpp" using namespace RichText; @@ -47,13 +48,16 @@ void TextBoxEditTool::edit_gui() { gui.new_id("edit tool text", [&] { text_label_centered(gui, "Edit Text"); - //left_to_right_line_layout(gui, [&] { - // text_label(gui, "Font"); - // if(gui.font_picker("font picker", &newFontName)) { - // currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES] = std::make_shared(std::vector{SkString{newFontName.c_str(), newFontName.size()}}); - // add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES]);}); - // } - //}); + left_to_right_line_layout(gui, [&] { + text_label(gui, "Font"); + gui.element("Font picker", &newFontName, FontPickerData{ + .onFontChange = [&] { + currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES] = std::make_shared(std::vector{SkString{newFontName.c_str(), newFontName.size()}}); + add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES]);}); + commit_update_func(); + } + }); + }); left_to_right_line_centered_layout(gui, [&] { svg_icon_button(gui, "Bold button", "data/icons/RemixIcon/bold.svg", { @@ -119,21 +123,27 @@ void TextBoxEditTool::edit_gui() { }; paragraph_operation_button("Align left button", "data/icons/RemixIcon/align-left.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kLeft, [&] { + currentPStyle.textAlignment = skia::textlayout::TextAlign::kLeft; a.textBox->set_text_alignment_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kLeft); }); paragraph_operation_button("Align center button", "data/icons/RemixIcon/align-center.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kCenter, [&] { + currentPStyle.textAlignment = skia::textlayout::TextAlign::kCenter; a.textBox->set_text_alignment_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kCenter); }); paragraph_operation_button("Align right button", "data/icons/RemixIcon/align-right.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kRight, [&] { + currentPStyle.textAlignment = skia::textlayout::TextAlign::kRight; a.textBox->set_text_alignment_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kRight); }); paragraph_operation_button("Align justify button", "data/icons/RemixIcon/align-justify.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kJustify, [&] { + currentPStyle.textAlignment = skia::textlayout::TextAlign::kJustify; a.textBox->set_text_alignment_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kJustify); }); paragraph_operation_button("Text direction left button", "data/icons/RemixIcon/text-direction-l.svg", currentPStyle.textDirection == skia::textlayout::TextDirection::kLtr, [&] { + currentPStyle.textDirection = skia::textlayout::TextDirection::kLtr; a.textBox->set_text_direction_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextDirection::kLtr); }); paragraph_operation_button("Text direction right button", "data/icons/RemixIcon/text-direction-r.svg", currentPStyle.textDirection == skia::textlayout::TextDirection::kRtl, [&] { + currentPStyle.textDirection = skia::textlayout::TextDirection::kRtl; a.textBox->set_text_direction_between(a.cursor->selectionBeginPos.fParagraphIndex, a.cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextDirection::kRtl); }); }); diff --git a/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.cpp b/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.cpp index 5222861..5b5b005 100644 --- a/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.cpp +++ b/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.cpp @@ -12,7 +12,7 @@ ScrollArea* scroll_area_many_entries(GUIManager& gui, const char* id, const Scro .innerContent = [&](const ScrollArea::InnerContentParameters& params) { if(options.innerContentExtraCallback) options.innerContentExtraCallback(params); - float absScrollAmount = std::fabs(params.scrollOffset.y()); + float absScrollAmount = std::fabs(params.scrollOffset->y()); size_t startPoint = absScrollAmount / options.entryHeight; size_t elementsContainable = (params.containerDimensions.y() / options.entryHeight) + 2; // Displaying 2 more elements ensures that there isn't any empty space in the container size_t endPoint = std::min(options.entryCount, startPoint + elementsContainable); diff --git a/src/GUIStuff/ElementHelpers/TextBoxHelpers.cpp b/src/GUIStuff/ElementHelpers/TextBoxHelpers.cpp index 14152c9..658f535 100644 --- a/src/GUIStuff/ElementHelpers/TextBoxHelpers.cpp +++ b/src/GUIStuff/ElementHelpers/TextBoxHelpers.cpp @@ -3,7 +3,7 @@ namespace GUIStuff { namespace ElementHelpers { -void input_color_component_255(GUIManager& gui, const char* id, float* val, const TextBoxOptions& options) { +TextBox* input_color_component_255(GUIManager& gui, const char* id, float* val, const TextBoxOptions& options) { InputManager::TextInputProperties textInputProps { .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_NUMBER, .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, @@ -29,10 +29,10 @@ void input_color_component_255(GUIManager& gui, const char* id, float* val, cons return ss.str(); }; - gui.element>(id, d); + return gui.element>(id, d); } -void input_text(GUIManager& gui, const char* id, std::string* val, const TextBoxOptions& options) { +TextBox* input_text(GUIManager& gui, const char* id, std::string* val, const TextBoxOptions& options) { InputManager::TextInputProperties textInputProps { .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_TEXT, .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, @@ -51,14 +51,16 @@ void input_text(GUIManager& gui, const char* id, std::string* val, const TextBox return a; }; - gui.element>(id, d); + return gui.element>(id, d); } -void input_text_field(GUIManager& gui, const char* id, std::string_view name, std::string* val, const TextBoxOptions& options) { +TextBox* input_text_field(GUIManager& gui, const char* id, std::string_view name, std::string* val, const TextBoxOptions& options) { + TextBox* toRet; left_to_right_line_layout(gui, [&]() { text_label(gui, name); - input_text(gui, id, val, options); + toRet = input_text(gui, id, val, options); }); + return toRet; } template <> void input_scalar(GUIManager& gui, const char* id, uint8_t* val, uint8_t minVal, uint8_t maxVal, const TextBoxScalarOptions& options) { @@ -91,7 +93,7 @@ template <> void input_scalar(GUIManager& gui, const char* id, uint8_t* gui.element>(id, d); } -void input_path(GUIManager& gui, const char* id, std::filesystem::path* val, const TextBoxPathOptions& options) { +TextBox* input_path(GUIManager& gui, const char* id, std::filesystem::path* val, const TextBoxPathOptions& options) { InputManager::TextInputProperties textInputProps { .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_TEXT, .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, @@ -113,14 +115,16 @@ void input_path(GUIManager& gui, const char* id, std::filesystem::path* val, con return a.string(); }; - gui.element>(id, d); + return gui.element>(id, d); } -void input_path_field(GUIManager& gui, const char* id, std::string_view name, std::filesystem::path* val, const TextBoxPathOptions& options) { +TextBox* input_path_field(GUIManager& gui, const char* id, std::string_view name, std::filesystem::path* val, const TextBoxPathOptions& options) { + TextBox* toRet; left_to_right_line_layout(gui, [&]() { text_label(gui, name); - input_path(gui, id, val, options); + toRet = input_path(gui, id, val, options); }); + return toRet; } } } diff --git a/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp b/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp index c322c3b..5119753 100644 --- a/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp +++ b/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp @@ -56,11 +56,11 @@ struct TextBoxHexColorOptions { std::function onDeselect; }; -void input_color_component_255(GUIManager& gui, const char* id, float* val, const TextBoxOptions& options = {}); -void input_text(GUIManager& gui, const char* id, std::string* val, const TextBoxOptions& options = {}); -void input_text_field(GUIManager& gui, const char* id, std::string_view name, std::string* val, const TextBoxOptions& options = {}); -void input_path(GUIManager& gui, const char* id, std::filesystem::path* val, const TextBoxPathOptions& options = {}); -void input_path_field(GUIManager& gui, const char* id, std::string_view name, std::filesystem::path* val, const TextBoxPathOptions& options = {}); +TextBox* input_color_component_255(GUIManager& gui, const char* id, float* val, const TextBoxOptions& options = {}); +TextBox* input_text(GUIManager& gui, const char* id, std::string* val, const TextBoxOptions& options = {}); +TextBox* input_text_field(GUIManager& gui, const char* id, std::string_view name, std::string* val, const TextBoxOptions& options = {}); +TextBox* input_path(GUIManager& gui, const char* id, std::filesystem::path* val, const TextBoxPathOptions& options = {}); +TextBox* input_path_field(GUIManager& gui, const char* id, std::string_view name, std::filesystem::path* val, const TextBoxPathOptions& options = {}); template void input_scalar(GUIManager& gui, const char* id, T* val, T minVal, T maxVal, const TextBoxScalarOptions& options = {}) { InputManager::TextInputProperties textInputProps { diff --git a/src/GUIStuff/Elements/DropDown.hpp b/src/GUIStuff/Elements/DropDown.hpp index 67fac05..69734f9 100644 --- a/src/GUIStuff/Elements/DropDown.hpp +++ b/src/GUIStuff/Elements/DropDown.hpp @@ -100,7 +100,7 @@ template class DropDown : public Element { SkColor4f entryColor; if(selectedEntry) entryColor = gui.io.theme->backColor2; - else if(hoveringOver.has_value() && hoveringOver.value() == i) + else if(hoveringOver == i) entryColor = gui.io.theme->backColor2; else entryColor = gui.io.theme->backColor1; @@ -145,15 +145,15 @@ template class DropDown : public Element { auto oldHoveringOver = hoveringOver; if(mouseHovering) hoveringOver = i; - else if(hoveringOver.has_value() && hoveringOver.value() == i) - hoveringOver = std::nullopt; + else if(hoveringOver == i) + hoveringOver = std::numeric_limits::max(); if(oldHoveringOver != hoveringOver) gui.set_to_layout(); } T* d; DropdownOptions opts; bool isOpen = false; - std::optional hoveringOver; + size_t hoveringOver = std::numeric_limits::max(); }; } diff --git a/src/GUIStuff/Elements/FontPicker.cpp b/src/GUIStuff/Elements/FontPicker.cpp index 0b8a9ae..f92743d 100644 --- a/src/GUIStuff/Elements/FontPicker.cpp +++ b/src/GUIStuff/Elements/FontPicker.cpp @@ -1,6 +1,7 @@ #include "FontPicker.hpp" #include "Helpers/ConvertVec.hpp" #include +#include #include #include #include @@ -8,182 +9,222 @@ #include #include "../GUIManager.hpp" +#include "../ElementHelpers/ButtonHelpers.hpp" +#include "../ElementHelpers/ScrollAreaHelpers.hpp" +#include "../ElementHelpers/TextBoxHelpers.hpp" +#include "TextParagraph.hpp" + +std::vector sortedFontList; +std::vector sortedFontListLowercase; + namespace GUIStuff { FontPicker::FontPicker(GUIManager& gui): Element(gui) {} -void FontPicker::layout(const Clay_ElementId& id, const Data& data) { - d = data; - //auto& io = *gui.io; +void FontPicker::layout(const Clay_ElementId& id, std::string* newFontName, const FontPickerData& newData) { + using namespace ElementHelpers; + fontName = newFontName; + data = newData; - //if(sortedFontList.empty()) { - // auto icu = SkUnicodes::ICU::Make(); - // std::set sortedFontSet; + if(sortedFontList.empty()) { + auto icu = SkUnicodes::ICU::Make(); + std::set sortedFontSet; - // if(io.fonts->localFontMgr) { - // for(int i = 0; i < io.fonts->localFontMgr->countFamilies(); i++) { - // SkString familyNameSkString; - // io.fonts->localFontMgr->getFamilyName(i, &familyNameSkString); - // if(familyNameSkString.isEmpty()) - // continue; - // std::string familyName(familyNameSkString.c_str(), familyNameSkString.size()); - // sortedFontSet.emplace(familyName); - // } - // } - // for(int i = 0; i < io.fonts->defaultFontMgr->countFamilies(); i++) { - // SkString familyNameSkString; - // io.fonts->defaultFontMgr->getFamilyName(i, &familyNameSkString); - // if(familyNameSkString.isEmpty()) - // continue; - // std::string familyName(familyNameSkString.c_str(), familyNameSkString.size()); - // sortedFontSet.emplace(familyName); - // } - // sortedFontList = std::vector(sortedFontSet.begin(), sortedFontSet.end()); - // sortedFontListLowercase = sortedFontList; - // for(std::string& s : sortedFontListLowercase) - // std::transform(s.begin(), s.end(), s.begin(), ::tolower); - //} + if(gui.io.fonts->localFontMgr) { + for(int i = 0; i < gui.io.fonts->localFontMgr->countFamilies(); i++) { + SkString familyNameSkString; + gui.io.fonts->localFontMgr->getFamilyName(i, &familyNameSkString); + if(familyNameSkString.isEmpty()) + continue; + std::string familyName(familyNameSkString.c_str(), familyNameSkString.size()); + sortedFontSet.emplace(familyName); + } + } + for(int i = 0; i < gui.io.fonts->defaultFontMgr->countFamilies(); i++) { + SkString familyNameSkString; + gui.io.fonts->defaultFontMgr->getFamilyName(i, &familyNameSkString); + if(familyNameSkString.isEmpty()) + continue; + std::string familyName(familyNameSkString.c_str(), familyNameSkString.size()); + sortedFontSet.emplace(familyName); + } + sortedFontList = std::vector(sortedFontSet.begin(), sortedFontSet.end()); + sortedFontListLowercase = sortedFontList; + for(std::string& s : sortedFontListLowercase) + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + } - //bool fontChangedFromTextbox = false; - //bool fontChangedFromSelection = false; + auto sortedFontListValIt = std::find(sortedFontList.begin(), sortedFontList.end(), *fontName); + size_t val = sortedFontListValIt != sortedFontList.end() ? sortedFontListValIt - sortedFontList.begin() : std::numeric_limits::max(); - //bool clickedOut = io.mouse.leftClick; + const float ENTRY_HEIGHT = 25.0f; - //const float ENTRY_HEIGHT = 25.0f; + CLAY(id, { + .layout = { + .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIT(0)}, + .childGap = 4 + } + }) { + auto textboxElement = ElementHelpers::input_text(gui, "Font textbox", fontName, { + .onEdit = [&] { + auto newFontName = get_valid_font_name(); + if(newFontName.has_value()) { + *fontName = newFontName.value(); + jumpToFontName = true; + if(data.onFontChange) data.onFontChange(); + } + gui.set_to_layout(); + }, + .onSelect = [&] { + dropdownOpen = true; + gui.set_to_layout(); + } + }); - //CLAY_AUTO_ID({ - // .layout = { - // .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIT(0)}, - // .childGap = 4 - // } - //}) { - // fontChangedFromTextbox = gui.input_text("Font textbox", fontName, true, [&](SelectionHelper& s) { - // if(s.selected) { - // dropdownOpen = true; - // clickedOut = false; - // } - // }); + auto dropdownButton = svg_icon_button(gui, "Open font dropdown", "data/icons/droparrowbold.svg", { + .size = SMALL_BUTTON_SIZE, + .onClick = [&] { + dropdownOpen = !dropdownOpen; + } + }); - // auto it = std::find(sortedFontList.begin(), sortedFontList.end(), *fontName); - // size_t val = std::numeric_limits::max(); - // if(it == sortedFontList.end()) { - // std::string lowerFamilyName = *fontName; - // std::transform(lowerFamilyName.begin(), lowerFamilyName.end(), lowerFamilyName.begin(), ::tolower); - // auto itLower = std::find(sortedFontListLowercase.begin(), sortedFontListLowercase.end(), lowerFamilyName); - // if(itLower != sortedFontListLowercase.end()) { - // val = itLower - sortedFontListLowercase.begin(); - // *fontName = sortedFontList[val]; - // } - // } - // else - // val = it - sortedFontList.begin(); + if(dropdownOpen) { + gui.set_z_index(gui.get_z_index() + 1, [&] { + gui.element("Font picker floating element", [&] (LayoutElement*, const Clay_ElementId& lId) { + CLAY(lId, { + .layout = { + .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIT(0, 300)}, + .layoutDirection = CLAY_TOP_TO_BOTTOM + }, + .backgroundColor = convert_vec4(gui.io.theme->backColor1), + .cornerRadius = CLAY_CORNER_RADIUS(4), + .floating = { + .offset = { + .y = 2 + }, + .zIndex = gui.get_z_index(), + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM + }, + .attachTo = CLAY_ATTACH_TO_PARENT + }, + .border = { + .color = convert_vec4(gui.io.theme->fillColor2), + .width = CLAY_BORDER_OUTSIDE(1) + } + }) { + scroll_area_many_entries(gui, "Font Selector", { + .entryHeight = ENTRY_HEIGHT, + .entryCount = sortedFontList.size(), + .clipHorizontal = true, + .elementContent = [&] (size_t i) { + bool selectedEntry = val == i; + gui.element("elem", [&](LayoutElement*, const Clay_ElementId& lId2) { + CLAY(lId2, { + .layout = { + .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIXED(25)}, + .childGap = 1, + .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP}, + .layoutDirection = CLAY_TOP_TO_BOTTOM + } + }) { + SkColor4f entryColor; + if(selectedEntry) + entryColor = gui.io.theme->backColor2; + else if(hoveringOver == i) + entryColor = gui.io.theme->backColor2; + else + entryColor = gui.io.theme->backColor1; + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(25)}, + .padding = {.left = 2, .right = 2}, + .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} + }, + .backgroundColor = convert_vec4(entryColor) + }) { + skia::textlayout::ParagraphStyle pStyle; + pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft); + pStyle.setTextHeightBehavior(skia::textlayout::kDisableAll); + + skia::textlayout::StrutStyle strutStyle; + strutStyle.setStrutEnabled(true); + strutStyle.setForceStrutHeight(true); + pStyle.setStrutStyle(strutStyle); + + skia::textlayout::TextStyle tStyle; + tStyle.setFontSize(gui.io.fontSize); + tStyle.setFontFamilies({SkString{sortedFontList[i].c_str()}, SkString{"Roboto"}}); + tStyle.setForegroundColor(SkPaint{gui.io.theme->frontColor1}); + pStyle.setTextStyle(tStyle); + + skia::textlayout::ParagraphBuilderImpl pBuilder(pStyle, gui.io.fonts->collection, SkUnicodes::ICU::Make()); + tStyle.setFontStyle(SkFontStyle::Normal()); + + pBuilder.pushStyle(tStyle); + pBuilder.addText(sortedFontList[i].c_str(), sortedFontList[i].size()); + + std::shared_ptr p = pBuilder.Build(); - // if(gui.svg_icon_button("Open font dropdown", "data/icons/droparrowbold.svg", dropdownOpen, GUIManager::SMALL_BUTTON_SIZE)) { - // dropdownOpen = !dropdownOpen; - // clickedOut = false; - // } - // if(dropdownOpen) { - // Clay_ElementId localID = CLAY_ID_LOCAL("Font picker floating element"); - // CLAY(localID, { - // .layout = { - // .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIT(0, 300)}, - // .layoutDirection = CLAY_TOP_TO_BOTTOM - // }, - // .backgroundColor = convert_vec4(io.theme->backColor1), - // .cornerRadius = CLAY_CORNER_RADIUS(4), - // .floating = { - // .offset = { - // .y = 2 - // }, - // .attachPoints = { - // .element = CLAY_ATTACH_POINT_LEFT_TOP, - // .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM - // }, - // .attachTo = CLAY_ATTACH_TO_PARENT - // }, - // .border = { - // .color = convert_vec4(io.theme->fillColor2), - // .width = CLAY_BORDER_OUTSIDE(1) - // } - // }) { - // gui->obstructing_window(localID); - // if(Clay_Hovered()) - // clickedOut = false; - // gui->scroll_bar_many_entries_area("Font Selector", ENTRY_HEIGHT, sortedFontList.size(), true, [fontName, gui, &fontChangedFromSelection, &sortedFontList = sortedFontList, val, &io = io](size_t i, bool listHovered) { - // bool selectedEntry = val == i; - // - // skia::textlayout::ParagraphStyle pStyle; - // pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft); - // pStyle.setTextHeightBehavior(skia::textlayout::kDisableAll); - // - // skia::textlayout::StrutStyle strutStyle; - // strutStyle.setStrutEnabled(true); - // strutStyle.setForceStrutHeight(true); - // pStyle.setStrutStyle(strutStyle); - // - // skia::textlayout::TextStyle tStyle; - // tStyle.setFontSize(io.fontSize); - // tStyle.setFontFamilies({SkString{sortedFontList[i].c_str()}, SkString{"Roboto"}}); - // tStyle.setForegroundColor(SkPaint{io.theme->frontColor1}); - // pStyle.setTextStyle(tStyle); - // - // skia::textlayout::ParagraphBuilderImpl pBuilder(pStyle, io.fonts->collection, SkUnicodes::ICU::Make()); - // tStyle.setFontStyle(SkFontStyle::Normal()); - // - // pBuilder.pushStyle(tStyle); - // pBuilder.addText(sortedFontList[i].c_str(), sortedFontList[i].size()); - // - // auto p = pBuilder.Build(); - // - // CLAY_AUTO_ID({ - // .layout = { - // .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIXED(25)}, - // .childGap = 1, - // .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP}, - // .layoutDirection = CLAY_TOP_TO_BOTTOM - // } - // }) { - // SkColor4f entryColor; - // if(selectedEntry) - // entryColor = io.theme->backColor2; - // else if(Clay_Hovered()) - // entryColor = io.theme->backColor2; - // else - // entryColor = io.theme->backColor1; - // CLAY_AUTO_ID({ - // .layout = { - // .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(25)}, - // .padding = {.left = 2, .right = 2}, - // .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} - // }, - // .backgroundColor = convert_vec4(entryColor) - // }) { - // gui->text_paragraph("font name", std::move(p), 10000.0f); // Setting width to numeric_limits::max will lead to multiplying it by any number greater than 1 to = infinity, which will cause issues - // - // if(io.mouse.leftClick && Clay_Hovered()) { - // *fontName = sortedFontList[i]; - // fontChangedFromSelection = true; - // } - // } - // } - // }, [ENTRY_HEIGHT, fontChangedFromTextbox, io = &io, val](float scrollContentHeight, float containerHeight, float& scrollAmount) { - // if(fontChangedFromTextbox && !io->mouse.leftClick && val != std::numeric_limits::max()) - // scrollAmount = -static_cast(val) * ENTRY_HEIGHT; - // }); - // #ifdef __EMSCRIPTEN__ - // CLAY_AUTO_ID({ - // .layout = { - // .padding = CLAY_PADDING_ALL(io.theme->padding1) - // }}) { - // gui->text_label_light_centered("Local fonts not usable in web version"); - // } - // #endif - // } - // } - //} + gui.element("font name", p, 10000.0f); // Setting width to numeric_limits::max will lead to multiplying it by any number greater than 1 to = infinity, which will cause issues + } + } + }, LayoutElement::Callbacks{ + .mouseButton = [&, i](LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button) { + if(l->mouseHovering && button.down && button.button == InputManager::MouseButton::LEFT) { + *fontName = sortedFontList[i]; + gui.set_post_callback_func([&] { if(data.onFontChange) data.onFontChange(); }); + gui.set_to_layout(); + } + }, + .mouseMotion = [&, i](LayoutElement* l, const InputManager::MouseMotionCallbackArgs& motion) { + size_t oldHovering = hoveringOver; + if(l->mouseHovering) + hoveringOver = i; + else if(hoveringOver == i) + hoveringOver = std::numeric_limits::max(); + if(oldHovering != hoveringOver) + gui.set_to_layout(); + } + }); + }, + .innerContentExtraCallback = [&](const ScrollArea::InnerContentParameters& innerContentParams) { + if(jumpToFontName) { + if(val != std::numeric_limits::max()) + innerContentParams.scrollOffset->y() = -static_cast(val) * ENTRY_HEIGHT; + jumpToFontName = false; + } + } + }); + } + }, LayoutElement::Callbacks{ + .mouseButton = [&, textboxElement, dropdownButton] (LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button) { + if(!textboxElement->mouseHovering && !dropdownButton->mouseHovering && !l->mouseHovering && !l->childMouseHovering) { + dropdownOpen = false; + gui.set_to_layout(); + } + } + }); + }); + } + } +} - //if(clickedOut) - // dropdownOpen = false; +std::optional FontPicker::get_valid_font_name() { + auto it = std::find(sortedFontList.begin(), sortedFontList.end(), *fontName); + if(it != sortedFontList.end()) + return *fontName; + else { + std::string lowerFamilyName = *fontName; + std::transform(lowerFamilyName.begin(), lowerFamilyName.end(), lowerFamilyName.begin(), ::tolower); + auto itLower = std::find(sortedFontListLowercase.begin(), sortedFontListLowercase.end(), lowerFamilyName); + if(itLower != sortedFontListLowercase.end()) { + size_t val = itLower - sortedFontListLowercase.begin(); + return sortedFontList[val]; + } + } + return std::nullopt; } } diff --git a/src/GUIStuff/Elements/FontPicker.hpp b/src/GUIStuff/Elements/FontPicker.hpp index 000594b..020517e 100644 --- a/src/GUIStuff/Elements/FontPicker.hpp +++ b/src/GUIStuff/Elements/FontPicker.hpp @@ -1,21 +1,27 @@ #pragma once #include "Element.hpp" +#include namespace GUIStuff { +struct FontPickerData { + std::function onFontChange; +}; + class FontPicker : public Element { public: - struct Data { - std::string* fontName = nullptr; - std::function onFontChange; - }; FontPicker(GUIManager& gui); - void layout(const Clay_ElementId& id, const Data& data); + void layout(const Clay_ElementId& id, std::string* newFontName, const FontPickerData& newData = {}); private: - Data d; + std::string* fontName; + FontPickerData data; + + size_t hoveringOver = std::numeric_limits::max(); + + bool jumpToFontName = false; + + std::optional get_valid_font_name(); - std::vector sortedFontList; - std::vector sortedFontListLowercase; bool dropdownOpen = false; }; diff --git a/src/GUIStuff/Elements/ScrollArea.cpp b/src/GUIStuff/Elements/ScrollArea.cpp index 408c2e6..8d5db9a 100644 --- a/src/GUIStuff/Elements/ScrollArea.cpp +++ b/src/GUIStuff/Elements/ScrollArea.cpp @@ -36,7 +36,8 @@ void ScrollArea::layout(const Clay_ElementId& id, const Options& options) { }, .clip = {.horizontal = opts.clipHorizontal, .vertical = opts.clipVertical, .childOffset = {.x = scrollOffset.x(), .y = scrollOffset.y()}} }) { - opts.innerContent({.contentDimensions = contentDimensions, .containerDimensions = containerDimensions, .scrollOffset = scrollOffset}); + opts.innerContent({.contentDimensions = contentDimensions, .containerDimensions = containerDimensions, .scrollOffset = &scrollOffset}); + clamp_scroll(); } if(scrollData.contentDimensions.height > scrollData.scrollContainerDimensions.height) { @@ -48,88 +49,90 @@ void ScrollArea::layout(const Clay_ElementId& id, const Options& options) { float scrollerPos = std::fabs(scrollOffset.y() / scrollPosMax); float areaAboveScrollerSize = scrollerPos * (sAreaDim - scrollerSize); - gui.element("scroll bar", [&] (LayoutElement*, const Clay_ElementId& lId) { - CLAY(lId, { - .layout = { - .sizing = {.width = CLAY_SIZING_FIXED(12), .height = CLAY_SIZING_GROW(0)}, - .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP}, - .layoutDirection = CLAY_TOP_TO_BOTTOM - }, - .backgroundColor = convert_vec4(gui.io.theme->backColor2) - }) { - SkColor4f scrollerColor; - if(isScrollbarHeldY) - scrollerColor = gui.io.theme->fillColor1; - else if(isScrollbarHoveredY) - scrollerColor = gui.io.theme->fillColor1; - else - scrollerColor = gui.io.theme->fillColor2; - - CLAY_AUTO_ID({ + gui.set_z_index(gui.get_z_index() + 1, [&] { + gui.element("scroll bar", [&] (LayoutElement*, const Clay_ElementId& lId) { + CLAY(lId, { .layout = { - .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(areaAboveScrollerSize)} - } - }) {} + .sizing = {.width = CLAY_SIZING_FIXED(12), .height = CLAY_SIZING_GROW(0)}, + .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP}, + .layoutDirection = CLAY_TOP_TO_BOTTOM + }, + .backgroundColor = convert_vec4(gui.io.theme->backColor2) + }) { + SkColor4f scrollerColor; + if(isScrollbarHeldY) + scrollerColor = gui.io.theme->fillColor1; + else if(isScrollbarHoveredY) + scrollerColor = gui.io.theme->fillColor1; + else + scrollerColor = gui.io.theme->fillColor2; - gui.element("scroller", [&] (LayoutElement*, const Clay_ElementId& lId2) { - CLAY(lId2, { - .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(scrollerSize)}}, - .backgroundColor = convert_vec4(scrollerColor), - .cornerRadius = CLAY_CORNER_RADIUS(3), + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(areaAboveScrollerSize)} + } }) {} - }, LayoutElement::Callbacks{ - .mouseMotion = [&] (LayoutElement* t, const InputManager::MouseMotionCallbackArgs& motion) { - if(isHoveringOverScrollerY != t->mouseHovering) { - isHoveringOverScrollerY = t->mouseHovering; + + gui.element("scroller", [&] (LayoutElement*, const Clay_ElementId& lId2) { + CLAY(lId2, { + .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(scrollerSize)}}, + .backgroundColor = convert_vec4(scrollerColor), + .cornerRadius = CLAY_CORNER_RADIUS(3), + }) {} + }, LayoutElement::Callbacks{ + .mouseMotion = [&] (LayoutElement* t, const InputManager::MouseMotionCallbackArgs& motion) { + if(isHoveringOverScrollerY != t->mouseHovering) { + isHoveringOverScrollerY = t->mouseHovering; + gui.set_to_layout(); + } + } + }); + + CLAY_AUTO_ID({ .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}}) {} + } + }, LayoutElement::Callbacks{ + .mouseButton = [&, scrollPosMax, sAreaStart, sAreaDim, scrollerSize, areaAboveScrollerSize](LayoutElement* t, const InputManager::MouseButtonCallbackArgs& button) { + if(button.button == InputManager::MouseButton::LEFT) { + if(button.down && t->mouseHovering) { + isScrollbarHeldY = true; + if(isHoveringOverScrollerY) + scrollerStartPosY = sAreaStart + areaAboveScrollerSize + scrollerSize * 0.5f; + else + scrollerStartPosY = std::clamp(button.pos.y(), sAreaStart + scrollerSize * 0.5f, sAreaStart + sAreaDim - scrollerSize * 0.5f); + mouseStartPosY = button.pos.y(); + + float newScrollPosFrac; + newScrollPosFrac = std::clamp((scrollerStartPosY - (mouseStartPosY - button.pos.y()) - sAreaStart - scrollerSize * 0.5f) / (sAreaDim - scrollerSize), 0.0f, 1.0f); + scrollOffset.y() = newScrollPosFrac * (-scrollPosMax); + + clamp_scroll(); + gui.set_to_layout(); + } + else if(isScrollbarHeldY) { + isScrollbarHeldY = false; gui.set_to_layout(); } } - }); - - CLAY_AUTO_ID({ .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}}) {} - } - }, LayoutElement::Callbacks{ - .mouseButton = [&, scrollPosMax, sAreaStart, sAreaDim, scrollerSize, areaAboveScrollerSize](LayoutElement* t, const InputManager::MouseButtonCallbackArgs& button) { - if(button.button == InputManager::MouseButton::LEFT) { - if(button.down && t->mouseHovering) { - isScrollbarHeldY = true; - if(isHoveringOverScrollerY) - scrollerStartPosY = sAreaStart + areaAboveScrollerSize + scrollerSize * 0.5f; - else - scrollerStartPosY = std::clamp(button.pos.y(), sAreaStart + scrollerSize * 0.5f, sAreaStart + sAreaDim - scrollerSize * 0.5f); - mouseStartPosY = button.pos.y(); + }, + .mouseMotion = [&, scrollPosMax, sAreaStart, sAreaDim, scrollerSize](LayoutElement* t, const InputManager::MouseMotionCallbackArgs& motion) { + if(isScrollbarHoveredY != t->mouseHovering) { + isScrollbarHoveredY = t->mouseHovering; + gui.set_to_layout(); + } + if(isScrollbarHeldY) { + float oldScrollOffset = scrollOffset.y(); float newScrollPosFrac; - newScrollPosFrac = std::clamp((scrollerStartPosY - (mouseStartPosY - button.pos.y()) - sAreaStart - scrollerSize * 0.5f) / (sAreaDim - scrollerSize), 0.0f, 1.0f); + newScrollPosFrac = std::clamp((scrollerStartPosY - (mouseStartPosY - motion.pos.y()) - sAreaStart - scrollerSize * 0.5f) / (sAreaDim - scrollerSize), 0.0f, 1.0f); scrollOffset.y() = newScrollPosFrac * (-scrollPosMax); clamp_scroll(); - gui.set_to_layout(); - } - else if(isScrollbarHeldY) { - isScrollbarHeldY = false; - gui.set_to_layout(); + + if(oldScrollOffset != scrollOffset.y()) + gui.set_to_layout(); } } - }, - .mouseMotion = [&, scrollPosMax, sAreaStart, sAreaDim, scrollerSize](LayoutElement* t, const InputManager::MouseMotionCallbackArgs& motion) { - if(isScrollbarHoveredY != t->mouseHovering) { - isScrollbarHoveredY = t->mouseHovering; - gui.set_to_layout(); - } - if(isScrollbarHeldY) { - float oldScrollOffset = scrollOffset.y(); - - float newScrollPosFrac; - newScrollPosFrac = std::clamp((scrollerStartPosY - (mouseStartPosY - motion.pos.y()) - sAreaStart - scrollerSize * 0.5f) / (sAreaDim - scrollerSize), 0.0f, 1.0f); - scrollOffset.y() = newScrollPosFrac * (-scrollPosMax); - - clamp_scroll(); - - if(oldScrollOffset != scrollOffset.y()) - gui.set_to_layout(); - } - } + }); }); } } diff --git a/src/GUIStuff/Elements/ScrollArea.hpp b/src/GUIStuff/Elements/ScrollArea.hpp index 4524d95..1c4cfaa 100644 --- a/src/GUIStuff/Elements/ScrollArea.hpp +++ b/src/GUIStuff/Elements/ScrollArea.hpp @@ -11,7 +11,7 @@ class ScrollArea : public Element { struct InnerContentParameters { Vector2f contentDimensions; Vector2f containerDimensions; - Vector2f scrollOffset; + Vector2f* scrollOffset; }; struct Options { diff --git a/src/GUIStuff/Elements/TextParagraph.cpp b/src/GUIStuff/Elements/TextParagraph.cpp index f4369c5..5aa50f8 100644 --- a/src/GUIStuff/Elements/TextParagraph.cpp +++ b/src/GUIStuff/Elements/TextParagraph.cpp @@ -7,8 +7,8 @@ namespace GUIStuff { TextParagraph::TextParagraph(GUIManager& gui): Element(gui) {} -void TextParagraph::layout(const Clay_ElementId& id, std::unique_ptr paragraph, float width) { - data = std::move(paragraph); +void TextParagraph::layout(const Clay_ElementId& id, const std::shared_ptr& paragraph, float width) { + data = paragraph; data->layout(width); CLAY(id, { .layout = { diff --git a/src/GUIStuff/Elements/TextParagraph.hpp b/src/GUIStuff/Elements/TextParagraph.hpp index 945ba65..aa32388 100644 --- a/src/GUIStuff/Elements/TextParagraph.hpp +++ b/src/GUIStuff/Elements/TextParagraph.hpp @@ -7,10 +7,10 @@ namespace GUIStuff { class TextParagraph : public Element { public: TextParagraph(GUIManager& gui); - void layout(const Clay_ElementId& id, std::unique_ptr paragraph, float width); + void layout(const Clay_ElementId& id, const std::shared_ptr& paragraph, float width); virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override; private: - std::unique_ptr data; + std::shared_ptr data; }; }