diff options
author | Julian Eisel <eiseljulian@gmail.com> | 2017-03-10 17:03:06 +0300 |
---|---|---|
committer | Julian Eisel <eiseljulian@gmail.com> | 2017-03-10 17:24:53 +0300 |
commit | 8e303aae255be2ef45d5818af046de7c22312aa4 (patch) | |
tree | 115b700e7b797645a6796dabcc2eae4ac51ed66f /source/blender/editors/space_outliner | |
parent | b3bb4a6936313a1358428ab95b0df96c93c9cd34 (diff) |
Support drag & drop of collections across multiple hierarchy levels
Two issues are remaining, they'll be fixed separately:
* Graphical feedback when dragging within the master collection is wrong
* There's some bug where collections swap places instead, Dalai will investigate
Diffstat (limited to 'source/blender/editors/space_outliner')
4 files changed, 104 insertions, 127 deletions
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 0ba60b21aa7..0b119b5d247 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1520,34 +1520,14 @@ static void outliner_draw_tree_element( } } -/** - * Count how many visible childs (and open grandchilds, great-grandchilds, ...) \a te has. - */ -static int outliner_count_visible_childs(const SpaceOops *soops, const TreeElement *te) -{ - TreeStoreElem *tselem = TREESTORE(te); - int current_count = 0; - - if (TSELEM_OPEN(tselem, soops)) { - for (TreeElement *te_child = te->subtree.first; te_child; te_child = te_child->next) { - current_count += outliner_count_visible_childs(soops, te_child); - current_count++; - } - } - - return current_count; -} - -static void outliner_draw_tree_element_floating(const SpaceOops *soops, const ARegion *ar, - const TreeElement *te_floating) +static void outliner_draw_tree_element_floating( + const ARegion *ar, const TreeElement *te_floating) { const TreeElement *te_insert = te_floating->drag_data->insert_handle; - const ListBase *lb_parent = te_floating->parent ? &te_floating->parent->subtree : &soops->tree; - const TreeElement *te_insert_fallback = te_insert ? te_insert : lb_parent->first; const int line_width = 2; unsigned int pos = add_attrib(immVertexFormat(), "pos", GL_FLOAT, 2, KEEP_FLOAT); - int coord_y = (te_insert ? te_insert->ys : (te_insert_fallback->ys + UI_UNIT_Y)) - (int)(line_width * 0.5f); + int coord_y = te_insert->ys; unsigned char col[4]; if (te_insert == te_floating) { @@ -1555,15 +1535,14 @@ static void outliner_draw_tree_element_floating(const SpaceOops *soops, const AR return; } - if (te_insert) { - coord_y -= UI_UNIT_Y * outliner_count_visible_childs(soops, te_insert); - } - UI_GetThemeColorShade4ubv(TH_BACK, -40, col); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); glEnable(GL_BLEND); - if (!te_insert || (te_floating->drag_data->insert_type == TE_INSERT_AFTER)) { + if (ELEM(te_floating->drag_data->insert_type, TE_INSERT_BEFORE, TE_INSERT_AFTER)) { + if (te_floating->drag_data->insert_type == TE_INSERT_BEFORE) { + coord_y += UI_UNIT_Y; + } immUniformColor4ubv(col); glLineWidth(line_width); @@ -1793,7 +1772,7 @@ static void outliner_draw_tree( startx, &starty, te_edit, &te_floating); } if (te_floating) { - outliner_draw_tree_element_floating(soops, ar, te_floating); + outliner_draw_tree_element_floating(ar, te_floating); } if (has_restrict_icons) { diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 4cee0e44fca..cad34c96db4 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -50,7 +50,7 @@ struct wmKeyConfig; typedef enum TreeElementInsertType { - /* no INSERT_BEFORE needed for now */ + TE_INSERT_BEFORE, TE_INSERT_AFTER, TE_INSERT_INTO, } TreeElementInsertType; @@ -91,7 +91,7 @@ typedef struct TreeElement { struct { TreeElementInsertType insert_type; - /* the element after which we may insert the dragged one (NULL to insert at top) */ + /* the element before/after/into which we may insert the dragged one (NULL to insert at top) */ struct TreeElement *insert_handle; } *drag_data; } TreeElement; diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 3db0b6ffa08..6fc088e0f48 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -52,11 +52,6 @@ enum { OUTLINER_ITEM_DRAG_CONFIRM, }; -typedef struct OutlinerItemDrag { - TreeElement *dragged_te; - int init_mouse_xy[2]; -} OutlinerItemDrag; - static int outliner_item_drag_drop_poll(bContext *C) { SpaceOops *soops = CTX_wm_space_outliner(C); @@ -67,70 +62,69 @@ static int outliner_item_drag_drop_poll(bContext *C) static TreeElement *outliner_item_drag_element_find(SpaceOops *soops, ARegion *ar, const wmEvent *event) { + /* note: using EVT_TWEAK_ events to trigger dragging is fine, + * it sends coordinates from where dragging was started */ const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]); return outliner_find_item_at_y(soops, &soops->tree, my); } -static OutlinerItemDrag *outliner_item_drag_data_create(TreeElement *dragged_te, const int mouse_xy[2]) -{ - OutlinerItemDrag *drag_data = MEM_mallocN(sizeof(*drag_data), __func__); - - drag_data->dragged_te = dragged_te; - copy_v2_v2_int(drag_data->init_mouse_xy, mouse_xy); - - return drag_data; -} - -static void outliner_item_drag_end(OutlinerItemDrag *op_drag_data) +static void outliner_item_drag_end(TreeElement *dragged_te) { - MEM_SAFE_FREE(op_drag_data->dragged_te->drag_data); - MEM_freeN(op_drag_data); + MEM_SAFE_FREE(dragged_te->drag_data); } -static void outliner_item_drag_handle(ARegion *ar, const wmEvent *event, OutlinerItemDrag *op_drag_data) +static void outliner_item_drag_handle( + SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged) { - TreeElement *dragged_te = op_drag_data->dragged_te; - const int delta_mouse_y = event->y - op_drag_data->init_mouse_xy[1]; - const int cmp_coord = (int)UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]); - const float margin = UI_UNIT_Y * (1.0f / 3); - - /* by default we don't change the item position */ - dragged_te->drag_data->insert_handle = dragged_te; - - if (delta_mouse_y > 0) { - for (TreeElement *te = dragged_te->prev; te && (cmp_coord >= (te->ys + margin)); te = te->prev) { - if (cmp_coord > (te->ys + (2 * margin))) { - dragged_te->drag_data->insert_type = TE_INSERT_AFTER; - /* will be NULL if we want to insert as first element */ - dragged_te->drag_data->insert_handle = te->prev; + TreeStoreElem *tselem_dragged = TREESTORE(te_dragged); + TreeElement *insert_handle; + float view_mval[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + insert_handle = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + te_dragged->drag_data->insert_handle = NULL; + if (insert_handle) { + TreeStoreElem *tselem_handle = TREESTORE(insert_handle); + if (tselem_handle->type == tselem_dragged->type) { + const float margin = UI_UNIT_Y * (1.0f / 4); + + te_dragged->drag_data->insert_handle = insert_handle; + if (view_mval[1] < (insert_handle->ys + margin)) { + te_dragged->drag_data->insert_type = TE_INSERT_AFTER; + } + else if (view_mval[1] > (insert_handle->ys + (2 * margin))) { + te_dragged->drag_data->insert_type = TE_INSERT_BEFORE; } else { - dragged_te->drag_data->insert_type = TE_INSERT_INTO; - dragged_te->drag_data->insert_handle = te; + te_dragged->drag_data->insert_type = TE_INSERT_INTO; } } } else { - for (TreeElement *te = dragged_te->next; te && (cmp_coord <= (te->ys + UI_UNIT_Y - margin)); te = te->next) { - if (cmp_coord < (te->ys + margin)) { - dragged_te->drag_data->insert_type = TE_INSERT_AFTER; - dragged_te->drag_data->insert_handle = te; - BLI_assert(te->prev != NULL); - } - else { - dragged_te->drag_data->insert_type = TE_INSERT_INTO; - dragged_te->drag_data->insert_handle = te; - } + TreeElement *first = soops->tree.first; + TreeElement *last = soops->tree.last; + + /* mouse doesn't hover any item (ignoring x axis), so it's either above list bounds or below. */ + if (view_mval[1] < last->ys) { + te_dragged->drag_data->insert_handle = last; + te_dragged->drag_data->insert_type = TE_INSERT_AFTER; + } + else if (view_mval[1] > (first->ys + UI_UNIT_Y)) { + te_dragged->drag_data->insert_handle = first; + te_dragged->drag_data->insert_type = TE_INSERT_BEFORE; + } + else { + BLI_assert(0); } } } -static bool outliner_item_drag_drop_apply(const Scene *scene, OutlinerItemDrag *op_drag_data) +static bool outliner_item_drag_drop_apply(const Scene *scene, TreeElement *dragged_te) { - TreeElement *dragged_te = op_drag_data->dragged_te; - TreeElement *insert_after = dragged_te->drag_data->insert_handle; + TreeElement *insert_handle = dragged_te->drag_data->insert_handle; - if (insert_after == dragged_te) { + if (insert_handle == dragged_te) { /* No need to do anything */ return false; } @@ -138,8 +132,8 @@ static bool outliner_item_drag_drop_apply(const Scene *scene, OutlinerItemDrag * if (dragged_te->reinsert) { /* Not sure yet what the best way to handle reordering elements of different types * (and stored in different lists). For collection display mode this is enough. */ - if (!insert_after || (insert_after->reinsert == dragged_te->reinsert)) { - dragged_te->reinsert(scene, dragged_te, insert_after, dragged_te->drag_data->insert_type); + if (!insert_handle || (insert_handle->reinsert == dragged_te->reinsert)) { + dragged_te->reinsert(scene, dragged_te, insert_handle, dragged_te->drag_data->insert_type); } } @@ -150,7 +144,7 @@ static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEv { ARegion *ar = CTX_wm_region(C); SpaceOops *soops = CTX_wm_space_outliner(C); - OutlinerItemDrag *op_drag_data = op->customdata; + TreeElement *te_dragged = op->customdata; int retval = OPERATOR_RUNNING_MODAL; bool redraw = false; bool skip_rebuild = true; @@ -158,7 +152,7 @@ static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEv switch (event->type) { case EVT_MODAL_MAP: if (event->val == OUTLINER_ITEM_DRAG_CONFIRM) { - outliner_item_drag_drop_apply(CTX_data_scene(C), op_drag_data); + outliner_item_drag_drop_apply(CTX_data_scene(C), te_dragged); skip_rebuild = false; retval = OPERATOR_FINISHED; } @@ -169,11 +163,11 @@ static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEv BLI_assert(0); } WM_event_add_mousemove(C); /* update highlight */ - outliner_item_drag_end(op_drag_data); + outliner_item_drag_end(te_dragged); redraw = true; break; case MOUSEMOVE: - outliner_item_drag_handle(ar, event, op_drag_data); + outliner_item_drag_handle(soops, ar, event, te_dragged); redraw = true; break; } @@ -192,17 +186,16 @@ static int outliner_item_drag_drop_invoke(bContext *C, wmOperator *op, const wmE { ARegion *ar = CTX_wm_region(C); SpaceOops *soops = CTX_wm_space_outliner(C); - TreeElement *te = outliner_item_drag_element_find(soops, ar, event); + TreeElement *te_dragged = outliner_item_drag_element_find(soops, ar, event); - if (!te) { + if (!te_dragged) { return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); } - - op->customdata = outliner_item_drag_data_create(te, &event->x); - te->drag_data = MEM_callocN(sizeof(*te->drag_data), __func__); + op->customdata = te_dragged; + te_dragged->drag_data = MEM_callocN(sizeof(*te_dragged->drag_data), __func__); /* by default we don't change the item position */ - te->drag_data->insert_handle = te; + te_dragged->drag_data->insert_handle = te_dragged; /* unset highlighted tree element, dragged one will be highlighted instead */ outliner_set_flag(&soops->tree, TSE_HIGHLIGHTED, false); diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 1273a9fd1f6..e8abef263eb 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1287,55 +1287,60 @@ static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops) } } -static void outliner_layer_collections_reorder(const Scene *scene, TreeElement *insert_element, TreeElement *insert_handle, - TreeElementInsertType action) +static void outliner_layer_collections_reorder( + const Scene *scene, TreeElement *insert_element, TreeElement *insert_handle, TreeElementInsertType action) { - LayerCollection *lc_src = insert_element->directdata; - LayerCollection *lc_dst = insert_handle ? insert_handle->directdata : NULL; - - if (action == TE_INSERT_AFTER) { - if (lc_dst == NULL) { - /* It needs a LayerCollection to use as reference, - * specially now that we are to allow insert in collections - * that don't belong to the same hierarchical level */ - TODO_LAYER_OPERATORS; - /* BKE_layer_collection_move_after(scene, sc_dst, sc_src); */ - } - else { - BKE_layer_collection_move_below(scene, lc_dst, lc_src); - } + LayerCollection *lc_insert = insert_element->directdata; + LayerCollection *lc_handle = insert_handle->directdata; + + if (action == TE_INSERT_BEFORE) { + BKE_layer_collection_move_above(scene, lc_handle, lc_insert); + } + else if (action == TE_INSERT_AFTER) { + BKE_layer_collection_move_below(scene, lc_handle, lc_insert); } else if (action == TE_INSERT_INTO) { - BKE_layer_collection_move_into(scene, lc_dst, lc_src); + BKE_layer_collection_move_into(scene, lc_insert, lc_handle); + } + else { + BLI_assert(0); } } -static void outliner_scene_collections_reorder(const Scene *scene, TreeElement *insert_element, TreeElement *insert_handle, - TreeElementInsertType action) +static void outliner_scene_collections_reorder( + const Scene *scene, TreeElement *insert_element, TreeElement *insert_handle, TreeElementInsertType action) { - SceneCollection *sc_src = insert_element->directdata; - SceneCollection *sc_dst = insert_handle ? insert_handle->directdata : NULL; - - if (action == TE_INSERT_AFTER) { - if (sc_dst == NULL) { - /* It needs a SceneCollection to use as reference, - * specially now that we are to allow insert in collections - * that don't belong to the same hierarchical level */ - TODO_LAYER_OPERATORS; - /* BKE_collection_move_after(scene, sc_dst, sc_src); */ + SceneCollection *sc_master = BKE_collection_master(scene); + SceneCollection *sc_insert = insert_element->directdata; + SceneCollection *sc_handle = insert_handle->directdata; + + if (sc_handle == sc_master) { + /* exception: Can't insert before/after master selection, has to be one of its childs */ + if (action == TE_INSERT_BEFORE) { + sc_handle = sc_master->scene_collections.first; } - else { - BKE_collection_move_below(scene, sc_dst, sc_src); + else if (action == TE_INSERT_AFTER) { + sc_handle = sc_master->scene_collections.last; } } + + if (action == TE_INSERT_BEFORE) { + BKE_collection_move_above(scene, sc_handle, sc_insert); + } + else if (action == TE_INSERT_AFTER) { + BKE_collection_move_below(scene, sc_handle, sc_insert); + } else if (action == TE_INSERT_INTO) { - BKE_collection_move_into(scene, sc_dst, sc_src); + BKE_collection_move_into(scene, sc_handle, sc_insert); + } + else { + BLI_assert(0); } } -static void outliner_add_layer_collections_recursive(SpaceOops *soops, ListBase *tree, Scene *scene, - ListBase *layer_collections, TreeElement *parent_ten, - int *io_collection_counter) +static void outliner_add_layer_collections_recursive( + SpaceOops *soops, ListBase *tree, Scene *scene, ListBase *layer_collections, TreeElement *parent_ten, + int *io_collection_counter) { for (LayerCollection *collection = layer_collections->first; collection; collection = collection->next) { TreeElement *ten = outliner_add_element(soops, tree, scene, parent_ten, TSE_LAYER_COLLECTION, |