#include "FontPicker.hpp" #include "Helpers/ConvertVec.hpp" #include #include #include #include #include #include #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, 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(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); } auto sortedFontListValIt = std::find(sortedFontList.begin(), sortedFontList.end(), *fontName); size_t val = sortedFontListValIt != sortedFontList.end() ? sortedFontListValIt - sortedFontList.begin() : std::numeric_limits::max(); 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(); } }); auto dropdownButton = svg_icon_button(gui, "Open font dropdown", "data/icons/droparrowbold.svg", { .size = SMALL_BUTTON_SIZE, .onClick = [&] { dropdownOpen = !dropdownOpen; } }); 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(); 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(); } } }); }); } } } 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; } }