From fd2ce71db66acad1e73c169fe154dbf15494bdb7 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Tue, 12 May 2026 15:19:43 +0300 Subject: [PATCH] Added top toolbar options --- src/DrawingProgram/DrawingProgram.cpp | 6 +- src/DrawingProgram/DrawingProgram.hpp | 3 +- src/GUIStuff/ElementHelpers/LayoutHelpers.cpp | 10 +- src/GUIStuff/ElementHelpers/LayoutHelpers.hpp | 2 +- .../ElementHelpers/TextBoxHelpers.hpp | 6 + src/GUIStuff/Elements/TextBox.decl.hpp | 1 + src/GUIStuff/Elements/TextBox.impl.hpp | 18 +- src/Screens/PhoneDrawingProgramScreen.cpp | 165 ++++++++++++++++-- src/Screens/PhoneDrawingProgramScreen.hpp | 28 ++- src/Toolbar.cpp | 4 +- src/World.cpp | 6 +- src/World.hpp | 2 +- 12 files changed, 205 insertions(+), 46 deletions(-) diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 7938248..44a1573 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -782,15 +782,11 @@ void DrawingProgram::get_used_resources(std::unordered_setcolor_picker_color(oldColor); } diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index 92363cc..df1028b 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -58,8 +58,7 @@ class DrawingProgram { CanvasComponentContainer::ObjInfo* add_file_to_canvas_by_data(const std::string& fileName, std::string_view fileBuffer, Vector2f dropPos); void get_used_resources(std::unordered_set& resourceSet); - Vector4f* color_picker_left(Vector4f* oldColor); - Vector4f* color_picker_right(Vector4f* oldColor); + Vector4f* color_picker_color(Vector4f* oldColor); bool phone_gui_tool_specific_bottom_toolbar_exists(); void phone_gui_tool_specific_bottom_toolbar(PhoneDrawingProgramScreen& t); diff --git a/src/GUIStuff/ElementHelpers/LayoutHelpers.cpp b/src/GUIStuff/ElementHelpers/LayoutHelpers.cpp index 5615692..9c9fac5 100644 --- a/src/GUIStuff/ElementHelpers/LayoutHelpers.cpp +++ b/src/GUIStuff/ElementHelpers/LayoutHelpers.cpp @@ -291,9 +291,14 @@ void left_right_offset_setup(GUIManager& gui, float dropdownHeight, Clay_Floatin offsetY = gui.io.safeWindowRect.min.y(); else if(offsetY + dropdownHeight > gui.io.safeWindowRect.max.y()) offsetY -= (offsetY + dropdownHeight) - gui.io.safeWindowRect.max.y(); + float offsetX = 0.0f; + if(isRightSide) + offsetX = bb.max.x() + d.dropdownOffset; + else + offsetX = bb.min.x() - d.entrySize.x() - d.dropdownOffset; floatConfig = { .offset = { - .x = isRightSide ? bb.max.x() + d.dropdownOffset : bb.min.x() - bb.width() - d.dropdownOffset, + .x = offsetX, .y = offsetY }, .zIndex = gui.get_z_index(), @@ -306,9 +311,10 @@ void left_right_offset_setup(GUIManager& gui, float dropdownHeight, Clay_Floatin maxHeight = gui.io.safeWindowRect.height(); } -void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, const DropDownPopupLayout& d) { +void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, DropDownPopupLayout d) { if(!d.button->get_bb().has_value()) return; + d.entrySize.x() = std::min(d.entrySize.x(), gui.io.windowSize.x()); gui.element(id, [&](LayoutElement* l, const Clay_ElementId& lId) { float dropdownHeight = d.entrySize.y() * d.entryCount + d.dropdownOffset; Clay_FloatingElementConfig floatConfig; diff --git a/src/GUIStuff/ElementHelpers/LayoutHelpers.hpp b/src/GUIStuff/ElementHelpers/LayoutHelpers.hpp index b328497..0f9b4ee 100644 --- a/src/GUIStuff/ElementHelpers/LayoutHelpers.hpp +++ b/src/GUIStuff/ElementHelpers/LayoutHelpers.hpp @@ -50,6 +50,6 @@ struct DropDownPopupLayout { std::function entryLayout; }; -void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, const DropDownPopupLayout& d); +void dropdown_many_element_popup_layout(GUIManager& gui, const char* id, DropDownPopupLayout d); }} diff --git a/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp b/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp index 5776f1e..cc379ff 100644 --- a/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp +++ b/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp @@ -29,6 +29,7 @@ template TextBoxData textbox_options_to_data(cons toRet.onEnter = options.onEnter; toRet.onEdit = options.onEdit; toRet.immutable = options.immutable; + toRet.decorations = options.decorations; toRet.onSelect = options.onSelect; toRet.onDeselect = options.onDeselect; toRet.emptyText = options.emptyText; @@ -37,6 +38,7 @@ template TextBoxData textbox_options_to_data(cons struct TextBoxOptions { bool immutable = false; + bool decorations = true; std::string emptyText; std::function onEnter; std::function onEdit; @@ -47,6 +49,7 @@ struct TextBoxOptions { struct TextBoxScalarOptions { int decimalPrecision = 0; bool immutable = false; + bool decorations = true; std::string emptyText; std::function onEnter; std::function onEdit; @@ -57,6 +60,7 @@ struct TextBoxScalarOptions { struct TextBoxScalarsOptions { int decimalPrecision = 0; bool immutable = false; + bool decorations = true; std::string emptyText; std::function onEnter; std::function onEdit; @@ -67,6 +71,7 @@ struct TextBoxScalarsOptions { struct TextBoxPathOptions { std::filesystem::file_type fileTypeRestriction; bool immutable = false; + bool decorations = true; std::string emptyText; std::function onEnter; std::function onEdit; @@ -77,6 +82,7 @@ struct TextBoxPathOptions { struct TextBoxHexColorOptions { bool hasAlpha = true; bool immutable = false; + bool decorations = true; std::string emptyText; std::function onEnter; std::function onEdit; diff --git a/src/GUIStuff/Elements/TextBox.decl.hpp b/src/GUIStuff/Elements/TextBox.decl.hpp index 06a7301..27dcab2 100644 --- a/src/GUIStuff/Elements/TextBox.decl.hpp +++ b/src/GUIStuff/Elements/TextBox.decl.hpp @@ -33,6 +33,7 @@ template struct TextBoxData { std::function toStr; bool singleLine = true; bool immutable = false; + bool decorations = true; InputManager::TextInputProperties textInputProps; std::string emptyText; std::function onEdit; diff --git a/src/GUIStuff/Elements/TextBox.impl.hpp b/src/GUIStuff/Elements/TextBox.impl.hpp index 1ec1ef9..99e1259 100644 --- a/src/GUIStuff/Elements/TextBox.impl.hpp +++ b/src/GUIStuff/Elements/TextBox.impl.hpp @@ -66,14 +66,16 @@ template void TextBox::clay_draw(SkCanvas* canvas, UpdateInputDa canvas->save(); SkRect r = SkRect::MakeXYWH(bb.min.x(), bb.min.y(), bb.width(), bb.height()); - canvas->drawRect(r, SkPaint(io.theme->backColor2)); - SkPaint outline(is_selected() ? io.theme->fillColor1 : io.theme->backColor2); - outline.setStroke(true); - outline.setStrokeWidth(2.0f); - outline.setAntiAlias(skiaAA); - canvas->drawRoundRect(r, 2.0f, 2.0f, outline); + if(userInfo.decorations) { + canvas->drawRect(r, SkPaint(io.theme->backColor2)); + SkPaint outline(is_selected() ? io.theme->fillColor1 : io.theme->backColor2); + outline.setStroke(true); + outline.setStrokeWidth(2.0f); + outline.setAntiAlias(skiaAA); + canvas->drawRoundRect(r, 2.0f, 2.0f, outline); + } - canvas->clipRect(SkRect::MakeXYWH(bb.min.x(), bb.min.y(), bb.width(), bb.height())); + canvas->clipRect(r); float yOffset = bb.height() * 0.5f; yOffset -= textbox->get_height() * 0.5f; @@ -105,7 +107,6 @@ template void TextBox::select() { if(isEmptyText) textbox->clear_text(); edit = std::make_unique(CustomEvents::text_box_get_new_id(), textbox, cur, nullptr); - Logger::get().log("INFO", "Select"); CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{}); } } @@ -114,7 +115,6 @@ template void TextBox::deselect() { if(is_selected()) { edit = nullptr; if(userInfo.onDeselect) userInfo.onDeselect(); - Logger::get().log("INFO", "Deselect"); CustomEvents::emit_event(CustomEvents::RefreshTextBoxInputEvent{}); reset_textbox_text(); populate_empty_textbox(); diff --git a/src/Screens/PhoneDrawingProgramScreen.cpp b/src/Screens/PhoneDrawingProgramScreen.cpp index c7a4bb7..9a9ced1 100644 --- a/src/Screens/PhoneDrawingProgramScreen.cpp +++ b/src/Screens/PhoneDrawingProgramScreen.cpp @@ -92,13 +92,140 @@ void PhoneDrawingProgramScreen::top_toolbar() { main.set_screen([&] (std::unique_ptr) { return std::make_unique(main); }); } }); - text_label(gui, main.world->name); + top_toolbar_remaining_area(); } }); } }); } +void PhoneDrawingProgramScreen::top_toolbar_remaining_area() { + auto& gui = main.g.gui; + std::vector listOfTopToolbarButtons = { + TopToolbarRemainingAreaButton{ + .name = "Undo", + .svgPath = "data/icons/undo.svg", + .onClick = [&] { + main.world->undo_with_checks(); + } + }, + TopToolbarRemainingAreaButton{ + .name = "Redo", + .svgPath = "data/icons/redo.svg", + .onClick = [&] { + main.world->redo_with_checks(); + } + }, + }; + gui.element("remaining area", [&](LayoutElement* l, const Clay_ElementId& lId) { + CLAY(lId, { + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)}, + .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} + } + }) { + if(l->get_bb().has_value()) { + constexpr float MINIMUM_SPACE_FOR_NAME = 100.0f; + constexpr float BUTTON_WIDTH = BIG_BUTTON_SIZE; + float spaceForWorldName = l->get_bb().value().width() - BUTTON_WIDTH; + if(spaceForWorldName < 4.0f) + spaceForWorldName = 4.0f; + size_t buttonFreeSpace; + for(buttonFreeSpace = 0; buttonFreeSpace < listOfTopToolbarButtons.size(); buttonFreeSpace++) { + if(spaceForWorldName <= MINIMUM_SPACE_FOR_NAME + BUTTON_WIDTH) + break; + spaceForWorldName -= BUTTON_WIDTH; + } + input_text(gui, "world name text", &main.world->name, { + .decorations = false, + .onSelect = [&] { + fileOldPath = main.world->filePath; + }, + .onDeselect = [&] { + std::filesystem::remove(fileOldPath); + std::filesystem::remove(fileOldPath.parent_path() / (fileOldPath.stem().string() + ".jpg")); + main.world->autosave_to_directory(main.world->filePath.parent_path()); + } + }); + gui.new_id("visible buttons", [&] { + for(size_t i = 0; i < buttonFreeSpace; i++) { + gui.new_id(i, [&] { + svg_icon_button(gui, "button", listOfTopToolbarButtons.front().svgPath, { + .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, + .onClick = listOfTopToolbarButtons.front().onClick + }); + listOfTopToolbarButtons.erase(listOfTopToolbarButtons.begin()); + }); + } + }); + Element* e = svg_icon_button(gui, "Hidden buttons", "data/icons/RemixIcon/more-2-fill.svg", { + .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, + .onClick = [&] { + topToolbarButtonPopupOpen = !topToolbarButtonPopupOpen; + } + }); + if(topToolbarButtonPopupOpen) + top_toolbar_hidden_button_popup(e, listOfTopToolbarButtons); + } + } + }); +} + +void PhoneDrawingProgramScreen::top_toolbar_hidden_button_popup(GUIStuff::Element* b, std::vector l) { + auto& gui = main.g.gui; + + l.emplace_back(TopToolbarRemainingAreaButton{ + .name = "Canvas Color", + .svgPath = "data/icons/RemixIcon/settings-3-line.svg", + .onClick = [&] { + settingsMenuPopup = SettingsMenuPopup::SETTINGS; + colorPickerPtr = &backgroundColorTemporary; + colorPickerData = { + .onChange = [&] { + main.world->canvasTheme.set_back_color({backgroundColorTemporary.x(), backgroundColorTemporary.y(), backgroundColorTemporary.z()}); + } + }; + } + }); + + gui.set_z_index(gui.get_z_index() + 1, [&] { + dropdown_many_element_popup_layout(gui, "top toolbar hidden button popup", { + .button = b, + .isOpen = &topToolbarButtonPopupOpen, + .entrySize = {150.0f, 30.0f}, + .entryCount = l.size(), + .entryLayout = [&] (size_t i) { + auto& listItem = l[i]; + gui.element("button", SelectableButton::Data{ + .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, + .onClick = [&, oC = listItem.onClick] { + oC(); + topToolbarButtonPopupOpen = false; + }, + .innerContent = [&] (auto&) { + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)}, + .childGap = gui.io.theme->childGap1, + .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER}, + } + }) { + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_FIXED(20), .height = CLAY_SIZING_FIXED(20)}, + } + }) { + gui.element("icon", listItem.svgPath); + } + mutable_text_label(gui, "text", listItem.name); + } + } + }); + } + }); + }); +} + void PhoneDrawingProgramScreen::bottom_toolbar() { auto& gui = main.g.gui; auto& io = gui.io; @@ -144,7 +271,8 @@ void PhoneDrawingProgramScreen::default_bottom_toolbar() { colorPickerPtr = nullptr; break; case SettingsMenuPopup::SETTINGS: - colorPickerPtr = main.world->drawProg.color_picker_right(colorPickerPtr); + if(colorPickerPtr != &backgroundColorTemporary) + colorPickerPtr = main.world->drawProg.color_picker_color(colorPickerPtr); if(colorPickerPtr) { if(colorPickerPopupData.screenType == ColorPickerPopupData::ScreenType::NORMAL) { CLAY_AUTO_ID({ @@ -167,8 +295,7 @@ void PhoneDrawingProgramScreen::default_bottom_toolbar() { tool_settings_popup(); } break; - case SettingsMenuPopup::FG_COLOR: - case SettingsMenuPopup::BG_COLOR: + case SettingsMenuPopup::COLOR_CHANGE: if(colorPickerPopupData.screenType == ColorPickerPopupData::ScreenType::NORMAL) { CLAY_AUTO_ID({ .layout = { @@ -176,9 +303,7 @@ void PhoneDrawingProgramScreen::default_bottom_toolbar() { } }) {} } - colorPickerData = ColorSelectorData(); - colorPickerPtr = nullptr; - color_settings_popup(settingsMenuPopup == SettingsMenuPopup::FG_COLOR ? &main.toolConfig.globalConf.foregroundColor : &main.toolConfig.globalConf.backgroundColor, {}, nullptr); + color_settings_popup(colorPickerPtr, colorPickerData, nullptr); break; } } @@ -612,19 +737,35 @@ void PhoneDrawingProgramScreen::bottom_extra_toolbar_gui() { color_button(gui, "foreground color", &main.toolConfig.globalConf.foregroundColor, { .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, - .isSelected = settingsMenuPopup == SettingsMenuPopup::FG_COLOR, + .isSelected = settingsMenuPopup == SettingsMenuPopup::COLOR_CHANGE && colorPickerPtr == &main.toolConfig.globalConf.foregroundColor, .hasAlpha = true, .onClick = [&] { - settingsMenuPopup = (settingsMenuPopup == SettingsMenuPopup::FG_COLOR) ? SettingsMenuPopup::NONE : SettingsMenuPopup::FG_COLOR; + if(settingsMenuPopup == SettingsMenuPopup::COLOR_CHANGE && colorPickerPtr == &main.toolConfig.globalConf.foregroundColor) { + colorPickerPtr = nullptr; + settingsMenuPopup = SettingsMenuPopup::NONE; + } + else { + colorPickerPtr = &main.toolConfig.globalConf.foregroundColor; + settingsMenuPopup = SettingsMenuPopup::COLOR_CHANGE; + } + colorPickerData = ColorSelectorData(); } }); color_button(gui, "background color", &main.toolConfig.globalConf.backgroundColor, { .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, - .isSelected = settingsMenuPopup == SettingsMenuPopup::BG_COLOR, + .isSelected = settingsMenuPopup == SettingsMenuPopup::COLOR_CHANGE && colorPickerPtr == &main.toolConfig.globalConf.backgroundColor, .hasAlpha = true, .onClick = [&] { - settingsMenuPopup = (settingsMenuPopup == SettingsMenuPopup::BG_COLOR) ? SettingsMenuPopup::NONE : SettingsMenuPopup::BG_COLOR; + if(settingsMenuPopup == SettingsMenuPopup::COLOR_CHANGE && colorPickerPtr == &main.toolConfig.globalConf.backgroundColor) { + colorPickerPtr = nullptr; + settingsMenuPopup = SettingsMenuPopup::NONE; + } + else { + colorPickerPtr = &main.toolConfig.globalConf.backgroundColor; + settingsMenuPopup = SettingsMenuPopup::COLOR_CHANGE; + } + colorPickerData = ColorSelectorData(); } }); } @@ -659,7 +800,7 @@ void PhoneDrawingProgramScreen::color_selector_button(const char* id, Vector4f* color_selector(color, { .onChange = colorSelectorData.onChange, .onSelect = colorSelectorData.onSelect, - .onDeselect = colorSelectorData.onDeselect, + .onDeselect = colorSelectorData.onDeselect }); } }); diff --git a/src/Screens/PhoneDrawingProgramScreen.hpp b/src/Screens/PhoneDrawingProgramScreen.hpp index 04cc4ec..dadc7bf 100644 --- a/src/Screens/PhoneDrawingProgramScreen.hpp +++ b/src/Screens/PhoneDrawingProgramScreen.hpp @@ -54,21 +54,31 @@ class PhoneDrawingProgramScreen : public DrawingProgramScreen { void color_settings_popup(Vector4f* color, const ColorSelectorData& d, std::function backOnPaletteButton = nullptr, bool setAlphaToOne = false, bool setToTransparentCancelButton = false); void reset_color_picker_popup_data(); private: + bool topToolbarButtonPopupOpen = false; + std::filesystem::path fileOldPath; + struct TopToolbarRemainingAreaButton { + std::string name; + std::string svgPath; + std::function onClick; + }; + enum class SettingsMenuPopup { + NONE, + SETTINGS, + COLOR_CHANGE + } settingsMenuPopup = SettingsMenuPopup::NONE; + + Vector4f backgroundColorTemporary; + Vector4f* colorPickerPtr = nullptr; + ColorSelectorData colorPickerData; + void default_bottom_toolbar(); void bottom_toolbar_gui(); void bottom_extra_toolbar_gui(); void save_files(); void top_toolbar(); + void top_toolbar_remaining_area(); + void top_toolbar_hidden_button_popup(GUIStuff::Element* b, std::vector l); void bottom_toolbar(); void main_display(); void tool_settings_popup(); - enum class SettingsMenuPopup { - NONE, - SETTINGS, - FG_COLOR, - BG_COLOR - } settingsMenuPopup = SettingsMenuPopup::NONE; - - Vector4f* colorPickerPtr = nullptr; - ColorSelectorData colorPickerData; }; diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index ae8c357..3904fcc 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -1186,7 +1186,7 @@ void Toolbar::drawing_program_gui() { }) { main.world->drawProg.toolbar_gui(*this); - colorLeft = main.world->drawProg.color_picker_left(colorLeft); + colorLeft = main.world->drawProg.color_picker_color(colorLeft); if(colorLeft) color_picker_window("Drawing program gui color picker left", &colorLeft, colorLeftButton, colorLeftData); CLAY_AUTO_ID({ @@ -1194,7 +1194,7 @@ void Toolbar::drawing_program_gui() { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)} } }) {} - colorRight = main.world->drawProg.color_picker_right(colorRight); + colorRight = main.world->drawProg.color_picker_color(colorRight); if(colorRight) color_picker_window("Drawing program gui color picker right", &colorRight, colorRightButton, colorRightData); diff --git a/src/World.cpp b/src/World.cpp index 8818425..b10013b 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -470,10 +470,10 @@ void World::autosave_to_directory(const std::filesystem::path& directoryToSaveAt catch(...) { } std::string nameToSaveUnder = ensure_string_unique(strList, name); - save_to_file(directoryToSaveAt / std::filesystem::path(nameToSaveUnder + "." + FILE_EXTENSION), true); + save_to_file(directoryToSaveAt / std::filesystem::path(nameToSaveUnder + "." + FILE_EXTENSION)); } -void World::save_to_file(const std::filesystem::path& filePathToSaveAt, bool disableThumbnailSaving) { +void World::save_to_file(const std::filesystem::path& filePathToSaveAt) { try { filePath = filePathToSaveAt; @@ -505,7 +505,7 @@ void World::save_to_file(const std::filesystem::path& filePathToSaveAt, bool dis #else if(!SDL_SaveFile(filePath.string().c_str(), f.view().data(), f.view().size())) throw std::runtime_error("SDL_SaveFile failed with error: " + std::string(SDL_GetError())); - else if(saveThumbnail && !disableThumbnailSaving) { + else if(saveThumbnail) { Vector2f imageCenter{main.window.size.x() * 0.5f, main.window.size.y() * 0.5f}; float imageDim = std::max(main.window.size.x(), main.window.size.y()); Vector2f imageDimVec{imageDim * 0.5f, imageDim * 0.5f}; diff --git a/src/World.hpp b/src/World.hpp index 8d7c7a1..2f0afc5 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -84,7 +84,7 @@ class World { bool clientStillConnecting = false; void autosave_to_directory(const std::filesystem::path& directoryToSaveAt); - void save_to_file(const std::filesystem::path& filePathToSaveAt, bool disableThumbnailSaving = false); + void save_to_file(const std::filesystem::path& filePathToSaveAt); void load_from_file(const std::filesystem::path& filePathToLoadFrom, std::string_view buffer); void undo_with_checks();