diff --git a/CMakeLists.txt b/CMakeLists.txt index 653128e..511901a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ set(sources "src/main.cpp" "src/GUIStuff/Elements/PaintCircleMenu.cpp" "src/GUIStuff/Elements/RotateWheel.cpp" "src/GUIStuff/Elements/TextParagraph.cpp" + "src/GUIStuff/Elements/FontPicker.cpp" "include/sago/platform_folders.cpp" "include/Helpers/Networking/NetLibrary.cpp" "include/Helpers/Networking/NetServer.cpp" diff --git a/src/DrawingProgram/TextBoxEditTool.cpp b/src/DrawingProgram/TextBoxEditTool.cpp index 3d718ba..4505b0b 100644 --- a/src/DrawingProgram/TextBoxEditTool.cpp +++ b/src/DrawingProgram/TextBoxEditTool.cpp @@ -2,9 +2,14 @@ #include "DrawingProgram.hpp" #include "../MainProgram.hpp" #include "../DrawData.hpp" +#include "Helpers/ConvertVec.hpp" #include "Helpers/SCollision.hpp" #include #include +#include +#include +#include +#include #include #include "EditTool.hpp" @@ -18,6 +23,18 @@ 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])->families.back().c_str(); + + t.gui.left_to_right_line_layout([&]() { + t.gui.text_label("Font"); + if(t.gui.font_picker("font picker", &newFontName)) { + auto fontFamilyMod = std::make_shared(); + fontFamilyMod->families.clear(); + fontFamilyMod->families.emplace_back(newFontName); + a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, fontFamilyMod); + } + }); t.gui.left_to_right_line_centered_layout([&]() { bool isBold = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::WEIGHT])->get_weight() == SkFontStyle::Weight::kBold_Weight; @@ -78,6 +95,7 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr& comp) { } newTextColor = std::static_pointer_cast(modsAtStartOfSelection[TextStyleModifier::ModifierType::COLOR])->color; + t.gui.pop_id(); if(a->textBox->inputChangedTextBox) diff --git a/src/DrawingProgram/TextBoxEditTool.hpp b/src/DrawingProgram/TextBoxEditTool.hpp index 7c64b80..4e3ac92 100644 --- a/src/DrawingProgram/TextBoxEditTool.hpp +++ b/src/DrawingProgram/TextBoxEditTool.hpp @@ -22,9 +22,12 @@ class TextBoxEditTool : public DrawingProgramEditToolBase { uint32_t newFontSize = 15; Vector4f newTextColor{1.0f, 1.0f, 1.0f, 1.0f}; + std::string newFontName; void set_mods_at_selection(const std::shared_ptr& a); TextStyleModifier::ModifierMap modsAtStartOfSelection; + std::vector sortedFontList; + TextBoxEditToolAllData get_all_data(const std::shared_ptr& a); }; diff --git a/src/FontData.cpp b/src/FontData.cpp index 5a0fc5c..b39bb25 100644 --- a/src/FontData.cpp +++ b/src/FontData.cpp @@ -30,7 +30,7 @@ FontData::FontData() #endif map["Roboto"] = mgr->makeFromFile("data/fonts/Roboto-variable.ttf"); collection = sk_make_sp(); - collection->setDefaultFontManager(mgr); + collection->setDefaultFontManager(mgr, {SkString{"Roboto"}}); } FontData::~FontData() { diff --git a/src/GUIStuff/Elements/FontPicker.cpp b/src/GUIStuff/Elements/FontPicker.cpp new file mode 100644 index 0000000..90760ee --- /dev/null +++ b/src/GUIStuff/Elements/FontPicker.cpp @@ -0,0 +1,160 @@ +#include "FontPicker.hpp" +#include "Helpers/ConvertVec.hpp" +#include +#include +#include +#include +#include +#include + +bool GUIStuff::FontPicker::update(UpdateInputData& io, std::string* fontName, GUIManager* gui) { + if(sortedFontList.empty()) { + auto icu = SkUnicodes::ICU::Make(); + for(int i = 0; i < io.textFontMgr->countFamilies(); i++) { + SkString familyNameSkString; + io.textFontMgr->getFamilyName(i, &familyNameSkString); + std::string familyName(familyNameSkString.c_str(), familyNameSkString.size()); + sortedFontList.emplace_back(familyName); + std::transform(familyName.begin(), familyName.end(), familyName.begin(), ::tolower); + sortedFontListLowercase.emplace_back(familyName); + } + std::sort(sortedFontList.begin(), sortedFontList.end()); + std::sort(sortedFontListLowercase.begin(), sortedFontListLowercase.end()); + } + + if(!fontName) + return false; + + bool fontChangedFromTextbox = false; + bool fontChangedFromSelection = false; + + bool clickedOut = io.mouse.leftClick; + + const float ENTRY_HEIGHT = 25.0f; + + CLAY({ + .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 it = std::lower_bound(sortedFontList.begin(), sortedFontList.end(), *fontName); + size_t val = (it != sortedFontList.end() && *it == *fontName) ? (it - sortedFontList.begin()): std::numeric_limits::max(); + if(val == std::numeric_limits::max()) { + std::string lowerFamilyName = *fontName; + std::transform(lowerFamilyName.begin(), lowerFamilyName.end(), lowerFamilyName.begin(), ::tolower); + auto itLower = std::lower_bound(sortedFontListLowercase.begin(), sortedFontListLowercase.end(), lowerFamilyName); + val = (itLower != sortedFontListLowercase.end() && *itLower == *fontName) ? (itLower - sortedFontListLowercase.begin()): std::numeric_limits::max(); + if(val != std::numeric_limits::max()) + *fontName = sortedFontList[val]; + } + + if(gui->svg_icon_button("Open font dropdown", "data/icons/droparrow.svg", dropdownOpen, GUIManager::SMALL_BUTTON_SIZE)) { + dropdownOpen = !dropdownOpen; + clickedOut = false; + } + if(dropdownOpen) { + CLAY({ + .layout = { + .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIT(0, 300)}, + }, + .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(); + if(Clay_Hovered()) + clickedOut = false; + gui->scroll_bar_many_entries_area("Font Selector", ENTRY_HEIGHT, sortedFontList.size(), [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.fontCollection, SkUnicodes::ICU::Make()); + tStyle.setFontStyle(SkFontStyle::Normal()); + + pBuilder.pushStyle(tStyle); + pBuilder.addText(sortedFontList[i].c_str(), sortedFontList[i].size()); + + auto p = pBuilder.Build(); + + CLAY({ + .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({ + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, + .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), std::numeric_limits::max()); + + 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; + }); + } + } + } + + if(clickedOut) + dropdownOpen = false; + + return fontChangedFromSelection | fontChangedFromTextbox; +} + +void GUIStuff::FontPicker::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) { +} diff --git a/src/GUIStuff/Elements/FontPicker.hpp b/src/GUIStuff/Elements/FontPicker.hpp new file mode 100644 index 0000000..d143b8b --- /dev/null +++ b/src/GUIStuff/Elements/FontPicker.hpp @@ -0,0 +1,18 @@ +#pragma once +#include "Element.hpp" +#include "../GUIManager.hpp" + +namespace GUIStuff { + +class FontPicker : public Element { + public: + bool update(UpdateInputData& io, std::string* fontName, GUIManager* gui); + virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) override; + SelectionHelper selection; + private: + std::vector sortedFontList; + std::vector sortedFontListLowercase; + bool dropdownOpen = false; +}; + +} diff --git a/src/GUIStuff/Elements/TextBox.hpp b/src/GUIStuff/Elements/TextBox.hpp index aca75ee..7b1e756 100644 --- a/src/GUIStuff/Elements/TextBox.hpp +++ b/src/GUIStuff/Elements/TextBox.hpp @@ -32,7 +32,7 @@ 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); - if(updateEveryEdit || io.key.enter) { + if((updateEveryEdit && textbox->inputChangedTextBox) || io.key.enter) { update_on_edit(); isUpdating = true; } @@ -48,6 +48,9 @@ template class TextBox : public Element { elemUpdate(selection); } + if(textbox) + textbox->inputChangedTextBox = false; + return isUpdating; } diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index 4f7b1d4..ae5f323 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -5,6 +5,7 @@ #include #include #include "Elements/SVGIcon.hpp" +#include "Elements/FontPicker.hpp" #include "Elements/SelectableButton.hpp" #include "Elements/RotateWheel.hpp" #include "Elements/TextParagraph.hpp" @@ -22,6 +23,7 @@ #include #include #include +#include namespace GUIStuff { @@ -342,10 +344,10 @@ void GUIManager::checkbox(const std::string& id, bool* val, const std::function< pop_id(); } -void GUIManager::input_text_field(const std::string& id, const std::string& name, std::string* val, const std::function& elemUpdate) { +void GUIManager::input_text_field(const std::string& id, const std::string& name, std::string* val, bool updateEveryEdit, const std::function& elemUpdate) { left_to_right_line_layout([&]() { text_label(name); - input_text(id, val, elemUpdate); + input_text(id, val, updateEveryEdit, elemUpdate); }); } @@ -392,16 +394,17 @@ bool GUIManager::input_color_component_255(const std::string& id, float* val, co return isUpdating; } -void GUIManager::input_text(const std::string& id, std::string* val, const std::function& elemUpdate) { +bool GUIManager::input_text(const std::string& id, std::string* val, bool updateEveryEdit, const std::function& elemUpdate) { push_id(id); - insert_element>()->update(*io, val, + bool toRet = insert_element>()->update(*io, val, [&](const std::string& str) { return str; }, [&](const std::string& str) { return str; - }, true, true, elemUpdate); + }, true, updateEveryEdit, elemUpdate); pop_id(); + return toRet; } bool GUIManager::selectable_button(const std::string& id, const std::function& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected) { @@ -552,6 +555,13 @@ void GUIManager::tab_list(const std::string& id, const std::vector()->update(*io, fontName, this); + pop_id(); + return toRet; +} + void GUIManager::dropdown_select(const std::string& id, size_t* val, const std::vector& selections, float width, const std::function& hoverboxElemUpdate) { push_id(id); bool& dropDownOpen = insert_any_with_id(0, false); @@ -648,7 +658,7 @@ void GUIManager::dropdown_select(const std::string& id, size_t* val, const std:: pop_id(); } -void GUIManager::scroll_bar_area(const std::string& uniqueId, const std::function& elemUpdate) { +void GUIManager::scroll_bar_area(const std::string& id, const std::function& elemUpdate) { CLAY({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, @@ -661,12 +671,16 @@ void GUIManager::scroll_bar_area(const std::string& uniqueId, const std::functio bool isMoving = false; float contentDimensions = 100.0f; float containerDimensions = 100.0f; + std::string uniqueID; }; - push_id(uniqueId); - ScrollAreaData& sD = insert_any(ScrollAreaData{}); + + push_id(id); + ScrollAreaData& sD = insert_any(ScrollAreaData{ + .uniqueID = Random::get().alphanumeric_str(30) + }); pop_id(); - Clay_ElementId clayID = Clay_GetElementId(strArena.std_str_to_clay_str(uniqueId)); + Clay_ElementId clayID = Clay_GetElementId(strArena.std_str_to_clay_str(sD.uniqueID)); CLAY({ .id = clayID, @@ -675,7 +689,7 @@ void GUIManager::scroll_bar_area(const std::string& uniqueId, const std::functio .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP}, .layoutDirection = CLAY_TOP_TO_BOTTOM }, - .clip = {.vertical = true, .childOffset = Clay_GetScrollOffset()} + .clip = {.horizontal = true, .vertical = true, .childOffset = {.x = 0, .y = Clay_GetScrollOffset().y}} }) { elemUpdate(sD.contentDimensions, sD.containerDimensions, sD.currentScrollPos); } @@ -742,17 +756,16 @@ void GUIManager::scroll_bar_area(const std::string& uniqueId, const std::functio } -void GUIManager::scroll_bar_many_entries_area(const std::string& uniqueId, float entryHeight, size_t entryCount, const std::function& entryUpdate, const std::function& elemUpdate) { - push_id(uniqueId); - scroll_bar_area(uniqueId, [&](float scrollContentHeight, float containerHeight, float scrollAmount) { +void GUIManager::scroll_bar_many_entries_area(const std::string& id, float entryHeight, size_t entryCount, const std::function& entryUpdate, const std::function& elemUpdate) { + push_id(id); + scroll_bar_area(id, [&](float scrollContentHeight, float containerHeight, float& scrollAmount) { if(elemUpdate) elemUpdate(scrollContentHeight, containerHeight, scrollAmount); bool listHovered = Clay_Hovered(); - scrollAmount = std::fabs(scrollAmount); - float entryHeight = 25.0f; - size_t startPoint = scrollAmount / entryHeight; + float absScrollAmount = std::fabs(scrollAmount); + size_t startPoint = absScrollAmount / entryHeight; size_t elementsContainable = (containerHeight / entryHeight) + 2; // Displaying 2 more elements ensures that there isn't any empty space in the container size_t endPoint = std::min(entryCount, startPoint + elementsContainable); diff --git a/src/GUIStuff/GUIManager.hpp b/src/GUIStuff/GUIManager.hpp index a879987..b3384a2 100644 --- a/src/GUIStuff/GUIManager.hpp +++ b/src/GUIStuff/GUIManager.hpp @@ -48,7 +48,7 @@ class GUIManager { Vector2f screen_pos_to_window_pos(const Vector2f& screenPos); Vector2f get_mouse_pos(); - void input_text_field(const std::string& id, const std::string& name, std::string* val, const std::function& elemUpdate = nullptr); + void input_text_field(const std::string& id, const std::string& name, std::string* val, bool updateEveryEdit = true, const std::function& elemUpdate = nullptr); void text_paragraph(const std::string& id, std::unique_ptr paragraph, float width, const std::function& elemUpdate = nullptr); void text_label_color(const std::string& val, const SkColor4f& color); @@ -80,11 +80,11 @@ class GUIManager { void push_id(const std::string& id); void pop_id(); - void scroll_bar_area(const std::string& uniqueId, const std::function& elemUpdate); - void scroll_bar_many_entries_area(const std::string& uniqueId, float entryHeight, size_t entryCount, const std::function& entryUpdate, const std::function& elemUpdate = nullptr); + void scroll_bar_area(const std::string& id, const std::function& elemUpdate); + void scroll_bar_many_entries_area(const std::string& id, float entryHeight, size_t entryCount, const std::function& entryUpdate, const std::function& elemUpdate = nullptr); bool input_color_component_255(const std::string& id, float* val, const std::function& elemUpdate = nullptr); - void input_text(const std::string& id, std::string* val, const std::function& elemUpdate = nullptr); + bool input_text(const std::string& id, std::string* val, bool updateEveryEdit = true, const std::function& elemUpdate = nullptr); bool radio_button(const std::string& id, bool val, const std::function& elemUpdate = nullptr); bool radio_button_field(const std::string& id, const std::string& name, bool val, const std::function& elemUpdate = nullptr); @@ -289,6 +289,8 @@ class GUIManager { return isUpdating; } + bool font_picker(const std::string& id, std::string* fontName); + template bool color_picker_items(const std::string& id, T* val, bool selectAlpha) { bool isUpdating = false; push_id(id); diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 97971e7..500c4ed 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -716,7 +716,7 @@ void Toolbar::grid_menu(bool justOpened) { main.world->gridMan.remove_grid(toDelete); gui.left_to_right_line_layout([&]() { bool addByEnter = false; - gui.input_text("grid text input", &gridMenu.newName, [&](GUIStuff::SelectionHelper& s) { + gui.input_text("grid text input", &gridMenu.newName, true, [&](GUIStuff::SelectionHelper& s) { addByEnter = s.selected && io->key.enter; }); if(gui.svg_icon_button("grid add button", "data/icons/plus.svg", false, GUIStuff::GUIManager::SMALL_BUTTON_SIZE) || (addByEnter && !gridMenu.newName.empty())) { @@ -801,7 +801,7 @@ void Toolbar::bookmark_menu(bool justOpened) { bool bookmarkExists = std::find(main.world->bMan.sorted_names().begin(), main.world->bMan.sorted_names().end(), bookMenu.newName) != main.world->bMan.sorted_names().end(); gui.left_to_right_line_layout([&]() { bool addByEnter = false; - gui.input_text("bookmark text input", &bookMenu.newName, [&](GUIStuff::SelectionHelper& s) { + gui.input_text("bookmark text input", &bookMenu.newName, true, [&](GUIStuff::SelectionHelper& s) { addByEnter = s.selected && io->key.enter; }); if(!bookmarkExists) { @@ -1692,7 +1692,7 @@ void Toolbar::about_menu_gui() { }) { if(gui.text_button_left_transparent("infinipaintnoticebutton", "InfiniPaint", selectedLicense == -1)) selectedLicense = -1; } - gui.text_label_light_centered("Third Party Libraries"); + gui.text_label_light_centered("Third Party Components"); for(int i = 0; i < static_cast(thirdPartyLicenses.size()); i++) { CLAY({ .layout = {