diff options
4 files changed, 105 insertions, 49 deletions
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 771591240d6..6207ed8579c 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1772,7 +1772,7 @@ static void outliner_draw_tree( outliner_draw_tree_element(C, block, fstyle, scene, sl, ar, soops, te, te->drag_data != NULL, startx, &starty, te_edit, &te_floating); } - if (te_floating) { + if (te_floating && te_floating->drag_data->insert_handle) { outliner_draw_tree_element_floating(ar, te_floating); } diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index cad34c96db4..2256e55a315 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -70,6 +70,13 @@ typedef enum TreeTraversalAction { */ typedef void (*TreeElementReinsertFunc)(const struct Scene *scene, struct TreeElement *insert_element, struct TreeElement *insert_handle, TreeElementInsertType action); +/** + * Executed on (almost) each mouse move while dragging. It's supposed to give info + * if reinserting insert_element before/after/into insert_handle would be allowed. + * It's allowed to change the reinsert info here for non const pointers. + */ +typedef bool (*TreeElementReinsertPollFunc)(const struct Scene *scene, const struct TreeElement *insert_element, + struct TreeElement **io_insert_handle, TreeElementInsertType *io_action); typedef TreeTraversalAction (*TreeTraversalFunc)(struct TreeElement *te, void *customdata); @@ -86,8 +93,9 @@ typedef struct TreeElement { void *directdata; // Armature Bones, Base, Sequence, Strip... PointerRNA rnaptr; // RNA Pointer - /* callbacks */ + /* callbacks - TODO should be moved into a type (like TreeElementType) */ TreeElementReinsertFunc reinsert; + TreeElementReinsertPollFunc reinsert_poll; struct { TreeElementInsertType insert_type; diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 4fc912f85c7..a96f99d332a 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -74,45 +74,48 @@ static void outliner_item_drag_end(TreeElement *dragged_te) MEM_SAFE_FREE(dragged_te->drag_data); } -static void outliner_item_drag_handle( - SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged) +static void outliner_item_drag_get_insert_data( + SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged, + TreeElement **r_te_insert_handle, TreeElementInsertType *r_insert_type) { - TreeStoreElem *tselem_dragged = TREESTORE(te_dragged); - TreeElement *insert_handle; + TreeElement *te_hovered; 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_hovered = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + if (te_hovered) { + TreeStoreElem *tselem_hovered = TREESTORE(te_hovered); - te_dragged->drag_data->insert_handle = NULL; - if (insert_handle) { - TreeStoreElem *tselem_handle = TREESTORE(insert_handle); - if (tselem_handle->type == tselem_dragged->type) { + if (te_hovered != te_dragged) { 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)) { - if (TSELEM_OPEN(tselem_handle, soops)) { + *r_te_insert_handle = te_hovered; + if (view_mval[1] < (te_hovered->ys + margin)) { + if (TSELEM_OPEN(tselem_hovered, soops)) { /* inserting after a open item means we insert into it, but as first child */ - if (BLI_listbase_is_empty(&insert_handle->subtree)) { - te_dragged->drag_data->insert_type = TE_INSERT_INTO; + if (BLI_listbase_is_empty(&te_hovered->subtree)) { + *r_insert_type = TE_INSERT_INTO; } else { - te_dragged->drag_data->insert_type = TE_INSERT_BEFORE; - te_dragged->drag_data->insert_handle = insert_handle->subtree.first; + *r_insert_type = TE_INSERT_BEFORE; + *r_te_insert_handle = te_hovered->subtree.first; } } else { - te_dragged->drag_data->insert_type = TE_INSERT_AFTER; + *r_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 if (view_mval[1] > (te_hovered->ys + (2 * margin))) { + *r_insert_type = TE_INSERT_BEFORE; } else { - te_dragged->drag_data->insert_type = TE_INSERT_INTO; + *r_insert_type = TE_INSERT_INTO; } } + else { + *r_te_insert_handle = te_dragged; + } } else { TreeElement *first = soops->tree.first; @@ -120,12 +123,12 @@ static void outliner_item_drag_handle( /* 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; + *r_te_insert_handle = last; + *r_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; + *r_te_insert_handle = first; + *r_insert_type = TE_INSERT_BEFORE; } else { BLI_assert(0); @@ -133,28 +136,48 @@ static void outliner_item_drag_handle( } } +static void outliner_item_drag_handle( + const Scene *scene, SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged) +{ + TreeElement *te_insert_handle; + TreeElementInsertType insert_type; + + outliner_item_drag_get_insert_data(soops, ar, event, te_dragged, &te_insert_handle, &insert_type); + + if ((te_dragged != te_insert_handle) && + te_dragged->reinsert_poll && + !te_dragged->reinsert_poll(scene, te_dragged, &te_insert_handle, &insert_type)) + { + te_insert_handle = NULL; + } + te_dragged->drag_data->insert_type = insert_type; + te_dragged->drag_data->insert_handle = te_insert_handle; +} + static bool outliner_item_drag_drop_apply(const Scene *scene, TreeElement *dragged_te) { TreeElement *insert_handle = dragged_te->drag_data->insert_handle; + TreeElementInsertType insert_type = dragged_te->drag_data->insert_type; - if (insert_handle == dragged_te) { + if ((insert_handle == dragged_te) || !insert_handle) { /* No need to do anything */ - return false; } - - 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_handle || (insert_handle->reinsert == dragged_te->reinsert)) { - dragged_te->reinsert(scene, dragged_te, insert_handle, dragged_te->drag_data->insert_type); - } + else if (dragged_te->reinsert) { + BLI_assert(!dragged_te->reinsert_poll || dragged_te->reinsert_poll(scene, dragged_te, &insert_handle, + &insert_type)); + /* call of assert above should not have changed insert_handle and insert_type at this point */ + BLI_assert(dragged_te->drag_data->insert_handle == insert_handle && + dragged_te->drag_data->insert_type == insert_type); + dragged_te->reinsert(scene, dragged_te, insert_handle, insert_type); + return true; } - return true; + return false; } static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEvent *event) { + Scene *scene = CTX_data_scene(C); ARegion *ar = CTX_wm_region(C); SpaceOops *soops = CTX_wm_space_outliner(C); TreeElement *te_dragged = op->customdata; @@ -165,8 +188,9 @@ 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), te_dragged); - skip_rebuild = false; + if (outliner_item_drag_drop_apply(scene, te_dragged)) { + skip_rebuild = false; + } retval = OPERATOR_FINISHED; } else if (event->val == OUTLINER_ITEM_DRAG_CANCEL) { @@ -180,7 +204,7 @@ static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEv redraw = true; break; case MOUSEMOVE: - outliner_item_drag_handle(soops, ar, event, te_dragged); + outliner_item_drag_handle(scene, soops, ar, event, te_dragged); redraw = true; break; } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 68b533ab31e..1e254b21ff5 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1306,6 +1306,13 @@ static void outliner_layer_collections_reorder( BLI_assert(0); } } +static bool outliner_layer_collections_reorder_poll( + const Scene *UNUSED(scene), const TreeElement *UNUSED(insert_element), + TreeElement **io_insert_handle, TreeElementInsertType *UNUSED(io_action)) +{ + const TreeStoreElem *tselem_handle = TREESTORE(*io_insert_handle); + return ELEM(tselem_handle->type, TSE_LAYER_COLLECTION); +} static void outliner_scene_collections_reorder( const Scene *scene, TreeElement *insert_element, TreeElement *insert_handle, TreeElementInsertType action) @@ -1314,16 +1321,7 @@ static void outliner_scene_collections_reorder( 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 if (action == TE_INSERT_AFTER) { - sc_handle = sc_master->scene_collections.last; - } - } - + BLI_assert((action == TE_INSERT_INTO) || (sc_handle != sc_master)); if (action == TE_INSERT_BEFORE) { BKE_collection_move_above(scene, sc_handle, sc_insert); } @@ -1337,6 +1335,30 @@ static void outliner_scene_collections_reorder( BLI_assert(0); } } +static bool outliner_scene_collections_reorder_poll( + const Scene *scene, const TreeElement *UNUSED(insert_element), + TreeElement **io_insert_handle, TreeElementInsertType *io_action) +{ + const TreeStoreElem *tselem_handle = TREESTORE(*io_insert_handle); + SceneCollection *sc_master = BKE_collection_master(scene); + SceneCollection *sc_handle = (*io_insert_handle)->directdata; + + if (!ELEM(tselem_handle->type, TSE_SCENE_COLLECTION)) { + return false; + } + + if (sc_handle == sc_master) { + /* exception: Can't insert before/after master selection, has to be one of its childs */ + TreeElement *te_master = *io_insert_handle; + if (*io_action == TE_INSERT_BEFORE) { + *io_insert_handle = te_master->subtree.first; + } + else if (*io_action == TE_INSERT_AFTER) { + *io_insert_handle = te_master->subtree.last; + } + } + return true; +} static void outliner_add_layer_collections_recursive( SpaceOops *soops, ListBase *tree, Scene *scene, ListBase *layer_collections, TreeElement *parent_ten, @@ -1349,6 +1371,7 @@ static void outliner_add_layer_collections_recursive( ten->name = collection->scene_collection->name; ten->directdata = collection; ten->reinsert = outliner_layer_collections_reorder; + ten->reinsert_poll = outliner_layer_collections_reorder_poll; for (LinkData *link = collection->object_bases.first; link; link = link->next) { outliner_add_element(soops, &ten->subtree, ((Base *)link->data)->object, NULL, 0, 0); @@ -1370,6 +1393,7 @@ static void outliner_add_scene_collection_init(TreeElement *te, SceneCollection te->name = collection->name; te->directdata = collection; te->reinsert = outliner_scene_collections_reorder; + te->reinsert_poll = outliner_scene_collections_reorder_poll; } static void outliner_add_scene_collections_recursive(SpaceOops *soops, ListBase *tree, Scene *scene, |