From 87f5f74c8677893a147b639410197dec7bcf84c8 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 19 Apr 2026 17:44:48 +0300 Subject: [PATCH] More scroll area fixes, added image display, and continuing work on file selector --- CMakeLists.txt | 1 + src/GUIHolder.cpp | 4 -- .../ElementHelpers/ScrollAreaHelpers.cpp | 30 +++++++---- .../ElementHelpers/ScrollAreaHelpers.hpp | 1 + src/GUIStuff/Elements/DropDown.hpp | 1 + src/GUIStuff/Elements/ImageDisplay.cpp | 42 +++++++++++++++ src/GUIStuff/Elements/ImageDisplay.hpp | 18 +++++++ src/GUIStuff/Elements/TreeListing.cpp | 1 + src/GUIStuff/Elements/TreeListing.hpp | 1 + src/Screens/FileSelectScreen.cpp | 53 ++++++++++++++----- src/Screens/FileSelectScreen.hpp | 3 ++ src/Toolbar.cpp | 1 + 12 files changed, 131 insertions(+), 25 deletions(-) create mode 100644 src/GUIStuff/Elements/ImageDisplay.cpp create mode 100644 src/GUIStuff/Elements/ImageDisplay.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 812114c..3e8e9d3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,7 @@ set(sources "src/main.cpp" "src/GUIStuff/ClayImplement.cpp" "src/GUIStuff/Elements/GUIStuffHelpers.cpp" "src/GUIStuff/Elements/Element.cpp" + "src/GUIStuff/Elements/ImageDisplay.cpp" "src/GUIStuff/Elements/SVGIcon.cpp" "src/GUIStuff/Elements/RadioButton.cpp" "src/GUIStuff/Elements/CheckBox.cpp" diff --git a/src/GUIHolder.cpp b/src/GUIHolder.cpp index d840ba9..fd3f0ab 100644 --- a/src/GUIHolder.cpp +++ b/src/GUIHolder.cpp @@ -19,10 +19,6 @@ GUIHolder::GUIHolder(MainProgram& m): // So, it's better to load the icons at the beginning of the program so that the icon loading doesn't fail later load_icons_at("data/icons"); -#ifdef __ANDROID__ - load_icons_at("data/icons/RemixIcon"); // SDL_GlobDirectory doesn't work recursively on Android -#endif - load_default_theme(); } diff --git a/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.cpp b/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.cpp index fac18fa..36a0c75 100644 --- a/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.cpp +++ b/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.cpp @@ -1,6 +1,5 @@ #include "ScrollAreaHelpers.hpp" #include "../GUIManager.hpp" -#include "../Elements/LayoutElement.hpp" namespace GUIStuff { namespace ElementHelpers { @@ -13,8 +12,23 @@ ScrollArea* scroll_area_many_entries(GUIManager& gui, const char* id, const Scro .innerContent = [&](const ScrollArea::InnerContentParameters& params) { if(options.innerContentExtraCallback) options.innerContentExtraCallback(params); - if(params.containerDimensions.y() == 0.0f) + if(params.containerDimensions.y() == 0.0f) { + if(options.growing) { + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(1)}, + } + }) { } + } + else { + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, + } + }) { } + } return; + } float absScrollAmount = std::fabs(params.scrollOffset->y()); size_t startPoint = absScrollAmount / options.entryHeight; @@ -32,13 +46,11 @@ ScrollArea* scroll_area_many_entries(GUIManager& gui, const char* id, const Scro gui.new_id("many element list", [&] { for(size_t i = startPoint; i < endPoint; i++) { gui.new_id(i - startPoint, [&] { - gui.element("elem", [&] (LayoutElement*, const Clay_ElementId& lId) { - CLAY(lId, { - .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(options.entryHeight)}} - }) { - options.elementContent(i); - } - }); + CLAY_AUTO_ID({ + .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(options.entryHeight)}} + }) { + options.elementContent(i); + } }); } }); diff --git a/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.hpp b/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.hpp index 08bd835..9dd9e03 100644 --- a/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.hpp +++ b/src/GUIStuff/ElementHelpers/ScrollAreaHelpers.hpp @@ -7,6 +7,7 @@ struct ScrollBarManyEntriesOptions { float entryHeight; size_t entryCount; bool clipHorizontal = false; + bool growing = false; // If set to false, this element has a fixed size. Otherwise, is a growing element. std::function elementContent; std::function innerContentExtraCallback; }; diff --git a/src/GUIStuff/Elements/DropDown.hpp b/src/GUIStuff/Elements/DropDown.hpp index d1e59e1..68be583 100644 --- a/src/GUIStuff/Elements/DropDown.hpp +++ b/src/GUIStuff/Elements/DropDown.hpp @@ -85,6 +85,7 @@ template class DropDown : public Element { .entryHeight = 18.0f, .entryCount = selections.size(), .clipHorizontal = true, + .growing = true, .elementContent = [&](size_t i) { bool selectedEntry = static_cast(*data) == i; gui.element("elem", [&] (LayoutElement*, const Clay_ElementId& lId) { diff --git a/src/GUIStuff/Elements/ImageDisplay.cpp b/src/GUIStuff/Elements/ImageDisplay.cpp new file mode 100644 index 0000000..70c5119 --- /dev/null +++ b/src/GUIStuff/Elements/ImageDisplay.cpp @@ -0,0 +1,42 @@ +#include "ImageDisplay.hpp" +#include "../GUIManager.hpp" +#include + +namespace GUIStuff { + +ImageDisplay::ImageDisplay(GUIManager& gui): Element(gui) {} + +void ImageDisplay::layout(const Clay_ElementId& id, const std::filesystem::path& imgPath) { + if(imgPath != oldImgPath) { + std::string imgData; + try { + imgData = read_file_to_string(imgPath); + auto codec = SkCodec::MakeFromData(SkData::MakeWithoutCopy(imgData.c_str(), imgData.size()), { + SkJpegDecoder::Decoder() + }); + img = std::get<0>(codec->getImage()); + } catch(...) { + img = nullptr; + } + gui.invalidate_draw_element(this); + } + + CLAY(id, { + .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}, + .custom = {.customData = this} + }) { + } +} + +void ImageDisplay::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) { + if(img) { + canvas->drawImageRect(img, boundingBox.value().get_sk_rect(), {SkFilterMode::kLinear, SkMipmapMode::kNone}); + } + else { + SkPaint p{io.theme->frontColor1}; + p.setAntiAlias(skiaAA); + canvas->drawRect(boundingBox.value().get_sk_rect(), p); + } +} + +} diff --git a/src/GUIStuff/Elements/ImageDisplay.hpp b/src/GUIStuff/Elements/ImageDisplay.hpp new file mode 100644 index 0000000..59e3f66 --- /dev/null +++ b/src/GUIStuff/Elements/ImageDisplay.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "Element.hpp" + +namespace GUIStuff { + +class ImageDisplay : public Element { + public: + ImageDisplay(GUIManager& gui); + void layout(const Clay_ElementId& id, const std::filesystem::path& imgPath); + virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override; + + private: + std::filesystem::path oldImgPath; + sk_sp img; +}; + +} diff --git a/src/GUIStuff/Elements/TreeListing.cpp b/src/GUIStuff/Elements/TreeListing.cpp index 6f28f1e..7d007b1 100644 --- a/src/GUIStuff/Elements/TreeListing.cpp +++ b/src/GUIStuff/Elements/TreeListing.cpp @@ -57,6 +57,7 @@ void TreeListing::layout(const Clay_ElementId& id, const Data& newDisplayData) { scroll_area_many_entries(gui, "scroll", { .entryHeight = ENTRY_HEIGHT, .entryCount = flattenedIndexList.size(), + .growing = d.scrollAreaGrowing, .elementContent = [&](size_t i) { const ObjInfo& objInfo = flattenedIndexList[i]; gui.element("elem", [&](LayoutElement*, const Clay_ElementId& lId) { diff --git a/src/GUIStuff/Elements/TreeListing.hpp b/src/GUIStuff/Elements/TreeListing.hpp index 52e4f86..5bdc982 100644 --- a/src/GUIStuff/Elements/TreeListing.hpp +++ b/src/GUIStuff/Elements/TreeListing.hpp @@ -31,6 +31,7 @@ class TreeListing : public Element { }; struct Data { + bool scrollAreaGrowing = true; std::set* selectedIndices = nullptr; std::function(const TreeListingObjIndexList& objIndex)> dirInfo; std::function setDirectoryOpen; diff --git a/src/Screens/FileSelectScreen.cpp b/src/Screens/FileSelectScreen.cpp index bb2e8fb..df09f22 100644 --- a/src/Screens/FileSelectScreen.cpp +++ b/src/Screens/FileSelectScreen.cpp @@ -4,8 +4,8 @@ #include "../GUIStuff/ElementHelpers/TextLabelHelpers.hpp" #include "Helpers/ConvertVec.hpp" #include "../GUIStuff/Elements/GridScrollArea.hpp" -#include "../GUIStuff/ElementHelpers/ButtonHelpers.hpp" -#include "../GUIStuff/ElementHelpers/ScrollAreaHelpers.hpp" +#include "../GUIStuff/Elements/ImageDisplay.hpp" +#include "../World.hpp" FileSelectScreen::FileSelectScreen(MainProgram& m): Screen(m) {} @@ -17,32 +17,61 @@ void FileSelectScreen::gui_layout_run() { CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(gui.io.windowSize.x()), .height = CLAY_SIZING_FIXED(gui.io.windowSize.y())}, + .padding = CLAY_PADDING_ALL(15), .childGap = 15, .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP }, .layoutDirection = CLAY_TOP_TO_BOTTOM, }, }) { - text_label(gui, "Test grid"); - gui.element("Test grid", GridScrollArea::Options{ - .entryMaximumWidth = 100.0f, + text_label(gui, "File selector"); + auto& fileList = get_file_list(); + gui.element("File selector grid", GridScrollArea::Options{ + .entryMaximumWidth = 200.0f, .childAlignmentX = CLAY_ALIGN_X_LEFT, - .entryHeight = 100.0f, - .entryCount = 20000, + .entryHeight = 250.0f, + .entryCount = fileList.size(), .elementContent = [&](size_t i) { CLAY_AUTO_ID({ .layout = { - .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, - .padding = CLAY_PADDING_ALL(2), + .sizing = { .width = CLAY_SIZING_FIXED(150), .height = CLAY_SIZING_GROW(0)}, + .padding = CLAY_PADDING_ALL(gui.io.theme->padding1), + .childGap = 2, .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }, + .layoutDirection = CLAY_TOP_TO_BOTTOM } }) { - text_button_sized(gui, "button", "Square " + std::to_string(i), CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)); + CLAY_AUTO_ID({ + .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }}, + .aspectRatio = { .aspectRatio = 1.0f } + }) { + gui.element("ico", main.conf.configPath / "saveThumbnails" / (fileList[i] + ".jpg")); + } + text_label(gui, fileList[i]); } } }); } } -void FileSelectScreen::draw(SkCanvas* canvas) { - canvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f}); +const std::vector& FileSelectScreen::get_file_list() { + if(!fileListOptional.has_value()) { + std::filesystem::path savePath = main.conf.configPath / "saves"; + fileListOptional = std::vector(); + auto& fL = fileListOptional.value(); + int globCount; + char** filesInPath = SDL_GlobDirectory(savePath.string().c_str(), "*", 0, &globCount); + if(filesInPath) { + for(int i = 0; i < globCount; i++) { + std::filesystem::path p = filesInPath[i]; + std::string fExt = p.extension().string(); + if(fExt == "." + World::FILE_EXTENSION) + fL.emplace_back(p.stem().string()); + } + } + } + return fileListOptional.value(); +} + +void FileSelectScreen::draw(SkCanvas* canvas) { + canvas->clear(main.g.gui.io.theme->backColor1); } diff --git a/src/Screens/FileSelectScreen.hpp b/src/Screens/FileSelectScreen.hpp index 9c6e4a7..109203d 100644 --- a/src/Screens/FileSelectScreen.hpp +++ b/src/Screens/FileSelectScreen.hpp @@ -6,4 +6,7 @@ class FileSelectScreen : public Screen { FileSelectScreen(MainProgram& m); virtual void gui_layout_run() override; virtual void draw(SkCanvas* canvas) override; + private: + const std::vector& get_file_list(); + std::optional> fileListOptional; }; diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 2fcaa69..e0286f2 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -800,6 +800,7 @@ void Toolbar::grid_menu(Element* gridMenuButton) { .entryHeight = ENTRY_HEIGHT, .entryCount = main.world->gridMan.grids->size(), .clipHorizontal = true, + .growing = true, .elementContent = [&](size_t i) { auto& grid = main.world->gridMan.grids->at(i)->obj; bool selectedEntry = gridMenu.selectedGrid == i;