diff options
author | Nathan Craddock <nzcraddock@gmail.com> | 2020-09-10 18:49:35 +0300 |
---|---|---|
committer | Nathan Craddock <nzcraddock@gmail.com> | 2020-09-10 18:53:49 +0300 |
commit | b0741e1dcbc5e4549e95745b1f1b501f8cd33add (patch) | |
tree | 4f4bbdb0cd0132c0e2150a453d4c26fc5a4e4d06 /source/blender/editors | |
parent | e628e8821e7c81b912968c28e1972afe7078343a (diff) |
Outliner: Use right click target element for context menu
When opening the outliner context menu with multiple data types
selected, the menu did not show operators related to the target of the
cursor. For example, if a modifier and object are selected, a right
click on the modifier does not show the modifier options, rather it
shows the object context menu.
Now the data type of the right-click element is used to determine the
context menu to draw. For this to work properly the active element is
now set on right click.
Part of T77408
Manifest Task: https://developer.blender.org/T77770
Differential Revision: https://developer.blender.org/D8647
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/space_outliner/outliner_edit.c | 3 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_tools.c | 406 |
2 files changed, 186 insertions, 223 deletions
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 7cd27ae3321..dea67a8678d 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -437,7 +437,8 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto Main *bmain = CTX_data_main(C); ID *id = tselem->id; - BLI_assert(te->idcode != 0 && id != NULL); + BLI_assert(id != NULL); + BLI_assert((tselem->type == 0 && te->idcode != 0) || tselem->type == TSE_LAYER_COLLECTION); UNUSED_VARS_NDEBUG(te); if (te->idcode == ID_LI && ((Library *)id)->parent != NULL) { diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index b6c5d860457..0743e841794 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -95,110 +95,105 @@ /** \name ID/Library/Data Set/Un-link Utilities * \{ */ -static void set_operation_types(SpaceOutliner *space_outliner, - ListBase *lb, - int *scenelevel, - int *objectlevel, - int *idlevel, - int *datalevel) +static void get_element_operation_type( + TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel) { - TreeElement *te; - TreeStoreElem *tselem; + TreeStoreElem *tselem = TREESTORE(te); + if (tselem->flag & TSE_SELECTED) { + /* Layer collection points to collection ID. */ + if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) { + if (*datalevel == 0) { + *datalevel = tselem->type; + } + else if (*datalevel != tselem->type) { + *datalevel = -1; + } + } + else { + const int idcode = (int)GS(tselem->id->name); + bool is_standard_id = false; + switch ((ID_Type)idcode) { + case ID_SCE: + *scenelevel = 1; + break; + case ID_OB: + *objectlevel = 1; + break; - for (te = lb->first; te; te = te->next) { - tselem = TREESTORE(te); - if (tselem->flag & TSE_SELECTED) { - /* Layer collection points to collection ID. */ - if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) { - if (*datalevel == 0) { - *datalevel = tselem->type; - } - else if (*datalevel != tselem->type) { - *datalevel = -1; - } + case ID_ME: + case ID_CU: + case ID_MB: + case ID_LT: + case ID_LA: + case ID_AR: + case ID_CA: + case ID_SPK: + case ID_MA: + case ID_TE: + case ID_IP: + case ID_IM: + case ID_SO: + case ID_KE: + case ID_WO: + case ID_AC: + case ID_TXT: + case ID_GR: + case ID_LS: + case ID_LI: + case ID_VF: + case ID_NT: + case ID_BR: + case ID_PA: + case ID_GD: + case ID_MC: + case ID_MSK: + case ID_PAL: + case ID_PC: + case ID_CF: + case ID_WS: + case ID_LP: + case ID_HA: + case ID_PT: + case ID_VO: + case ID_SIM: + is_standard_id = true; + break; + case ID_WM: + case ID_SCR: + /* Those are ignored here. */ + /* Note: while Screens should be manageable here, deleting a screen used by a workspace + * will cause crashes when trying to use that workspace, so for now let's play minimal, + * safe change. */ + break; + } + if (idcode == ID_NLA) { + /* Fake one, not an actual ID type... */ + is_standard_id = true; } - else { - const int idcode = (int)GS(tselem->id->name); - bool is_standard_id = false; - switch ((ID_Type)idcode) { - case ID_SCE: - *scenelevel = 1; - break; - case ID_OB: - *objectlevel = 1; - break; - case ID_ME: - case ID_CU: - case ID_MB: - case ID_LT: - case ID_LA: - case ID_AR: - case ID_CA: - case ID_SPK: - case ID_MA: - case ID_TE: - case ID_IP: - case ID_IM: - case ID_SO: - case ID_KE: - case ID_WO: - case ID_AC: - case ID_TXT: - case ID_GR: - case ID_LS: - case ID_LI: - case ID_VF: - case ID_NT: - case ID_BR: - case ID_PA: - case ID_GD: - case ID_MC: - case ID_MSK: - case ID_PAL: - case ID_PC: - case ID_CF: - case ID_WS: - case ID_LP: - case ID_HA: - case ID_PT: - case ID_VO: - case ID_SIM: - is_standard_id = true; - break; - case ID_WM: - case ID_SCR: - /* Those are ignored here. */ - /* Note: while Screens should be manageable here, deleting a screen used by a workspace - * will cause crashes when trying to use that workspace, so for now let's play minimal, - * safe change. */ - break; + if (is_standard_id) { + if (*idlevel == 0) { + *idlevel = idcode; } - if (idcode == ID_NLA) { - /* Fake one, not an actual ID type... */ - is_standard_id = true; + else if (*idlevel != idcode) { + *idlevel = -1; } - - if (is_standard_id) { - if (*idlevel == 0) { - *idlevel = idcode; - } - else if (*idlevel != idcode) { - *idlevel = -1; - } - if (ELEM(*datalevel, TSE_VIEW_COLLECTION_BASE, TSE_SCENE_COLLECTION_BASE)) { - *datalevel = 0; - } + if (ELEM(*datalevel, TSE_VIEW_COLLECTION_BASE, TSE_SCENE_COLLECTION_BASE)) { + *datalevel = 0; } } } - if (TSELEM_OPEN(tselem, space_outliner)) { - set_operation_types( - space_outliner, &te->subtree, scenelevel, objectlevel, idlevel, datalevel); - } } } +static TreeElement *get_target_element(SpaceOutliner *space_outliner) +{ + TreeElement *te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE); + BLI_assert(te); + + return te; +} + static void unlink_action_fn(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -405,7 +400,7 @@ static void outliner_do_libdata_operation(bContext *C, for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { - if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) { + if ((tselem->type == 0 && te->idcode != 0) || tselem->type == TSE_LAYER_COLLECTION) { TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL; operation_fn(C, reports, scene, te, tsep, tselem, user_data); } @@ -1796,18 +1791,16 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - eOutlinerIdOpTypes event; /* check for invalid states */ if (space_outliner == NULL) { return OPERATOR_CANCELLED; } - set_operation_types( - space_outliner, &space_outliner->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); - - event = RNA_enum_get(op->ptr, "type"); + TreeElement *te = get_target_element(space_outliner); + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); + eOutlinerIdOpTypes event = RNA_enum_get(op->ptr, "type"); switch (event) { case OUTLINER_IDOP_UNLINK: { /* unlink datablock from its parent */ @@ -2128,18 +2121,16 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - eOutlinerLibOpTypes event; /* check for invalid states */ if (space_outliner == NULL) { return OPERATOR_CANCELLED; } - set_operation_types( - space_outliner, &space_outliner->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); - - event = RNA_enum_get(op->ptr, "type"); + TreeElement *te = get_target_element(space_outliner); + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); + eOutlinerLibOpTypes event = RNA_enum_get(op->ptr, "type"); switch (event) { case OL_LIB_RENAME: { outliner_do_libdata_operation( @@ -2261,8 +2252,9 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) if (space_outliner == NULL) { return OPERATOR_CANCELLED; } - set_operation_types( - space_outliner, &space_outliner->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); + + TreeElement *te = get_target_element(space_outliner); + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); /* get action to use */ act = BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action")); @@ -2369,22 +2361,21 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - eOutliner_AnimDataOps event; /* check for invalid states */ if (space_outliner == NULL) { return OPERATOR_CANCELLED; } - event = RNA_enum_get(op->ptr, "type"); - set_operation_types( - space_outliner, &space_outliner->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); + TreeElement *te = get_target_element(space_outliner); + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); if (datalevel != TSE_ANIM_DATA) { return OPERATOR_CANCELLED; } /* perform the core operation */ + eOutliner_AnimDataOps event = RNA_enum_get(op->ptr, "type"); switch (event) { case OUTLINER_ANIMOP_CLEAR_ADT: /* Remove Animation Data - this may remove the active action, in some cases... */ @@ -2474,15 +2465,10 @@ static const EnumPropertyItem prop_constraint_op_types[] = { static int outliner_constraint_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - eOutliner_PropConstraintOps event; - - event = RNA_enum_get(op->ptr, "type"); - set_operation_types( - space_outliner, &space_outliner->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); + eOutliner_PropConstraintOps event = RNA_enum_get(op->ptr, "type"); outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, constraint_fn, C); + space_outliner, TSE_CONSTRAINT, event, &space_outliner->tree, constraint_fn, C); if (event == OL_CONSTRAINTOP_DELETE) { outliner_cleanup_tree(space_outliner); @@ -2526,15 +2512,10 @@ static const EnumPropertyItem prop_modifier_op_types[] = { static int outliner_modifier_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - eOutliner_PropModifierOps event; - - event = RNA_enum_get(op->ptr, "type"); - set_operation_types( - space_outliner, &space_outliner->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); + eOutliner_PropModifierOps event = RNA_enum_get(op->ptr, "type"); outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, modifier_fn, C); + space_outliner, TSE_MODIFIER, event, &space_outliner->tree, modifier_fn, C); if (event == OL_MODIFIER_OP_DELETE) { outliner_cleanup_tree(space_outliner); @@ -2581,17 +2562,16 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - eOutliner_PropDataOps event; /* check for invalid states */ if (space_outliner == NULL) { return OPERATOR_CANCELLED; } - event = RNA_enum_get(op->ptr, "type"); - set_operation_types( - space_outliner, &space_outliner->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); + TreeElement *te = get_target_element(space_outliner); + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); + eOutliner_PropDataOps event = RNA_enum_get(op->ptr, "type"); switch (datalevel) { case TSE_POSE_CHANNEL: { outliner_do_data_operation( @@ -2690,134 +2670,116 @@ static int outliner_operator_menu(bContext *C, const char *opname) } static int do_outliner_operation_event(bContext *C, + ReportList *reports, ARegion *region, SpaceOutliner *space_outliner, - TreeElement *te, - const float mval[2]) + TreeElement *te) { - ReportList *reports = CTX_wm_reports(C); /* XXX... */ - - if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { - int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - TreeStoreElem *tselem = TREESTORE(te); - - /* select object that's clicked on and popup context menu */ - if (!(tselem->flag & TSE_SELECTED)) { + int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; + TreeStoreElem *tselem = TREESTORE(te); - if (outliner_flag_is_any_test(&space_outliner->tree, TSE_SELECTED, 1)) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); - } + int select_flag = OL_ITEM_ACTIVATE | OL_ITEM_SELECT; + if (tselem->flag & TSE_SELECTED) { + select_flag |= OL_ITEM_EXTEND; + } - tselem->flag |= TSE_SELECTED; + outliner_item_select(C, space_outliner, te, select_flag); - /* Only redraw, don't rebuild here because TreeElement pointers will - * become invalid and operations will crash. */ - ED_region_tag_redraw_no_rebuild(region); - ED_outliner_select_sync_from_outliner(C, space_outliner); - } + /* Only redraw, don't rebuild here because TreeElement pointers will + * become invalid and operations will crash. */ + ED_region_tag_redraw_no_rebuild(region); + ED_outliner_select_sync_from_outliner(C, space_outliner); - set_operation_types( - space_outliner, &space_outliner->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); + get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); - if (scenelevel) { - if (objectlevel || datalevel || idlevel) { - BKE_report(reports, RPT_WARNING, "Mixed selection"); - return OPERATOR_CANCELLED; - } - return outliner_operator_menu(C, "OUTLINER_OT_scene_operation"); + if (scenelevel) { + if (objectlevel || datalevel || idlevel) { + BKE_report(reports, RPT_WARNING, "Mixed selection"); + return OPERATOR_CANCELLED; } - if (objectlevel) { - WM_menu_name_call(C, "OUTLINER_MT_object", WM_OP_INVOKE_REGION_WIN); - return OPERATOR_FINISHED; + return outliner_operator_menu(C, "OUTLINER_OT_scene_operation"); + } + if (objectlevel) { + WM_menu_name_call(C, "OUTLINER_MT_object", WM_OP_INVOKE_REGION_WIN); + return OPERATOR_FINISHED; + } + if (idlevel) { + if (idlevel == -1 || datalevel) { + BKE_report(reports, RPT_WARNING, "Mixed selection"); + return OPERATOR_CANCELLED; } - if (idlevel) { - if (idlevel == -1 || datalevel) { - BKE_report(reports, RPT_WARNING, "Mixed selection"); - return OPERATOR_CANCELLED; - } - switch (idlevel) { - case ID_GR: - WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN); - return OPERATOR_FINISHED; - break; - case ID_LI: - return outliner_operator_menu(C, "OUTLINER_OT_lib_operation"); - break; - default: - return outliner_operator_menu(C, "OUTLINER_OT_id_operation"); - break; - } - } - else if (datalevel) { - if (datalevel == -1) { - BKE_report(reports, RPT_WARNING, "Mixed selection"); - return OPERATOR_CANCELLED; - } - if (datalevel == TSE_ANIM_DATA) { - return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation"); - } - if (datalevel == TSE_DRIVER_BASE) { - /* do nothing... no special ops needed yet */ - return OPERATOR_CANCELLED; - } - if (datalevel == TSE_LAYER_COLLECTION) { + switch (idlevel) { + case ID_GR: WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN); return OPERATOR_FINISHED; - } - if (ELEM(datalevel, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { - WM_menu_name_call(C, "OUTLINER_MT_collection_new", WM_OP_INVOKE_REGION_WIN); - return OPERATOR_FINISHED; - } - if (datalevel == TSE_ID_BASE) { - /* do nothing... there are no ops needed here yet */ - return 0; - } - if (datalevel == TSE_CONSTRAINT) { - return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation"); - } - if (datalevel == TSE_MODIFIER) { - return outliner_operator_menu(C, "OUTLINER_OT_modifier_operation"); - } - return outliner_operator_menu(C, "OUTLINER_OT_data_operation"); + break; + case ID_LI: + return outliner_operator_menu(C, "OUTLINER_OT_lib_operation"); + break; + default: + return outliner_operator_menu(C, "OUTLINER_OT_id_operation"); + break; } - - return 0; } - - for (te = te->subtree.first; te; te = te->next) { - int retval = do_outliner_operation_event(C, region, space_outliner, te, mval); - if (retval) { - return retval; + else if (datalevel) { + if (datalevel == -1) { + BKE_report(reports, RPT_WARNING, "Mixed selection"); + return OPERATOR_CANCELLED; + } + if (datalevel == TSE_ANIM_DATA) { + return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation"); + } + if (datalevel == TSE_DRIVER_BASE) { + /* do nothing... no special ops needed yet */ + return OPERATOR_CANCELLED; + } + if (datalevel == TSE_LAYER_COLLECTION) { + WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN); + return OPERATOR_FINISHED; + } + if (ELEM(datalevel, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { + WM_menu_name_call(C, "OUTLINER_MT_collection_new", WM_OP_INVOKE_REGION_WIN); + return OPERATOR_FINISHED; + } + if (datalevel == TSE_ID_BASE) { + /* do nothing... there are no ops needed here yet */ + return 0; + } + if (datalevel == TSE_CONSTRAINT) { + return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation"); } + if (datalevel == TSE_MODIFIER) { + return outliner_operator_menu(C, "OUTLINER_OT_modifier_operation"); + } + return outliner_operator_menu(C, "OUTLINER_OT_data_operation"); } return 0; } -static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); uiBut *but = UI_context_active_but_get(C); - TreeElement *te; - float fmval[2]; + float view_mval[2]; if (but) { UI_but_tooltip_timer_remove(C, but); } - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + UI_view2d_region_to_view( + ®ion->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); - for (te = space_outliner->tree.first; te; te = te->next) { - int retval = do_outliner_operation_event(C, region, space_outliner, te, fmval); - if (retval) { - return retval; - } + TreeElement *hovered_te = outliner_find_item_at_y( + space_outliner, &space_outliner->tree, view_mval[1]); + if (!hovered_te) { + /* Let this fall through to 'OUTLINER_MT_context_menu'. */ + return OPERATOR_PASS_THROUGH; } - /* Let this fall through to 'OUTLINER_MT_context_menu'. */ - return OPERATOR_PASS_THROUGH; + return do_outliner_operation_event(C, op->reports, region, space_outliner, hovered_te); } /* Menu only! Calls other operators */ |