diff --git a/data/icons/droparrowbold.svg b/data/icons/droparrowbold.svg new file mode 100644 index 0000000..0f09e67 --- /dev/null +++ b/data/icons/droparrowbold.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + diff --git a/data/icons/folderbold.svg b/data/icons/folderbold.svg new file mode 100644 index 0000000..bb3d4ae --- /dev/null +++ b/data/icons/folderbold.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + diff --git a/data/icons/plusbold.svg b/data/icons/plusbold.svg new file mode 100644 index 0000000..9cca9aa --- /dev/null +++ b/data/icons/plusbold.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + diff --git a/src/Bookmarks/BookmarkListItem.cpp b/src/Bookmarks/BookmarkListItem.cpp index f313d20..5dd4073 100644 --- a/src/Bookmarks/BookmarkListItem.cpp +++ b/src/Bookmarks/BookmarkListItem.cpp @@ -4,7 +4,7 @@ using namespace NetworkingObjects; -void BookmarkData::jump_to(World& world) { +void BookmarkData::jump_to(World& world) const { world.drawData.cam.smooth_move_to(world, coords, windowSize.cast()); } diff --git a/src/Bookmarks/BookmarkListItem.hpp b/src/Bookmarks/BookmarkListItem.hpp index 288b949..bdefebe 100644 --- a/src/Bookmarks/BookmarkListItem.hpp +++ b/src/Bookmarks/BookmarkListItem.hpp @@ -10,7 +10,7 @@ struct BookmarkData { template void serialize(Archive& a) { a(coords, windowSize); } - void jump_to(World& world); + void jump_to(World& world) const; }; class BookmarkListItem { diff --git a/src/Bookmarks/BookmarkManager.cpp b/src/Bookmarks/BookmarkManager.cpp index b530f52..6b6b21d 100644 --- a/src/Bookmarks/BookmarkManager.cpp +++ b/src/Bookmarks/BookmarkManager.cpp @@ -1,6 +1,7 @@ #include "BookmarkManager.hpp" #include "../World.hpp" #include "../MainProgram.hpp" +#include "BookmarkListItem.hpp" #include "Helpers/NetworkingObjects/NetObjOrderedList.hpp" #include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp" @@ -12,45 +13,15 @@ void BookmarkManager::init() { // NOTE: Should find a way for bookmarks added from the network to take into account the client's canvasScale // Have the canvasScale variable come with the ClientData object, and the grid scale commands can be part of // the client data class - if(world.netObjMan.is_server()) { + if(world.netObjMan.is_server()) bookmarkListRoot = world.netObjMan.make_obj_from_ptr(new BookmarkListItem(world.netObjMan, "ROOT", true, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 1", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 2", false, {})); - auto folder1Ptr = bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Folder 1", true, {})); - folder1Ptr->obj->get_folder_list()->push_back_and_send_create(folder1Ptr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark A", false, {})); - folder1Ptr->obj->get_folder_list()->push_back_and_send_create(folder1Ptr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark B", false, {})); - folder1Ptr->obj->get_folder_list()->push_back_and_send_create(folder1Ptr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark C", false, {})); - folder1Ptr->obj->get_folder_list()->push_back_and_send_create(folder1Ptr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark D", false, {})); - folder1Ptr->obj->get_folder_list()->push_back_and_send_create(folder1Ptr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark E", false, {})); - auto folderAPtr = folder1Ptr->obj->get_folder_list()->push_back_and_send_create(folder1Ptr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Folder A", true, {})); - folderAPtr->obj->get_folder_list()->push_back_and_send_create(folderAPtr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 1", false, {})); - folderAPtr->obj->get_folder_list()->push_back_and_send_create(folderAPtr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 2", false, {})); - folderAPtr->obj->get_folder_list()->push_back_and_send_create(folderAPtr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 3", false, {})); - folderAPtr->obj->get_folder_list()->push_back_and_send_create(folderAPtr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 4", false, {})); - folderAPtr->obj->get_folder_list()->push_back_and_send_create(folderAPtr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 5", false, {})); - folderAPtr->obj->get_folder_list()->push_back_and_send_create(folderAPtr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 6", false, {})); - folderAPtr->obj->get_folder_list()->push_back_and_send_create(folderAPtr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 7", false, {})); - folder1Ptr->obj->get_folder_list()->push_back_and_send_create(folder1Ptr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark F", false, {})); - folder1Ptr->obj->get_folder_list()->push_back_and_send_create(folder1Ptr->obj->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark G", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 3", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 4", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 5", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 6", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 7", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 8", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 9", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 10", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 11", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 12", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 13", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 14", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 15", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 16", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 17", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 18", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 19", false, {})); - bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), new BookmarkListItem(world.netObjMan, "Bookmark 20", false, {})); - } +} + +void BookmarkManager::refresh_gui_data() { + selectionData = GUIStuff::TreeListing::SelectionData(); + oldSelection.clear(); + nameToEdit.clear(); + nameForNew.clear(); } void BookmarkManager::setup_list_gui(const std::string& id) { @@ -59,7 +30,8 @@ void BookmarkManager::setup_list_gui(const std::string& id) { std::optional toDeleteParent; std::optional toDeleteObject; auto& gui = world.main.toolbar.gui; - gui.tree_listing(id, bookmarkListRoot.get_net_id(), GUIStuff::TreeListing::DisplayData{ + gui.push_id(id); + gui.tree_listing("list", bookmarkListRoot.get_net_id(), GUIStuff::TreeListing::DisplayData{ .getObjInListAtIndex = [&](NetObjID parentId, size_t index) -> std::optional { NetObjOrderedList& bookmarkParentFolder = *world.netObjMan.get_obj_temporary_ref_from_id(parentId)->get_folder_list(); if(bookmarkParentFolder.size() <= index) @@ -88,17 +60,25 @@ void BookmarkManager::setup_list_gui(const std::string& id) { return false; }, .drawObjGUI = [&](const GUIStuff::TreeListing::ParentObjectIDPair& idPair) { + auto tempPtr = world.netObjMan.get_obj_temporary_ref_from_id(idPair.object); + if(Clay_Hovered() && gui.io->mouse.leftClick >= 2 && !tempPtr->is_folder()) + tempPtr->get_bookmark_data().jump_to(world); CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} }, }) { - gui.text_label(world.netObjMan.get_obj_temporary_ref_from_id(idPair.object)->get_name()); + gui.text_label(tempPtr->get_name()); + } + if(!tempPtr->is_folder() && gui.svg_icon_button_transparent("jump button", "data/icons/jump.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) { + tempPtr->get_bookmark_data().jump_to(world); + return true; } if(gui.svg_icon_button_transparent("delete button", "data/icons/trash.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) { toDeleteParent = idPair.parent; toDeleteObject = idPair.object; + return true; } return false; }, @@ -131,13 +111,80 @@ void BookmarkManager::setup_list_gui(const std::string& id) { listPtr->insert_ordered_list_and_send_create(listPtr, toInsert); }, NetObjManager::SendUpdateType::SEND_TO_ALL, nullptr); } - }); + }, selectionData); + + if(selectionData.objsSelected != oldSelection) { + oldSelection = selectionData.objsSelected; + if(selectionData.objsSelected.size() == 1) { + NetObjTemporaryPtr tempPtr = world.netObjMan.get_obj_temporary_ref_from_id(*selectionData.objsSelected.begin()); + if(tempPtr) + nameToEdit = tempPtr->get_name(); + else { + nameToEdit.clear(); + } + } + else + nameToEdit.clear(); + } + if(toDeleteObject.has_value()) { auto& folderList = world.netObjMan.get_obj_temporary_ref_from_id(toDeleteParent.value())->get_folder_list(); folderList->erase(folderList, toDeleteObject.value()); } + + gui.left_to_right_line_layout([&]() { + bool addByEnter = false; + gui.input_text("input new name", &nameForNew, true, [&](GUIStuff::SelectionHelper& s) { + addByEnter = s.selected && gui.io->key.enter; + }); + + if(gui.svg_icon_button("create new bookmark", "data/icons/plusbold.svg", false, gui.SMALL_BUTTON_SIZE) || addByEnter) + create_in_proper_position(new BookmarkListItem(world.netObjMan, nameForNew, false, {world.drawData.cam.c, world.main.window.size.cast()})); + else if(gui.svg_icon_button("create new folder", "data/icons/folderbold.svg", false, gui.SMALL_BUTTON_SIZE)) { + auto newFolderObjInfo = create_in_proper_position(new BookmarkListItem(world.netObjMan, nameForNew, true, {})); + newFolderObjInfo->obj->set_folder_open(true); + } + }); + + if(selectionData.objsSelected.size() == 1) { + NetObjTemporaryPtr tempPtr = world.netObjMan.get_obj_temporary_ref_from_id(*selectionData.objsSelected.begin()); + if(tempPtr) { + gui.text_label_centered(tempPtr->is_folder() ? "Edit Bookmark Folder" : "Edit Bookmark"); + gui.input_text_field("input edit name", "Name", &nameToEdit); + if(nameToEdit != tempPtr->get_name()) + tempPtr->set_name(tempPtr, nameToEdit); + } + } + + gui.pop_id(); } } +NetworkingObjects::NetObjOrderedListObjectInfoPtr BookmarkManager::try_to_create_in_proper_position(BookmarkListItem* newItem) { + using namespace NetworkingObjects; + if(selectionData.orderedObjsSelected.empty()) + return nullptr; + auto& backObjParentObjectPair = selectionData.orderedObjsSelected.back(); + NetObjTemporaryPtr parentPtr = world.netObjMan.get_obj_temporary_ref_from_id(backObjParentObjectPair.parent); + if(!parentPtr) + return nullptr; + NetObjTemporaryPtr objectPtr = world.netObjMan.get_obj_temporary_ref_from_id(backObjParentObjectPair.object); + if(!objectPtr) + return nullptr; + if(objectPtr->is_folder()) { + objectPtr->set_folder_open(true); + return objectPtr->get_folder_list()->push_back_and_send_create(objectPtr->get_folder_list(), newItem); + } + uint32_t insertPos = parentPtr->get_folder_list()->get(objectPtr.get_net_id())->pos; + return parentPtr->get_folder_list()->insert_and_send_create(parentPtr->get_folder_list(), insertPos + 1, newItem); +} + +NetworkingObjects::NetObjOrderedListObjectInfoPtr BookmarkManager::create_in_proper_position(BookmarkListItem* newItem) { + auto toRet = try_to_create_in_proper_position(newItem); + if(!toRet) + return bookmarkListRoot->get_folder_list()->push_back_and_send_create(bookmarkListRoot->get_folder_list(), newItem); + return toRet; +} + void BookmarkManager::scale_up(const WorldScalar& scaleUpAmount) { } diff --git a/src/Bookmarks/BookmarkManager.hpp b/src/Bookmarks/BookmarkManager.hpp index 6db040b..5c6b920 100644 --- a/src/Bookmarks/BookmarkManager.hpp +++ b/src/Bookmarks/BookmarkManager.hpp @@ -2,6 +2,7 @@ #include #include #include +#include "../GUIStuff/Elements/TreeListing.hpp" #include "BookmarkListItem.hpp" class World; @@ -12,6 +13,7 @@ class BookmarkManager { void init(); void scale_up(const WorldScalar& scaleUpAmount); void setup_list_gui(const std::string& id); + void refresh_gui_data(); template void save(Archive& a) const { } @@ -21,5 +23,12 @@ class BookmarkManager { NetworkingObjects::NetObjOwnerPtr bookmarkListRoot; private: + NetworkingObjects::NetObjOrderedListObjectInfoPtr create_in_proper_position(BookmarkListItem* newItem); + NetworkingObjects::NetObjOrderedListObjectInfoPtr try_to_create_in_proper_position(BookmarkListItem* newItem); + GUIStuff::TreeListing::SelectionData selectionData; + std::unordered_set oldSelection; + std::string nameToEdit; + std::string nameForNew; + World& world; }; diff --git a/src/GUIStuff/Elements/FontPicker.cpp b/src/GUIStuff/Elements/FontPicker.cpp index 1156eb2..a0089ac 100644 --- a/src/GUIStuff/Elements/FontPicker.cpp +++ b/src/GUIStuff/Elements/FontPicker.cpp @@ -73,7 +73,7 @@ bool GUIStuff::FontPicker::update(UpdateInputData& io, std::string* fontName, GU else val = it - sortedFontList.begin(); - if(gui->svg_icon_button("Open font dropdown", "data/icons/droparrow.svg", dropdownOpen, GUIManager::SMALL_BUTTON_SIZE)) { + if(gui->svg_icon_button("Open font dropdown", "data/icons/droparrowbold.svg", dropdownOpen, GUIManager::SMALL_BUTTON_SIZE)) { dropdownOpen = !dropdownOpen; clickedOut = false; } diff --git a/src/GUIStuff/Elements/TreeListing.cpp b/src/GUIStuff/Elements/TreeListing.cpp index 068f293..fa31a46 100644 --- a/src/GUIStuff/Elements/TreeListing.cpp +++ b/src/GUIStuff/Elements/TreeListing.cpp @@ -8,11 +8,11 @@ namespace GUIStuff { -void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects::NetObjID rootObjID, const DisplayData& displayData) { +void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects::NetObjID rootObjID, const DisplayData& displayData, SelectionData& selectionData) { if(!io.mouse.leftHeld) dragHoldSelected = false; - orderedObjsSelected.clear(); + selectionData.orderedObjsSelected.clear(); std::optional hoveredObject; CLAY_AUTO_ID({ @@ -20,11 +20,13 @@ void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects }) { Clay_ElementId localID = CLAY_ID_LOCAL("scroll area"); float setScrollAmount; + float setScrollContentHeight; CLAY(localID, { .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}} }) { gui.scroll_bar_area("scroll area", true, [&](float scrollContentHeight, float containerHeight, float& scrollAmount) { setScrollAmount = scrollAmount; + setScrollContentHeight = scrollContentHeight; size_t itemCount = 0; size_t itemCountToStartAt = -scrollAmount / ENTRY_HEIGHT; size_t itemCountToEndAt = (containerHeight - scrollAmount) / ENTRY_HEIGHT; @@ -35,7 +37,7 @@ void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(ENTRY_HEIGHT * itemCountToStartAt)} } }) {} - recursive_gui(io, gui, displayData, rootObjID, itemCount, itemCountToStartAt, itemCountToEndAt, 0, hoveredObject, parentIDStack, false); + recursive_gui(io, gui, displayData, selectionData, rootObjID, itemCount, itemCountToStartAt, itemCountToEndAt, 0, hoveredObject, parentIDStack, false); if(itemCount > itemCountToEndAt) { CLAY_AUTO_ID({ .layout = { @@ -47,18 +49,19 @@ void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects } Clay_ElementData scrollAreaBB = Clay_GetElementData(localID); if(scrollAreaBB.found) { - float mouseRelativeYPos = std::fabs(io.mouse.pos.y() - scrollAreaBB.boundingBox.y - setScrollAmount); + // Clamp by fabs(scrollcontentheight - 3), small offset is added to account for error (when youre at the very edge it might assume youre at the top of an element) + float mouseRelativeYPos = std::clamp(std::fabs(io.mouse.pos.y() - scrollAreaBB.boundingBox.y - setScrollAmount), 0.0f, std::fabs(setScrollContentHeight - 3.0f)); topHalfOfHovered = std::fmod(mouseRelativeYPos, ENTRY_HEIGHT) < (ENTRY_HEIGHT / 2.0f); } } // Any objects that were part of a collapsed directory will be unselected - objsSelected.clear(); - for(auto& [parent, object] : orderedObjsSelected) - objsSelected.emplace(object); + selectionData.objsSelected.clear(); + for(auto& [parent, object] : selectionData.orderedObjsSelected) + selectionData.objsSelected.emplace(object); if(objDragged.has_value()) { - if(hoveredObject.has_value() && !objsSelected.contains(hoveredObject.value().object) && (std::chrono::steady_clock::now() - timeStartedHoveringOverObject) >= TIME_TO_HOVER_TO_OPEN_DIRECTORY) { + if(hoveredObject.has_value() && !selectionData.objsSelected.contains(hoveredObject.value().object) && (std::chrono::steady_clock::now() - timeStartedHoveringOverObject) >= TIME_TO_HOVER_TO_OPEN_DIRECTORY) { ParentObjectIDStack& hoveredObjVal = hoveredObject.value(); size_t hoveredObjIndex = displayData.getIndexOfObjInList({hoveredObjVal.parents.back(), hoveredObjVal.object}); std::optional objHoveringOver = displayData.getObjInListAtIndex(hoveredObjVal.parents.back(), hoveredObjIndex); @@ -67,11 +70,11 @@ void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects } ParentObjectIDPair& objDraggedVal = objDragged.value(); - if(!objsSelected.contains(objDraggedVal.object)) + if(!selectionData.objsSelected.contains(objDraggedVal.object)) objDragged = std::nullopt; else if(!io.mouse.leftHeld) { if(!hoveredObject.has_value()) - set_single_selected_object(); + set_single_selected_object(selectionData); else { ParentObjectIDStack hoveredObjValToUse = hoveredObject.value(); // By value since it'll be edited size_t indexToMoveFrom = displayData.getIndexOfObjInList(objDraggedVal); @@ -84,23 +87,23 @@ void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects else if(!topHalfOfHovered) indexToMoveTo++; if((indexToMoveFrom == indexToMoveTo || (indexToMoveFrom + 1) == indexToMoveTo) && objDraggedVal.parent == hoveredObjValToUse.parents.back()) - set_single_selected_object(); + set_single_selected_object(selectionData); else { bool wrongMovement = false; // Check if object is about to move into itself for(const NetworkingObjects::NetObjID& parent : hoveredObjValToUse.parents) { - if(objsSelected.contains(parent)) { + if(selectionData.objsSelected.contains(parent)) { wrongMovement = true; break; } } if(!wrongMovement) { - displayData.moveObjectsToListAtIndex(hoveredObjValToUse.parents.back(), indexToMoveTo, orderedObjsSelected); - objsSelected.clear(); - orderedObjsSelected.clear(); + displayData.moveObjectsToListAtIndex(hoveredObjValToUse.parents.back(), indexToMoveTo, selectionData.orderedObjsSelected); + selectionData.objsSelected.clear(); + selectionData.orderedObjsSelected.clear(); } else - set_single_selected_object(); + set_single_selected_object(selectionData); } } objDragged = std::nullopt; @@ -110,14 +113,14 @@ void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects oldHoveredObject = hoveredObject; } -void TreeListing::set_single_selected_object() { - objsSelected.clear(); - orderedObjsSelected.clear(); - objsSelected.emplace(objDragged.value().object); - orderedObjsSelected.emplace_back(objDragged.value()); +void TreeListing::set_single_selected_object(SelectionData& selectionData) { + selectionData.objsSelected.clear(); + selectionData.orderedObjsSelected.clear(); + selectionData.objsSelected.emplace(objDragged.value().object); + selectionData.orderedObjsSelected.emplace_back(objDragged.value()); } -void TreeListing::recursive_gui(UpdateInputData& io, GUIManager& gui, const DisplayData& displayData, NetworkingObjects::NetObjID objID, size_t& itemCount, size_t itemCountToStartAt, size_t itemCountToEndAt, size_t listDepth, std::optional& hoveredObject, std::vector& parentIDStack, bool parentJustSelected) { +void TreeListing::recursive_gui(UpdateInputData& io, GUIManager& gui, const DisplayData& displayData, SelectionData& selectionData, NetworkingObjects::NetObjID objID, size_t& itemCount, size_t itemCountToStartAt, size_t itemCountToEndAt, size_t listDepth, std::optional& hoveredObject, std::vector& parentIDStack, bool parentJustSelected) { parentIDStack.emplace_back(objID); for(size_t i = 0;; i++) { std::optional objInListOptional = displayData.getObjInListAtIndex(objID, i); @@ -134,7 +137,7 @@ void TreeListing::recursive_gui(UpdateInputData& io, GUIManager& gui, const Disp bool isOldHoveredObject = oldHoveredObject.has_value() && oldHoveredObject.value().object == objInList.id; SkColor4f backgroundColor; - if(objsSelected.contains(objInList.id)) + if(selectionData.objsSelected.contains(objInList.id)) backgroundColor = io.theme->backColor1; else if(isOldHoveredObject && objInList.isDirectory && !objInList.isDirectoryOpen && objDragged.has_value()) backgroundColor = lerp_vec(io.theme->backColor2, io.theme->fillColor1, std::clamp(std::chrono::duration(std::chrono::steady_clock::now() - timeStartedHoveringOverObject) / TIME_TO_HOVER_TO_OPEN_DIRECTORY, 0.0f, 1.0f)); @@ -215,26 +218,26 @@ void TreeListing::recursive_gui(UpdateInputData& io, GUIManager& gui, const Disp if(io.key.leftShift) dragHoldSelected = true; else if(io.key.leftCtrl) { - bool alreadySelected = objsSelected.contains(objInList.id); + bool alreadySelected = selectionData.objsSelected.contains(objInList.id); if(alreadySelected) - objsSelected.erase(objInList.id); + selectionData.objsSelected.erase(objInList.id); else { - select_and_unselect_parents(parentIDStack, objInList.id); + select_and_unselect_parents(parentIDStack, objInList.id, selectionData); selfJustSelected = true; } } else { - if(!objsSelected.contains(objInList.id)) { - objsSelected.clear(); - orderedObjsSelected.clear(); - objsSelected.emplace(objInList.id); + if(!selectionData.objsSelected.contains(objInList.id)) { + selectionData.objsSelected.clear(); + selectionData.orderedObjsSelected.clear(); + selectionData.objsSelected.emplace(objInList.id); selfJustSelected = true; } objDragged = {objID, objInList.id}; } } if(io.key.leftShift && dragHoldSelected && !parentJustSelected) { - select_and_unselect_parents(parentIDStack, objInList.id); + select_and_unselect_parents(parentIDStack, objInList.id, selectionData); selfJustSelected = true; } } @@ -242,34 +245,34 @@ void TreeListing::recursive_gui(UpdateInputData& io, GUIManager& gui, const Disp gui.pop_id(); } if(!parentJustSelected) { - if(objsSelected.contains(objInList.id)) - orderedObjsSelected.emplace_back(objID, objInList.id); + if(selectionData.objsSelected.contains(objInList.id)) + selectionData.orderedObjsSelected.emplace_back(objID, objInList.id); } else - unselect_object(objInList.id); + unselect_object(objInList.id, selectionData); itemCount++; if(objInList.isDirectoryOpen) - recursive_gui(io, gui, displayData, objInList.id, itemCount, itemCountToStartAt, itemCountToEndAt, listDepth + 1, hoveredObject, parentIDStack, selfJustSelected | parentJustSelected); + recursive_gui(io, gui, displayData, selectionData, objInList.id, itemCount, itemCountToStartAt, itemCountToEndAt, listDepth + 1, hoveredObject, parentIDStack, selfJustSelected | parentJustSelected); } } parentIDStack.pop_back(); } -void TreeListing::unselect_object(NetworkingObjects::NetObjID id) { - objsSelected.erase(id); - std::erase_if(orderedObjsSelected, [&id](const auto& p) { +void TreeListing::unselect_object(NetworkingObjects::NetObjID id, SelectionData& selectionData) { + selectionData.objsSelected.erase(id); + std::erase_if(selectionData.orderedObjsSelected, [&id](const auto& p) { return p.object == id; }); } -void TreeListing::select_and_unselect_parents(const std::vector& parentIDStack, NetworkingObjects::NetObjID newSelectedObj) { +void TreeListing::select_and_unselect_parents(const std::vector& parentIDStack, NetworkingObjects::NetObjID newSelectedObj, SelectionData& selectionData) { for(const NetworkingObjects::NetObjID& parent : parentIDStack) { - if(objsSelected.contains(parent)) { - unselect_object(parent); + if(selectionData.objsSelected.contains(parent)) { + unselect_object(parent, selectionData); break; } } - objsSelected.emplace(newSelectedObj); + selectionData.objsSelected.emplace(newSelectedObj); } void TreeListing::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) { diff --git a/src/GUIStuff/Elements/TreeListing.hpp b/src/GUIStuff/Elements/TreeListing.hpp index cc4a596..f6e9990 100644 --- a/src/GUIStuff/Elements/TreeListing.hpp +++ b/src/GUIStuff/Elements/TreeListing.hpp @@ -34,20 +34,24 @@ class TreeListing : public Element { std::function drawObjGUI; std::function&)> moveObjectsToListAtIndex; }; - void update(UpdateInputData& io, GUIManager& gui, NetworkingObjects::NetObjID rootObjID, const DisplayData& displayData); + + struct SelectionData { + std::vector orderedObjsSelected; + std::unordered_set objsSelected; + }; + + void update(UpdateInputData& io, GUIManager& gui, NetworkingObjects::NetObjID rootObjID, const DisplayData& displayData, SelectionData& selectionData); virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) override; private: bool dragHoldSelected = false; - void set_single_selected_object(); + void set_single_selected_object(SelectionData& selectionData); - void recursive_gui(UpdateInputData& io, GUIManager& gui, const DisplayData& displayData, NetworkingObjects::NetObjID objID, size_t& itemCount, size_t itemCountToStartAt, size_t itemCountToEndAt, size_t listDepth, std::optional& hoveredObject, std::vector& parentIDStack, bool parentJustSelected); - std::vector orderedObjsSelected; - std::unordered_set objsSelected; + void recursive_gui(UpdateInputData& io, GUIManager& gui, const DisplayData& displayData, SelectionData& selectionData, NetworkingObjects::NetObjID objID, size_t& itemCount, size_t itemCountToStartAt, size_t itemCountToEndAt, size_t listDepth, std::optional& hoveredObject, std::vector& parentIDStack, bool parentJustSelected); - void select_and_unselect_parents(const std::vector& parentIDStack, NetworkingObjects::NetObjID newSelectedObj); + void select_and_unselect_parents(const std::vector& parentIDStack, NetworkingObjects::NetObjID newSelectedObj, SelectionData& selectionData); - void unselect_object(NetworkingObjects::NetObjID id); + void unselect_object(NetworkingObjects::NetObjID id, SelectionData& selectionData); bool topHalfOfHovered = false; std::optional objDragged; diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index be0044a..cfac90b 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -346,11 +346,13 @@ void GUIManager::checkbox(const std::string& id, bool* val, const std::function< pop_id(); } -void GUIManager::input_text_field(const std::string& id, const std::string& name, std::string* val, bool updateEveryEdit, const std::function& elemUpdate) { +bool GUIManager::input_text_field(const std::string& id, const std::string& name, std::string* val, bool updateEveryEdit, const std::function& elemUpdate) { + bool toRet = false; left_to_right_line_layout([&]() { text_label(name); - input_text(id, val, updateEveryEdit, elemUpdate); + toRet = input_text(id, val, updateEveryEdit, elemUpdate); }); + return toRet; } void GUIManager::input_path_field(const std::string& id, const std::string& name, std::filesystem::path* val, std::filesystem::file_type fileTypeRestriction, const std::function& elemUpdate) { @@ -897,9 +899,9 @@ void GUIManager::list_popup_menu(const std::string& id, Vector2f popupPos, const } } -void GUIManager::tree_listing(const std::string& id, NetworkingObjects::NetObjID rootObjID, const TreeListing::DisplayData& displayData) { +void GUIManager::tree_listing(const std::string& id, NetworkingObjects::NetObjID rootObjID, const TreeListing::DisplayData& displayData, TreeListing::SelectionData& selectionData) { push_id(id); - insert_element()->update(*io, *this, rootObjID, displayData); + insert_element()->update(*io, *this, rootObjID, displayData, selectionData); pop_id(); } diff --git a/src/GUIStuff/GUIManager.hpp b/src/GUIStuff/GUIManager.hpp index 31c3e67..fa6311b 100644 --- a/src/GUIStuff/GUIManager.hpp +++ b/src/GUIStuff/GUIManager.hpp @@ -48,7 +48,7 @@ class GUIManager { Vector2f screen_pos_to_window_pos(const Vector2f& screenPos); Vector2f get_mouse_pos(); - void input_text_field(const std::string& id, const std::string& name, std::string* val, bool updateEveryEdit = true, const std::function& elemUpdate = nullptr); + bool input_text_field(const std::string& id, const std::string& name, std::string* val, bool updateEveryEdit = true, const std::function& elemUpdate = nullptr); void text_paragraph(const std::string& id, std::unique_ptr paragraph, float width, const std::function& elemUpdate = nullptr); void text_label_color(const std::string& val, const SkColor4f& color); @@ -102,7 +102,7 @@ class GUIManager { void paint_circle_popup_menu(const std::string& id, const Vector2f& centerPos, const PaintCircleMenu::Data& val, const std::function& elemUpdate = nullptr); void list_popup_menu(const std::string& id, Vector2f popupPos, const std::function& elemUpdate); - void tree_listing(const std::string& id, NetworkingObjects::NetObjID rootObjID, const TreeListing::DisplayData& displayData); + void tree_listing(const std::string& id, NetworkingObjects::NetObjID rootObjID, const TreeListing::DisplayData& displayData, TreeListing::SelectionData& selectionData); void obstructing_window(); diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 34d0d5f..4cd386c 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -486,11 +486,11 @@ void Toolbar::top_toolbar() { gridMenuPopUpJustOpen = true; } } - if(gui.svg_icon_button_transparent("Bookmark Menu Button", "data/icons/bookmark.svg", bookMenu.popupOpen)) { - if(bookMenu.popupOpen) - bookMenu.popupOpen = false; + if(gui.svg_icon_button_transparent("Bookmark Menu Button", "data/icons/bookmark.svg", bookmarkMenuPopupOpen)) { + if(bookmarkMenuPopupOpen) + bookmarkMenuPopupOpen = false; else { - bookMenu.popupOpen = true; + bookmarkMenuPopupOpen = true; bookmarkMenuPopUpJustOpen = true; } } @@ -500,7 +500,7 @@ void Toolbar::top_toolbar() { main.set_tab_to_close(closedTab.value()); if(gridMenu.popupOpen) grid_menu(gridMenuPopUpJustOpen); - if(bookMenu.popupOpen) + if(bookmarkMenuPopupOpen) bookmark_menu(bookmarkMenuPopUpJustOpen); if(menuPopUpOpen) { CLAY_AUTO_ID({ @@ -765,7 +765,7 @@ void Toolbar::grid_menu(bool justOpened) { gui.input_text("grid text input", &gridMenu.newName, true, [&](GUIStuff::SelectionHelper& s) { addByEnter = s.selected && io->key.enter; }); - if(gui.svg_icon_button("grid add button", "data/icons/plus.svg", false, GUIStuff::GUIManager::SMALL_BUTTON_SIZE) || (addByEnter && !gridMenu.newName.empty())) { + if(gui.svg_icon_button("grid add button", "data/icons/plusbold.svg", false, GUIStuff::GUIManager::SMALL_BUTTON_SIZE) || (addByEnter && !gridMenu.newName.empty())) { main.world->gridMan.add_default_grid(gridMenu.newName); main.world->drawProg.modify_grid(main.world->gridMan.grids->at(main.world->gridMan.grids->size() - 1)->obj); stop_displaying_grid_menu(); @@ -802,8 +802,10 @@ void Toolbar::bookmark_menu(bool justOpened) { gui.obstructing_window(); gui.text_label_centered("Bookmarks"); main.world->bMan.setup_list_gui("bookmark menu list"); - if(io->mouse.leftClick && !Clay_Hovered() && !justOpened) - bookMenu.popupOpen = false; + if(io->mouse.leftClick && !Clay_Hovered() && !justOpened) { + bookmarkMenuPopupOpen = false; + main.world->bMan.refresh_gui_data(); + } } gui.pop_id(); } diff --git a/src/Toolbar.hpp b/src/Toolbar.hpp index 8b17bf3..f922652 100644 --- a/src/Toolbar.hpp +++ b/src/Toolbar.hpp @@ -188,9 +188,7 @@ class Toolbar { GSETTINGS_DEBUG } generalSettingsOptions = GSETTINGS_GENERAL; - struct BookmarkMenu { - bool popupOpen = false; - } bookMenu; + bool bookmarkMenuPopupOpen = false; struct GridMenu { bool popupOpen = false;