Font picker works

This commit is contained in:
Yousef Khadadeh 2026-04-05 11:02:48 +03:00
parent 7286f01035
commit 695393392f
11 changed files with 337 additions and 273 deletions

View file

@ -1,6 +1,7 @@
#include "FontPicker.hpp"
#include "Helpers/ConvertVec.hpp"
#include <algorithm>
#include <limits>
#include <modules/skparagraph/include/DartTypes.h>
#include <modules/skparagraph/include/ParagraphStyle.h>
#include <modules/skparagraph/src/ParagraphBuilderImpl.h>
@ -8,182 +9,222 @@
#include <Helpers/Random.hpp>
#include "../GUIManager.hpp"
#include "../ElementHelpers/ButtonHelpers.hpp"
#include "../ElementHelpers/ScrollAreaHelpers.hpp"
#include "../ElementHelpers/TextBoxHelpers.hpp"
#include "TextParagraph.hpp"
std::vector<std::string> sortedFontList;
std::vector<std::string> 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<std::string> sortedFontSet;
if(sortedFontList.empty()) {
auto icu = SkUnicodes::ICU::Make();
std::set<std::string> 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<std::string>(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<std::string>(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<size_t>::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<size_t>::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<LayoutElement>("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<Clay_Color>(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<Clay_Color>(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<LayoutElement>("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<Clay_Color>(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<skia::textlayout::Paragraph> 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<Clay_Color>(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<Clay_Color>(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<Clay_Color>(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<size_t>::max())
// scrollAmount = -static_cast<float>(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<TextParagraph>("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<size_t>::max();
if(oldHovering != hoveringOver)
gui.set_to_layout();
}
});
},
.innerContentExtraCallback = [&](const ScrollArea::InnerContentParameters& innerContentParams) {
if(jumpToFontName) {
if(val != std::numeric_limits<size_t>::max())
innerContentParams.scrollOffset->y() = -static_cast<float>(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<std::string> 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;
}
}