Fixed moving bookmarks
This commit is contained in:
parent
915fbe598b
commit
c97266769b
3 changed files with 191 additions and 88 deletions
|
|
@ -125,7 +125,22 @@ void BookmarkManager::setup_list_gui(const char* id) {
|
|||
else
|
||||
nameToEdit.clear();
|
||||
},
|
||||
.moveObj = [&](const std::vector<TreeListingObjIndexList>& objectIndices, const TreeListingObjIndexList& newObjIndex) {
|
||||
.moveObj = [&](const std::vector<TreeListingObjIndexList>& objectIndicesTreeListing, const TreeListingObjIndexList& newObjIndexTreeListing) {
|
||||
NetObjID listObj;
|
||||
size_t index;
|
||||
struct ParentObjectIDPair {
|
||||
NetObjID parent;
|
||||
NetObjID object;
|
||||
};
|
||||
std::vector<ParentObjectIDPair> objsToInsert;
|
||||
|
||||
{
|
||||
listObj = get_bookmark_parent_from_obj_index(newObjIndexTreeListing).get_net_id();
|
||||
index = newObjIndexTreeListing.back();
|
||||
for(auto& objIndex : objectIndicesTreeListing)
|
||||
objsToInsert.emplace_back(get_bookmark_parent_from_obj_index(objIndex).get_net_id(), get_bookmark_from_obj_index(objIndex).get_net_id());
|
||||
}
|
||||
|
||||
std::unordered_map<WorldUndoManager::UndoObjectID, std::pair<std::vector<uint32_t>, std::vector<WorldUndoManager::UndoObjectID>>> toEraseMapUndo;
|
||||
WorldUndoManager::UndoObjectID insertParentUndoID;
|
||||
uint32_t insertUndoPosition;
|
||||
|
|
@ -133,15 +148,15 @@ void BookmarkManager::setup_list_gui(const char* id) {
|
|||
|
||||
world.netObjMan.send_multi_update_messsage([&]() {
|
||||
std::unordered_map<NetObjID, std::vector<NetObjOrderedListIterator<BookmarkListItem>>> toEraseMap;
|
||||
uint32_t newIndex = newObjIndex.back();
|
||||
uint32_t newIndex = index;
|
||||
std::vector<NetObjOwnerPtr<BookmarkListItem>> toInsertObjPtrs;
|
||||
auto& listPtr = get_bookmark_parent_from_obj_index(newObjIndex)->get_folder_list();
|
||||
auto& listPtr = world.netObjMan.get_obj_temporary_ref_from_id<BookmarkListItem>(listObj)->get_folder_list();
|
||||
|
||||
// Map parent folders to list of objects that should be erased from them
|
||||
for(size_t i = 0; i < objectIndices.size(); i++) {
|
||||
auto parentBookmarkList = get_bookmark_parent_from_obj_index(objectIndices[i]);
|
||||
toEraseMap[parentBookmarkList.get_net_id()].emplace_back(parentBookmarkList->get_folder_list()->at(objectIndices[i].back()));
|
||||
if(newIndex != 0 && parentBookmarkList == listPtr && objectIndices[i].back() <= newObjIndex.back())
|
||||
for(size_t i = 0; i < objsToInsert.size(); i++) {
|
||||
auto& parentListPtr = world.netObjMan.get_obj_temporary_ref_from_id<BookmarkListItem>(objsToInsert[i].parent)->get_folder_list();
|
||||
toEraseMap[objsToInsert[i].parent].emplace_back(parentListPtr->get(objsToInsert[i].object));
|
||||
if(newIndex != 0 && objsToInsert[i].parent == listObj && listPtr->get(objsToInsert[i].object)->pos <= index)
|
||||
newIndex--;
|
||||
}
|
||||
|
||||
|
|
@ -165,10 +180,9 @@ void BookmarkManager::setup_list_gui(const char* id) {
|
|||
// Insert objects in toInsertObjPtrs into the folder we have selected
|
||||
std::vector<std::pair<NetObjOrderedListIterator<BookmarkListItem>, NetObjOwnerPtr<BookmarkListItem>>> toInsert;
|
||||
auto insertIt = listPtr->at(newIndex);
|
||||
for(uint32_t i = 0; i < objectIndices.size(); i++) {
|
||||
auto o = get_bookmark_from_obj_index(objectIndices[i]);
|
||||
auto it = std::find_if(toInsertObjPtrs.begin(), toInsertObjPtrs.end(), [&o](NetObjOwnerPtr<BookmarkListItem>& objPtr) {
|
||||
return objPtr == o;
|
||||
for(uint32_t i = 0; i < objsToInsert.size(); i++) {
|
||||
auto it = std::find_if(toInsertObjPtrs.begin(), toInsertObjPtrs.end(), [id = objsToInsert[i].object](NetObjOwnerPtr<BookmarkListItem>& objPtr) {
|
||||
return objPtr.get_net_id() == id;
|
||||
});
|
||||
toInsert.emplace_back(insertIt, std::move(*it));
|
||||
toInsert.back().second.reassign_ids();
|
||||
|
|
@ -176,7 +190,7 @@ void BookmarkManager::setup_list_gui(const char* id) {
|
|||
std::vector<NetObjOrderedListIterator<BookmarkListItem>> insertedIterators = listPtr->insert_sorted_list_and_send_create(listPtr, toInsert);
|
||||
|
||||
// Prepare insert list for undo
|
||||
insertParentUndoID = world.undo.get_undoid_from_netid(listPtr.get_net_id());
|
||||
insertParentUndoID = world.undo.get_undoid_from_netid(listObj);
|
||||
insertUndoPosition = newIndex;
|
||||
for(auto& it : insertedIterators)
|
||||
insertedUndoIDs.emplace_back(world.undo.get_undoid_from_netid(it->obj.get_net_id()));
|
||||
|
|
|
|||
|
|
@ -26,6 +26,16 @@ bool operator<(const GUIStuff::TreeListingObjIndexList& a, const GUIStuff::TreeL
|
|||
|
||||
namespace GUIStuff {
|
||||
|
||||
bool is_tree_listing_obj_index_parent(const TreeListingObjIndexList& parentToCheck, const TreeListingObjIndexList& obj) {
|
||||
if(parentToCheck.size() >= obj.size())
|
||||
return false;
|
||||
for(size_t i = 0; i < parentToCheck.size(); i++) {
|
||||
if(parentToCheck[i] != obj[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
using namespace ElementHelpers;
|
||||
|
||||
TreeListing::TreeListing(GUIManager& gui):
|
||||
|
|
@ -40,95 +50,167 @@ void TreeListing::layout(const Clay_ElementId& id, const Data& newDisplayData) {
|
|||
CLAY(id, {
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}
|
||||
}) {
|
||||
scroll_area_many_entries(gui, "scroll", {
|
||||
.entryHeight = ENTRY_HEIGHT,
|
||||
.entryCount = flattenedIndexList.size(),
|
||||
.elementContent = [&](size_t i) {
|
||||
const ObjInfo& objInfo = flattenedIndexList[i];
|
||||
gui.element<LayoutElement>("elem", [&](LayoutElement*, const Clay_ElementId& lId) {
|
||||
SkColor4f backgroundColor;
|
||||
if(d.selectedIndices && d.selectedIndices->contains(flattenedIndexList[i].objIndex))
|
||||
backgroundColor = gui.io.theme->backColor1;
|
||||
else
|
||||
backgroundColor = gui.io.theme->backColor2;
|
||||
gui.element<LayoutElement>("scroll parent", [&] (LayoutElement*, const Clay_ElementId& lIdParent) {
|
||||
CLAY(lIdParent, {
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}
|
||||
}) {
|
||||
scroll_area_many_entries(gui, "scroll", {
|
||||
.entryHeight = ENTRY_HEIGHT,
|
||||
.entryCount = flattenedIndexList.size(),
|
||||
.elementContent = [&](size_t i) {
|
||||
const ObjInfo& objInfo = flattenedIndexList[i];
|
||||
gui.element<LayoutElement>("elem", [&](LayoutElement*, const Clay_ElementId& lId) {
|
||||
SkColor4f backgroundColor;
|
||||
if(d.selectedIndices && d.selectedIndices->contains(flattenedIndexList[i].objIndex))
|
||||
backgroundColor = gui.io.theme->backColor1;
|
||||
else
|
||||
backgroundColor = gui.io.theme->backColor2;
|
||||
|
||||
CLAY(lId, {
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
|
||||
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER}
|
||||
},
|
||||
.backgroundColor = convert_vec4<Clay_Color>(backgroundColor)
|
||||
}) {
|
||||
if(objInfo.objIndex.size() > 1) {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_FIXED((objInfo.objIndex.size() - 1) * ICON_SIZE), .height = CLAY_SIZING_GROW(0)}}
|
||||
}) {}
|
||||
}
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_FIXED(ICON_SIZE), .height = CLAY_SIZING_FIXED(ICON_SIZE)}}
|
||||
}) {
|
||||
if(!objInfo.isDirectory)
|
||||
d.drawNonDirectoryObjIconGUI(objInfo.objIndex);
|
||||
else {
|
||||
gui.set_z_index_keep_clipping_region(gui.get_z_index() + 1, [&] {
|
||||
svg_icon_button(gui, "open dir button", objInfo.isOpen ? "data/icons/droparrow.svg" : "data/icons/droparrowclose.svg", {
|
||||
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
|
||||
.size = ICON_SIZE,
|
||||
.onClick = [&] {
|
||||
d.setDirectoryOpen(objInfo.objIndex, !objInfo.isOpen);
|
||||
if(d.selectedIndices) {
|
||||
d.selectedIndices->clear();
|
||||
uint16_t borderWidth = (isDragging && i == dragIndexEnd && dragIndexStart != dragIndexEnd) ? 1 : 0;
|
||||
|
||||
CLAY(lId, {
|
||||
.layout = {
|
||||
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
|
||||
.childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER}
|
||||
},
|
||||
.backgroundColor = convert_vec4<Clay_Color>(backgroundColor),
|
||||
.border = {
|
||||
.color = convert_vec4<Clay_Color>(gui.io.theme->frontColor1),
|
||||
.width = {
|
||||
.left = 0,
|
||||
.right = 0,
|
||||
.top = borderWidth,
|
||||
.bottom = 0,
|
||||
.betweenChildren = 0
|
||||
}
|
||||
}
|
||||
}) {
|
||||
if(objInfo.objIndex.size() > 1) {
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_FIXED((objInfo.objIndex.size() - 1) * ICON_SIZE), .height = CLAY_SIZING_GROW(0)}}
|
||||
}) {}
|
||||
}
|
||||
CLAY_AUTO_ID({
|
||||
.layout = {.sizing = {.width = CLAY_SIZING_FIXED(ICON_SIZE), .height = CLAY_SIZING_FIXED(ICON_SIZE)}}
|
||||
}) {
|
||||
if(!objInfo.isDirectory)
|
||||
d.drawNonDirectoryObjIconGUI(objInfo.objIndex);
|
||||
else {
|
||||
gui.set_z_index_keep_clipping_region(gui.get_z_index() + 1, [&] {
|
||||
svg_icon_button(gui, "open dir button", objInfo.isOpen ? "data/icons/droparrow.svg" : "data/icons/droparrowclose.svg", {
|
||||
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
|
||||
.size = ICON_SIZE,
|
||||
.onClick = [&] {
|
||||
d.setDirectoryOpen(objInfo.objIndex, !objInfo.isOpen);
|
||||
if(d.selectedIndices) {
|
||||
d.selectedIndices->clear();
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
d.drawObjGUI(objInfo.objIndex);
|
||||
}
|
||||
}, LayoutElement::Callbacks {
|
||||
.mouseButton = [&, i] (LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button) {
|
||||
if(l->mouseHovering && button.button == InputManager::MouseButton::LEFT && button.down) {
|
||||
gui.set_post_callback_func([&, i, buttonClicks = button.clicks]{
|
||||
auto& objIndex = flattenedIndexList[i].objIndex;
|
||||
if(d.selectedIndices) {
|
||||
auto it = d.selectedIndices->find(objIndex);
|
||||
bool wasSelected = it != d.selectedIndices->end();
|
||||
|
||||
if(gui.io.input->key(InputManager::KEY_GENERIC_LSHIFT).held) {
|
||||
if(!wasSelected) {
|
||||
d.selectedIndices->emplace(objIndex);
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
}
|
||||
}
|
||||
else if(gui.io.input->key(InputManager::KEY_GENERIC_LCTRL).held) {
|
||||
if(!wasSelected)
|
||||
d.selectedIndices->emplace(objIndex);
|
||||
else
|
||||
d.selectedIndices->erase(it);
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
}
|
||||
else if(buttonClicks == 2) {
|
||||
if(d.onDoubleClick) d.onDoubleClick(objIndex);
|
||||
}
|
||||
else {
|
||||
if(buttonClicks == 2 && d.onDoubleClick) d.onDoubleClick(objIndex);
|
||||
if(!wasSelected) {
|
||||
d.selectedIndices->clear();
|
||||
d.selectedIndices->emplace(objIndex);
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
}
|
||||
isDragging = true;
|
||||
dragIndexStart = dragIndexEnd = i;
|
||||
}
|
||||
}
|
||||
else if(buttonClicks == 2)
|
||||
if(d.onDoubleClick) d.onDoubleClick(objIndex);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
d.drawObjGUI(objInfo.objIndex);
|
||||
}
|
||||
}, LayoutElement::Callbacks {
|
||||
.mouseButton = [&, i] (LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button) {
|
||||
if(l->mouseHovering && button.button == InputManager::MouseButton::LEFT && button.down) {
|
||||
gui.set_post_callback_func([&]{
|
||||
auto& objIndex = flattenedIndexList[i].objIndex;
|
||||
if(d.selectedIndices) {
|
||||
auto it = d.selectedIndices->find(objIndex);
|
||||
bool wasSelected = it != d.selectedIndices->end();
|
||||
|
||||
if(gui.io.input->key(InputManager::KEY_GENERIC_LSHIFT).held) {
|
||||
if(!wasSelected) {
|
||||
d.selectedIndices->emplace(objIndex);
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
}
|
||||
}
|
||||
else if(gui.io.input->key(InputManager::KEY_GENERIC_LCTRL).held) {
|
||||
if(!wasSelected)
|
||||
d.selectedIndices->emplace(objIndex);
|
||||
else
|
||||
d.selectedIndices->erase(it);
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
}
|
||||
else if(!wasSelected || d.selectedIndices->size() != 1) {
|
||||
d.selectedIndices->clear();
|
||||
d.selectedIndices->emplace(objIndex);
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
}
|
||||
else if(button.clicks == 2)
|
||||
if(d.onDoubleClick) d.onDoubleClick(objIndex);
|
||||
gui.set_to_layout();
|
||||
}
|
||||
else if(button.clicks == 2)
|
||||
if(d.onDoubleClick) d.onDoubleClick(objIndex);
|
||||
});
|
||||
gui.set_to_layout();
|
||||
}
|
||||
},
|
||||
.mouseMotion = [&, i] (LayoutElement* l, const InputManager::MouseMotionCallbackArgs& motion) {
|
||||
if(isDragging && l->mouseHovering && dragIndexEnd != i) {
|
||||
dragIndexEnd = i;
|
||||
gui.set_to_layout();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, LayoutElement::Callbacks {
|
||||
.mouseButton = [&] (LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button) {
|
||||
if(button.button == InputManager::MouseButton::LEFT && !button.down) {
|
||||
if(isDragging) {
|
||||
gui.set_to_layout();
|
||||
isDragging = false;
|
||||
if(l->mouseHovering || l->childMouseHovering) {
|
||||
if(dragIndexStart != dragIndexEnd)
|
||||
gui.set_post_callback_func([&] {move_selected_objects();});
|
||||
else if(!gui.io.input->key(InputManager::KEY_GENERIC_LSHIFT).held && !gui.io.input->key(InputManager::KEY_GENERIC_LCTRL).held) {
|
||||
gui.set_post_callback_func([&, i = dragIndexEnd] {
|
||||
if(d.selectedIndices->size() != 1 || !d.selectedIndices->contains(flattenedIndexList[i].objIndex)) {
|
||||
d.selectedIndices->clear();
|
||||
d.selectedIndices->emplace(flattenedIndexList[i].objIndex);
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void TreeListing::move_selected_objects() {
|
||||
if(d.selectedIndices && dragIndexEnd < flattenedIndexList.size()) {
|
||||
TreeListingObjIndexList objIndexToMoveTo = flattenedIndexList[dragIndexEnd].objIndex;
|
||||
// Check if object is about to move into itself (wrong movement)
|
||||
bool wrongMovement = false;
|
||||
for(const TreeListingObjIndexList& o : *d.selectedIndices) {
|
||||
if(is_tree_listing_obj_index_parent(o, objIndexToMoveTo)) {
|
||||
wrongMovement = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!wrongMovement) {
|
||||
std::vector<TreeListingObjIndexList> toMove(d.selectedIndices->begin(), d.selectedIndices->end());
|
||||
d.selectedIndices->clear();
|
||||
if(d.onSelectChange) d.onSelectChange();
|
||||
if(d.moveObj) d.moveObj(toMove, objIndexToMoveTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeListing::recursive_visible_flattened_obj_list(std::vector<ObjInfo>& objs, const TreeListingObjIndexList& objIndex) {
|
||||
std::optional<DirectoryInfo> dirInfo = d.dirInfo(objIndex);
|
||||
bool isRoot = objIndex.empty();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
namespace GUIStuff {
|
||||
typedef std::vector<size_t> TreeListingObjIndexList;
|
||||
bool is_tree_listing_obj_index_parent(const TreeListingObjIndexList& parentToCheck, const TreeListingObjIndexList& obj);
|
||||
}
|
||||
|
||||
bool operator<(const GUIStuff::TreeListingObjIndexList& a, const GUIStuff::TreeListingObjIndexList& b);
|
||||
|
|
@ -45,6 +46,12 @@ class TreeListing : public Element {
|
|||
private:
|
||||
void recursive_visible_flattened_obj_list(std::vector<ObjInfo>& objs, const TreeListingObjIndexList& objIndex);
|
||||
|
||||
void move_selected_objects();
|
||||
|
||||
bool isDragging = false;
|
||||
size_t dragIndexStart;
|
||||
size_t dragIndexEnd;
|
||||
|
||||
std::vector<ObjInfo> flattenedIndexList;
|
||||
Data d;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue