Added drag to move code to tree listing
This commit is contained in:
parent
86063f373b
commit
032c52d843
4 changed files with 243 additions and 49 deletions
|
|
@ -100,6 +100,7 @@ namespace NetworkingObjects {
|
|||
virtual const std::vector<NetObjOrderedListObjectInfoPtr<T>>& get_data() const = 0;
|
||||
virtual uint32_t size() const = 0;
|
||||
virtual const NetObjOrderedListObjectInfoPtr<T>& at(uint32_t index) const = 0;
|
||||
virtual NetObjOrderedListObjectInfoPtr<T> get(const NetObjID& id) const = 0;
|
||||
virtual bool empty() const = 0;
|
||||
virtual ~NetObjOrderedList() {}
|
||||
protected:
|
||||
|
|
@ -172,6 +173,12 @@ namespace NetworkingObjects {
|
|||
virtual const NetObjOrderedListObjectInfoPtr<T>& at(uint32_t index) const override {
|
||||
return data[index];
|
||||
}
|
||||
virtual NetObjOrderedListObjectInfoPtr<T> get(const NetObjID& id) const override {
|
||||
auto it = idToDataMap.find(id);
|
||||
if(it == idToDataMap.end())
|
||||
return nullptr;
|
||||
return it->second;
|
||||
}
|
||||
virtual bool empty() const override {
|
||||
return data.empty();
|
||||
}
|
||||
|
|
@ -343,6 +350,12 @@ namespace NetworkingObjects {
|
|||
virtual const NetObjOrderedListObjectInfoPtr<T>& at(uint32_t index) const override {
|
||||
return clientData[index];
|
||||
}
|
||||
virtual NetObjOrderedListObjectInfoPtr<T> get(const NetObjID& id) const override {
|
||||
auto it = clientIdToDataMap.find(id);
|
||||
if(it == clientIdToDataMap.end())
|
||||
return nullptr;
|
||||
return it->second;
|
||||
}
|
||||
virtual bool empty() const override {
|
||||
return clientData.empty();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,9 @@ void BookmarkManager::setup_list_gui(const std::string& id) {
|
|||
.isDirectoryOpen = bookmarkParentFolder.at(index)->obj->is_folder() ? bookmarkParentFolder.at(index)->obj->is_folder_open() : false
|
||||
};
|
||||
},
|
||||
.getIndexOfObjInList = [&](const GUIStuff::TreeListing::ParentObjectIDPair& idPair) {
|
||||
return world.netObjMan.get_obj_temporary_ref_from_id<BookmarkListItem>(idPair.parent)->get_folder_list()->get(idPair.object)->pos;
|
||||
},
|
||||
.setDirectoryOpen = [&](NetObjID netID, bool newDirectoryOpen) {
|
||||
world.netObjMan.get_obj_temporary_ref_from_id<BookmarkListItem>(netID)->set_folder_open(newDirectoryOpen);
|
||||
},
|
||||
|
|
@ -84,20 +87,24 @@ void BookmarkManager::setup_list_gui(const std::string& id) {
|
|||
}
|
||||
return false;
|
||||
},
|
||||
.drawObjGUI = [&](NetObjID parentNetID, NetObjID netID) {
|
||||
.drawObjGUI = [&](const GUIStuff::TreeListing::ParentObjectIDPair& idPair) {
|
||||
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<BookmarkListItem>(netID)->get_name());
|
||||
gui.text_label(world.netObjMan.get_obj_temporary_ref_from_id<BookmarkListItem>(idPair.object)->get_name());
|
||||
}
|
||||
if(gui.svg_icon_button_transparent("delete button", "data/icons/trash.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) {
|
||||
toDeleteParent = parentNetID;
|
||||
toDeleteObject = netID;
|
||||
toDeleteParent = idPair.parent;
|
||||
toDeleteObject = idPair.object;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
.moveObjectsToListAtIndex = [](NetworkingObjects::NetObjID listObj, size_t index, const std::vector<GUIStuff::TreeListing::ParentObjectIDPair>& objsToInsert) {
|
||||
static int i = 0;
|
||||
std::cout << "Move happened! " << i++ << std::endl;
|
||||
}
|
||||
});
|
||||
if(toDeleteObject.has_value()) {
|
||||
|
|
|
|||
|
|
@ -5,49 +5,129 @@
|
|||
namespace GUIStuff {
|
||||
|
||||
void TreeListing::update(UpdateInputData& io, GUIManager& gui, NetworkingObjects::NetObjID rootObjID, const DisplayData& displayData) {
|
||||
if(!io.mouse.leftHeld)
|
||||
dragHoldSelected = false;
|
||||
|
||||
orderedObjsSelected.clear();
|
||||
std::optional<ParentObjectIDStack> hoveredObject;
|
||||
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}
|
||||
}
|
||||
.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) {
|
||||
size_t itemCount = 0;
|
||||
size_t itemCountToStartAt = -scrollAmount / ENTRY_HEIGHT;
|
||||
size_t itemCountToEndAt = (containerHeight - scrollAmount) / ENTRY_HEIGHT;
|
||||
itemCountToEndAt++;
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(ENTRY_HEIGHT * itemCountToStartAt)}
|
||||
}
|
||||
}) {}
|
||||
recursive_gui(io, gui, displayData, rootObjID, itemCount, itemCountToStartAt, itemCountToEndAt, 0);
|
||||
if(itemCount > itemCountToEndAt) {
|
||||
Clay_ElementId localID = CLAY_ID_LOCAL("scroll area");
|
||||
float setScrollAmount;
|
||||
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;
|
||||
size_t itemCount = 0;
|
||||
size_t itemCountToStartAt = -scrollAmount / ENTRY_HEIGHT;
|
||||
size_t itemCountToEndAt = (containerHeight - scrollAmount) / ENTRY_HEIGHT;
|
||||
itemCountToEndAt++;
|
||||
std::vector<NetworkingObjects::NetObjID> parentIDStack;
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(ENTRY_HEIGHT * (itemCount - itemCountToEndAt))}
|
||||
.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);
|
||||
if(itemCount > itemCountToEndAt) {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(ENTRY_HEIGHT * (itemCount - itemCountToEndAt))}
|
||||
}
|
||||
}) {}
|
||||
}
|
||||
});
|
||||
}
|
||||
Clay_ElementData scrollAreaBB = Clay_GetElementData(localID);
|
||||
if(scrollAreaBB.found) {
|
||||
float mouseRelativeYPos = std::fabs(io.mouse.pos.y() - scrollAreaBB.boundingBox.y - setScrollAmount);
|
||||
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);
|
||||
|
||||
if(objDragged.has_value()) {
|
||||
ParentObjectIDPair& objDraggedVal = objDragged.value();
|
||||
if(!objsSelected.contains(objDraggedVal.object))
|
||||
objDragged = std::nullopt;
|
||||
else if(!io.mouse.leftHeld) {
|
||||
if(!hoveredObject.has_value())
|
||||
set_single_selected_object();
|
||||
else {
|
||||
ParentObjectIDStack& hoveredObjVal = hoveredObject.value();
|
||||
size_t indexToMoveFrom = displayData.getIndexOfObjInList(objDraggedVal);
|
||||
size_t indexToMoveTo = displayData.getIndexOfObjInList({hoveredObjVal.parents.back(), hoveredObjVal.object});
|
||||
if(!topHalfOfHovered)
|
||||
indexToMoveTo++;
|
||||
if((indexToMoveFrom == indexToMoveTo || (indexToMoveFrom + 1) == indexToMoveTo) && objDraggedVal.parent == hoveredObjVal.parents.back())
|
||||
set_single_selected_object();
|
||||
else {
|
||||
bool wrongMovement = false;
|
||||
// Check if object is about to move into itself
|
||||
for(const NetworkingObjects::NetObjID& parent : hoveredObjVal.parents) {
|
||||
if(objsSelected.contains(parent)) {
|
||||
wrongMovement = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!wrongMovement)
|
||||
displayData.moveObjectsToListAtIndex(hoveredObjVal.parents.back(), indexToMoveTo, orderedObjsSelected);
|
||||
objsSelected.clear();
|
||||
orderedObjsSelected.clear();
|
||||
}
|
||||
}
|
||||
objDragged = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
oldHoveredObject = hoveredObject;
|
||||
}
|
||||
|
||||
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) {
|
||||
void TreeListing::set_single_selected_object() {
|
||||
objsSelected.clear();
|
||||
orderedObjsSelected.clear();
|
||||
objsSelected.emplace(objDragged.value().object);
|
||||
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<ParentObjectIDStack>& hoveredObject, std::vector<NetworkingObjects::NetObjID>& parentIDStack, bool parentJustSelected) {
|
||||
parentIDStack.emplace_back(objID);
|
||||
for(size_t i = 0;; i++) {
|
||||
std::optional<DisplayData::ObjInList> objInListOptional = displayData.getObjInListAtIndex(objID, i);
|
||||
if(!objInListOptional.has_value())
|
||||
break;
|
||||
else {
|
||||
auto& objInList = objInListOptional.value();
|
||||
bool selfJustSelected = false;
|
||||
if(itemCount >= itemCountToStartAt && itemCount < itemCountToEndAt) {
|
||||
gui.push_id(itemCount);
|
||||
bool buttonClicked = false;
|
||||
uint16_t topOutlineWidth = 0;
|
||||
uint16_t bottomOutlineWidth = 0;
|
||||
if(oldHoveredObject.has_value() && oldHoveredObject.value().object == objInList.id) {
|
||||
if(objDragged.has_value()) {
|
||||
ParentObjectIDPair& objDraggedVal = objDragged.value();
|
||||
if(objDraggedVal.object != objInList.id) {
|
||||
if(topHalfOfHovered)
|
||||
topOutlineWidth = 1;
|
||||
else
|
||||
bottomOutlineWidth = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(ENTRY_HEIGHT)},
|
||||
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER}
|
||||
},
|
||||
.backgroundColor = convert_vec4<Clay_Color>((objSelected.has_value() && objSelected.value() == objInList.id) ? io.theme->backColor1 : io.theme->backColor2),
|
||||
.backgroundColor = convert_vec4<Clay_Color>((objsSelected.contains(objInList.id)) ? io.theme->backColor1 : io.theme->backColor2)
|
||||
}) {
|
||||
if(listDepth != 0) {
|
||||
CLAY_AUTO_ID({
|
||||
|
|
@ -56,41 +136,108 @@ void TreeListing::recursive_gui(UpdateInputData& io, GUIManager& gui, const Disp
|
|||
},
|
||||
}) {}
|
||||
}
|
||||
if(objInList.isDirectory) {
|
||||
if(gui.svg_icon_button_transparent("ico", objInList.isDirectoryOpen ? "data/icons/droparrow.svg" : "data/icons/droparrowclose.svg", false, ENTRY_HEIGHT)) {
|
||||
displayData.setDirectoryOpen(objInList.id, !objInList.isDirectoryOpen);
|
||||
buttonClicked = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_FIXED(ENTRY_HEIGHT), .height = CLAY_SIZING_FIXED(ENTRY_HEIGHT)},
|
||||
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER}
|
||||
},
|
||||
}) {
|
||||
buttonClicked |= displayData.drawNonDirectoryObjIconGUI(objInList.id);
|
||||
}
|
||||
}
|
||||
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}
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}
|
||||
},
|
||||
.border = {
|
||||
.color = convert_vec4<Clay_Color>(io.theme->frontColor1),
|
||||
.width = {
|
||||
.left = 0,
|
||||
.right = 0,
|
||||
.top = topOutlineWidth,
|
||||
.bottom = bottomOutlineWidth,
|
||||
.betweenChildren = 0
|
||||
}
|
||||
}
|
||||
}) {
|
||||
buttonClicked |= displayData.drawObjGUI(objID, objInList.id);
|
||||
if(objInList.isDirectory) {
|
||||
if(gui.svg_icon_button_transparent("ico", objInList.isDirectoryOpen ? "data/icons/droparrow.svg" : "data/icons/droparrowclose.svg", false, ENTRY_HEIGHT)) {
|
||||
displayData.setDirectoryOpen(objInList.id, !objInList.isDirectoryOpen);
|
||||
buttonClicked = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_FIXED(ENTRY_HEIGHT), .height = CLAY_SIZING_FIXED(ENTRY_HEIGHT)},
|
||||
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER}
|
||||
},
|
||||
}) {
|
||||
buttonClicked |= displayData.drawNonDirectoryObjIconGUI(objInList.id);
|
||||
}
|
||||
}
|
||||
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}
|
||||
}
|
||||
}) {
|
||||
buttonClicked |= displayData.drawObjGUI({objID, objInList.id});
|
||||
}
|
||||
}
|
||||
|
||||
if(Clay_Hovered() && io.mouse.leftClick && !buttonClicked)
|
||||
objSelected = objInList.id;
|
||||
if(Clay_Hovered()) {
|
||||
hoveredObject = {parentIDStack, objInList.id};
|
||||
if(io.mouse.leftClick && !buttonClicked && !parentJustSelected) {
|
||||
if(io.key.leftShift)
|
||||
dragHoldSelected = true;
|
||||
else if(io.key.leftCtrl) {
|
||||
bool alreadySelected = objsSelected.contains(objInList.id);
|
||||
if(alreadySelected)
|
||||
objsSelected.erase(objInList.id);
|
||||
else {
|
||||
select_and_unselect_parents(parentIDStack, objInList.id);
|
||||
selfJustSelected = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!objsSelected.contains(objInList.id)) {
|
||||
objsSelected.clear();
|
||||
orderedObjsSelected.clear();
|
||||
objsSelected.emplace(objInList.id);
|
||||
selfJustSelected = true;
|
||||
}
|
||||
objDragged = {objID, objInList.id};
|
||||
}
|
||||
}
|
||||
if(io.key.leftShift && dragHoldSelected && !parentJustSelected) {
|
||||
select_and_unselect_parents(parentIDStack, objInList.id);
|
||||
selfJustSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
gui.pop_id();
|
||||
}
|
||||
if(!parentJustSelected) {
|
||||
if(objsSelected.contains(objInList.id))
|
||||
orderedObjsSelected.emplace_back(objID, objInList.id);
|
||||
}
|
||||
else
|
||||
unselect_object(objInList.id);
|
||||
itemCount++;
|
||||
if(objInList.isDirectoryOpen)
|
||||
recursive_gui(io, gui, displayData, objInList.id, itemCount, itemCountToStartAt, itemCountToEndAt, listDepth + 1);
|
||||
recursive_gui(io, gui, displayData, objInList.id, itemCount, itemCountToStartAt, itemCountToEndAt, listDepth + 1, hoveredObject, parentIDStack, selfJustSelected);
|
||||
}
|
||||
}
|
||||
parentIDStack.pop_back();
|
||||
}
|
||||
|
||||
void TreeListing::unselect_object(NetworkingObjects::NetObjID id) {
|
||||
objsSelected.erase(id);
|
||||
std::erase_if(orderedObjsSelected, [&id](const auto& p) {
|
||||
return p.object == id;
|
||||
});
|
||||
}
|
||||
|
||||
void TreeListing::select_and_unselect_parents(const std::vector<NetworkingObjects::NetObjID>& parentIDStack, NetworkingObjects::NetObjID newSelectedObj) {
|
||||
for(const NetworkingObjects::NetObjID& parent : parentIDStack) {
|
||||
if(objsSelected.contains(parent)) {
|
||||
unselect_object(parent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
objsSelected.emplace(newSelectedObj);
|
||||
}
|
||||
|
||||
void TreeListing::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,18 @@ class GUIManager;
|
|||
class TreeListing : public Element {
|
||||
public:
|
||||
static constexpr float ENTRY_HEIGHT = 25.0f;
|
||||
|
||||
struct ParentObjectIDPair {
|
||||
NetworkingObjects::NetObjID parent;
|
||||
NetworkingObjects::NetObjID object;
|
||||
bool operator==(const ParentObjectIDPair&) const = default;
|
||||
};
|
||||
|
||||
struct ParentObjectIDStack {
|
||||
std::vector<NetworkingObjects::NetObjID> parents;
|
||||
NetworkingObjects::NetObjID object;
|
||||
};
|
||||
|
||||
struct DisplayData {
|
||||
struct ObjInList {
|
||||
NetworkingObjects::NetObjID id;
|
||||
|
|
@ -16,15 +28,30 @@ class TreeListing : public Element {
|
|||
bool isDirectoryOpen = false;
|
||||
};
|
||||
std::function<std::optional<ObjInList>(NetworkingObjects::NetObjID, size_t)> getObjInListAtIndex;
|
||||
std::function<size_t(const ParentObjectIDPair&)> getIndexOfObjInList;
|
||||
std::function<void(NetworkingObjects::NetObjID, bool)> setDirectoryOpen;
|
||||
std::function<bool(NetworkingObjects::NetObjID)> drawNonDirectoryObjIconGUI;
|
||||
std::function<bool(NetworkingObjects::NetObjID, NetworkingObjects::NetObjID)> drawObjGUI;
|
||||
std::function<bool(const ParentObjectIDPair&)> drawObjGUI;
|
||||
std::function<void(NetworkingObjects::NetObjID, size_t, const std::vector<ParentObjectIDPair>&)> moveObjectsToListAtIndex;
|
||||
};
|
||||
void update(UpdateInputData& io, GUIManager& gui, NetworkingObjects::NetObjID rootObjID, const DisplayData& displayData);
|
||||
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) override;
|
||||
private:
|
||||
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<NetworkingObjects::NetObjID> objSelected;
|
||||
bool dragHoldSelected = false;
|
||||
|
||||
void set_single_selected_object();
|
||||
|
||||
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<ParentObjectIDStack>& hoveredObject, std::vector<NetworkingObjects::NetObjID>& parentIDStack, bool parentJustSelected);
|
||||
std::vector<ParentObjectIDPair> orderedObjsSelected;
|
||||
std::unordered_set<NetworkingObjects::NetObjID> objsSelected;
|
||||
|
||||
void select_and_unselect_parents(const std::vector<NetworkingObjects::NetObjID>& parentIDStack, NetworkingObjects::NetObjID newSelectedObj);
|
||||
|
||||
void unselect_object(NetworkingObjects::NetObjID id);
|
||||
|
||||
bool topHalfOfHovered = false;
|
||||
std::optional<ParentObjectIDPair> objDragged;
|
||||
std::optional<ParentObjectIDStack> oldHoveredObject;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue