diff --git a/assets/data/icons/RemixIcon/settings-3-line.svg b/assets/data/icons/RemixIcon/settings-3-line.svg new file mode 100644 index 0000000..d28a6b1 --- /dev/null +++ b/assets/data/icons/RemixIcon/settings-3-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/GUIStuff/ElementHelpers/ButtonHelpers.cpp b/src/GUIStuff/ElementHelpers/ButtonHelpers.cpp index 36366e2..8b1fd09 100644 --- a/src/GUIStuff/ElementHelpers/ButtonHelpers.cpp +++ b/src/GUIStuff/ElementHelpers/ButtonHelpers.cpp @@ -1,5 +1,6 @@ #include "ButtonHelpers.hpp" #include "../Elements/SVGIcon.hpp" +#include "../Elements/LayoutElement.hpp" #include "TextLabelHelpers.hpp" namespace GUIStuff { namespace ElementHelpers { @@ -26,6 +27,51 @@ void text_button(GUIManager& gui, const char* id, std::string_view text, const T } } +void text_button_with_icon(GUIManager& gui, const char* id, const std::string& svgPath, std::string_view text, const TextButtonOptions& options) { + gui.new_id(id, [&] { + SelectableButton::Data d = selectable_button_options_to_data(options); + d.innerContent = [&gui, svgPath, text, centered = options.centered] (const SelectableButton::InnerContentCallbackParameters&) { + gui.element("layoutelem", [&] (LayoutElement* l, const Clay_ElementId& lId) { + CLAY(lId, { + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }, + .childGap = gui.io.theme->childGap1, + .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} + } + }) { + if(l->get_bb().has_value()) { + float iconSize = l->get_bb().value().height(); + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_FIXED(iconSize), .height = CLAY_SIZING_FIXED(iconSize)}, + }, + .aspectRatio = {.aspectRatio = 1.0f} + }) { + gui.element("icon", svgPath, false); + } + } + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }, + .childAlignment = {.x = centered ? CLAY_ALIGN_X_CENTER : CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} + } + }) { + text_label(gui, text); + } + } + }); + }; + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = options.wide ? CLAY_SIZING_GROW(0) : CLAY_SIZING_FIT(0), .height = CLAY_SIZING_FIT(0) }, + .childAlignment = {.x = options.centered ? CLAY_ALIGN_X_CENTER : CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} + } + }) { + gui.element("button", d); + } + }); +} + void text_button_sized(GUIManager& gui, const char* id, std::string_view text, Clay_SizingAxis x, Clay_SizingAxis y, const TextButtonOptions& options) { SelectableButton::Data d = selectable_button_options_to_data(options); d.innerContent = [&gui, text] (const SelectableButton::InnerContentCallbackParameters&) { diff --git a/src/GUIStuff/ElementHelpers/ButtonHelpers.hpp b/src/GUIStuff/ElementHelpers/ButtonHelpers.hpp index 09f46b7..c99312c 100644 --- a/src/GUIStuff/ElementHelpers/ButtonHelpers.hpp +++ b/src/GUIStuff/ElementHelpers/ButtonHelpers.hpp @@ -51,6 +51,7 @@ struct FixedSizeColorButtonOptions { }; void text_button(GUIManager& gui, const char* id, std::string_view text, const TextButtonOptions& options = {}); +void text_button_with_icon(GUIManager& gui, const char* id, const std::string& svgPath, std::string_view text, const TextButtonOptions& options = {}); void text_button_sized(GUIManager& gui, const char* id, std::string_view text, Clay_SizingAxis x, Clay_SizingAxis y, const TextButtonOptions& options = {}); SelectableButton* svg_icon_button(GUIManager& gui, const char* id, const std::string& svgPath, const SVGButtonOptions& options = {}); diff --git a/src/GUIStuff/Elements/GridScrollArea.cpp b/src/GUIStuff/Elements/GridScrollArea.cpp index b2cece3..35f3731 100644 --- a/src/GUIStuff/Elements/GridScrollArea.cpp +++ b/src/GUIStuff/Elements/GridScrollArea.cpp @@ -12,11 +12,13 @@ void GridScrollArea::layout(const Clay_ElementId& id, const Options& o) { options = o; + if(boundingBox.has_value()) + rowWidth = boundingBox.value().width(); // Use grid element bounding box instead of inner content width because row width will decide whether a scroll bar appears or not, which will change the inner content width (circular dependency) + ScrollBarManyEntriesOptions opts; opts.clipHorizontal = true; opts.entryHeight = options.entryHeight; opts.innerContentExtraCallback = [&, extraCallback = options.innerContentExtraCallback] (const ScrollArea::InnerContentParameters& contParams) { - rowWidth = std::fabs(contParams.containerDimensions.x()); if(extraCallback) extraCallback(contParams); }; size_t entriesPerRow = std::max(rowWidth / options.entryWidth, 1); diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index 0c9eb4b..ac4a49d 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -258,9 +258,11 @@ void GUIManager::draw(SkCanvas* c, bool skiaAA) { break; } case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: { - canvas->restore(); - clipRectStack.pop_back(); - clip_rect_transform(canvas, clipRectStack, clipRect, clipNoDraw); + if(!clipRectStack.empty()) { + canvas->restore(); + clipRectStack.pop_back(); + clip_rect_transform(canvas, clipRectStack, clipRect, clipNoDraw); + } break; } case CLAY_RENDER_COMMAND_TYPE_CUSTOM: { diff --git a/src/Screens/FileSelectScreen.cpp b/src/Screens/FileSelectScreen.cpp index 27ffbb0..fc180ff 100644 --- a/src/Screens/FileSelectScreen.cpp +++ b/src/Screens/FileSelectScreen.cpp @@ -6,75 +6,204 @@ #include "Helpers/ConvertVec.hpp" #include "../GUIStuff/Elements/GridScrollArea.hpp" #include "../GUIStuff/Elements/ImageDisplay.hpp" +#include "../GUIStuff/Elements/LayoutElement.hpp" #include "../World.hpp" #include "Helpers/StringHelpers.hpp" #include +#define MAIN_MENU_SIZE 300 + +using namespace GUIStuff; +using namespace ElementHelpers; + FileSelectScreen::FileSelectScreen(MainProgram& m): Screen(m) {} void FileSelectScreen::gui_layout_run() { - using namespace GUIStuff; - using namespace ElementHelpers; auto& gui = main.g.gui; - CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, - .padding = CLAY_PADDING_ALL(gui.io.theme->padding1), - .childGap = gui.io.theme->childGap1, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP }, - .layoutDirection = CLAY_TOP_TO_BOTTOM, + .layoutDirection = CLAY_LEFT_TO_RIGHT }, }) { - CLAY_AUTO_ID({ + if(mainMenuOpen) + main_menu(); + if(!(mainMenuOpen && main_menu_fills_screen())) { + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_FIXED(gui.io.windowSize.x()), .height = CLAY_SIZING_GROW(0)}, + .padding = CLAY_PADDING_ALL(gui.io.theme->padding1), + .childGap = gui.io.theme->childGap1, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, + }) { + switch(selectedMenu) { + case SelectedMenu::FILES: + title_bar("Files"); + file_view(); + break; + case SelectedMenu::TRASH: + title_bar("Trash"); + file_view(); + break; + case SelectedMenu::SETTINGS: + title_bar("Settings"); + break; + } + menu_black_box(); + } + } + } +} + +bool FileSelectScreen::main_menu_fills_screen() { + return main.g.gui.io.windowSize.x() < MAIN_MENU_SIZE + 50; +} + +void FileSelectScreen::title_bar(std::string_view title) { + auto& gui = main.g.gui; + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(40)}, + .childGap = gui.io.theme->childGap1, + .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + } + }) { + svg_icon_button(gui, "main settings button", "data/icons/menu.svg", { + .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, + .onClick = [&] { + mainMenuOpen = true; + } + }); + text_label(gui, title); + } +} + +void FileSelectScreen::main_menu() { + auto& gui = main.g.gui; + gui.element("main menu popup", [&] (LayoutElement*, const Clay_ElementId& lId) { + CLAY(lId, { .layout = { - .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(40)}, + .sizing = {.width = CLAY_SIZING_GROW(static_cast(main_menu_fills_screen() ? 0 : 300)), .height = CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(gui.io.theme->padding1), .childGap = gui.io.theme->childGap1, - .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_LEFT_TO_RIGHT, - } + .layoutDirection = CLAY_TOP_TO_BOTTOM, + }, }) { - svg_icon_button(gui, "main settings button", "data/icons/menu.svg", { - .drawType = SelectableButton::DrawType::TRANSPARENT_ALL + text_button_with_icon(gui, "Files", "data/icons/folder.svg", "Files", { + .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, + .isSelected = selectedMenu == SelectedMenu::FILES, + .wide = true, + .centered = false, + .onClick = [&] { + selectedMenu = SelectedMenu::FILES; + fileListOptional.reset(); + mainMenuOpen = false; + } + }); + text_button_with_icon(gui, "Trash", "data/icons/trash.svg", "Trash", { + .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, + .isSelected = selectedMenu == SelectedMenu::TRASH, + .wide = true, + .centered = false, + .onClick = [&] { + selectedMenu = SelectedMenu::TRASH; + fileListOptional.reset(); + mainMenuOpen = false; + } + }); + text_button_with_icon(gui, "Settings", "data/icons/RemixIcon/settings-3-line.svg", "Settings", { + .drawType = SelectableButton::DrawType::TRANSPARENT_ALL, + .isSelected = selectedMenu == SelectedMenu::SETTINGS, + .wide = true, + .centered = false, + .onClick = [&] { + selectedMenu = SelectedMenu::SETTINGS; + mainMenuOpen = false; + } }); - text_label(gui, "File selector"); } - auto& fileList = get_file_list(); - gui.element("File selector grid", GridScrollArea::Options{ - .entryWidth = 200.0f, - .childAlignmentX = CLAY_ALIGN_X_LEFT, - .entryHeight = 220.0f, - .entryCount = fileList.size(), - .elementContent = [&](size_t i) { - CLAY_AUTO_ID({ + }); +} + +void FileSelectScreen::file_view() { + auto& gui = main.g.gui; + bool isTrash = selectedMenu == SelectedMenu::TRASH; + std::string folder = isTrash ? "trash" : "saves"; + auto& fileList = get_file_list(folder); + gui.element("File selector grid", GridScrollArea::Options{ + .entryWidth = 180.0f, + .childAlignmentX = CLAY_ALIGN_X_LEFT, + .entryHeight = 200.0f, + .entryCount = fileList.size(), + .elementContent = [&](size_t i) { + gui.element("file button", SelectableButton::Data{ + .onClick = [&] { + }, + .innerContent = [&](const SelectableButton::InnerContentCallbackParameters& c){ + CLAY_AUTO_ID({ + .layout = { + .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, + .padding = CLAY_PADDING_ALL(gui.io.theme->padding1), + .childGap = 6, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM + } + }) { + CLAY_AUTO_ID({ + .layout = { .sizing = {.width = CLAY_SIZING_FIXED(130), .height = CLAY_SIZING_FIXED(130) }}, + }) { + gui.element("ico", ImageDisplay::Data{ + .imgPath = main.conf.configPath / folder / (fileList[i].fileName + ".jpg"), + .radius = 20 + }); + } + text_label(gui, fileList[i].fileName); + text_label_light(gui, fileList[i].lastModifyDate); + } + } + }); + } + }); +} + +void FileSelectScreen::menu_black_box() { + auto& gui = main.g.gui; + if(mainMenuOpen) { + gui.set_z_index(gui.get_z_index() + 1, [&] { + gui.element("file list disable fill", [&] (LayoutElement* l, const Clay_ElementId& lId) { + CLAY(lId, { .layout = { - .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, - .padding = CLAY_PADDING_ALL(gui.io.theme->padding1), - .childGap = 6, - .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, - .layoutDirection = CLAY_TOP_TO_BOTTOM + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, + }, + .backgroundColor = {0.0f, 0.0f, 0.0f, 0.6f}, + .floating = { + .zIndex = gui.get_z_index(), + .attachPoints = { + .element = CLAY_ATTACH_POINT_LEFT_TOP, + .parent = CLAY_ATTACH_POINT_LEFT_TOP + }, + .attachTo = CLAY_ATTACH_TO_PARENT } }) { - CLAY_AUTO_ID({ - .layout = { .sizing = {.width = CLAY_SIZING_FIXED(150), .height = CLAY_SIZING_FIXED(150) }}, - }) { - gui.element("ico", ImageDisplay::Data{ - .imgPath = main.conf.configPath / "saveThumbnails" / (fileList[i].fileName + ".jpg"), - .radius = 20 - }); - } - text_label(gui, fileList[i].fileName); - text_label_light(gui, fileList[i].lastModifyDate); } - } + }, LayoutElement::Callbacks{ + .mouseButton = [&] (LayoutElement* l, const InputManager::MouseButtonCallbackArgs& m) { + if(m.down && m.button == InputManager::MouseButton::LEFT && l->mouseHovering) { + mainMenuOpen = false; + gui.set_to_layout(); + } + } + }); }); } } -const std::vector& FileSelectScreen::get_file_list() { +const std::vector& FileSelectScreen::get_file_list(const std::string& folder) { if(!fileListOptional.has_value()) { - std::filesystem::path savePath = main.conf.configPath / "saves"; + std::filesystem::path savePath = main.conf.configPath / folder; fileListOptional = std::vector(); auto& fL = fileListOptional.value(); int globCount; diff --git a/src/Screens/FileSelectScreen.hpp b/src/Screens/FileSelectScreen.hpp index c8bbbb4..5fbb029 100644 --- a/src/Screens/FileSelectScreen.hpp +++ b/src/Screens/FileSelectScreen.hpp @@ -12,6 +12,19 @@ class FileSelectScreen : public Screen { SDL_Time lastModifyTime; std::string lastModifyDate; }; - const std::vector& get_file_list(); + const std::vector& get_file_list(const std::string& folder); std::optional> fileListOptional; + + void main_menu(); + void file_view(); + bool main_menu_fills_screen(); + void menu_black_box(); + void title_bar(std::string_view title); + + enum class SelectedMenu { + FILES, + TRASH, + SETTINGS + } selectedMenu = SelectedMenu::FILES; + bool mainMenuOpen = false; };