Outliner: Add new delete operator

In the industry standard keymap, both deleting objects and collections
were mapped to the same keys causing confusion when only collections
could be deleted through the keymap.

This adds a new delete operator to delete all selected objects and
collections, accessible from both the keymap and context menu. Now any
selected objects and collections are deleted when Delete is chosen from
the keymap. This also updates the tooltip description which was
previously undocumented.

Resolves T67462
This commit is contained in:
Nathan Craddock 2020-04-30 19:51:14 +10:00 committed by Campbell Barton
parent 79269e4237
commit ae98a033c8
Notes: blender-bot 2023-02-14 02:22:07 +01:00
Referenced by commit 26c0ca3aa7, Outliner: Unified delete hierarchy operator
Referenced by issue #77883, Cannot delete an object in the scene collection using the delete key
Referenced by issue #67462, Outliner: support delete in the keymap for different data-types
7 changed files with 109 additions and 62 deletions

View File

@ -798,8 +798,8 @@ def km_outliner(params):
("outliner.drivers_add_selected", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
("outliner.drivers_delete_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("outliner.collection_new", {"type": 'C', "value": 'PRESS'}, None),
("outliner.collection_delete", {"type": 'X', "value": 'PRESS'}, None),
("outliner.collection_delete", {"type": 'DEL', "value": 'PRESS'}, None),
("outliner.delete", {"type": 'X', "value": 'PRESS'}, None),
("outliner.delete", {"type": 'DEL', "value": 'PRESS'}, None),
("object.move_to_collection", {"type": 'M', "value": 'PRESS'}, None),
("object.link_to_collection", {"type": 'M', "value": 'PRESS', "shift": True}, None),
("outliner.collection_exclude_set", {"type": 'E', "value": 'PRESS'}, None),

View File

@ -520,10 +520,8 @@ def km_outliner(params):
("anim.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None),
("outliner.drivers_add_selected", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
("outliner.drivers_delete_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("outliner.collection_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
("outliner.collection_delete", {"type": 'DEL', "value": 'PRESS'}, None),
("outliner.object_operation", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("type", 'DELETE')]}),
("outliner.object_operation", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("type", 'DELETE')]}),
("outliner.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
("outliner.delete", {"type": 'DEL', "value": 'PRESS'}, None),
("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
("object.link_to_collection", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("outliner.collection_exclude_set", {"type": 'E', "value": 'PRESS'}, None),

View File

@ -212,8 +212,8 @@ class OUTLINER_MT_collection(Menu):
layout.separator()
layout.operator("outliner.collection_delete", text="Delete", icon='X').hierarchy = False
layout.operator("outliner.collection_delete", text="Delete Hierarchy").hierarchy = True
layout.operator("outliner.delete", text="Delete", icon='X')
layout.operator("outliner.collection_hierarchy_delete")
layout.separator()
@ -278,7 +278,7 @@ class OUTLINER_MT_object(Menu):
layout.separator()
layout.operator("outliner.object_operation", text="Delete", icon='X').type = 'DELETE'
layout.operator("outliner.delete", text="Delete", icon='X')
if space.display_mode == 'VIEW_LAYER' and not space.use_filter_collection:
layout.operator("outliner.object_operation", text="Delete Hierarchy").type = 'DELETE_HIERARCHY'

View File

@ -300,19 +300,15 @@ static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *c
return TRAVERSE_CONTINUE;
}
static int collection_delete_exec(bContext *C, wmOperator *op)
void outliner_collection_delete(
bContext *C, Main *bmain, Scene *scene, ReportList *reports, bool hierarchy)
{
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const Base *basact_prev = BASACT(view_layer);
SpaceOutliner *soops = CTX_wm_space_outliner(C);
struct CollectionEditData data = {
.scene = scene,
.soops = soops,
};
bool hierarchy = RNA_boolean_get(op->ptr, "hierarchy");
data.collections_to_edit = BLI_gset_ptr_new(__func__);
@ -358,7 +354,7 @@ static int collection_delete_exec(bContext *C, wmOperator *op)
}
else {
BKE_reportf(
op->reports,
reports,
RPT_WARNING,
"Cannot delete linked collection '%s', it is used by other linked scenes/collections",
collection->id.name + 2);
@ -367,6 +363,17 @@ static int collection_delete_exec(bContext *C, wmOperator *op)
}
BLI_gset_free(data.collections_to_edit, NULL);
}
static int collection_hierarchy_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
const Base *basact_prev = BASACT(view_layer);
outliner_collection_delete(C, bmain, scene, op->reports, true);
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(bmain);
@ -382,24 +389,19 @@ static int collection_delete_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
void OUTLINER_OT_collection_delete(wmOperatorType *ot)
void OUTLINER_OT_collection_hierarchy_delete(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Collection";
ot->idname = "OUTLINER_OT_collection_delete";
ot->description = "Delete selected collections";
ot->name = "Delete Hierarchy";
ot->idname = "OUTLINER_OT_collection_hierarchy_delete";
ot->description = "Delete selected collection hierarchies";
/* api callbacks */
ot->exec = collection_delete_exec;
ot->exec = collection_hierarchy_delete_exec;
ot->poll = ED_outliner_collections_editor_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
PropertyRNA *prop = RNA_def_boolean(
ot->srna, "hierarchy", false, "Hierarchy", "Delete child objects and collections");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */

View File

@ -431,6 +431,7 @@ void OUTLINER_OT_animdata_operation(struct wmOperatorType *ot);
void OUTLINER_OT_action_set(struct wmOperatorType *ot);
void OUTLINER_OT_constraint_operation(struct wmOperatorType *ot);
void OUTLINER_OT_modifier_operation(struct wmOperatorType *ot);
void OUTLINER_OT_delete(struct wmOperatorType *ot);
/* ---------------------------------------------------------------- */
@ -442,11 +443,16 @@ void outliner_keymap(struct wmKeyConfig *keyconf);
bool outliner_is_collection_tree_element(const TreeElement *te);
struct Collection *outliner_collection_from_tree_element(const TreeElement *te);
void outliner_collection_delete(struct bContext *C,
struct Main *bmain,
struct Scene *scene,
struct ReportList *reports,
bool hierarchy);
void OUTLINER_OT_collection_new(struct wmOperatorType *ot);
void OUTLINER_OT_collection_duplicate_linked(struct wmOperatorType *ot);
void OUTLINER_OT_collection_duplicate(struct wmOperatorType *ot);
void OUTLINER_OT_collection_delete(struct wmOperatorType *ot);
void OUTLINER_OT_collection_hierarchy_delete(struct wmOperatorType *ot);
void OUTLINER_OT_collection_objects_select(struct wmOperatorType *ot);
void OUTLINER_OT_collection_objects_deselect(struct wmOperatorType *ot);
void OUTLINER_OT_collection_link(struct wmOperatorType *ot);

View File

@ -66,6 +66,7 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_action_set);
WM_operatortype_append(OUTLINER_OT_constraint_operation);
WM_operatortype_append(OUTLINER_OT_modifier_operation);
WM_operatortype_append(OUTLINER_OT_delete);
WM_operatortype_append(OUTLINER_OT_show_one_level);
WM_operatortype_append(OUTLINER_OT_show_active);
@ -93,7 +94,7 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_collection_new);
WM_operatortype_append(OUTLINER_OT_collection_duplicate_linked);
WM_operatortype_append(OUTLINER_OT_collection_duplicate);
WM_operatortype_append(OUTLINER_OT_collection_delete);
WM_operatortype_append(OUTLINER_OT_collection_hierarchy_delete);
WM_operatortype_append(OUTLINER_OT_collection_objects_select);
WM_operatortype_append(OUTLINER_OT_collection_objects_deselect);
WM_operatortype_append(OUTLINER_OT_collection_link);

View File

@ -673,13 +673,10 @@ static void object_deselect_cb(bContext *C,
}
}
static void object_delete_cb(bContext *C,
ReportList *reports,
Scene *scene,
TreeElement *UNUSED(te),
TreeStoreElem *UNUSED(tsep),
TreeStoreElem *tselem,
void *UNUSED(user_data))
static void outliner_object_delete(bContext *C,
ReportList *reports,
Scene *scene,
TreeStoreElem *tselem)
{
Object *ob = (Object *)tselem->id;
if (ob) {
@ -1316,7 +1313,6 @@ enum {
OL_OP_SELECT = 1,
OL_OP_DESELECT,
OL_OP_SELECT_HIERARCHY,
OL_OP_DELETE,
OL_OP_DELETE_HIERARCHY,
OL_OP_REMAP,
OL_OP_LOCALIZED, /* disabled, see below */
@ -1332,7 +1328,6 @@ static const EnumPropertyItem prop_object_op_types[] = {
{OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""},
{OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""},
{OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""},
{OL_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
{OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""},
{OL_OP_REMAP,
"REMAP",
@ -1388,29 +1383,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
str = "Deselect Objects";
selection_changed = true;
}
else if (event == OL_OP_DELETE) {
ViewLayer *view_layer = CTX_data_view_layer(C);
const Base *basact_prev = BASACT(view_layer);
outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_delete_cb);
/* XXX: tree management normally happens from draw_outliner(), but when
* you're clicking to fast on Delete object from context menu in
* outliner several mouse events can be handled in one cycle without
* handling notifiers/redraw which leads to deleting the same object twice.
* cleanup tree here to prevent such cases. */
outliner_cleanup_tree(soops);
DEG_relations_tag_update(bmain);
str = "Delete Objects";
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
if (basact_prev != BASACT(view_layer)) {
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
}
selection_changed = true;
}
else if (event == OL_OP_DELETE_HIERARCHY) {
ViewLayer *view_layer = CTX_data_view_layer(C);
const Base *basact_prev = BASACT(view_layer);
@ -1435,7 +1407,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
BKE_id_multi_tagged_delete(bmain);
}
/* XXX: See OL_OP_DELETE comment above. */
/* XXX: See outliner_delete_exec comment below. */
outliner_cleanup_tree(soops);
DEG_relations_tag_update(bmain);
@ -1502,6 +1474,74 @@ void OUTLINER_OT_object_operation(wmOperatorType *ot)
ot->prop = RNA_def_enum(ot->srna, "type", prop_object_op_types, 0, "Object Operation", "");
}
static void outliner_objects_delete(
bContext *C, Scene *scene, SpaceOutliner *soops, ReportList *reports, ListBase *lb)
{
LISTBASE_FOREACH (TreeElement *, te, lb) {
TreeStoreElem *tselem = TREESTORE(te);
if (tselem->flag & TSE_SELECTED) {
if (tselem->type == 0 && te->idcode == ID_OB) {
outliner_object_delete(C, reports, scene, tselem);
}
}
if (TSELEM_OPEN(tselem, soops)) {
outliner_objects_delete(C, scene, soops, reports, &te->subtree);
}
}
}
static int outliner_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
SpaceOutliner *soops = CTX_wm_space_outliner(C);
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const Base *basact_prev = BASACT(view_layer);
outliner_collection_delete(C, bmain, scene, op->reports, false);
outliner_objects_delete(C, scene, soops, op->reports, &soops->tree);
/* Tree management normally happens from draw_outliner(), but when
* you're clicking too fast on Delete object from context menu in
* outliner several mouse events can be handled in one cycle without
* handling notifiers/redraw which leads to deleting the same object twice.
* cleanup tree here to prevent such cases. */
outliner_cleanup_tree(soops);
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(bmain);
if (basact_prev != BASACT(view_layer)) {
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
}
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
ED_outliner_select_sync_from_object_tag(C);
return OPERATOR_FINISHED;
}
void OUTLINER_OT_delete(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete";
ot->idname = "OUTLINER_OT_delete";
ot->description = "Delete selected objects and collections";
/* callbacks */
ot->exec = outliner_delete_exec;
ot->poll = ED_operator_outliner_active;
/* flags */
ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* **************************************** */
typedef enum eOutlinerIdOpTypes {