Dropdown reacts to window size, and color picker always fits in window

This commit is contained in:
Yousef Khadadeh 2025-12-01 13:05:59 +03:00
parent 270cd98195
commit 903914c7ef
3 changed files with 145 additions and 71 deletions

View file

@ -24,7 +24,6 @@
#include <modules/skparagraph/include/FontCollection.h>
#include <modules/skparagraph/include/TextStyle.h>
#include <modules/skunicode/include/SkUnicode_icu.h>
#include <Helpers/Random.hpp>
#include "../FontData.hpp"
namespace GUIStuff {
@ -536,6 +535,11 @@ bool GUIManager::input_scalar(const std::string& id, uint8_t* val, uint8_t min,
return isUpdating;
}
std::string GUIManager::get_clay_unique_id() {
std::string toRet = "CLAY_UNIQUE_ID_" + std::to_string(clayUniqueIDCounter++);
return toRet;
}
void GUIManager::input_path(const std::string& id, std::filesystem::path* val, std::filesystem::file_type fileTypeRestriction, const std::function<void(SelectionHelper&)>& elemUpdate) {
push_id(id);
insert_element<TextBox<std::filesystem::path>>()->update(*io, val,
@ -564,9 +568,17 @@ bool GUIManager::font_picker(const std::string& id, std::string* fontName) {
return toRet;
}
void GUIManager::dropdown_select(const std::string& id, size_t* val, const std::vector<std::string>& selections, float width, float maxHeight, const std::function<void()>& hoverboxElemUpdate) {
void GUIManager::dropdown_select(const std::string& id, size_t* val, const std::vector<std::string>& selections, float width, const std::function<void()>& hoverboxElemUpdate) {
push_id(id);
bool& dropDownOpen = insert_any_with_id(0, false);
struct DropdownData {
std::string uniqueID;
bool isOpen = false;
};
DropdownData& dropdownData = insert_any_with_id_with_function<DropdownData>(0, [&]() {
return DropdownData {
.uniqueID = get_clay_unique_id()
};
});
left_to_right_layout(CLAY_SIZING_FIXED(width), CLAY_SIZING_FIT(0), [&]() {
bool click = selectable_button(id, [&](SelectionHelper& s, bool iS) {
CLAY({
@ -585,14 +597,20 @@ void GUIManager::dropdown_select(const std::string& id, size_t* val, const std::
.sizing = {.width = CLAY_SIZING_FIT(SMALL_BUTTON_SIZE), .height = CLAY_SIZING_FIT(SMALL_BUTTON_SIZE)}
}
}) {
svg_icon("dropico", "data/icons/droparrow.svg", dropDownOpen);
svg_icon("dropico", "data/icons/droparrow.svg", dropdownData.isOpen);
}
}
}, SelectableButton::DrawType::FILLED, dropDownOpen);
if(dropDownOpen) {
}, SelectableButton::DrawType::FILLED, dropdownData.isOpen);
if(dropdownData.isOpen) {
Clay_ElementId clayID = Clay_GetElementId(strArena.std_str_to_clay_str(dropdownData.uniqueID));
Clay_ElementData dropdownElemData = Clay_GetElementData(clayID);
float calculatedDropdownMaxHeight = 0.0f;
if(dropdownElemData.found)
calculatedDropdownMaxHeight = std::max(windowSize.y() - dropdownElemData.boundingBox.y - 2.0f, 0.0f);
CLAY({
.id = clayID,
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(width), .height = CLAY_SIZING_FIT(0, maxHeight)},
.sizing = {.width = CLAY_SIZING_FIXED(width), .height = CLAY_SIZING_FIT(0, calculatedDropdownMaxHeight)},
.childGap = 0
},
.backgroundColor = convert_vec4<Clay_Color>(io->theme->backColor1),
@ -644,7 +662,7 @@ void GUIManager::dropdown_select(const std::string& id, size_t* val, const std::
text_label(selections[i]);
if(io->mouse.leftClick && Clay_Hovered()) {
*val = i;
dropDownOpen = false;
dropdownData.isOpen = false;
}
}
}
@ -654,12 +672,13 @@ void GUIManager::dropdown_select(const std::string& id, size_t* val, const std::
}
}
if(click)
dropDownOpen = !dropDownOpen;
dropdownData.isOpen = !dropdownData.isOpen;
});
pop_id();
}
void GUIManager::scroll_bar_area(const std::string& id, bool clipHorizontal, const std::function<void(float, float, float&)>& elemUpdate) {
push_id(id);
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
@ -679,11 +698,11 @@ void GUIManager::scroll_bar_area(const std::string& id, bool clipHorizontal, con
std::string uniqueID;
};
push_id(id);
ScrollAreaData& sD = insert_any(ScrollAreaData{
.uniqueID = Random::get().alphanumeric_str(30)
ScrollAreaData& sD = insert_any_with_id_with_function<ScrollAreaData>(0, [&](){
return ScrollAreaData{
.uniqueID = get_clay_unique_id()
};
});
pop_id();
Clay_ElementId clayID = Clay_GetElementId(strArena.std_str_to_clay_str(sD.uniqueID));
@ -696,7 +715,9 @@ void GUIManager::scroll_bar_area(const std::string& id, bool clipHorizontal, con
},
.clip = {.horizontal = clipHorizontal, .vertical = true, .childOffset = {.x = 0, .y = Clay_GetScrollOffset().y}}
}) {
push_id(1);
elemUpdate(sD.contentDimensions, sD.containerDimensions, sD.currentScrollPos);
pop_id();
}
// Make sure you are accessing this data after the element has been created
@ -770,6 +791,7 @@ void GUIManager::scroll_bar_area(const std::string& id, bool clipHorizontal, con
else
sD.currentScrollPos = 0.0f;
}
pop_id();
}

View file

@ -96,7 +96,7 @@ class GUIManager {
void input_path(const std::string& id, std::filesystem::path* val, std::filesystem::file_type fileTypeRestriction, const std::function<void(SelectionHelper&)>& elemUpdate = nullptr);
void input_path_field(const std::string& id, const std::string& name, std::filesystem::path* val, std::filesystem::file_type fileTypeRestriction, const std::function<void(SelectionHelper&)>& elemUpdate = nullptr);
void dropdown_select(const std::string& id, size_t* val, const std::vector<std::string>& selections, float width = 200.0f, float maxHeight = 0.0f, const std::function<void()>& hoverboxElemUpdate = nullptr);
void dropdown_select(const std::string& id, size_t* val, const std::vector<std::string>& selections, float width = 200.0f, const std::function<void()>& hoverboxElemUpdate = nullptr);
void paint_circle_popup_menu(const std::string& id, const Vector2f& centerPos, const PaintCircleMenu::Data& val, const std::function<void()>& elemUpdate = nullptr);
@ -126,6 +126,22 @@ class GUIManager {
pop_id();
return toRet;
}
template <typename T> T& insert_any_with_function(std::function<T()> funcToRun) {
auto [it, inserted] = elements.emplace(idStack, ElementContainer());
auto& container = it->second;
if(inserted)
it->second.extra = funcToRun();
container.isUsedThisFrame = true;
return std::any_cast<T&>(it->second.extra);
}
template <typename T> T& insert_any_with_id_with_function(int64_t id, std::function<T()> funcToRun) {
push_id(id);
T& toRet = insert_any_with_function(funcToRun);
pop_id();
return toRet;
}
template <typename T> bool input_generic(const std::string& id, T* val, const std::function<std::optional<T>(const std::string&)>& fromStr, const std::function<std::string(const T&)>& toStr, bool singleLine, bool updateEveryEdit, const std::function<void(SelectionHelper&)>& elemUpdate = nullptr) {
bool isUpdating = false;
@ -307,10 +323,20 @@ class GUIManager {
bool font_picker(const std::string& id, std::string* fontName);
template <typename T> bool color_picker_items(const std::string& id, T* val, bool selectAlpha) {
template <typename T> bool color_picker_items(const std::string& id, T* val, bool selectAlpha, float fixedPickerWidth = 0.0f) {
bool isUpdating = false;
push_id(id);
isUpdating |= color_picker("c", val, selectAlpha);
if(fixedPickerWidth == 0.0f)
isUpdating |= color_picker("c", val, selectAlpha);
else {
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(fixedPickerWidth), .height = CLAY_SIZING_FIXED(fixedPickerWidth)}
}
}) {
isUpdating |= color_picker("c", val, selectAlpha);
}
}
left_to_right_line_layout([&]() {
text_label("R");
isUpdating |= input_color_component_255("r", &(*val)[0]);
@ -341,6 +367,9 @@ class GUIManager {
}
private:
std::string get_clay_unique_id();
uint64_t clayUniqueIDCounter = 0;
static void clay_error_handler(Clay_ErrorData errorData);
static Clay_Dimensions clay_skia_measure_text(Clay_StringSlice str, Clay_TextElementConfig* config, void* userData);

View file

@ -1062,21 +1062,27 @@ void Toolbar::drawing_program_gui() {
if(colorLeft) {
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(300), .height = CLAY_SIZING_FIT(0)},
.padding = CLAY_PADDING_ALL(io->theme->padding1),
.childGap = io->theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.backgroundColor = convert_vec4<Clay_Color>(io->theme->backColor1),
.cornerRadius = CLAY_CORNER_RADIUS(io->theme->windowCorners1)
.padding = {.top = 40, .bottom = 40}
}
}) {
gui.obstructing_window();
isUpdatingColorLeft |= gui.color_picker_items("colorpickerleft", colorLeft, true);
bool hoveringOnDropdown = false;
isUpdatingColorLeft |= color_palette("colorpickerleftpalette", colorLeft, hoveringOnDropdown);
if(!Clay_Hovered() && !justAssignedColorLeft && !hoveringOnDropdown && io->mouse.leftClick)
colorLeft = nullptr;
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(300), .height = CLAY_SIZING_FIT(0)},
.padding = CLAY_PADDING_ALL(io->theme->padding1),
.childGap = io->theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.backgroundColor = convert_vec4<Clay_Color>(io->theme->backColor1),
.cornerRadius = CLAY_CORNER_RADIUS(io->theme->windowCorners1)
}) {
gui.obstructing_window();
isUpdatingColorLeft |= gui.color_picker_items("colorpickerleft", colorLeft, true, 300.0f - io->theme->padding1 * 2.0f);
bool hoveringOnDropdown = false;
isUpdatingColorLeft |= color_palette("colorpickerleftpalette", colorLeft, hoveringOnDropdown);
if(!Clay_Hovered() && !justAssignedColorLeft && !hoveringOnDropdown && io->mouse.leftClick)
colorLeft = nullptr;
}
}
}
CLAY({
@ -1087,21 +1093,27 @@ void Toolbar::drawing_program_gui() {
if(colorRight) {
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(300), .height = CLAY_SIZING_FIT(0)},
.padding = CLAY_PADDING_ALL(io->theme->padding1),
.childGap = io->theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.backgroundColor = convert_vec4<Clay_Color>(io->theme->backColor1),
.cornerRadius = CLAY_CORNER_RADIUS(io->theme->windowCorners1)
.padding = {.top = 40, .bottom = 40}
}
}) {
gui.obstructing_window();
isUpdatingColorRight |= gui.color_picker_items("colorpickerright", colorRight, true);
bool hoveringOnDropdown = false;
isUpdatingColorRight |= color_palette("colorpickerrightpalette", colorRight, hoveringOnDropdown);
if(!Clay_Hovered() && !justAssignedColorRight && !hoveringOnDropdown && io->mouse.leftClick)
colorRight = nullptr;
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(300), .height = CLAY_SIZING_FIT(0)},
.padding = CLAY_PADDING_ALL(io->theme->padding1),
.childGap = io->theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP},
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
.backgroundColor = convert_vec4<Clay_Color>(io->theme->backColor1),
.cornerRadius = CLAY_CORNER_RADIUS(io->theme->windowCorners1)
}) {
gui.obstructing_window();
isUpdatingColorRight |= gui.color_picker_items("colorpickerright", colorRight, true, 300.0f - io->theme->padding1 * 2.0f);
bool hoveringOnDropdown = false;
isUpdatingColorRight |= color_palette("colorpickerrightpalette", colorRight, hoveringOnDropdown);
if(!Clay_Hovered() && !justAssignedColorRight && !hoveringOnDropdown && io->mouse.leftClick)
colorRight = nullptr;
}
}
}
main.world->drawProg.tool_options_gui();
@ -1112,42 +1124,53 @@ bool Toolbar::color_palette(const std::string& id, Vector4f* color, bool& hoveri
bool isUpdating = false;
gui.push_id(id);
auto& palette = paletteData.palettes[paletteData.selectedPalette].colors;
size_t i = 0;
constexpr float COLOR_BUTTON_SIZE = GUIStuff::GUIManager::BIG_BUTTON_SIZE;
while(i < palette.size()) {
gui.scroll_bar_area("color palette scroll area", false, [&](float, float, float &) {
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(COLOR_BUTTON_SIZE)},
.padding = {.top = 3, .bottom = 3},
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
.childGap = io->theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER},
.layoutDirection = CLAY_LEFT_TO_RIGHT
.layoutDirection = CLAY_TOP_TO_BOTTOM
}
}) {
size_t i = 0;
while(i < palette.size()) {
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(COLOR_BUTTON_SIZE), .height = CLAY_SIZING_FIXED(COLOR_BUTTON_SIZE)},
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(COLOR_BUTTON_SIZE)},
.childGap = io->theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER},
.layoutDirection = CLAY_LEFT_TO_RIGHT
}
}) {
Vector4f newC = {palette[i].x(), palette[i].y(), palette[i].z(), 1.0f};
if(gui.color_button("c" + std::to_string(i), &newC, (paletteData.selectedColor == (int)i))) {
paletteData.selectedColor = (int)i;
// We want to keep the old color's alpha
color->x() = newC.x();
color->y() = newC.y();
color->z() = newC.z();
isUpdating = true;
while(i < palette.size()) {
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(COLOR_BUTTON_SIZE), .height = CLAY_SIZING_FIXED(COLOR_BUTTON_SIZE)}
}
}) {
Vector4f newC = {palette[i].x(), palette[i].y(), palette[i].z(), 1.0f};
if(gui.color_button("c" + std::to_string(i), &newC, (paletteData.selectedColor == (int)i))) {
paletteData.selectedColor = (int)i;
// We want to keep the old color's alpha
color->x() = newC.x();
color->y() = newC.y();
color->z() = newC.z();
isUpdating = true;
}
if(paletteData.selectedColor == (int)i && (newC.x() != color->x() || newC.y() != color->y() || newC.z() != color->z()))
paletteData.selectedColor = -1;
}
i++;
if(i % 6 == 0)
break;
}
if(paletteData.selectedColor == (int)i && (newC.x() != color->x() || newC.y() != color->y() || newC.z() != color->z()))
paletteData.selectedColor = -1;
}
i++;
if(i % 6 == 0)
break;
}
}
}
});
if(paletteData.selectedPalette != 0) {
CLAY({
.layout = {
@ -1182,7 +1205,7 @@ bool Toolbar::color_palette(const std::string& id, Vector4f* color, bool& hoveri
std::vector<std::string> paletteNames;
for(auto& p : paletteData.palettes)
paletteNames.emplace_back(p.name);
gui.dropdown_select("paletteselector", &paletteData.selectedPalette, paletteNames, 200.0f, 90.0f, [&]() {
gui.dropdown_select("paletteselector", &paletteData.selectedPalette, paletteNames, 200.0f, [&]() {
hoveringOnDropdown = Clay_Hovered();
});
if(gui.svg_icon_button("paletteadd", "data/icons/plus.svg", paletteData.addingPalette, 25.0f)) paletteData.addingPalette = !paletteData.addingPalette;
@ -1442,14 +1465,14 @@ void Toolbar::options_menu() {
switch(generalSettingsOptions) {
case GSETTINGS_GENERAL: {
gui.push_id("general settings");
gui.input_text_field("display name input", "Display Name", &main.displayName);
gui.input_text_field("display name input", "Display name", &main.displayName);
main.update_display_names();
SkColor4f newBackColor{main.defaultCanvasBackgroundColor.x(), main.defaultCanvasBackgroundColor.y(), main.defaultCanvasBackgroundColor.z(), 1.0f};
gui.color_picker_button_field("defaultCanvasBackgroundColor", "Default canvas background color", &newBackColor, false);
main.defaultCanvasBackgroundColor = convert_vec3<Vector3f>(newBackColor);
#ifndef __EMSCRIPTEN__
gui.checkbox_field("native file pick", "Use Native File Picker", &useNativeFilePicker);
gui.checkbox_field("update notifications enable", "Check For Updates on Startup", &updateCheckerData.checkForUpdates);
gui.checkbox_field("native file pick", "Use native file picker", &useNativeFilePicker);
gui.checkbox_field("update notifications enable", "Check for updates on startup", &updateCheckerData.checkForUpdates);
#endif
gui.slider_scalar_field("drag zoom slider", "Drag zoom speed", &dragZoomSpeed, 0.0, 1.0, 3);
gui.slider_scalar_field("scroll zoom slider", "Scroll zoom speed", &scrollZoomSpeed, 0.0, 1.0, 3);
@ -1597,8 +1620,8 @@ void Toolbar::options_menu() {
}
case GSETTINGS_DEBUG: {
gui.push_id("debug settings menu");
gui.checkbox_field("show performance metrics", "Show Metrics", &showPerformance);
gui.input_scalar_fields("jump transition easing", "Jump Easing", &jumpTransitionEasing, 4, -10.0f, 10.0f, 2);
gui.checkbox_field("show performance metrics", "Show metrics", &showPerformance);
gui.input_scalar_fields("jump transition easing", "Jump easing", &jumpTransitionEasing, 4, -10.0f, 10.0f, 2);
#ifndef __EMSCRIPTEN__
gui.input_scalar_field("fps cap slider", "FPS cap", &main.fpsLimit, 3.0f, 10000.0f);
#endif