diff options
Diffstat (limited to 'source/blender/editors/space_outliner')
-rw-r--r-- | source/blender/editors/space_outliner/CMakeLists.txt | 3 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_collections.c | 717 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_draw.c | 1767 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_edit.c | 484 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_intern.h | 183 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_ops.c | 438 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_select.c | 685 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_tools.c | 391 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_tree.c | 1214 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_utils.c | 246 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/space_outliner.c | 77 |
11 files changed, 4228 insertions, 1977 deletions
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 289d6e715e1..96d27c6fd89 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ../../blenkernel ../../blenlib ../../blentranslation + ../../depsgraph ../../imbuf ../../gpu ../../makesdna @@ -37,12 +38,14 @@ set(INC_SYS ) set(SRC + outliner_collections.c outliner_draw.c outliner_edit.c outliner_ops.c outliner_select.c outliner_tools.c outliner_tree.c + outliner_utils.c space_outliner.c outliner_intern.h diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c new file mode 100644 index 00000000000..b97cc13d713 --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -0,0 +1,717 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Blender Foundation, Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_outliner/outliner_collections.c + * \ingroup spoutliner + */ + +#include <string.h> + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" + +#include "DNA_group_types.h" +#include "DNA_object_types.h" + +#include "BKE_context.h" +#include "BKE_collection.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "ED_object.h" +#include "ED_outliner.h" +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_resources.h" + +#include "outliner_intern.h" /* own include */ + +/* -------------------------------------------------------------------- */ + +bool outliner_is_collection_tree_element(const TreeElement *te) +{ + TreeStoreElem *tselem = TREESTORE(te); + + if (!tselem) { + return false; + } + + if (ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { + return true; + } + else if (tselem->type == 0 && te->idcode == ID_GR) { + return true; + } + + return false; +} + +Collection *outliner_collection_from_tree_element(const TreeElement *te) +{ + TreeStoreElem *tselem = TREESTORE(te); + + if (!tselem) { + return false; + } + + if (tselem->type == TSE_LAYER_COLLECTION) { + LayerCollection *lc = te->directdata; + return lc->collection; + } + else if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { + Scene *scene = (Scene *)tselem->id; + return BKE_collection_master(scene); + } + else if (tselem->type == 0 && te->idcode == ID_GR) { + return (Collection *)tselem->id; + } + + return NULL; +} + +/* -------------------------------------------------------------------- */ +/* Poll functions. */ + +int ED_outliner_collections_editor_poll(bContext *C) +{ + SpaceOops *so = CTX_wm_space_outliner(C); + return (so != NULL) && ELEM(so->outlinevis, SO_VIEW_LAYER, SO_SCENES, SO_LIBRARIES); +} + + +/********************************* New Collection ****************************/ + +struct CollectionNewData +{ + bool error; + Collection *collection; +}; + +static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata) +{ + struct CollectionNewData *data = customdata; + Collection *collection = outliner_collection_from_tree_element(te); + + if (!collection) { + return TRAVERSE_SKIP_CHILDS; + } + + if (data->collection != NULL) { + data->error = true; + return TRAVERSE_BREAK; + } + + data->collection = collection; + return TRAVERSE_CONTINUE; +} + +static int collection_new_exec(bContext *C, wmOperator *op) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + struct CollectionNewData data = { + .error = false, + .collection = NULL, + }; + + if (RNA_boolean_get(op->ptr, "nested")) { + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_to_add, &data); + + if (data.error) { + BKE_report(op->reports, RPT_ERROR, "More than one collection is selected"); + return OPERATOR_CANCELLED; + } + } + + if (!data.collection && (soops->outlinevis == SO_VIEW_LAYER)) { + data.collection = BKE_collection_master(scene); + } + + BKE_collection_add( + bmain, + data.collection, + NULL); + + DEG_id_tag_update(&data.collection->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + + outliner_cleanup_tree(soops); + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_collection_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Collection"; + ot->idname = "OUTLINER_OT_collection_new"; + ot->description = "Add a new collection inside selected collection"; + + /* api callbacks */ + ot->exec = collection_new_exec; + ot->poll = ED_outliner_collections_editor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop = RNA_def_boolean(ot->srna, "nested", true, "Nested", "Add as child of selected collection");; + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/**************************** Delete Collection ******************************/ + +struct CollectionEditData { + Scene *scene; + SpaceOops *soops; + GSet *collections_to_edit; +}; + +static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *customdata) +{ + struct CollectionEditData *data = customdata; + Collection *collection = outliner_collection_from_tree_element(te); + + if (!collection) { + return TRAVERSE_SKIP_CHILDS; + } + + if (collection == BKE_collection_master(data->scene)) { + /* skip - showing warning/error message might be missleading + * when deleting multiple collections, so just do nothing */ + } + else { + /* Delete, duplicate and link don't edit children, those will come along + * with the parents. */ + BLI_gset_add(data->collections_to_edit, collection); + return TRAVERSE_SKIP_CHILDS; + } + + return TRAVERSE_CONTINUE; +} + +static int collection_delete_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + SpaceOops *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__); + + /* We first walk over and find the Collections we actually want to delete (ignoring duplicates). */ + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); + + /* Effectively delete the collections. */ + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); + + /* Test in case collection got deleted as part of another one. */ + if (BLI_findindex(&bmain->collection, collection) != -1) { + BKE_collection_delete(bmain, collection, hierarchy); + } + } + + BLI_gset_free(data.collections_to_edit, NULL); + + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_collection_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Collection"; + ot->idname = "OUTLINER_OT_collection_delete"; + ot->description = "Delete selected collections"; + + /* api callbacks */ + ot->exec = collection_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); +} + +/****************************** Select Objects *******************************/ + +struct CollectionObjectsSelectData { + bool error; + LayerCollection *layer_collection; +}; + +static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te, void *customdata) +{ + struct CollectionObjectsSelectData *data = customdata; + TreeStoreElem *tselem = TREESTORE(te); + + switch (tselem->type) { + case TSE_LAYER_COLLECTION: + data->layer_collection = te->directdata; + return TRAVERSE_BREAK; + case TSE_R_LAYER: + case TSE_SCENE_COLLECTION_BASE: + case TSE_VIEW_COLLECTION_BASE: + return TRAVERSE_CONTINUE; + default: + return TRAVERSE_SKIP_CHILDS; + } +} + +static LayerCollection *outliner_active_layer_collection(bContext *C) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + + struct CollectionObjectsSelectData data = { + .layer_collection = NULL, + }; + + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_layer_collection, &data); + return data.layer_collection; +} + +static int collection_objects_select_exec(bContext *C, wmOperator *op) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + LayerCollection *layer_collection = outliner_active_layer_collection(C); + bool deselect = STREQ(op->idname, "OUTLINER_OT_collection_objects_deselect"); + + if (layer_collection == NULL) { + return OPERATOR_CANCELLED; + } + + BKE_layer_collection_objects_select(view_layer, layer_collection, deselect); + + Scene *scene = CTX_data_scene(C); + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_collection_objects_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Objects"; + ot->idname = "OUTLINER_OT_collection_objects_select"; + ot->description = "Select objects in collection"; + + /* api callbacks */ + ot->exec = collection_objects_select_exec; + ot->poll = ED_outliner_collections_editor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void OUTLINER_OT_collection_objects_deselect(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Deselect Objects"; + ot->idname = "OUTLINER_OT_collection_objects_deselect"; + ot->description = "Deselect objects in collection"; + + /* api callbacks */ + ot->exec = collection_objects_select_exec; + ot->poll = ED_outliner_collections_editor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/************************** Duplicate Collection *****************************/ + +struct CollectionDuplicateData { + TreeElement *te; +}; + +static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te, void *customdata) +{ + struct CollectionDuplicateData *data = customdata; + TreeStoreElem *tselem = TREESTORE(te); + + switch (tselem->type) { + case TSE_LAYER_COLLECTION: + data->te = te; + return TRAVERSE_BREAK; + case TSE_R_LAYER: + case TSE_SCENE_COLLECTION_BASE: + case TSE_VIEW_COLLECTION_BASE: + default: + return TRAVERSE_CONTINUE; + } +} + +static TreeElement *outliner_active_collection(bContext *C) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + + struct CollectionDuplicateData data = { + .te = NULL, + }; + + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_collection, &data); + return data.te; +} + +static int collection_duplicate_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te = outliner_active_collection(C); + BLI_assert(te != NULL); + + Collection *collection = outliner_collection_from_tree_element(te); + Collection *parent = (te->parent) ? outliner_collection_from_tree_element(te->parent) : NULL; + + if (collection->flag & COLLECTION_IS_MASTER) { + BKE_report(op->reports, RPT_ERROR, "Can't duplicate the master collection"); + return OPERATOR_CANCELLED; + } + + switch (soops->outlinevis) { + case SO_SCENES: + case SO_VIEW_LAYER: + case SO_LIBRARIES: + BKE_collection_copy(bmain, parent, collection); + break; + } + + DEG_relations_tag_update(bmain); + WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C)); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_collection_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Collection"; + ot->idname = "OUTLINER_OT_collection_duplicate"; + ot->description = "Duplicate selected collections"; + + /* api callbacks */ + ot->exec = collection_duplicate_exec; + ot->poll = ED_outliner_collections_editor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/**************************** Link Collection ******************************/ + +static int collection_link_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Collection *active_collection = CTX_data_layer_collection(C)->collection; + SpaceOops *soops = CTX_wm_space_outliner(C); + struct CollectionEditData data = {.scene = scene, .soops = soops}; + + data.collections_to_edit = BLI_gset_ptr_new(__func__); + + /* We first walk over and find the Collections we actually want to link (ignoring duplicates). */ + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); + + /* Effectively link the collections. */ + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); + BKE_collection_child_add(bmain, active_collection, collection); + id_fake_user_clear(&collection->id); + } + + BLI_gset_free(data.collections_to_edit, NULL); + + DEG_id_tag_update(&active_collection->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_collection_link(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Link Collection"; + ot->idname = "OUTLINER_OT_collection_link"; + ot->description = "Link selected collections to active scene"; + + /* api callbacks */ + ot->exec = collection_link_exec; + ot->poll = ED_outliner_collections_editor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/************************** Instance Collection ******************************/ + +static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + struct CollectionEditData data = {.scene = scene, .soops = soops}; + + data.collections_to_edit = BLI_gset_ptr_new(__func__); + + /* We first walk over and find the Collections we actually want to instance (ignoring duplicates). */ + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); + + /* Find an active collection to add to, that doesn't give dependency cycles. */ + LayerCollection *active_lc = BKE_layer_collection_get_active(view_layer); + + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); + + while (BKE_collection_find_cycle(active_lc->collection, collection)) { + active_lc = BKE_layer_collection_activate_parent(view_layer, active_lc); + } + } + + /* Effectively instance the collections. */ + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); + Object *ob = ED_object_add_type(C, OB_EMPTY, collection->id.name + 2, scene->cursor.location, NULL, false, scene->layact); + ob->dup_group = collection; + ob->transflag |= OB_DUPLICOLLECTION; + id_lib_extern(&collection->id); + } + + BLI_gset_free(data.collections_to_edit, NULL); + + DEG_relations_tag_update(bmain); + + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_collection_instance(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Instance Collection"; + ot->idname = "OUTLINER_OT_collection_instance"; + ot->description = "Instance selected collections to active scene"; + + /* api callbacks */ + ot->exec = collection_instance_exec; + ot->poll = ED_outliner_collections_editor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/************************** Exclude Collection ******************************/ + +static TreeTraversalAction layer_collection_find_data_to_edit(TreeElement *te, void *customdata) +{ + struct CollectionEditData *data = customdata; + TreeStoreElem *tselem = TREESTORE(te); + + if (!(tselem && tselem->type == TSE_LAYER_COLLECTION)) { + return TRAVERSE_CONTINUE; + } + + LayerCollection *lc = te->directdata; + + if (lc->collection->flag & COLLECTION_IS_MASTER) { + /* skip - showing warning/error message might be missleading + * when deleting multiple collections, so just do nothing */ + } + else { + /* Delete, duplicate and link don't edit children, those will come along + * with the parents. */ + BLI_gset_add(data->collections_to_edit, lc); + } + + return TRAVERSE_CONTINUE; +} + +static int collections_view_layer_poll(bContext *C, bool include) +{ + /* Poll function so the right click menu show current state of selected collections. */ + SpaceOops *soops = CTX_wm_space_outliner(C); + if (!(soops && soops->outlinevis == SO_VIEW_LAYER)) { + return false; + } + + Scene *scene = CTX_data_scene(C); + struct CollectionEditData data = {.scene = scene, .soops = soops}; + data.collections_to_edit = BLI_gset_ptr_new(__func__); + bool result = false; + + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data); + + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter); + + if (include && (lc->flag & LAYER_COLLECTION_EXCLUDE)) { + result = true; + } + else if (!include && !(lc->flag & LAYER_COLLECTION_EXCLUDE)) { + result = true; + } + } + + BLI_gset_free(data.collections_to_edit, NULL); + return result; +} + +static int collections_exclude_poll(bContext *C) +{ + return collections_view_layer_poll(C, false); +} + +static int collections_include_poll(bContext *C) +{ + return collections_view_layer_poll(C, true); +} + +static void layer_collection_exclude_recursive_set(LayerCollection *lc) +{ + for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) { + if (lc->flag & LAYER_COLLECTION_EXCLUDE) { + nlc->flag |= LAYER_COLLECTION_EXCLUDE; + } + else { + nlc->flag &= ~LAYER_COLLECTION_EXCLUDE; + } + + layer_collection_exclude_recursive_set(nlc); + } +} + +static int collection_view_layer_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); + SpaceOops *soops = CTX_wm_space_outliner(C); + struct CollectionEditData data = {.scene = scene, .soops = soops}; + bool include = STREQ(op->idname, "OUTLINER_OT_collection_include_set"); + + data.collections_to_edit = BLI_gset_ptr_new(__func__); + + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data); + + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter); + + if (!(lc->collection->flag & COLLECTION_IS_MASTER)) { + if (include) { + lc->flag &= ~LAYER_COLLECTION_EXCLUDE; + } + else { + lc->flag |= LAYER_COLLECTION_EXCLUDE; + } + + layer_collection_exclude_recursive_set(lc); + } + } + + BLI_gset_free(data.collections_to_edit, NULL); + + BKE_layer_collection_sync(scene, view_layer); + DEG_relations_tag_update(bmain); + + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_collection_exclude_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Exclude from View Layer"; + ot->idname = "OUTLINER_OT_collection_exclude_set"; + ot->description = "Exclude collection from the active view layer"; + + /* api callbacks */ + ot->exec = collection_view_layer_exec; + ot->poll = collections_exclude_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void OUTLINER_OT_collection_include_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Include in View Layer"; + ot->idname = "OUTLINER_OT_collection_include_set"; + ot->description = "Include collection in the active view layer"; + + /* api callbacks */ + ot->exec = collection_view_layer_exec; + ot->poll = collections_include_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** + * Populates the \param objects ListBase with all the outliner selected objects + * We store it as (Object *)LinkData->data + * \param objects expected to be empty + */ +void ED_outliner_selected_objects_get(const bContext *C, ListBase *objects) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + struct ObjectsSelectedData data = {{NULL}}; + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data); + LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) { + TreeElement *ten_selected = (TreeElement *)link->data; + Object *ob = (Object *)TREESTORE(ten_selected)->id; + BLI_addtail(objects, BLI_genericNodeN(ob)); + } + BLI_freelistN(&data.objects_selected_array); +} diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index a35dffc0fcc..5d2be638964 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -34,6 +34,7 @@ #include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" +#include "DNA_lightprobe_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" @@ -48,9 +49,10 @@ #include "BKE_context.h" #include "BKE_deform.h" -#include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_global.h" +#include "BKE_idcode.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_modifier.h" @@ -58,6 +60,9 @@ #include "BKE_scene.h" #include "BKE_object.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + #include "ED_armature.h" #include "ED_keyframing.h" #include "ED_object.h" @@ -66,8 +71,7 @@ #include "WM_api.h" #include "WM_types.h" -#include "BIF_gl.h" -#include "BIF_glutil.h" +#include "GPU_immediate.h" #include "UI_interface.h" #include "UI_interface_icons.h" @@ -89,8 +93,9 @@ static void outliner_height(SpaceOops *soops, ListBase *lb, int *h) TreeElement *te = lb->first; while (te) { TreeStoreElem *tselem = TREESTORE(te); - if (TSELEM_OPEN(tselem, soops)) + if (TSELEM_OPEN(tselem, soops)) { outliner_height(soops, &te->subtree, h); + } (*h) += UI_UNIT_Y; te = te->next; } @@ -129,12 +134,27 @@ static void outliner_rna_width(SpaceOops *soops, ListBase *lb, int *w, int start if (startx + 100 > *w) *w = startx + 100; - if (TSELEM_OPEN(tselem, soops)) + if (TSELEM_OPEN(tselem, soops)) { outliner_rna_width(soops, &te->subtree, w, startx + UI_UNIT_X); + } te = te->next; } } +/** + * The active object is only needed for reference. + */ +static bool is_object_data_in_editmode(const ID *id, const Object *obact) +{ + const short id_type = GS(id->name); + return ( + (obact && (obact->mode & OB_MODE_EDIT)) && + (id && OB_DATA_SUPPORT_EDITMODE(id_type)) && + (GS(((ID *)obact->data)->name) == id_type) && + BKE_object_data_is_in_editmode(id) + ); +} + /* ****************************************************** */ static void restrictbutton_recursive_ebone(bContext *C, EditBone *ebone_parent, int flag, bool set_flag) @@ -172,116 +192,6 @@ static void restrictbutton_recursive_bone(Bone *bone_parent, int flag, bool set_ } -static void restrictbutton_recursive_child(bContext *C, Scene *scene, Object *ob_parent, char flag, - bool state, bool deselect, const char *rnapropname) -{ - Main *bmain = CTX_data_main(C); - Object *ob; - - for (ob = bmain->object.first; ob; ob = ob->id.next) { - if (BKE_object_is_child_recursive(ob_parent, ob)) { - /* only do if child object is selectable */ - if ((flag == OB_RESTRICT_SELECT) || (ob->restrictflag & OB_RESTRICT_SELECT) == 0) { - if (state) { - ob->restrictflag |= flag; - if (deselect) { - ED_base_object_select(BKE_scene_base_find(scene, ob), BA_DESELECT); - } - } - else { - ob->restrictflag &= ~flag; - } - } - - if (rnapropname) { - PointerRNA ptr; - PropertyRNA *prop; - ID *id; - bAction *action; - FCurve *fcu; - bool driven, special; - - RNA_id_pointer_create(&ob->id, &ptr); - prop = RNA_struct_find_property(&ptr, rnapropname); - fcu = rna_get_fcurve_context_ui(C, &ptr, prop, 0, NULL, &action, &driven, &special); - - if (fcu && !driven) { - id = ptr.id.data; - if (autokeyframe_cfra_can_key(scene, id)) { - ReportList *reports = CTX_wm_reports(C); - ToolSettings *ts = scene->toolsettings; - eInsertKeyFlags key_flag = ANIM_get_keyframing_flags(scene, 1); - - fcu->flag &= ~FCURVE_SELECTED; - insert_keyframe(bmain, reports, id, action, ((fcu->grp) ? (fcu->grp->name) : (NULL)), - fcu->rna_path, fcu->array_index, CFRA, ts->keyframe_type, key_flag); - /* Assuming this is not necessary here, since 'ancestor' object button will do it anyway. */ - /* WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); */ - } - } - } - } - } -} - -static void restrictbutton_view_cb(bContext *C, void *poin, void *poin2) -{ - Scene *scene = (Scene *)poin; - Object *ob = (Object *)poin2; - - if (!common_restrict_check(C, ob)) return; - - /* deselect objects that are invisible */ - if (ob->restrictflag & OB_RESTRICT_VIEW) { - /* Ouch! There is no backwards pointer from Object to Base, - * so have to do loop to find it. */ - ED_base_object_select(BKE_scene_base_find(scene, ob), BA_DESELECT); - } - - if (CTX_wm_window(C)->eventstate->ctrl) { - restrictbutton_recursive_child(C, scene, ob, OB_RESTRICT_VIEW, - (ob->restrictflag & OB_RESTRICT_VIEW) != 0, true, "hide"); - } - - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - -} - -static void restrictbutton_sel_cb(bContext *C, void *poin, void *poin2) -{ - Scene *scene = (Scene *)poin; - Object *ob = (Object *)poin2; - - if (!common_restrict_check(C, ob)) return; - - /* if select restriction has just been turned on */ - if (ob->restrictflag & OB_RESTRICT_SELECT) { - /* Ouch! There is no backwards pointer from Object to Base, - * so have to do loop to find it. */ - ED_base_object_select(BKE_scene_base_find(scene, ob), BA_DESELECT); - } - - if (CTX_wm_window(C)->eventstate->ctrl) { - restrictbutton_recursive_child(C, scene, ob, OB_RESTRICT_SELECT, - (ob->restrictflag & OB_RESTRICT_SELECT) != 0, true, NULL); - } - - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - -} - -static void restrictbutton_rend_cb(bContext *C, void *poin, void *poin2) -{ - Object *ob = (Object *)poin2; - - if (CTX_wm_window(C)->eventstate->ctrl) { - restrictbutton_recursive_child(C, (Scene *)poin, ob, OB_RESTRICT_RENDER, - (ob->restrictflag & OB_RESTRICT_RENDER) != 0, false, "hide_render"); - } - - WM_event_add_notifier(C, NC_SCENE | ND_OB_RENDER, poin); -} - static void restrictbutton_r_lay_cb(bContext *C, void *poin, void *UNUSED(poin2)) { WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, poin); @@ -291,7 +201,7 @@ static void restrictbutton_modifier_cb(bContext *C, void *UNUSED(poin), void *po { Object *ob = (Object *)poin2; - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); } @@ -355,118 +265,62 @@ static void restrictbutton_gp_layer_flag_cb(bContext *C, void *UNUSED(poin), voi WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } -static int group_restrict_flag(Group *gr, int flag) +static void restrictbutton_id_user_toggle(bContext *UNUSED(C), void *poin, void *UNUSED(poin2)) { - GroupObject *gob; + ID *id = (ID *)poin; -#ifdef USE_GROUP_SELECT - for (gob = gr->gobject.first; gob; gob = gob->next) { - if ((gob->ob->restrictflag & flag) == 0) - return 0; - } - return 1; -#else - /* weak but fast */ - if ((gob = gr->gobject.first)) - if ((gob->ob->restrictflag & flag) == 0) - return 0; - return 1; -#endif -} + BLI_assert(id != NULL); -static int group_select_flag(Group *gr) -{ - GroupObject *gob; - -#ifdef USE_GROUP_SELECT - for (gob = gr->gobject.first; gob; gob = gob->next) - if ((gob->ob->flag & SELECT)) - return 1; - - return 0; -#else - /* weak but fast */ - if ((gob = gr->gobject.first)) - if (gob->ob->flag & SELECT) - return 1; - return 0; -#endif + if (id->flag & LIB_FAKEUSER) { + id_us_plus(id); + } + else { + id_us_min(id); + } } -void restrictbutton_gr_restrict_flag(void *poin, void *poin2, int flag) +static void hidebutton_base_flag_cb(bContext *C, void *poin, void *poin2) { - Scene *scene = (Scene *)poin; - GroupObject *gob; - Group *gr = (Group *)poin2; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = poin; + Base *base = poin2; + bool extend = (CTX_wm_window(C)->eventstate->ctrl == 0); - if (group_restrict_flag(gr, flag)) { - for (gob = gr->gobject.first; gob; gob = gob->next) { - if (ID_IS_LINKED(gob->ob)) - continue; + /* Undo button toggle, let function do it. */ + base->flag ^= BASE_HIDE; - gob->ob->restrictflag &= ~flag; + BKE_base_set_visible(scene, view_layer, base, extend); - if (flag == OB_RESTRICT_VIEW) - if (gob->ob->flag & SELECT) - ED_base_object_select(BKE_scene_base_find(scene, gob->ob), BA_DESELECT); - } + if (!extend && (base->flag & BASE_VISIBLED)) { + /* Auto select solo-ed object. */ + ED_object_base_select(base, BA_SELECT); + view_layer->basact = base; } - else { - for (gob = gr->gobject.first; gob; gob = gob->next) { - if (ID_IS_LINKED(gob->ob)) - continue; - - /* not in editmode */ - if (scene->obedit != gob->ob) { - gob->ob->restrictflag |= flag; - if (ELEM(flag, OB_RESTRICT_SELECT, OB_RESTRICT_VIEW)) { - if ((gob->ob->flag & SELECT)) { - ED_base_object_select(BKE_scene_base_find(scene, gob->ob), BA_DESELECT); - } - } - } - } - } + DEG_id_tag_update(&scene->id, DEG_TAG_BASE_FLAGS_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } -static void restrictbutton_gr_restrict_view(bContext *C, void *poin, void *poin2) +static void hidebutton_layer_collection_flag_cb(bContext *C, void *poin, void *poin2) { - restrictbutton_gr_restrict_flag(poin, poin2, OB_RESTRICT_VIEW); - WM_event_add_notifier(C, NC_GROUP, NULL); - DAG_id_type_tag(CTX_data_main(C), ID_OB); -} -static void restrictbutton_gr_restrict_select(bContext *C, void *poin, void *poin2) -{ - restrictbutton_gr_restrict_flag(poin, poin2, OB_RESTRICT_SELECT); - WM_event_add_notifier(C, NC_GROUP, NULL); -} -static void restrictbutton_gr_restrict_render(bContext *C, void *poin, void *poin2) -{ - restrictbutton_gr_restrict_flag(poin, poin2, OB_RESTRICT_RENDER); - WM_event_add_notifier(C, NC_GROUP, NULL); -} + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = poin; + LayerCollection *lc = poin2; + bool extend = (CTX_wm_window(C)->eventstate->ctrl == 0); -static void restrictbutton_id_user_toggle(bContext *UNUSED(C), void *poin, void *UNUSED(poin2)) -{ - ID *id = (ID *)poin; + /* Undo button toggle, let function do it. */ + lc->runtime_flag ^= LAYER_COLLECTION_HAS_VISIBLE_OBJECTS; - BLI_assert(id != NULL); + BKE_layer_collection_set_visible(scene, view_layer, lc, extend); - if (id->flag & LIB_FAKEUSER) { - id_us_plus(id); - } - else { - id_us_min(id); - } + DEG_id_tag_update(&scene->id, DEG_TAG_BASE_FLAGS_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } - static void namebutton_cb(bContext *C, void *tsep, char *oldname) { Main *bmain = CTX_data_main(C); SpaceOops *soops = CTX_wm_space_outliner(C); - Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); BLI_mempool *ts = soops->treestore; TreeStoreElem *tselem = tsep; @@ -528,37 +382,39 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) BLI_strncpy(newname, ebone->name, sizeof(ebone->name)); BLI_strncpy(ebone->name, oldname, sizeof(ebone->name)); ED_armature_bone_rename(bmain, obedit->data, oldname, newname); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, OBACT); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); } break; } case TSE_BONE: { + ViewLayer *view_layer = CTX_data_view_layer(C); + Scene *scene = CTX_data_scene(C); + bArmature *arm = (bArmature *)tselem->id; Bone *bone = te->directdata; - Object *ob; char newname[sizeof(bone->name)]; /* always make current object active */ - tree_element_active(C, scene, soops, te, OL_SETSEL_NORMAL, true); - ob = OBACT; + tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true); /* restore bone name */ BLI_strncpy(newname, bone->name, sizeof(bone->name)); BLI_strncpy(bone->name, oldname, sizeof(bone->name)); - ED_armature_bone_rename(bmain, ob->data, oldname, newname); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + ED_armature_bone_rename(bmain, arm, oldname, newname); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); break; } case TSE_POSE_CHANNEL: { + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = (Object *)tselem->id; bPoseChannel *pchan = te->directdata; - Object *ob; char newname[sizeof(pchan->name)]; /* always make current pose-bone active */ - tree_element_active(C, scene, soops, te, OL_SETSEL_NORMAL, true); - ob = OBACT; + tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true); BLI_assert(ob->type == OB_ARMATURE); @@ -566,7 +422,7 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) BLI_strncpy(newname, pchan->name, sizeof(pchan->name)); BLI_strncpy(pchan->name, oldname, sizeof(pchan->name)); ED_armature_bone_rename(bmain, ob->data, oldname, newname); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); break; } case TSE_POSEGRP: @@ -591,237 +447,240 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) break; } case TSE_R_LAYER: + { + Scene *scene = (Scene *)tselem->id; + ViewLayer *view_layer = te->directdata; + + /* Restore old name. */ + char newname[sizeof(view_layer->name)]; + BLI_strncpy(newname, view_layer->name, sizeof(view_layer->name)); + BLI_strncpy(view_layer->name, oldname, sizeof(view_layer->name)); + + /* Rename, preserving animation and compositing data. */ + BKE_view_layer_rename(bmain, scene, view_layer, newname); + WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); + break; + } + case TSE_LAYER_COLLECTION: + { + BLI_libblock_ensure_unique_name(bmain, tselem->id->name); + WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); break; + } } } tselem->flag &= ~TSE_TEXTBUT; } } -static void outliner_draw_restrictbuts(uiBlock *block, Scene *scene, ARegion *ar, SpaceOops *soops, ListBase *lb) +static void outliner_draw_restrictbuts( + uiBlock *block, Scene *scene, ViewLayer *view_layer, ARegion *ar, SpaceOops *soops, ListBase *lb) { uiBut *bt; - TreeElement *te; - TreeStoreElem *tselem; - Object *ob = NULL; - Group *gr = NULL; - PropertyRNA *object_prop_hide, *object_prop_hide_select, *object_prop_hide_render; + /* get RNA properties (once for speed) */ + PropertyRNA *object_prop_hide_viewport, *object_prop_hide_select, *object_prop_hide_render; + PropertyRNA *collection_prop_hide_viewport, *collection_prop_hide_select, *collection_prop_hide_render; - /* get RNA properties (once) */ - object_prop_hide = RNA_struct_type_find_property(&RNA_Object, "hide"); + object_prop_hide_viewport = RNA_struct_type_find_property(&RNA_Object, "hide_viewport"); object_prop_hide_select = RNA_struct_type_find_property(&RNA_Object, "hide_select"); object_prop_hide_render = RNA_struct_type_find_property(&RNA_Object, "hide_render"); - BLI_assert(object_prop_hide && object_prop_hide_select && object_prop_hide_render); - - - for (te = lb->first; te; te = te->next) { - tselem = TREESTORE(te); + collection_prop_hide_select = RNA_struct_type_find_property(&RNA_Collection, "hide_select"); + collection_prop_hide_viewport = RNA_struct_type_find_property(&RNA_Collection, "hide_viewport"); + collection_prop_hide_render = RNA_struct_type_find_property(&RNA_Collection, "hide_render"); + + BLI_assert(object_prop_hide_viewport && + object_prop_hide_select && + object_prop_hide_render && + collection_prop_hide_viewport && + collection_prop_hide_select && + collection_prop_hide_render); + + for (TreeElement *te = lb->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) { - /* objects have toggle-able restriction flags */ - if (tselem->type == 0 && te->idcode == ID_OB) { - PointerRNA ptr; + if (tselem->type == TSE_R_LAYER && (soops->outlinevis == SO_SCENES)) { + /* View layer render toggle. */ + ViewLayer *layer = te->directdata; + + bt = uiDefIconButBitS( + block, UI_BTYPE_ICON_TOGGLE_N, VIEW_LAYER_RENDER, 0, ICON_RESTRICT_RENDER_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &layer->flag, 0, 0, 0, 0, TIP_("Use view layer for rendering")); + UI_but_func_set(bt, restrictbutton_r_lay_cb, tselem->id, NULL); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + } + else if (tselem->type == 0 && te->idcode == ID_OB) { + Object *ob = (Object *)tselem->id; + Base *base = BKE_view_layer_base_find(view_layer, ob); + + if (base) { + bt = uiDefIconButBitS( + block, UI_BTYPE_ICON_TOGGLE, BASE_HIDE, 0, ICON_HIDE_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &base->flag, 0, 0, 0, 0, + TIP_("Hide object in viewport (Ctrl to isolate)")); + UI_but_func_set(bt, hidebutton_base_flag_cb, view_layer, base); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + } - ob = (Object *)tselem->id; + PointerRNA ptr; RNA_pointer_create((ID *)ob, &RNA_Object, ob, &ptr); - UI_block_emboss_set(block, UI_EMBOSS_NONE); bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, ICON_RESTRICT_VIEW_OFF, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y, - &ptr, object_prop_hide, -1, 0, 0, -1, -1, - TIP_("Restrict viewport visibility (Ctrl - Recursive)")); - UI_but_func_set(bt, restrictbutton_view_cb, scene, ob); + &ptr, object_prop_hide_viewport, -1, 0, 0, -1, -1, + TIP_("Restrict viewport visibility")); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, ICON_RESTRICT_SELECT_OFF, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, UI_UNIT_Y, &ptr, object_prop_hide_select, -1, 0, 0, -1, -1, - TIP_("Restrict viewport selection (Ctrl - Recursive)")); - UI_but_func_set(bt, restrictbutton_sel_cb, scene, ob); + TIP_("Restrict viewport selection")); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, ICON_RESTRICT_RENDER_OFF, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y, &ptr, object_prop_hide_render, -1, 0, 0, -1, -1, - TIP_("Restrict rendering (Ctrl - Recursive)")); - UI_but_func_set(bt, restrictbutton_rend_cb, scene, ob); + TIP_("Restrict render visibility")); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - UI_block_emboss_set(block, UI_EMBOSS); - - } - if (tselem->type == 0 && te->idcode == ID_GR) { - int restrict_bool; - int but_flag = UI_BUT_DRAG_LOCK; - gr = (Group *)tselem->id; - - if (ID_IS_LINKED(gr)) - but_flag |= UI_BUT_DISABLED; - - UI_block_emboss_set(block, UI_EMBOSS_NONE); - - restrict_bool = group_restrict_flag(gr, OB_RESTRICT_VIEW); - bt = uiDefIconBut(block, UI_BTYPE_ICON_TOGGLE, 0, restrict_bool ? ICON_RESTRICT_VIEW_ON : ICON_RESTRICT_VIEW_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y, - NULL, 0, 0, 0, 0, TIP_("Restrict/Allow visibility in the 3D View")); - UI_but_func_set(bt, restrictbutton_gr_restrict_view, scene, gr); - UI_but_flag_enable(bt, but_flag); - - restrict_bool = group_restrict_flag(gr, OB_RESTRICT_SELECT); - bt = uiDefIconBut(block, UI_BTYPE_ICON_TOGGLE, 0, restrict_bool ? ICON_RESTRICT_SELECT_ON : ICON_RESTRICT_SELECT_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, UI_UNIT_Y, - NULL, 0, 0, 0, 0, TIP_("Restrict/Allow selection in the 3D View")); - UI_but_func_set(bt, restrictbutton_gr_restrict_select, scene, gr); - UI_but_flag_enable(bt, but_flag); - - restrict_bool = group_restrict_flag(gr, OB_RESTRICT_RENDER); - bt = uiDefIconBut(block, UI_BTYPE_ICON_TOGGLE, 0, restrict_bool ? ICON_RESTRICT_RENDER_ON : ICON_RESTRICT_RENDER_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y, - NULL, 0, 0, 0, 0, TIP_("Restrict/Allow renderability")); - UI_but_func_set(bt, restrictbutton_gr_restrict_render, scene, gr); - UI_but_flag_enable(bt, but_flag); - - UI_block_emboss_set(block, UI_EMBOSS); - } - /* scene render layers and passes have toggle-able flags too! */ - else if (tselem->type == TSE_R_LAYER) { - UI_block_emboss_set(block, UI_EMBOSS_NONE); - - bt = uiDefIconButBitI(block, UI_BTYPE_ICON_TOGGLE_N, SCE_LAY_DISABLE, 0, ICON_CHECKBOX_HLT - 1, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, - UI_UNIT_Y, te->directdata, 0, 0, 0, 0, TIP_("Render this RenderLayer")); - UI_but_func_set(bt, restrictbutton_r_lay_cb, tselem->id, NULL); - UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - - UI_block_emboss_set(block, UI_EMBOSS); - } - else if (tselem->type == TSE_R_PASS) { - int *layflag = te->directdata; - int passflag = 1 << tselem->nr; - - UI_block_emboss_set(block, UI_EMBOSS_NONE); - - - bt = uiDefIconButBitI(block, UI_BTYPE_ICON_TOGGLE, passflag, 0, ICON_CHECKBOX_HLT - 1, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, - UI_UNIT_Y, layflag, 0, 0, 0, 0, TIP_("Render this Pass")); - UI_but_func_set(bt, restrictbutton_r_lay_cb, tselem->id, NULL); - UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - - layflag++; /* is lay_xor */ - if (ELEM(passflag, SCE_PASS_SPEC, SCE_PASS_SHADOW, SCE_PASS_AO, SCE_PASS_REFLECT, SCE_PASS_REFRACT, - SCE_PASS_INDIRECT, SCE_PASS_EMIT, SCE_PASS_ENVIRONMENT)) - { - bt = uiDefIconButBitI(block, UI_BTYPE_TOGGLE, passflag, 0, (*layflag & passflag) ? ICON_DOT : ICON_BLANK1, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, - UI_UNIT_Y, layflag, 0, 0, 0, 0, TIP_("Exclude this Pass from Combined")); - UI_but_func_set(bt, restrictbutton_r_lay_cb, tselem->id, NULL); - UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - } - - UI_block_emboss_set(block, UI_EMBOSS); } else if (tselem->type == TSE_MODIFIER) { ModifierData *md = (ModifierData *)te->directdata; - ob = (Object *)tselem->id; + Object *ob = (Object *)tselem->id; - UI_block_emboss_set(block, UI_EMBOSS_NONE); - bt = uiDefIconButBitI(block, UI_BTYPE_ICON_TOGGLE_N, eModifierMode_Realtime, 0, ICON_RESTRICT_VIEW_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, - UI_UNIT_Y, &(md->mode), 0, 0, 0, 0, - TIP_("Restrict/Allow visibility in the 3D View")); + bt = uiDefIconButBitI( + block, UI_BTYPE_ICON_TOGGLE_N, eModifierMode_Realtime, 0, ICON_RESTRICT_VIEW_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &(md->mode), 0, 0, 0, 0, + TIP_("Restrict/Allow visibility in the 3D View")); UI_but_func_set(bt, restrictbutton_modifier_cb, scene, ob); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - bt = uiDefIconButBitI(block, UI_BTYPE_ICON_TOGGLE_N, eModifierMode_Render, 0, ICON_RESTRICT_RENDER_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, - UI_UNIT_Y, &(md->mode), 0, 0, 0, 0, TIP_("Restrict/Allow renderability")); + bt = uiDefIconButBitI( + block, UI_BTYPE_ICON_TOGGLE_N, eModifierMode_Render, 0, ICON_RESTRICT_RENDER_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &(md->mode), 0, 0, 0, 0, TIP_("Restrict/Allow renderability")); UI_but_func_set(bt, restrictbutton_modifier_cb, scene, ob); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - - UI_block_emboss_set(block, UI_EMBOSS); } else if (tselem->type == TSE_POSE_CHANNEL) { bPoseChannel *pchan = (bPoseChannel *)te->directdata; Bone *bone = pchan->bone; - ob = (Object *)tselem->id; + Object *ob = (Object *)tselem->id; - UI_block_emboss_set(block, UI_EMBOSS_NONE); - bt = uiDefIconButBitI(block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_P, 0, ICON_RESTRICT_VIEW_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, - UI_UNIT_Y, &(bone->flag), 0, 0, 0, 0, - TIP_("Restrict/Allow visibility in the 3D View")); + bt = uiDefIconButBitI( + block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_P, 0, ICON_HIDE_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &(bone->flag), 0, 0, 0, 0, + TIP_("Restrict/Allow visibility in the 3D View")); UI_but_func_set(bt, restrictbutton_bone_visibility_cb, ob->data, bone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - bt = uiDefIconButBitI(block, UI_BTYPE_ICON_TOGGLE, BONE_UNSELECTABLE, 0, ICON_RESTRICT_SELECT_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, - UI_UNIT_Y, &(bone->flag), 0, 0, 0, 0, - TIP_("Restrict/Allow selection in the 3D View")); + bt = uiDefIconButBitI( + block, UI_BTYPE_ICON_TOGGLE, BONE_UNSELECTABLE, 0, ICON_RESTRICT_SELECT_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &(bone->flag), 0, 0, 0, 0, + TIP_("Restrict/Allow selection in the 3D View")); UI_but_func_set(bt, restrictbutton_bone_select_cb, ob->data, bone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - - UI_block_emboss_set(block, UI_EMBOSS); } else if (tselem->type == TSE_EBONE) { EditBone *ebone = (EditBone *)te->directdata; - UI_block_emboss_set(block, UI_EMBOSS_NONE); - bt = uiDefIconButBitI(block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_A, 0, ICON_RESTRICT_VIEW_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, - UI_UNIT_Y, &(ebone->flag), 0, 0, 0, 0, - TIP_("Restrict/Allow visibility in the 3D View")); + bt = uiDefIconButBitI( + block, UI_BTYPE_ICON_TOGGLE, BONE_HIDDEN_A, 0, ICON_RESTRICT_VIEW_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &(ebone->flag), 0, 0, 0, 0, + TIP_("Restrict/Allow visibility in the 3D View")); UI_but_func_set(bt, restrictbutton_ebone_visibility_cb, NULL, ebone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - bt = uiDefIconButBitI(block, UI_BTYPE_ICON_TOGGLE, BONE_UNSELECTABLE, 0, ICON_RESTRICT_SELECT_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, - UI_UNIT_Y, &(ebone->flag), 0, 0, 0, 0, - TIP_("Restrict/Allow selection in the 3D View")); + bt = uiDefIconButBitI( + block, UI_BTYPE_ICON_TOGGLE, BONE_UNSELECTABLE, 0, ICON_RESTRICT_SELECT_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &(ebone->flag), 0, 0, 0, 0, + TIP_("Restrict/Allow selection in the 3D View")); UI_but_func_set(bt, restrictbutton_ebone_select_cb, NULL, ebone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - - UI_block_emboss_set(block, UI_EMBOSS); } else if (tselem->type == TSE_GP_LAYER) { bGPDlayer *gpl = (bGPDlayer *)te->directdata; - UI_block_emboss_set(block, UI_EMBOSS_NONE); - - bt = uiDefIconButBitS(block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_HIDE, 0, ICON_RESTRICT_VIEW_OFF, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, - UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0, - TIP_("Restrict/Allow visibility in the 3D View")); + bt = uiDefIconButBitS( + block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_HIDE, 0, ICON_HIDE_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0, + TIP_("Restrict/Allow visibility in the 3D View")); UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, NULL, gpl); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - bt = uiDefIconButBitS(block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_LOCKED, 0, ICON_UNLOCKED, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, - UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0, - TIP_("Restrict/Allow editing of strokes and keyframes in this layer")); + bt = uiDefIconButBitS( + block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_LOCKED, 0, ICON_UNLOCKED, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0, + TIP_("Restrict/Allow editing of strokes and keyframes in this layer")); UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, NULL, gpl); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); /* TODO: visibility in renders */ + } + else if (outliner_is_collection_tree_element(te)) { + LayerCollection *lc = (tselem->type == TSE_LAYER_COLLECTION) ? te->directdata : NULL; + Collection *collection = outliner_collection_from_tree_element(te); + + if ((!lc || !(lc->flag & LAYER_COLLECTION_EXCLUDE)) && + !(collection->flag & COLLECTION_IS_MASTER)) + { + if (lc && (lc->runtime_flag & LAYER_COLLECTION_HAS_ENABLED_OBJECTS)) { + bt = uiDefIconButBitS( + block, UI_BTYPE_ICON_TOGGLE_N, LAYER_COLLECTION_HAS_VISIBLE_OBJECTS, 0, ICON_HIDE_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &lc->runtime_flag, 0, 0, 0, 0, + TIP_("Hide collection in viewport (Ctrl to isolate)")); + UI_but_func_set(bt, hidebutton_layer_collection_flag_cb, view_layer, lc); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + } + + PointerRNA collection_ptr; + RNA_id_pointer_create(&collection->id, &collection_ptr); + + bt = uiDefIconButR_prop( + block, UI_BTYPE_ICON_TOGGLE, 0, ICON_RESTRICT_VIEW_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &collection_ptr, collection_prop_hide_viewport, -1, 0, 0, 0, 0, NULL); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + + bt = uiDefIconButR_prop( + block, UI_BTYPE_ICON_TOGGLE, 0, ICON_RESTRICT_RENDER_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &collection_ptr, collection_prop_hide_render, -1, 0, 0, 0, 0, NULL); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); - UI_block_emboss_set(block, UI_EMBOSS); + bt = uiDefIconButR_prop( + block, UI_BTYPE_ICON_TOGGLE, 0, ICON_RESTRICT_SELECT_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &collection_ptr, collection_prop_hide_select, -1, 0, 0, 0, 0, NULL); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + } } } - if (TSELEM_OPEN(tselem, soops)) outliner_draw_restrictbuts(block, scene, ar, soops, &te->subtree); + if (TSELEM_OPEN(tselem, soops)) { + outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &te->subtree); + } } } static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOops *soops, ListBase *lb) { - uiBut *bt; - TreeElement *te; - TreeStoreElem *tselem; - for (te = lb->first; te; te = te->next) { - tselem = TREESTORE(te); + for (TreeElement *te = lb->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) { if (tselem->type == 0) { + uiBut *bt; ID *id = tselem->id; const char *tip = NULL; int icon = ICON_NONE; @@ -831,8 +690,6 @@ static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOops *soops if (ID_IS_LINKED(id)) but_flag |= UI_BUT_DISABLED; - UI_block_emboss_set(block, UI_EMBOSS_NONE); - if (id->flag & LIB_FAKEUSER) { icon = ICON_FILE_TICK; tip = TIP_("Data-block will be retained using a fake user"); @@ -841,34 +698,37 @@ static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOops *soops icon = ICON_X; tip = TIP_("Data-block has no users and will be deleted"); } - bt = uiDefIconButBitS(block, UI_BTYPE_TOGGLE, LIB_FAKEUSER, 1, icon, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y, - &id->flag, 0, 0, 0, 0, tip); + bt = uiDefIconButBitS( + block, UI_BTYPE_TOGGLE, LIB_FAKEUSER, 1, icon, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y, + &id->flag, 0, 0, 0, 0, tip); UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL); UI_but_flag_enable(bt, but_flag); BLI_str_format_int_grouped(buf, id->us); - bt = uiDefBut(block, UI_BTYPE_BUT, 1, buf, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, - UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, - TIP_("Number of users of this data-block")); + bt = uiDefBut( + block, UI_BTYPE_BUT, 1, buf, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, + UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, + TIP_("Number of users of this data-block")); UI_but_flag_enable(bt, but_flag); - bt = uiDefButBitS(block, UI_BTYPE_TOGGLE, LIB_FAKEUSER, 1, (id->flag & LIB_FAKEUSER) ? "F" : " ", - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y, - &id->flag, 0, 0, 0, 0, - TIP_("Data-block has a 'fake' user which will keep it in the file " - "even if nothing else uses it")); + bt = uiDefButBitS( + block, UI_BTYPE_TOGGLE, LIB_FAKEUSER, 1, (id->flag & LIB_FAKEUSER) ? "F" : " ", + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), te->ys, UI_UNIT_X, UI_UNIT_Y, + &id->flag, 0, 0, 0, 0, + TIP_("Data-block has a 'fake' user which will keep it in the file " + "even if nothing else uses it")); UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL); UI_but_flag_enable(bt, but_flag); - - UI_block_emboss_set(block, UI_EMBOSS); } } - if (TSELEM_OPEN(tselem, soops)) outliner_draw_userbuts(block, ar, soops, &te->subtree); + if (TSELEM_OPEN(tselem, soops)) { + outliner_draw_userbuts(block, ar, soops, &te->subtree); + } } } @@ -879,29 +739,32 @@ static void outliner_draw_rnacols(ARegion *ar, int sizex) float miny = v2d->cur.ymin; if (miny < v2d->tot.ymin) miny = v2d->tot.ymin; - UI_ThemeColorShadeAlpha(TH_BACK, -15, -200); + glLineWidth(1.0f); + + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShadeAlpha(TH_BACK, -15, -200); + + immBegin(GWN_PRIM_LINES, 4); + + immVertex2f(pos, sizex, v2d->cur.ymax); + immVertex2f(pos, sizex, miny); - /* draw column separator lines */ - fdrawline((float)sizex, - v2d->cur.ymax, - (float)sizex, - miny); + immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, v2d->cur.ymax); + immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, miny); - fdrawline((float)sizex + OL_RNA_COL_SIZEX, - v2d->cur.ymax, - (float)sizex + OL_RNA_COL_SIZEX, - miny); + immEnd(); + + immUnbindProgram(); } static void outliner_draw_rnabuts(uiBlock *block, ARegion *ar, SpaceOops *soops, int sizex, ListBase *lb) { - TreeElement *te; - TreeStoreElem *tselem; PointerRNA *ptr; PropertyRNA *prop; - for (te = lb->first; te; te = te->next) { - tselem = TREESTORE(te); + for (TreeElement *te = lb->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) { if (tselem->type == TSE_RNA_PROPERTY) { ptr = &te->rnaptr; @@ -909,17 +772,20 @@ static void outliner_draw_rnabuts(uiBlock *block, ARegion *ar, SpaceOops *soops, if (!TSELEM_OPEN(tselem, soops)) { if (RNA_property_type(prop) == PROP_POINTER) { - uiBut *but = uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, sizex, te->ys, - OL_RNA_COL_SIZEX, UI_UNIT_Y - 1); + uiBut *but = uiDefAutoButR( + block, ptr, prop, -1, "", ICON_NONE, sizex, te->ys, + OL_RNA_COL_SIZEX, UI_UNIT_Y - 1); UI_but_flag_enable(but, UI_BUT_DISABLED); } else if (RNA_property_type(prop) == PROP_ENUM) { - uiDefAutoButR(block, ptr, prop, -1, NULL, ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); + uiDefAutoButR( + block, ptr, prop, -1, NULL, ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); } else { - uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); + uiDefAutoButR( + block, ptr, prop, -1, "", ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); } } } @@ -927,15 +793,16 @@ static void outliner_draw_rnabuts(uiBlock *block, ARegion *ar, SpaceOops *soops, ptr = &te->rnaptr; prop = te->directdata; - uiDefAutoButR(block, ptr, prop, te->index, "", ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); + uiDefAutoButR( + block, ptr, prop, te->index, "", ICON_NONE, sizex, te->ys, OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); } } - if (TSELEM_OPEN(tselem, soops)) outliner_draw_rnabuts(block, ar, soops, sizex, &te->subtree); + if (TSELEM_OPEN(tselem, soops)) { + outliner_draw_rnabuts(block, ar, soops, sizex, &te->subtree); + } } - - UI_block_emboss_set(block, UI_EMBOSS); } static void outliner_buttons(const bContext *C, uiBlock *block, ARegion *ar, TreeElement *te) @@ -959,8 +826,9 @@ static void outliner_buttons(const bContext *C, uiBlock *block, ARegion *ar, Tre spx = te->xs + 1.8f * UI_UNIT_X; dx = ar->v2d.cur.xmax - (spx + 3.2f * UI_UNIT_X); - bt = uiDefBut(block, UI_BTYPE_TEXT, OL_NAMEBUTTON, "", spx, te->ys, dx, UI_UNIT_Y - 1, (void *)te->name, - 1.0, (float)len, 0, 0, ""); + bt = uiDefBut( + block, UI_BTYPE_TEXT, OL_NAMEBUTTON, "", spx, te->ys, dx, UI_UNIT_Y - 1, (void *)te->name, + 1.0, (float)len, 0, 0, ""); UI_but_func_rename_set(bt, namebutton_cb, tselem); /* returns false if button got removed */ @@ -988,13 +856,14 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon) /* restrict column clip... it has been coded by simply overdrawing, doesnt work for buttons */ if (arg->x >= arg->xmax) { glEnable(GL_BLEND); - UI_icon_draw_aspect(arg->x, arg->y, icon, 1.0f / UI_DPI_FAC, arg->alpha); + UI_icon_draw_alpha(arg->x, arg->y, icon, arg->alpha); glDisable(GL_BLEND); } else { - uiBut *but = uiDefIconBut(arg->block, UI_BTYPE_LABEL, 0, icon, arg->xb, arg->yb, UI_UNIT_X, UI_UNIT_Y, NULL, - 0.0, 0.0, 1.0, arg->alpha, - (arg->id && ID_IS_LINKED(arg->id)) ? arg->id->lib->name : ""); + uiBut *but = uiDefIconBut( + arg->block, UI_BTYPE_LABEL, 0, icon, arg->xb, arg->yb, UI_UNIT_X, UI_UNIT_Y, NULL, + 0.0, 0.0, 1.0, arg->alpha, + (arg->id && ID_IS_LINKED(arg->id)) ? arg->id->lib->name : ""); if (arg->id) UI_but_drag_set_id(but, arg->id); @@ -1035,8 +904,9 @@ static void UNUSED_FUNCTION(tselem_draw_gp_icon_uibut)(struct DrawIconArg *arg, } } -static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeStoreElem *tselem, TreeElement *te, - float alpha) +static void tselem_draw_icon( + uiBlock *block, int xmax, float x, float y, TreeStoreElem *tselem, TreeElement *te, + float alpha, const bool is_clickable) { struct DrawIconArg arg; float aspect; @@ -1056,184 +926,262 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto arg.x = x; arg.y = y; +#define ICON_DRAW(_icon) UI_icon_draw_alpha(x, y, _icon, alpha) +#define ICON_CLICK_DRAW(_icon) if (!is_clickable) ICON_DRAW(_icon); else tselem_draw_icon_uibut(&arg, _icon) + if (tselem->type) { switch (tselem->type) { case TSE_ANIM_DATA: - UI_icon_draw(x, y, ICON_ANIM_DATA); break; // xxx + ICON_DRAW(ICON_ANIM_DATA); /* XXX */ + break; case TSE_NLA: - UI_icon_draw(x, y, ICON_NLA); break; + ICON_DRAW(ICON_NLA); + break; case TSE_NLA_TRACK: - UI_icon_draw(x, y, ICON_NLA); break; // XXX + ICON_DRAW(ICON_NLA); /* XXX */ + break; case TSE_NLA_ACTION: - UI_icon_draw(x, y, ICON_ACTION); break; + ICON_DRAW(ICON_ACTION); + break; case TSE_DRIVER_BASE: - UI_icon_draw(x, y, ICON_DRIVER); break; + ICON_DRAW(ICON_DRIVER); + break; case TSE_DEFGROUP_BASE: - UI_icon_draw(x, y, ICON_GROUP_VERTEX); break; + ICON_DRAW(ICON_GROUP_VERTEX); + break; case TSE_BONE: case TSE_EBONE: - UI_icon_draw(x, y, ICON_BONE_DATA); break; + ICON_DRAW(ICON_BONE_DATA); + break; case TSE_CONSTRAINT_BASE: - UI_icon_draw(x, y, ICON_CONSTRAINT); break; + ICON_DRAW(ICON_CONSTRAINT); + break; case TSE_MODIFIER_BASE: - UI_icon_draw(x, y, ICON_MODIFIER); break; + ICON_DRAW(ICON_MODIFIER); + break; case TSE_LINKED_OB: - UI_icon_draw(x, y, ICON_OBJECT_DATA); break; + ICON_DRAW(ICON_OBJECT_DATA); + break; case TSE_LINKED_PSYS: - UI_icon_draw(x, y, ICON_PARTICLES); break; + ICON_DRAW(ICON_PARTICLES); + break; case TSE_MODIFIER: { Object *ob = (Object *)tselem->id; ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr); switch ((ModifierType)md->type) { case eModifierType_Subsurf: - UI_icon_draw(x, y, ICON_MOD_SUBSURF); break; + ICON_DRAW(ICON_MOD_SUBSURF); + break; case eModifierType_Armature: - UI_icon_draw(x, y, ICON_MOD_ARMATURE); break; + ICON_DRAW(ICON_MOD_ARMATURE); + break; case eModifierType_Lattice: - UI_icon_draw(x, y, ICON_MOD_LATTICE); break; + ICON_DRAW(ICON_MOD_LATTICE); + break; case eModifierType_Curve: - UI_icon_draw(x, y, ICON_MOD_CURVE); break; + ICON_DRAW(ICON_MOD_CURVE); + break; case eModifierType_Build: - UI_icon_draw(x, y, ICON_MOD_BUILD); break; + ICON_DRAW(ICON_MOD_BUILD); + break; case eModifierType_Mirror: - UI_icon_draw(x, y, ICON_MOD_MIRROR); break; + ICON_DRAW(ICON_MOD_MIRROR); + break; case eModifierType_Decimate: - UI_icon_draw(x, y, ICON_MOD_DECIM); break; + ICON_DRAW(ICON_MOD_DECIM); + break; case eModifierType_Wave: - UI_icon_draw(x, y, ICON_MOD_WAVE); break; + ICON_DRAW(ICON_MOD_WAVE); + break; case eModifierType_Hook: - UI_icon_draw(x, y, ICON_HOOK); break; + ICON_DRAW(ICON_HOOK); + break; case eModifierType_Softbody: - UI_icon_draw(x, y, ICON_MOD_SOFT); break; + ICON_DRAW(ICON_MOD_SOFT); + break; case eModifierType_Boolean: - UI_icon_draw(x, y, ICON_MOD_BOOLEAN); break; + ICON_DRAW(ICON_MOD_BOOLEAN); + break; case eModifierType_ParticleSystem: - UI_icon_draw(x, y, ICON_MOD_PARTICLES); break; + ICON_DRAW(ICON_MOD_PARTICLES); + break; case eModifierType_ParticleInstance: - UI_icon_draw(x, y, ICON_MOD_PARTICLES); break; + ICON_DRAW(ICON_MOD_PARTICLES); + break; case eModifierType_EdgeSplit: - UI_icon_draw(x, y, ICON_MOD_EDGESPLIT); break; + ICON_DRAW(ICON_MOD_EDGESPLIT); + break; case eModifierType_Array: - UI_icon_draw(x, y, ICON_MOD_ARRAY); break; + ICON_DRAW(ICON_MOD_ARRAY); + break; case eModifierType_UVProject: case eModifierType_UVWarp: /* TODO, get own icon */ - UI_icon_draw(x, y, ICON_MOD_UVPROJECT); break; + ICON_DRAW(ICON_MOD_UVPROJECT); + break; case eModifierType_Displace: - UI_icon_draw(x, y, ICON_MOD_DISPLACE); break; + ICON_DRAW(ICON_MOD_DISPLACE); + break; case eModifierType_Shrinkwrap: - UI_icon_draw(x, y, ICON_MOD_SHRINKWRAP); break; + ICON_DRAW(ICON_MOD_SHRINKWRAP); + break; case eModifierType_Cast: - UI_icon_draw(x, y, ICON_MOD_CAST); break; + ICON_DRAW(ICON_MOD_CAST); + break; case eModifierType_MeshDeform: case eModifierType_SurfaceDeform: - UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; + ICON_DRAW(ICON_MOD_MESHDEFORM); + break; case eModifierType_Bevel: - UI_icon_draw(x, y, ICON_MOD_BEVEL); break; + ICON_DRAW(ICON_MOD_BEVEL); + break; case eModifierType_Smooth: case eModifierType_LaplacianSmooth: case eModifierType_CorrectiveSmooth: - UI_icon_draw(x, y, ICON_MOD_SMOOTH); break; + ICON_DRAW(ICON_MOD_SMOOTH); + break; case eModifierType_SimpleDeform: - UI_icon_draw(x, y, ICON_MOD_SIMPLEDEFORM); break; + ICON_DRAW(ICON_MOD_SIMPLEDEFORM); + break; case eModifierType_Mask: - UI_icon_draw(x, y, ICON_MOD_MASK); break; + ICON_DRAW(ICON_MOD_MASK); + break; case eModifierType_Cloth: - UI_icon_draw(x, y, ICON_MOD_CLOTH); break; + ICON_DRAW(ICON_MOD_CLOTH); + break; case eModifierType_Explode: - UI_icon_draw(x, y, ICON_MOD_EXPLODE); break; + ICON_DRAW(ICON_MOD_EXPLODE); + break; case eModifierType_Collision: case eModifierType_Surface: - UI_icon_draw(x, y, ICON_MOD_PHYSICS); break; + ICON_DRAW(ICON_MOD_PHYSICS); + break; case eModifierType_Fluidsim: - UI_icon_draw(x, y, ICON_MOD_FLUIDSIM); break; + ICON_DRAW(ICON_MOD_FLUIDSIM); + break; case eModifierType_Multires: - UI_icon_draw(x, y, ICON_MOD_MULTIRES); break; + ICON_DRAW(ICON_MOD_MULTIRES); + break; case eModifierType_Smoke: - UI_icon_draw(x, y, ICON_MOD_SMOKE); break; + ICON_DRAW(ICON_MOD_SMOKE); + break; case eModifierType_Solidify: - UI_icon_draw(x, y, ICON_MOD_SOLIDIFY); break; + ICON_DRAW(ICON_MOD_SOLIDIFY); + break; case eModifierType_Screw: - UI_icon_draw(x, y, ICON_MOD_SCREW); break; + ICON_DRAW(ICON_MOD_SCREW); + break; case eModifierType_Remesh: - UI_icon_draw(x, y, ICON_MOD_REMESH); break; + ICON_DRAW(ICON_MOD_REMESH); + break; case eModifierType_WeightVGEdit: case eModifierType_WeightVGMix: case eModifierType_WeightVGProximity: - UI_icon_draw(x, y, ICON_MOD_VERTEX_WEIGHT); break; + ICON_DRAW(ICON_MOD_VERTEX_WEIGHT); + break; case eModifierType_DynamicPaint: - UI_icon_draw(x, y, ICON_MOD_DYNAMICPAINT); break; + ICON_DRAW(ICON_MOD_DYNAMICPAINT); + break; case eModifierType_Ocean: - UI_icon_draw(x, y, ICON_MOD_OCEAN); break; + ICON_DRAW(ICON_MOD_OCEAN); + break; case eModifierType_Warp: - UI_icon_draw(x, y, ICON_MOD_WARP); break; + ICON_DRAW(ICON_MOD_WARP); + break; case eModifierType_Skin: - UI_icon_draw(x, y, ICON_MOD_SKIN); break; + ICON_DRAW(ICON_MOD_SKIN); + break; case eModifierType_Triangulate: - UI_icon_draw(x, y, ICON_MOD_TRIANGULATE); break; + ICON_DRAW(ICON_MOD_TRIANGULATE); + break; case eModifierType_MeshCache: - UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; case eModifierType_MeshSequenceCache: - UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; case eModifierType_Wireframe: - UI_icon_draw(x, y, ICON_MOD_WIREFRAME); break; + ICON_DRAW(ICON_MOD_WIREFRAME); + break; case eModifierType_LaplacianDeform: - UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; case eModifierType_DataTransfer: - UI_icon_draw(x, y, ICON_MOD_DATA_TRANSFER); break; + ICON_DRAW(ICON_MOD_DATA_TRANSFER); + break; case eModifierType_NormalEdit: - UI_icon_draw(x, y, ICON_MOD_NORMALEDIT); break; + ICON_DRAW(ICON_MOD_NORMALEDIT); + break; /* Default */ case eModifierType_None: case eModifierType_ShapeKey: case NUM_MODIFIER_TYPES: - UI_icon_draw(x, y, ICON_DOT); break; + ICON_DRAW(ICON_DOT); + break; } break; } case TSE_POSE_BASE: - UI_icon_draw(x, y, ICON_ARMATURE_DATA); break; + ICON_DRAW(ICON_ARMATURE_DATA); + break; case TSE_POSE_CHANNEL: - UI_icon_draw(x, y, ICON_BONE_DATA); break; + ICON_DRAW(ICON_BONE_DATA); + break; case TSE_PROXY: - UI_icon_draw(x, y, ICON_GHOST); break; + ICON_DRAW(ICON_GHOST); + break; case TSE_R_LAYER_BASE: - UI_icon_draw(x, y, ICON_RENDERLAYERS); break; + ICON_DRAW(ICON_RENDERLAYERS); + break; + case TSE_SCENE_OBJECTS_BASE: + ICON_DRAW(ICON_OUTLINER_OB_GROUP_INSTANCE); + break; case TSE_R_LAYER: - UI_icon_draw(x, y, ICON_RENDERLAYERS); break; + ICON_DRAW(ICON_RENDER_RESULT); + break; case TSE_LINKED_LAMP: - UI_icon_draw(x, y, ICON_LAMP_DATA); break; + ICON_DRAW(ICON_LAMP_DATA); + break; case TSE_LINKED_MAT: - UI_icon_draw(x, y, ICON_MATERIAL_DATA); break; + ICON_DRAW(ICON_MATERIAL_DATA); + break; case TSE_POSEGRP_BASE: - UI_icon_draw(x, y, ICON_GROUP_BONE); break; + ICON_DRAW(ICON_GROUP_BONE); + break; case TSE_SEQUENCE: if (te->idcode == SEQ_TYPE_MOVIE) - UI_icon_draw(x, y, ICON_SEQUENCE); + ICON_DRAW(ICON_SEQUENCE); else if (te->idcode == SEQ_TYPE_META) - UI_icon_draw(x, y, ICON_DOT); + ICON_DRAW(ICON_DOT); else if (te->idcode == SEQ_TYPE_SCENE) - UI_icon_draw(x, y, ICON_SCENE); + ICON_DRAW(ICON_SCENE); else if (te->idcode == SEQ_TYPE_SOUND_RAM) - UI_icon_draw(x, y, ICON_SOUND); + ICON_DRAW(ICON_SOUND); else if (te->idcode == SEQ_TYPE_IMAGE) - UI_icon_draw(x, y, ICON_IMAGE_COL); + ICON_DRAW(ICON_IMAGE_COL); else - UI_icon_draw(x, y, ICON_PARTICLES); + ICON_DRAW(ICON_PARTICLES); break; case TSE_SEQ_STRIP: - UI_icon_draw(x, y, ICON_LIBRARY_DATA_DIRECT); + ICON_DRAW(ICON_LIBRARY_DATA_DIRECT); break; case TSE_SEQUENCE_DUP: - UI_icon_draw(x, y, ICON_OBJECT_DATA); + ICON_DRAW(ICON_OBJECT_DATA); break; case TSE_RNA_STRUCT: if (RNA_struct_is_ID(te->rnaptr.type)) { arg.id = (ID *)te->rnaptr.data; tselem_draw_icon_uibut(&arg, RNA_struct_ui_icon(te->rnaptr.type)); } - else - UI_icon_draw(x, y, RNA_struct_ui_icon(te->rnaptr.type)); + else { + int icon = RNA_struct_ui_icon(te->rnaptr.type); + ICON_DRAW(icon); + } + break; + case TSE_LAYER_COLLECTION: + case TSE_SCENE_COLLECTION_BASE: + case TSE_VIEW_COLLECTION_BASE: + ICON_DRAW(ICON_GROUP); break; /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */ #if 0 @@ -1242,7 +1190,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto break; #endif default: - UI_icon_draw(x, y, ICON_DOT); break; + ICON_DRAW(ICON_DOT); + break; } } else if (tselem->id) { @@ -1250,27 +1199,35 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto Object *ob = (Object *)tselem->id; switch (ob->type) { case OB_LAMP: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LAMP); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_LAMP); break; case OB_MESH: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_MESH); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_MESH); break; case OB_CAMERA: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CAMERA); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_CAMERA); break; case OB_CURVE: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CURVE); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_CURVE); break; case OB_MBALL: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_META); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_META); break; case OB_LATTICE: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LATTICE); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_LATTICE); break; case OB_ARMATURE: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_ARMATURE); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_ARMATURE); break; case OB_FONT: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_FONT); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_FONT); break; case OB_SURF: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SURFACE); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_SURFACE); break; case OB_SPEAKER: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SPEAKER); break; + ICON_CLICK_DRAW(ICON_OUTLINER_OB_SPEAKER); break; + case OB_LIGHTPROBE: + ICON_CLICK_DRAW(ICON_OUTLINER_OB_LIGHTPROBE); break; case OB_EMPTY: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_EMPTY); break; + if (ob->dup_group) { + ICON_CLICK_DRAW(ICON_OUTLINER_OB_GROUP_INSTANCE); + } + else { + ICON_CLICK_DRAW(ICON_OUTLINER_OB_EMPTY); + } + break; } } else { @@ -1347,75 +1304,233 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto tselem_draw_icon_uibut(&arg, ICON_LINE_DATA); break; case ID_GD: tselem_draw_icon_uibut(&arg, ICON_GREASEPENCIL); break; + case ID_LP: + { + LightProbe * lp = (LightProbe *)tselem->id; + switch (lp->type) { + case LIGHTPROBE_TYPE_CUBE: + tselem_draw_icon_uibut(&arg, ICON_LIGHTPROBE_CUBEMAP); break; + case LIGHTPROBE_TYPE_PLANAR: + tselem_draw_icon_uibut(&arg, ICON_LIGHTPROBE_PLANAR); break; + case LIGHTPROBE_TYPE_GRID: + tselem_draw_icon_uibut(&arg, ICON_LIGHTPROBE_GRID); break; + default: + tselem_draw_icon_uibut(&arg, ICON_LIGHTPROBE_CUBEMAP); break; + } + break; + } + case ID_BR: + tselem_draw_icon_uibut(&arg, ICON_BRUSH_DATA); break; + case ID_SCR: + case ID_WS: + tselem_draw_icon_uibut(&arg, ICON_SPLITSCREEN); break; default: break; } } } + +#undef ICON_DRAW } -static void outliner_draw_iconrow(bContext *C, uiBlock *block, Scene *scene, SpaceOops *soops, ListBase *lb, int level, - int xmax, int *offsx, int ys) +/** + * For icon-only children of a collapsed tree, + * Draw small number over the icon to show how many items of this type are displayed. + */ +static void outliner_draw_iconrow_number( + const uiFontStyle *fstyle, + int offsx, int ys, + const eOLDrawState active, + const int num_elements) { - TreeElement *te; - TreeStoreElem *tselem; - eOLDrawState active; + float color[4] = {0.4f, 0.4f, 0.4f, 0.9f}; + copy_v3_fl(color, 0.2f); + if (active != OL_DRAWSEL_NONE) { + copy_v3_fl(color, 0.65f); + color[3] = 1.0f; + } - for (te = lb->first; te; te = te->next) { + float ufac = 0.25f * UI_UNIT_X; + float offset_x = (float) offsx + UI_UNIT_X * 0.35f; + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_aa(true, + offset_x + ufac, + (float)ys - UI_UNIT_Y * 0.2f + ufac, + offset_x + UI_UNIT_X - ufac, + (float)ys - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac, + (float)UI_UNIT_Y / 2.0f - ufac, + color); + + /* Now the numbers. */ + unsigned char text_col[4]; + + UI_GetThemeColor4ubv(TH_TEXT_HI, text_col); + text_col[3] = 255; + + uiFontStyle fstyle_small = *fstyle; + fstyle_small.points *= 0.8f; + + /* We treat +99 as 4 digits to make sure the (eyeballed) alignment looks nice. */ + int num_digits = 4; + char number_text[4] = "+99\0"; + if (num_elements < 100) { + BLI_snprintf(number_text, sizeof(number_text), "%d", num_elements); + num_digits = num_elements < 10 ? 1 : 2; + } + UI_fontstyle_draw_simple(&fstyle_small, + (offset_x + ufac + UI_UNIT_X * (2 - num_digits) * 0.12f), + (float)ys - UI_UNIT_Y * 0.095f + ufac, + number_text, text_col); + UI_fontstyle_set(fstyle); + glEnable(GL_BLEND); /* Roundbox and text drawing disables. */ +} + +static void outliner_draw_iconrow_doit( + uiBlock *block, TreeElement *te, + const uiFontStyle *fstyle, + int xmax, int *offsx, int ys, float alpha_fac, + const eOLDrawState active, + const int num_elements) +{ + TreeStoreElem *tselem = TREESTORE(te); + + if (active != OL_DRAWSEL_NONE) { + float ufac = UI_UNIT_X / 20.0f; + float color[4] = {1.0f, 1.0f, 1.0f, 0.4f}; + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + color[3] *= alpha_fac; + + UI_draw_roundbox_aa(true, + (float) *offsx + 1.0f * ufac, + (float)ys + 1.0f * ufac, + (float)*offsx + UI_UNIT_X - 1.0f * ufac, + (float)ys + UI_UNIT_Y - ufac, + (float)UI_UNIT_Y / 2.0f - ufac, + color); + glEnable(GL_BLEND); /* Roundbox disables. */ + } + + /* No inlined icon should be clickable. */ + tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.5f * alpha_fac, false); + te->xs = *offsx; + te->ys = ys; + te->xend = (short)*offsx + UI_UNIT_X; + if (num_elements > 1) { + outliner_draw_iconrow_number(fstyle, *offsx, ys, active, num_elements); + } + (*offsx) += UI_UNIT_X; +} + +/** + * Return the index to use based on the TreeElement ID and object type + * + * We use a continuum of indeces until we get to the object datablocks + * and we then make room for the object types. + */ +static int tree_element_id_type_to_index(TreeElement *te) +{ + TreeStoreElem *tselem = TREESTORE(te); + + const int id_index = tselem->type == 0 ? BKE_idcode_to_index(te->idcode) : INDEX_ID_GR; + if (id_index < INDEX_ID_OB) { + return id_index; + } + else if (id_index == INDEX_ID_OB) { + const Object *ob = (Object *)tselem->id; + return INDEX_ID_OB + ob->type; + } + else { + return id_index + OB_TYPE_MAX; + } +} + +static void outliner_draw_iconrow( + bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, + ListBase *lb, int level, int xmax, int *offsx, int ys, float alpha_fac) +{ + eOLDrawState active; + const Object *obact = OBACT(view_layer); + + struct { + eOLDrawState active[INDEX_ID_MAX + OB_TYPE_MAX]; + int num_elements[INDEX_ID_MAX + OB_TYPE_MAX]; + TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX]; + } data = { + .active = {0}, + .num_elements = {0}, + .tree_element = {NULL}, + }; + + for (TreeElement *te = lb->first; te; te = te->next) { /* exit drawing early */ if ((*offsx) - UI_UNIT_X > xmax) break; - tselem = TREESTORE(te); + TreeStoreElem *tselem = TREESTORE(te); /* object hierarchy always, further constrained on level */ if (level < 1 || (tselem->type == 0 && te->idcode == ID_OB)) { - /* active blocks get white circle */ if (tselem->type == 0) { if (te->idcode == ID_OB) { - active = (OBACT == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE; + active = (OBACT(view_layer) == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE; } - else if (scene->obedit && scene->obedit->data == tselem->id) { + else if (is_object_data_in_editmode(tselem->id, obact)) { active = OL_DRAWSEL_NORMAL; } else { - active = tree_element_active(C, scene, soops, te, OL_SETSEL_NONE, false); + active = tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false); } } else { - active = tree_element_type_active(NULL, scene, soops, te, tselem, OL_SETSEL_NONE, false); + active = tree_element_type_active(C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false); } - if (active != OL_DRAWSEL_NONE) { - float ufac = UI_UNIT_X / 20.0f; - - UI_draw_roundbox_corner_set(UI_CNR_ALL); - glColor4ub(255, 255, 255, 100); - UI_draw_roundbox( - (float) *offsx + 1.0f * ufac, - (float)ys + 1.0f * ufac, - (float)*offsx + UI_UNIT_X - 1.0f * ufac, - (float)ys + UI_UNIT_Y - ufac, - (float)UI_UNIT_Y / 2.0f - ufac); - glEnable(GL_BLEND); /* roundbox disables */ + if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) { + outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1); + } + else { + const int index = tree_element_id_type_to_index(te); + data.num_elements[index]++; + if ((data.tree_element[index] == NULL) || + (active > data.active[index])) + { + data.tree_element[index] = te; + } + data.active[index] = MAX2(active, data.active[index]); } - - tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.5f); - te->xs = *offsx; - te->ys = ys; - te->xend = (short)*offsx + UI_UNIT_X; - te->flag |= TE_ICONROW; // for click - - (*offsx) += UI_UNIT_X; } /* this tree element always has same amount of branches, so don't draw */ - if (tselem->type != TSE_R_LAYER) - outliner_draw_iconrow(C, block, scene, soops, &te->subtree, level + 1, xmax, offsx, ys); + if (tselem->type != TSE_R_LAYER) { + outliner_draw_iconrow( + C, block, fstyle, scene, view_layer, soops, + &te->subtree, level + 1, xmax, offsx, ys, alpha_fac); + } } + for (int i = 0; i < INDEX_ID_MAX; i++) { + const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1; + /* See tree_element_id_type_to_index for the index logic. */ + int index_base = i; + if (i > INDEX_ID_OB) { + index_base += OB_TYPE_MAX; + } + for (int j = 0; j < num_subtypes; j++) { + const int index = index_base + j; + if (data.num_elements[index] != 0) { + outliner_draw_iconrow_doit(block, + data.tree_element[index], + fstyle, + xmax, offsx, ys, alpha_fac, + data.active[index], + data.num_elements[index]); + } + } + } } /* closed tree element */ @@ -1423,9 +1538,12 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta { TreeElement *ten; - /* store coord and continue, we need coordinates for elements outside view too */ - te->xs = startx; - te->ys = starty; + /* closed items may be displayed in row of parent, don't change their coordinate! */ + if ((te->flag & TE_ICONROW) == 0) { + /* store coord and continue, we need coordinates for elements outside view too */ + te->xs = startx; + te->ys = starty; + } for (ten = te->subtree.first; ten; ten = ten->next) { outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty); @@ -1434,24 +1552,28 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta static void outliner_draw_tree_element( - bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ARegion *ar, SpaceOops *soops, - TreeElement *te, int startx, int *starty, TreeElement **te_edit) + bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ViewLayer *view_layer, + ARegion *ar, SpaceOops *soops, TreeElement *te, bool draw_grayed_out, + int startx, int *starty, TreeElement **te_edit, TreeElement **te_floating) { - TreeElement *ten; TreeStoreElem *tselem; float ufac = UI_UNIT_X / 20.0f; int offsx = 0; eOLDrawState active = OL_DRAWSEL_NONE; - + float color[4]; tselem = TREESTORE(te); if (*starty + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && *starty <= ar->v2d.cur.ymax) { + const float alpha_fac = ((te->flag & TE_DISABLED) || draw_grayed_out) ? 0.5f : 1.0f; + const float alpha = 0.5f * alpha_fac; int xmax = ar->v2d.cur.xmax; - unsigned char alpha = 128; if ((tselem->flag & TSE_TEXTBUT) && (*te_edit == NULL)) { *te_edit = te; } + if ((te->drag_data != NULL) && (*te_floating == NULL)) { + *te_floating = te; + } /* icons can be ui buts, we don't want it to overlap with restrict */ if ((soops->flag & SO_HIDE_RESTRICTCOLS) == 0) @@ -1459,151 +1581,145 @@ static void outliner_draw_tree_element( glEnable(GL_BLEND); - /* start by highlighting search matches - * we don't expand items when searching in the datablocks but we - * still want to highlight any filter matches. - */ - if ((SEARCHING_OUTLINER(soops) || (soops->outlinevis == SO_DATABLOCKS && soops->search_string[0] != 0)) && - (tselem->flag & TSE_SEARCHMATCH)) - { - char col[4]; - UI_GetThemeColorType4ubv(TH_MATCH, SPACE_OUTLINER, col); - col[3] = alpha; - glColor4ubv((GLubyte *)col); - glRecti(startx, *starty + 1, ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1); - } - /* colors for active/selected data */ if (tselem->type == 0) { - + const Object *obact = OBACT(view_layer); if (te->idcode == ID_SCE) { if (tselem->id == (ID *)scene) { - glColor4ub(255, 255, 255, alpha); - active = OL_DRAWSEL_ACTIVE; - } - } - else if (te->idcode == ID_GR) { - Group *gr = (Group *)tselem->id; - if (group_select_flag(gr)) { - char col[4]; - UI_GetThemeColorType4ubv(TH_SELECT, SPACE_VIEW3D, col); - col[3] = alpha; - glColor4ubv((GLubyte *)col); - + rgba_float_args_set(color, 1.0f, 1.0f, 1.0f, alpha); active = OL_DRAWSEL_ACTIVE; } } else if (te->idcode == ID_OB) { Object *ob = (Object *)tselem->id; + Base *base = (Base *)te->directdata; + const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0); - if (ob == OBACT || (ob->flag & SELECT)) { + if (ob == obact || is_selected) { char col[4] = {0, 0, 0, 0}; /* outliner active ob: always white text, circle color now similar to view3d */ active = OL_DRAWSEL_ACTIVE; - if (ob == OBACT) { - if (ob->flag & SELECT) { + if (ob == obact) { + if (is_selected) { UI_GetThemeColorType4ubv(TH_ACTIVE, SPACE_VIEW3D, col); col[3] = alpha; } active = OL_DRAWSEL_NORMAL; } - else if (ob->flag & SELECT) { + else if (is_selected) { UI_GetThemeColorType4ubv(TH_SELECT, SPACE_VIEW3D, col); col[3] = alpha; } - - glColor4ubv((GLubyte *)col); + rgba_float_args_set(color, (float)col[0] / 255, (float)col[1] / 255, (float)col[2] / 255, alpha); } - } - else if (scene->obedit && scene->obedit->data == tselem->id) { - glColor4ub(255, 255, 255, alpha); + else if (is_object_data_in_editmode(tselem->id, obact)) { + rgba_float_args_set(color, 1.0f, 1.0f, 1.0f, alpha); active = OL_DRAWSEL_ACTIVE; } else { - if (tree_element_active(C, scene, soops, te, OL_SETSEL_NONE, false)) { - glColor4ub(220, 220, 255, alpha); + if (tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false)) { + rgba_float_args_set(color, 0.85f, 0.85f, 1.0f, alpha); active = OL_DRAWSEL_ACTIVE; } } } else { - if (tree_element_type_active(NULL, scene, soops, te, tselem, OL_SETSEL_NONE, false) != OL_DRAWSEL_NONE) { - active = OL_DRAWSEL_ACTIVE; - } - glColor4ub(220, 220, 255, alpha); + active = tree_element_type_active(C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false); + rgba_float_args_set(color, 0.85f, 0.85f, 1.0f, alpha); } /* active circle */ if (active != OL_DRAWSEL_NONE) { UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox( + UI_draw_roundbox_aa( + true, (float)startx + UI_UNIT_X + 1.0f * ufac, (float)*starty + 1.0f * ufac, (float)startx + 2.0f * UI_UNIT_X - 1.0f * ufac, (float)*starty + UI_UNIT_Y - 1.0f * ufac, - UI_UNIT_Y / 2.0f - 1.0f * ufac); + UI_UNIT_Y / 2.0f - 1.0f * ufac, color); glEnable(GL_BLEND); /* roundbox disables it */ te->flag |= TE_ACTIVE; // for lookup in display hierarchies } - /* open/close icon, only when sublevels, except for scene */ - if (te->subtree.first || (tselem->type == 0 && te->idcode == ID_SCE) || (te->flag & TE_LAZY_CLOSED)) { - int icon_x; - icon_x = startx; + if (tselem->type == TSE_VIEW_COLLECTION_BASE) { + /* Scene collection in view layer can't expand/collapse. */ + } + else if (te->subtree.first || (tselem->type == 0 && te->idcode == ID_SCE) || (te->flag & TE_LAZY_CLOSED)) { + /* open/close icon, only when sublevels, except for scene */ + int icon_x = startx; // icons a bit higher - if (TSELEM_OPEN(tselem, soops)) - UI_icon_draw((float)icon_x + 2 * ufac, (float)*starty + 1 * ufac, ICON_DISCLOSURE_TRI_DOWN); - else - UI_icon_draw((float)icon_x + 2 * ufac, (float)*starty + 1 * ufac, ICON_DISCLOSURE_TRI_RIGHT); + if (TSELEM_OPEN(tselem, soops)) { + UI_icon_draw_alpha( + (float)icon_x + 2 * ufac, (float)*starty + 1 * ufac, ICON_DISCLOSURE_TRI_DOWN, + alpha_fac); + } + else { + UI_icon_draw_alpha( + (float)icon_x + 2 * ufac, (float)*starty + 1 * ufac, ICON_DISCLOSURE_TRI_RIGHT, + alpha_fac); + } } offsx += UI_UNIT_X; /* datatype icon */ - if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM))) { - - tselem_draw_icon(block, xmax, (float)startx + offsx, (float)*starty, tselem, te, 1.0f); - + if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE))) { + tselem_draw_icon(block, xmax, (float)startx + offsx, (float)*starty, tselem, te, alpha_fac, true); offsx += UI_UNIT_X + 2 * ufac; } else offsx += 2 * ufac; - if (tselem->type == 0 && ID_IS_LINKED(tselem->id)) { - glPixelTransferf(GL_ALPHA_SCALE, 0.5f); + if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_LINKED(tselem->id)) { if (tselem->id->tag & LIB_TAG_MISSING) { - UI_icon_draw((float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_BROKEN); + UI_icon_draw_alpha( + (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_BROKEN, + alpha_fac); } else if (tselem->id->tag & LIB_TAG_INDIRECT) { - UI_icon_draw((float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_INDIRECT); + UI_icon_draw_alpha( + (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_INDIRECT, + alpha_fac); } else { - UI_icon_draw((float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_DIRECT); + UI_icon_draw_alpha( + (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_DIRECT, + alpha_fac); } - glPixelTransferf(GL_ALPHA_SCALE, 1.0f); + offsx += UI_UNIT_X + 2 * ufac; + } + else if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_STATIC_OVERRIDE(tselem->id)) { + UI_icon_draw_alpha( + (float)startx + offsx + 2 * ufac, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_OVERRIDE, + alpha_fac); offsx += UI_UNIT_X + 2 * ufac; } glDisable(GL_BLEND); /* name */ if ((tselem->flag & TSE_TEXTBUT) == 0) { + unsigned char text_col[4]; + if (active == OL_DRAWSEL_NORMAL) { - UI_ThemeColor(TH_TEXT_HI); + UI_GetThemeColor4ubv(TH_TEXT_HI, text_col); } else if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { - UI_ThemeColorBlend(TH_BACK, TH_TEXT, 0.75f); + UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.75f, text_col); + text_col[3] = 255; } else { - UI_ThemeColor(TH_TEXT); + UI_GetThemeColor4ubv(TH_TEXT, text_col); } + text_col[3] *= alpha_fac; - UI_fontstyle_draw_simple(fstyle, startx + offsx, *starty + 5 * ufac, te->name); + UI_fontstyle_draw_simple(fstyle, startx + offsx, *starty + 5 * ufac, te->name, text_col); } offsx += (int)(UI_UNIT_X + UI_fontstyle_string_width(fstyle, te->name)); @@ -1614,24 +1730,34 @@ static void outliner_draw_tree_element( if (tselem->type == 0 && te->idcode == ID_SCE) { /* pass */ } + /* this tree element always has same amount of branches, so don't draw */ else if (tselem->type != TSE_R_LAYER) { - /* this tree element always has same amount of branches, so don't draw */ - int tempx = startx + offsx; - /* divider */ - UI_ThemeColorShade(TH_BACK, -40); - glRecti(tempx - 10.0f * ufac, - *starty + 4.0f * ufac, - tempx - 8.0f * ufac, - *starty + UI_UNIT_Y - 4.0f * ufac); - glEnable(GL_BLEND); - glPixelTransferf(GL_ALPHA_SCALE, 0.5); - outliner_draw_iconrow(C, block, scene, soops, &te->subtree, 0, xmax, &tempx, *starty); + /* divider */ + { + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + unsigned char col[4]; + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + UI_GetThemeColorShade4ubv(TH_BACK, -40, col); + col[3] *= alpha_fac; + + immUniformColor4ubv(col); + immRecti(pos, tempx - 10.0f * ufac, + *starty + 4.0f * ufac, + tempx - 8.0f * ufac, + *starty + UI_UNIT_Y - 4.0f * ufac); + immUnbindProgram(); + } + + outliner_draw_iconrow( + C, block, fstyle, scene, view_layer, soops, &te->subtree, 0, xmax, &tempx, + *starty, alpha_fac); - glPixelTransferf(GL_ALPHA_SCALE, 1.0); glDisable(GL_BLEND); } } @@ -1645,12 +1771,18 @@ static void outliner_draw_tree_element( if (TSELEM_OPEN(tselem, soops)) { *starty -= UI_UNIT_Y; - for (ten = te->subtree.first; ten; ten = ten->next) { - outliner_draw_tree_element(C, block, fstyle, scene, ar, soops, ten, startx + UI_UNIT_X, starty, te_edit); + for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) { + /* check if element needs to be drawn grayed out, but also gray out + * childs of a grayed out parent (pass on draw_grayed_out to childs) */ + bool draw_childs_grayed_out = draw_grayed_out || (ten->drag_data != NULL); + outliner_draw_tree_element( + C, block, fstyle, scene, view_layer, + ar, soops, ten, draw_childs_grayed_out, + startx + UI_UNIT_X, starty, te_edit, te_floating); } } else { - for (ten = te->subtree.first; ten; ten = ten->next) { + for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) { outliner_set_coord_tree_element(ten, startx, *starty); } @@ -1658,115 +1790,286 @@ static void outliner_draw_tree_element( } } -static void outliner_draw_hierarchy(SpaceOops *soops, ListBase *lb, int startx, int *starty) +static void outliner_draw_tree_element_floating( + const ARegion *ar, const TreeElement *te_floating) { - TreeElement *te; - TreeStoreElem *tselem; + const TreeElement *te_insert = te_floating->drag_data->insert_handle; + const int line_width = 2; + + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + int coord_y = te_insert->ys; + int coord_x = te_insert->xs; + float col[4]; + + if (te_insert == te_floating) { + /* don't draw anything */ + return; + } + + UI_GetThemeColorShade4fv(TH_BACK, -40, col); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + glEnable(GL_BLEND); + + 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; + } + immUniformColor4fv(col); + glLineWidth(line_width); + + immBegin(GWN_PRIM_LINE_STRIP, 2); + immVertex2f(pos, coord_x, coord_y); + immVertex2f(pos, ar->v2d.cur.xmax, coord_y); + immEnd(); + } + else { + BLI_assert(te_floating->drag_data->insert_type == TE_INSERT_INTO); + immUniformColor3fvAlpha(col, col[3] * 0.5f); + + immBegin(GWN_PRIM_TRI_STRIP, 4); + immVertex2f(pos, coord_x, coord_y + UI_UNIT_Y); + immVertex2f(pos, coord_x, coord_y); + immVertex2f(pos, ar->v2d.cur.xmax, coord_y + UI_UNIT_Y); + immVertex2f(pos, ar->v2d.cur.xmax, coord_y); + immEnd(); + } + + glDisable(GL_BLEND); + immUnbindProgram(); +} + +static void outliner_draw_hierarchy_lines_recursive( + unsigned pos, SpaceOops *soops, ListBase *lb, int startx, + const unsigned char col[4], bool draw_grayed_out, + int *starty) +{ + TreeElement *te, *te_vertical_line_last = NULL; int y1, y2; - if (BLI_listbase_is_empty(lb)) return; + if (BLI_listbase_is_empty(lb)) { + return; + } + + const unsigned char grayed_alpha = col[3] / 2; - y1 = y2 = *starty; /* for vertical lines between objects */ + /* For vertical lines between objects. */ + y1 = y2 = *starty; for (te = lb->first; te; te = te->next) { - y2 = *starty; - tselem = TREESTORE(te); + bool draw_childs_grayed_out = draw_grayed_out || (te->drag_data != NULL); + TreeStoreElem *tselem = TREESTORE(te); + + if (draw_childs_grayed_out) { + immUniformColor3ubvAlpha(col, grayed_alpha); + } + else { + immUniformColor4ubv(col); + } + + /* Horizontal Line? */ + if (tselem->type == 0 && (te->idcode == ID_OB || te->idcode == ID_SCE)) { + immRecti(pos, startx, *starty, startx + UI_UNIT_X, *starty - 1); - /* horizontal line? */ - if (tselem->type == 0 && (te->idcode == ID_OB || te->idcode == ID_SCE)) - glRecti(startx, *starty, startx + UI_UNIT_X, *starty - 1); + /* Vertical Line? */ + if (te->idcode == ID_OB) { + te_vertical_line_last = te; + y2 = *starty; + } + } *starty -= UI_UNIT_Y; if (TSELEM_OPEN(tselem, soops)) - outliner_draw_hierarchy(soops, &te->subtree, startx + UI_UNIT_X, starty); + outliner_draw_hierarchy_lines_recursive( + pos, soops, &te->subtree, startx + UI_UNIT_X, + col, draw_childs_grayed_out, starty); } - /* vertical line */ - te = lb->last; - if (te->parent || lb->first != lb->last) { - tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == ID_OB) { + if (draw_grayed_out) { + immUniformColor3ubvAlpha(col, grayed_alpha); + } + else { + immUniformColor4ubv(col); + } - glRecti(startx, y1 + UI_UNIT_Y, startx + 1, y2); - } + /* Vertical line. */ + te = te_vertical_line_last; + if ((te != NULL) && (te->parent || lb->first != lb->last)) { + immRecti(pos, startx, y1 + UI_UNIT_Y, startx + 1, y2); } } -static void outliner_draw_struct_marks(ARegion *ar, SpaceOops *soops, ListBase *lb, int *starty) +static void outliner_draw_hierarchy_lines(SpaceOops *soops, ListBase *lb, int startx, int *starty) { - TreeElement *te; - TreeStoreElem *tselem; + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + unsigned char col[4]; - for (te = lb->first; te; te = te->next) { - tselem = TREESTORE(te); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.4f, col); + col[3] = 255; + + glEnable(GL_BLEND); + outliner_draw_hierarchy_lines_recursive(pos, soops, lb, startx, col, false, starty); + glDisable(GL_BLEND); + + immUnbindProgram(); +} + +static void outliner_draw_struct_marks(ARegion *ar, SpaceOops *soops, ListBase *lb, int *starty) +{ + for (TreeElement *te = lb->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); /* selection status */ - if (TSELEM_OPEN(tselem, soops)) - if (tselem->type == TSE_RNA_STRUCT) - glRecti(0, *starty + 1, (int)ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1); + if (TSELEM_OPEN(tselem, soops)) { + if (tselem->type == TSE_RNA_STRUCT) { + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immThemeColorShadeAlpha(TH_BACK, -15, -200); + immRecti(pos, 0, *starty + 1, (int)ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1); + immUnbindProgram(); + } + } *starty -= UI_UNIT_Y; if (TSELEM_OPEN(tselem, soops)) { outliner_draw_struct_marks(ar, soops, &te->subtree, starty); - if (tselem->type == TSE_RNA_STRUCT) - fdrawline(0, (float)*starty + UI_UNIT_Y, ar->v2d.cur.xmax, (float)*starty + UI_UNIT_Y); + if (tselem->type == TSE_RNA_STRUCT) { + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immThemeColorShadeAlpha(TH_BACK, -15, -200); + + immBegin(GWN_PRIM_LINES, 2); + immVertex2f(pos, 0, (float)*starty + UI_UNIT_Y); + immVertex2f(pos, ar->v2d.cur.xmax, (float)*starty + UI_UNIT_Y); + immEnd(); + + immUnbindProgram(); + } } } } -static void outliner_draw_selection(ARegion *ar, SpaceOops *soops, ListBase *lb, int *starty) +static void outliner_draw_highlights_recursive( + unsigned pos, const ARegion *ar, const SpaceOops *soops, const ListBase *lb, + const float col_selection[4], const float col_highlight[4], const float col_searchmatch[4], + int start_x, int *io_start_y) { - TreeElement *te; - TreeStoreElem *tselem; + const bool is_searching = ( + SEARCHING_OUTLINER(soops) || + (soops->outlinevis == SO_DATA_API && + soops->search_string[0] != 0)); - for (te = lb->first; te; te = te->next) { - tselem = TREESTORE(te); + for (TreeElement *te = lb->first; te; te = te->next) { + const TreeStoreElem *tselem = TREESTORE(te); + const int start_y = *io_start_y; /* selection status */ if (tselem->flag & TSE_SELECTED) { - glRecti(0, *starty + 1, (int)ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1); + immUniformColor4fv(col_selection); + immRecti(pos, 0, start_y + 1, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1); + } + + /* search match highlights + * we don't expand items when searching in the datablocks but we + * still want to highlight any filter matches. */ + if (is_searching && (tselem->flag & TSE_SEARCHMATCH)) { + immUniformColor4fv(col_searchmatch); + immRecti(pos, start_x, start_y + 1, ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1); + } + + /* mouse hover highlights */ + if ((tselem->flag & TSE_HIGHLIGHTED) || (te->drag_data != NULL)) { + immUniformColor4fv(col_highlight); + immRecti(pos, 0, start_y + 1, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y - 1); + } + + *io_start_y -= UI_UNIT_Y; + if (TSELEM_OPEN(tselem, soops)) { + outliner_draw_highlights_recursive( + pos, ar, soops, &te->subtree, col_selection, col_highlight, col_searchmatch, + start_x + UI_UNIT_X, io_start_y); } - *starty -= UI_UNIT_Y; - if (TSELEM_OPEN(tselem, soops)) outliner_draw_selection(ar, soops, &te->subtree, starty); } } +static void outliner_draw_highlights(ARegion *ar, SpaceOops *soops, int startx, int *starty) +{ + const float col_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f}; + float col_selection[4], col_searchmatch[4]; + + UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col_selection); + col_selection[3] = 1.0f; /* no alpha */ + UI_GetThemeColor4fv(TH_MATCH, col_searchmatch); + col_searchmatch[3] = 0.5f; + + glEnable(GL_BLEND); + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + outliner_draw_highlights_recursive( + pos, ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch, + startx, starty); + immUnbindProgram(); + glDisable(GL_BLEND); +} -static void outliner_draw_tree(bContext *C, uiBlock *block, Scene *scene, ARegion *ar, - SpaceOops *soops, TreeElement **te_edit) +static void outliner_draw_tree( + bContext *C, uiBlock *block, Scene *scene, ViewLayer *view_layer, + ARegion *ar, SpaceOops *soops, const bool has_restrict_icons, + TreeElement **te_edit) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - TreeElement *te; + TreeElement *te_floating = NULL; int starty, startx; - float col[3]; - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // only once + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // only once - if (ELEM(soops->outlinevis, SO_DATABLOCKS, SO_USERDEF)) { + if (soops->outlinevis == SO_DATA_API) { /* struct marks */ - UI_ThemeColorShadeAlpha(TH_BACK, -15, -200); - //UI_ThemeColorShade(TH_BACK, -20); starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET; outliner_draw_struct_marks(ar, soops, &soops->tree, &starty); } - /* always draw selection fill before hierarchy */ - UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col); - glColor3fv(col); + /* draw highlights before hierarchy */ starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET; - outliner_draw_selection(ar, soops, &soops->tree, &starty); + startx = 0; + outliner_draw_highlights(ar, soops, startx, &starty); + + /* set scissor so tree elements or lines can't overlap restriction icons */ + GLfloat scissor[4] = {0}; + if (has_restrict_icons) { + int mask_x = BLI_rcti_size_x(&ar->v2d.mask) - (int)OL_TOGW + 1; + CLAMP_MIN(mask_x, 0); + + glGetFloatv(GL_SCISSOR_BOX, scissor); + glScissor(0, 0, mask_x, ar->winy); + } // gray hierarchy lines - UI_ThemeColorBlend(TH_BACK, TH_TEXT, 0.4f); + starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y / 2 - OL_Y_OFFSET; startx = UI_UNIT_X / 2 - 1.0f; - outliner_draw_hierarchy(soops, &soops->tree, startx, &starty); + outliner_draw_hierarchy_lines(soops, &soops->tree, startx, &starty); // items themselves starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET; startx = 0; - for (te = soops->tree.first; te; te = te->next) { - outliner_draw_tree_element(C, block, fstyle, scene, ar, soops, te, startx, &starty, te_edit); + for (TreeElement *te = soops->tree.first; te; te = te->next) { + outliner_draw_tree_element( + C, block, fstyle, scene, view_layer, + ar, soops, te, te->drag_data != NULL, + startx, &starty, te_edit, &te_floating); + } + if (te_floating && te_floating->drag_data->insert_handle) { + outliner_draw_tree_element_floating(ar, te_floating); + } + + if (has_restrict_icons) { + /* reset scissor */ + glScissor(UNPACK4(scissor)); } } @@ -1775,53 +2078,60 @@ static void outliner_back(ARegion *ar) { int ystart; - UI_ThemeColorShade(TH_BACK, 6); ystart = (int)ar->v2d.tot.ymax; ystart = UI_UNIT_Y * (ystart / (UI_UNIT_Y)) - OL_Y_OFFSET; - while (ystart + 2 * UI_UNIT_Y > ar->v2d.cur.ymin) { - glRecti(0, ystart, (int)ar->v2d.cur.xmax, ystart + UI_UNIT_Y); - ystart -= 2 * UI_UNIT_Y; + Gwn_VertFormat *format = immVertexFormat(); + unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShade(TH_BACK, 6); + + const float x1 = 0.0f, x2 = ar->v2d.cur.xmax; + float y1 = ystart, y2; + int tot = (int)floor(ystart - ar->v2d.cur.ymin + 2 * UI_UNIT_Y) / (2 * UI_UNIT_Y); + + if (tot > 0) { + immBegin(GWN_PRIM_TRIS, 6 * tot); + while (tot--) { + y1 -= 2 * UI_UNIT_Y; + y2 = y1 + UI_UNIT_Y; + immVertex2f(pos, x1, y1); + immVertex2f(pos, x2, y1); + immVertex2f(pos, x2, y2); + + immVertex2f(pos, x1, y1); + immVertex2f(pos, x2, y2); + immVertex2f(pos, x1, y2); + } + immEnd(); } + immUnbindProgram(); } static void outliner_draw_restrictcols(ARegion *ar) { - int ystart; + glLineWidth(1.0f); - /* background underneath */ - UI_ThemeColor(TH_BACK); - glRecti((int)(ar->v2d.cur.xmax - OL_TOGW), - (int)(ar->v2d.cur.ymin - 1), (int)ar->v2d.cur.xmax, (int)ar->v2d.cur.ymax); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColorShadeAlpha(TH_BACK, -15, -200); + immBegin(GWN_PRIM_LINES, 8); - UI_ThemeColorShade(TH_BACK, 6); - ystart = (int)ar->v2d.tot.ymax; - ystart = UI_UNIT_Y * (ystart / (UI_UNIT_Y)) - OL_Y_OFFSET; + immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), (int)ar->v2d.cur.ymax); + immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), (int)ar->v2d.cur.ymin); - while (ystart + 2 * UI_UNIT_Y > ar->v2d.cur.ymin) { - glRecti((int)ar->v2d.cur.xmax - OL_TOGW, ystart, (int)ar->v2d.cur.xmax, ystart + UI_UNIT_Y); - ystart -= 2 * UI_UNIT_Y; - } + immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymax); + immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymin); + + immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), (int)ar->v2d.cur.ymax); + immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), (int)ar->v2d.cur.ymin); - UI_ThemeColorShadeAlpha(TH_BACK, -15, -200); - - /* view */ - sdrawline((int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), - (int)ar->v2d.cur.ymax, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), - (int)ar->v2d.cur.ymin); - - /* render */ - sdrawline((int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), - (int)ar->v2d.cur.ymax, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), - (int)ar->v2d.cur.ymin); - - /* render */ - sdrawline((int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), - (int)ar->v2d.cur.ymax, - (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), - (int)ar->v2d.cur.ymin); + immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), (int)ar->v2d.cur.ymax); + immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_RENDERX), (int)ar->v2d.cur.ymin); + + immEnd(); + immUnbindProgram(); } /* ****************************************************** */ @@ -1831,19 +2141,21 @@ void draw_outliner(const bContext *C) { Main *mainvar = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); ARegion *ar = CTX_wm_region(C); View2D *v2d = &ar->v2d; SpaceOops *soops = CTX_wm_space_outliner(C); uiBlock *block; int sizey = 0, sizex = 0, sizex_rna = 0; TreeElement *te_edit = NULL; + bool has_restrict_icons; - outliner_build_tree(mainvar, scene, soops); // always + outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always /* get extents of data */ outliner_height(soops, &soops->tree, &sizey); - if (ELEM(soops->outlinevis, SO_DATABLOCKS, SO_USERDEF)) { + if (soops->outlinevis == SO_DATA_API) { /* RNA has two columns: * - column 1 is (max_width + OL_RNA_COL_SPACEX) or * (OL_RNA_COL_X), whichever is wider... @@ -1858,6 +2170,7 @@ void draw_outliner(const bContext *C) /* get width of data (for setting 'tot' rect, this is column 1 + column 2 + a bit extra) */ sizex = sizex_rna + OL_RNA_COL_SIZEX + 50; + has_restrict_icons = false; } else { /* width must take into account restriction columns (if visible) so that entries will still be visible */ @@ -1867,9 +2180,11 @@ void draw_outliner(const bContext *C) /* constant offset for restriction columns */ // XXX this isn't that great yet... - if ((soops->flag & SO_HIDE_RESTRICTCOLS) == 0) + if ((soops->flag & SO_HIDE_RESTRICTCOLS) == 0) { sizex += OL_TOGW * 3; + } + has_restrict_icons = !(soops->flag & SO_HIDE_RESTRICTCOLS); } /* adds vertical offset */ @@ -1886,24 +2201,35 @@ void draw_outliner(const bContext *C) /* draw outliner stuff (background, hierarchy lines and names) */ outliner_back(ar); block = UI_block_begin(C, ar, __func__, UI_EMBOSS); - outliner_draw_tree((bContext *)C, block, scene, ar, soops, &te_edit); + outliner_draw_tree( + (bContext *)C, block, scene, view_layer, + ar, soops, has_restrict_icons, &te_edit); - if (ELEM(soops->outlinevis, SO_DATABLOCKS, SO_USERDEF)) { + /* Default to no emboss for outliner UI. */ + UI_block_emboss_set(block, UI_EMBOSS_NONE); + + if (soops->outlinevis == SO_DATA_API) { /* draw rna buttons */ outliner_draw_rnacols(ar, sizex_rna); + + UI_block_emboss_set(block, UI_EMBOSS); outliner_draw_rnabuts(block, ar, soops, sizex_rna, &soops->tree); + UI_block_emboss_set(block, UI_EMBOSS_NONE); } - else if ((soops->outlinevis == SO_ID_ORPHANS) && !(soops->flag & SO_HIDE_RESTRICTCOLS)) { + else if ((soops->outlinevis == SO_ID_ORPHANS) && has_restrict_icons) { /* draw user toggle columns */ outliner_draw_restrictcols(ar); outliner_draw_userbuts(block, ar, soops, &soops->tree); } - else if (!(soops->flag & SO_HIDE_RESTRICTCOLS)) { + else if (has_restrict_icons) { /* draw restriction columns */ outliner_draw_restrictcols(ar); - outliner_draw_restrictbuts(block, scene, ar, soops, &soops->tree); + + outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &soops->tree); } + UI_block_emboss_set(block, UI_EMBOSS); + /* draw edit buttons if nessecery */ if (te_edit) { outliner_buttons(C, block, ar, te_edit); @@ -1911,7 +2237,4 @@ void draw_outliner(const bContext *C) UI_block_end(C, block); UI_block_draw(C, block); - - /* clear flag that allows quick redraws */ - soops->storeflag &= ~SO_TREESTORE_REDRAW; } diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index d547b419d48..990d50ae42a 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -50,9 +50,10 @@ #include "BLT_translation.h" #include "BKE_animsys.h" +#include "BKE_collection.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" #include "BKE_idcode.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_library_query.h" #include "BKE_library_remap.h" @@ -61,7 +62,9 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_material.h" -#include "BKE_group.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "../blenloader/BLO_readfile.h" @@ -147,8 +150,45 @@ TreeElement *outliner_dropzone_find(const SpaceOops *soops, const float fmval[2] return NULL; } + /* ************************************************************** */ -/* Click Activated */ + +/* Highlight --------------------------------------------------- */ + +static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]); + + TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, my); + bool changed = false; + + if (!hovered_te || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED)) { + changed = outliner_set_flag(&soops->tree, TSE_HIGHLIGHTED, false); + if (hovered_te) { + hovered_te->store_elem->flag |= TSE_HIGHLIGHTED; + changed = true; + } + } + + if (changed) { + ED_region_tag_redraw_no_rebuild(ar); + } + + return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); +} + +void OUTLINER_OT_highlight_update(wmOperatorType *ot) +{ + ot->name = "Update Highlight"; + ot->idname = "OUTLINER_OT_highlight_update"; + ot->description = "Update the item highlight based on the current mouse position"; + + ot->invoke = outliner_highlight_update; + + ot->poll = ED_operator_outliner_active; +} /* Toggle Open/Closed ------------------------------------------- */ @@ -213,22 +253,85 @@ void OUTLINER_OT_item_openclose(wmOperatorType *ot) RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items"); } +/* -------------------------------------------------------------------- */ +/** \name Object Mode Enter/Exit + * \{ */ + +static void item_object_mode_enter_exit( + bContext *C, ReportList *reports, Object *ob, + bool enter) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obact = OBACT(view_layer); + + if ((ob->type != obact->type) || ID_IS_LINKED(ob->data)) { + return; + } + if (((ob->mode & obact->mode) != 0) == enter) { + return; + } + + if (ob == obact) { + BKE_report(reports, RPT_WARNING, "Active object mode not changed"); + return; + } + + Base *base = BKE_view_layer_base_find(view_layer, ob); + if (base == NULL) { + return; + } + Scene *scene = CTX_data_scene(C); + outliner_object_mode_toggle(C, scene, view_layer, base); +} + +void item_object_mode_enter_cb( + bContext *C, ReportList *reports, Scene *UNUSED(scene), TreeElement *UNUSED(te), + TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) +{ + Object *ob = (Object *)tselem->id; + item_object_mode_enter_exit(C, reports, ob, true); +} + +void item_object_mode_exit_cb( + bContext *C, ReportList *reports, Scene *UNUSED(scene), TreeElement *UNUSED(te), + TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) +{ + Object *ob = (Object *)tselem->id; + item_object_mode_enter_exit(C, reports, ob, false); +} + +/** \} */ + /* Rename --------------------------------------------------- */ -static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem, ReportList *reports) +static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem, + ReportList *reports) { + bool add_textbut = false; + /* can't rename rna datablocks entries or listbases */ - if (ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE)) { + if (ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE, TSE_SCENE_OBJECTS_BASE)) { /* do nothing */; } else if (ELEM(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, - TSE_DRIVER_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS)) + TSE_DRIVER_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_SCENE_COLLECTION_BASE, + TSE_VIEW_COLLECTION_BASE)) { BKE_report(reports, RPT_WARNING, "Cannot edit builtin name"); } else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { BKE_report(reports, RPT_WARNING, "Cannot edit sequence name"); } + else if (outliner_is_collection_tree_element(te)) { + Collection *collection = outliner_collection_from_tree_element(te); + + if (collection->flag & COLLECTION_IS_MASTER) { + BKE_report(reports, RPT_WARNING, "Cannot edit name of master collection"); + } + else { + add_textbut = true; + } + } else if (ID_IS_LINKED(tselem->id)) { BKE_report(reports, RPT_WARNING, "Cannot edit external libdata"); } @@ -236,6 +339,10 @@ static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem, BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); } else { + add_textbut = true; + } + + if (add_textbut) { tselem->flag |= TSE_TEXTBUT; ED_region_tag_redraw(ar); } @@ -249,7 +356,8 @@ void item_rename_cb( do_item_rename(ar, te, tselem, reports); } -static int do_outliner_item_rename(ReportList *reports, ARegion *ar, TreeElement *te, const float mval[2]) +static int do_outliner_item_rename(ReportList *reports, ARegion *ar, TreeElement *te, + const float mval[2]) { if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { TreeStoreElem *tselem = TREESTORE(te); @@ -402,7 +510,6 @@ void OUTLINER_OT_id_delete(wmOperatorType *ot) static int outliner_id_remap_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); SpaceOops *soops = CTX_wm_space_outliner(C); const short id_type = (short)RNA_enum_get(op->ptr, "id_type"); @@ -432,7 +539,7 @@ static int outliner_id_remap_exec(bContext *C, wmOperator *op) BKE_main_lib_objects_recalc_all(bmain); /* recreate dependency graph to include new objects */ - DAG_scene_relations_rebuild(bmain, scene); + DEG_relations_tag_update(bmain); /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ GPU_materials_free(bmain); @@ -743,17 +850,34 @@ int outliner_has_one_flag(ListBase *lb, short flag, const int curlevel) return 0; } -void outliner_set_flag(ListBase *lb, short flag, short set) +/** + * Set or unset \a flag for all outliner elements in \a lb and sub-trees. + * \return if any flag was modified. + */ +bool outliner_set_flag(ListBase *lb, short flag, short set) { TreeElement *te; TreeStoreElem *tselem; + bool changed = false; + bool has_flag; for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); - if (set == 0) tselem->flag &= ~flag; - else tselem->flag |= flag; - outliner_set_flag(&te->subtree, flag, set); + has_flag = (tselem->flag & flag); + if (set == 0) { + if (has_flag) { + tselem->flag &= ~flag; + changed = true; + } + } + else if (!has_flag) { + tselem->flag |= flag; + changed = true; + } + changed |= outliner_set_flag(&te->subtree, flag, set); } + + return changed; } /* Restriction Columns ------------------------------- */ @@ -781,181 +905,6 @@ int common_restrict_check(bContext *C, Object *ob) } /* =============================================== */ -/* Restriction toggles */ - -/* Toggle Visibility ---------------------------------------- */ - -void object_toggle_visibility_cb( - bContext *C, ReportList *reports, Scene *scene, TreeElement *te, - TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) -{ - Base *base = (Base *)te->directdata; - Object *ob = (Object *)tselem->id; - - if (ID_IS_LINKED(tselem->id)) { - BKE_report(reports, RPT_WARNING, "Cannot edit external libdata"); - return; - } - - /* add check for edit mode */ - if (!common_restrict_check(C, ob)) return; - - if (base || (base = BKE_scene_base_find(scene, ob))) { - if ((base->object->restrictflag ^= OB_RESTRICT_VIEW)) { - ED_base_object_select(base, BA_DESELECT); - } - } -} - -void group_toggle_visibility_cb( - bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) -{ - Group *group = (Group *)tselem->id; - restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_VIEW); -} - -static int outliner_toggle_visibility_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - Scene *scene = CTX_data_scene(C); - ARegion *ar = CTX_wm_region(C); - - outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_visibility_cb); - - DAG_id_type_tag(bmain, ID_OB); - WM_event_add_notifier(C, NC_SCENE | ND_OB_VISIBLE, scene); - ED_region_tag_redraw(ar); - - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_visibility_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Visibility"; - ot->idname = "OUTLINER_OT_visibility_toggle"; - ot->description = "Toggle the visibility of selected items"; - - /* callbacks */ - ot->exec = outliner_toggle_visibility_exec; - ot->poll = ED_operator_outliner_active_no_editobject; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* Toggle Selectability ---------------------------------------- */ - -void object_toggle_selectability_cb( - bContext *UNUSED(C), ReportList *reports, Scene *scene, TreeElement *te, - TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) -{ - Base *base = (Base *)te->directdata; - - if (ID_IS_LINKED(tselem->id)) { - BKE_report(reports, RPT_WARNING, "Cannot edit external libdata"); - return; - } - - if (base == NULL) base = BKE_scene_base_find(scene, (Object *)tselem->id); - if (base) { - base->object->restrictflag ^= OB_RESTRICT_SELECT; - } -} - -void group_toggle_selectability_cb( - bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) -{ - Group *group = (Group *)tselem->id; - restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_SELECT); -} - -static int outliner_toggle_selectability_exec(bContext *C, wmOperator *op) -{ - SpaceOops *soops = CTX_wm_space_outliner(C); - Scene *scene = CTX_data_scene(C); - ARegion *ar = CTX_wm_region(C); - - outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_selectability_cb); - - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - ED_region_tag_redraw(ar); - - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_selectability_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Selectability"; - ot->idname = "OUTLINER_OT_selectability_toggle"; - ot->description = "Toggle the selectability"; - - /* callbacks */ - ot->exec = outliner_toggle_selectability_exec; - ot->poll = ED_operator_outliner_active_no_editobject; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* Toggle Renderability ---------------------------------------- */ - -void object_toggle_renderability_cb( - bContext *UNUSED(C), ReportList *reports, Scene *scene, TreeElement *te, - TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) -{ - Base *base = (Base *)te->directdata; - - if (ID_IS_LINKED(tselem->id)) { - BKE_report(reports, RPT_WARNING, "Cannot edit external libdata"); - return; - } - - if (base == NULL) base = BKE_scene_base_find(scene, (Object *)tselem->id); - if (base) { - base->object->restrictflag ^= OB_RESTRICT_RENDER; - } -} - -void group_toggle_renderability_cb( - bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) -{ - Group *group = (Group *)tselem->id; - restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_RENDER); -} - -static int outliner_toggle_renderability_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - Scene *scene = CTX_data_scene(C); - - outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_renderability_cb); - - DAG_id_type_tag(bmain, ID_OB); - WM_event_add_notifier(C, NC_SCENE | ND_OB_RENDER, scene); - - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_renderability_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle Renderability"; - ot->idname = "OUTLINER_OT_renderability_toggle"; - ot->description = "Toggle the renderability of selected items"; - - /* callbacks */ - ot->exec = outliner_toggle_renderability_exec; - ot->poll = ED_operator_outliner_active; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* =============================================== */ /* Outliner setting toggles */ /* Toggle Expanded (Outliner) ---------------------------------------- */ @@ -1002,10 +951,9 @@ static int outliner_toggle_selected_exec(bContext *C, wmOperator *UNUSED(op)) else outliner_set_flag(&soops->tree, TSE_SELECTED, 1); - soops->storeflag |= SO_TREESTORE_REDRAW; - + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - ED_region_tag_redraw(ar); + ED_region_tag_redraw_no_rebuild(ar); return OPERATOR_FINISHED; } @@ -1047,7 +995,7 @@ static void outliner_set_coordinates_element_recursive(SpaceOops *soops, TreeEle } /* to retrieve coordinates with redrawing the entire tree */ -static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops) +void outliner_set_coordinates(ARegion *ar, SpaceOops *soops) { TreeElement *te; int starty = (int)(ar->v2d.tot.ymax) - UI_UNIT_Y; @@ -1076,14 +1024,14 @@ static int outliner_open_back(TreeElement *te) static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceOops *so = CTX_wm_space_outliner(C); - Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); ARegion *ar = CTX_wm_region(C); View2D *v2d = &ar->v2d; TreeElement *te; int xdelta, ytop; - Object *obact = OBACT; + Object *obact = OBACT(view_layer); if (!obact) return OPERATOR_CANCELLED; @@ -1126,11 +1074,9 @@ static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) xdelta = (int)(te->xs - v2d->cur.xmin); v2d->cur.xmin += xdelta; v2d->cur.xmax += xdelta; - - so->storeflag |= SO_TREESTORE_REDRAW; } - ED_region_tag_redraw(ar); + ED_region_tag_redraw_no_rebuild(ar); return OPERATOR_FINISHED; } @@ -1162,7 +1108,7 @@ static int outliner_scroll_page_exec(bContext *C, wmOperator *op) ar->v2d.cur.ymin += dy; ar->v2d.cur.ymax += dy; - ED_region_tag_redraw(ar); + ED_region_tag_redraw_no_rebuild(ar); return OPERATOR_FINISHED; } @@ -1286,7 +1232,7 @@ static void outliner_find_panel(Scene *UNUSED(scene), ARegion *ar, SpaceOops *so soops->search_flags = flags; /* redraw */ - soops->storeflag |= SO_TREESTORE_REDRAW; + ED_region_tag_redraw_no_rebuild(ar); } } else { @@ -1444,7 +1390,7 @@ static int ed_operator_outliner_datablocks_active(bContext *C) ScrArea *sa = CTX_wm_area(C); if ((sa) && (sa->spacetype == SPACE_OUTLINER)) { SpaceOops *so = CTX_wm_space_outliner(C); - return (so->outlinevis == SO_DATABLOCKS); + return (so->outlinevis == SO_DATA_API); } return 0; } @@ -2001,9 +1947,9 @@ static int parent_drop_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - ED_object_parent_set(op->reports, bmain, scene, ob, par, partype, false, false, NULL); + ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL); - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); @@ -2061,8 +2007,8 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) } if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) { - if (ED_object_parent_set(op->reports, bmain, scene, ob, par, partype, false, false, NULL)) { - DAG_relations_tag_update(bmain); + if (ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL)) { + DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); } @@ -2169,7 +2115,14 @@ static int outliner_parenting_poll(bContext *C) SpaceOops *soops = CTX_wm_space_outliner(C); if (soops) { - return ELEM(soops->outlinevis, SO_ALL_SCENES, SO_CUR_SCENE, SO_VISIBLE, SO_GROUPS); + if (soops->outlinevis == SO_SCENES) { + return true; + } + else if ((soops->outlinevis == SO_VIEW_LAYER) && + (soops->filter & SO_FILTER_NO_COLLECTION)) + { + return true; + } } return false; @@ -2190,7 +2143,7 @@ static int parent_clear_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE ED_object_parent_clear(ob, RNA_enum_get(op->ptr, "type")); - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); return OPERATOR_FINISHED; @@ -2233,8 +2186,6 @@ static int scene_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) te = outliner_dropzone_find(soops, fmval, false); if (te) { - Base *base; - RNA_string_set(op->ptr, "scene", te->name); scene = (Scene *)BKE_libblock_find_name(bmain, ID_SCE, te->name); @@ -2245,20 +2196,31 @@ static int scene_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - base = ED_object_scene_link(scene, ob); - - if (base == NULL) { + if (BKE_scene_has_object(scene, ob)) { return OPERATOR_CANCELLED; } - if (scene == CTX_data_scene(C)) { - /* when linking to an inactive scene don't touch the layer */ - ob->lay = base->lay; - ED_base_object_select(base, BA_SELECT); + Collection *collection; + if (scene != CTX_data_scene(C)) { + /* when linking to an inactive scene link to the master collection */ + collection = BKE_collection_master(scene); } + else { + collection = CTX_data_collection(C); + } + + BKE_collection_object_add(bmain, collection, ob); - DAG_relations_tag_update(bmain); + for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { + Base *base = BKE_view_layer_base_find(view_layer, ob); + if (base) { + ED_object_base_select(base, BA_SELECT); + } + } + DEG_relations_tag_update(bmain); + + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene); return OPERATOR_FINISHED; @@ -2345,59 +2307,82 @@ void OUTLINER_OT_material_drop(wmOperatorType *ot) RNA_def_string(ot->srna, "material", "Material", MAX_ID_NAME, "Material", "Target Material"); } -static int group_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) +/* ******************** Collection Drop Operator *********************** */ + +static int collection_drop_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) { + /* TODO: implement */ +#if 0 + Object *par = NULL, *ob = NULL; Main *bmain = CTX_data_main(C); - Group *group = NULL; - Object *ob = NULL; Scene *scene = CTX_data_scene(C); + int partype = -1; + char parname[MAX_ID_NAME], childname[MAX_ID_NAME]; + + RNA_string_get(op->ptr, "parent", parname); + par = (Object *)BKE_libblock_find_name(ID_OB, parname); + RNA_string_get(op->ptr, "child", childname); + ob = (Object *)BKE_libblock_find_name(ID_OB, childname); + + if (ID_IS_LINKED(ob)) { + BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); + return OPERATOR_CANCELLED; + } + + ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); +#endif + + return OPERATOR_FINISHED; +} + +static int collection_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ SpaceOops *soops = CTX_wm_space_outliner(C); ARegion *ar = CTX_wm_region(C); - TreeElement *te = NULL; - char ob_name[MAX_ID_NAME - 2]; + Main *bmain = CTX_data_main(C); + char childname[MAX_ID_NAME]; float fmval[2]; UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); /* Find object hovered over */ - te = outliner_dropzone_find(soops, fmval, true); + TreeElement *te = outliner_dropzone_find(soops, fmval, true); - if (te) { - group = (Group *)BKE_libblock_find_name(bmain, ID_GR, te->name); - - RNA_string_get(op->ptr, "object", ob_name); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, ob_name); - - if (ELEM(NULL, group, ob)) { - return OPERATOR_CANCELLED; - } - if (BKE_group_object_exists(group, ob)) { - return OPERATOR_FINISHED; - } + if (!te || !outliner_is_collection_tree_element(te)) { + return OPERATOR_CANCELLED; + } - if (BKE_group_object_cyclic_check(bmain, ob, group)) { - BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected"); - return OPERATOR_CANCELLED; - } + Collection *collection = outliner_collection_from_tree_element(te); - BKE_group_object_add(group, ob, scene, NULL); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + // TODO: don't use scene, makes no sense anymore + // TODO: move rather than link, change hover text + Scene *scene = BKE_scene_find_from_collection(bmain, collection); + BLI_assert(scene); + RNA_string_get(op->ptr, "child", childname); + Object *ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, childname); + BKE_collection_object_add(bmain, collection, ob); - return OPERATOR_FINISHED; - } + DEG_id_tag_update(&collection->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); - return OPERATOR_CANCELLED; + return OPERATOR_FINISHED; } -void OUTLINER_OT_group_link(wmOperatorType *ot) +void OUTLINER_OT_collection_drop(wmOperatorType *ot) { /* identifiers */ - ot->name = "Link Object to Group"; - ot->description = "Link Object to Group in Outliner"; - ot->idname = "OUTLINER_OT_group_link"; + ot->name = "Link to Collection"; // TODO: rename to move? + ot->description = "Drag to move to collection in Outliner"; + ot->idname = "OUTLINER_OT_collection_drop"; /* api callbacks */ - ot->invoke = group_link_invoke; + ot->invoke = collection_drop_invoke; + ot->exec = collection_drop_exec; ot->poll = ED_operator_outliner_active; @@ -2405,5 +2390,6 @@ void OUTLINER_OT_group_link(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); + RNA_def_string(ot->srna, "child", "Object", MAX_ID_NAME, "Child", "Child Object"); + RNA_def_string(ot->srna, "parent", "Collection", MAX_ID_NAME, "Parent", "Parent Collection"); } diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 9bf2231b313..0ab22208841 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -36,14 +36,56 @@ /* internal exports only */ +struct ARegion; +struct ListBase; struct wmOperatorType; +struct TreeElement; struct TreeStoreElem; +struct Main; struct bContext; struct Scene; +struct ViewLayer; struct ID; struct Object; struct bPoseChannel; struct EditBone; +struct wmEvent; +struct wmKeyConfig; + +typedef enum TreeElementInsertType { + TE_INSERT_BEFORE, + TE_INSERT_AFTER, + TE_INSERT_INTO, +} TreeElementInsertType; + +typedef enum TreeTraversalAction { + /* Continue traversal regularly, don't skip children. */ + TRAVERSE_CONTINUE = 0, + /* Stop traversal */ + TRAVERSE_BREAK, + /* Continue traversal, but skip childs of traversed element */ + TRAVERSE_SKIP_CHILDS, +} TreeTraversalAction; + +/** + * Callback type for reinserting elements at a different position, used to allow user customizable element order. + */ +typedef void (*TreeElementReinsertFunc)(struct Main *bmain, + struct Scene *scene, + struct SpaceOops *soops, + struct TreeElement *insert_element, + struct TreeElement *insert_handle, + TreeElementInsertType action, + const struct wmEvent *event); +/** + * 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 TreeElement *insert_element, + struct TreeElement **io_insert_handle, TreeElementInsertType *io_action); +typedef TreeTraversalAction (*TreeTraversalFunc)(struct TreeElement *te, void *customdata); + typedef struct TreeElement { struct TreeElement *next, *prev, *parent; @@ -57,18 +99,34 @@ typedef struct TreeElement { const char *name; void *directdata; // Armature Bones, Base, Sequence, Strip... PointerRNA rnaptr; // RNA Pointer -} TreeElement; + + /* callbacks - TODO should be moved into a type (like TreeElementType) */ + TreeElementReinsertFunc reinsert; + TreeElementReinsertPollFunc reinsert_poll; + + struct { + TreeElementInsertType insert_type; + /* the element before/after/into which we may insert the dragged one (NULL to insert at top) */ + struct TreeElement *insert_handle; + void *tooltip_draw_handle; + } *drag_data; +} TreeElement; #define TREESTORE_ID_TYPE(_id) \ (ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \ - ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS) || \ - ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL, ID_MC)) /* Only in 'blendfile' mode ... :/ */ + ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS, ID_LP) || \ + ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF, ID_PAL, ID_MC, ID_WS)) /* Only in 'blendfile' mode ... :/ */ /* TreeElement->flag */ -#define TE_ACTIVE 1 -#define TE_ICONROW 2 -#define TE_LAZY_CLOSED 4 -#define TE_FREE_NAME 8 +enum { + TE_ACTIVE = (1 << 0), + /* Closed items display their children as icon within the row. TE_ICONROW is for + * these child-items that are visible but only within the row of the closed parent. */ + TE_ICONROW = (1 << 1), + TE_LAZY_CLOSED = (1 << 2), + TE_FREE_NAME = (1 << 3), + TE_DISABLED = (1 << 4), +}; /* button events */ #define OL_NAMEBUTTON 1 @@ -93,16 +151,20 @@ typedef enum { /* size constants */ #define OL_Y_OFFSET 2 -#define OL_TOG_RESTRICT_VIEWX (UI_UNIT_X * 3.0f) -#define OL_TOG_RESTRICT_SELECTX (UI_UNIT_X * 2.0f) +#define OL_TOG_HIDEX (UI_UNIT_X * 4.0f) +#define OL_TOG_RESTRICT_SELECTX (UI_UNIT_X * 3.0f) +#define OL_TOG_RESTRICT_VIEWX (UI_UNIT_X * 2.0f) #define OL_TOG_RESTRICT_RENDERX UI_UNIT_X -#define OL_TOGW OL_TOG_RESTRICT_VIEWX +#define OL_TOGW OL_TOG_HIDEX #define OL_RNA_COLX (UI_UNIT_X * 15) #define OL_RNA_COL_SIZEX (UI_UNIT_X * 7.5f) #define OL_RNA_COL_SPACEX (UI_UNIT_X * 2.5f) +/* The outliner display modes that support the filter system. + * Note: keep it synced with space_outliner.py */ +#define SUPPORT_FILTER_OUTLINER(soops_) (ELEM((soops_)->outlinevis, SO_VIEW_LAYER)) /* Outliner Searching -- * @@ -127,17 +189,20 @@ typedef enum { /* outliner_tree.c ----------------------------------------------- */ -void outliner_free_tree(ListBase *lb); +void outliner_free_tree(ListBase *tree); void outliner_cleanup_tree(struct SpaceOops *soops); +void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree); -TreeElement *outliner_find_tse(struct SpaceOops *soops, const TreeStoreElem *tse); -TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem); -TreeElement *outliner_find_id(struct SpaceOops *soops, ListBase *lb, const struct ID *id); -TreeElement *outliner_find_posechannel(ListBase *lb, const struct bPoseChannel *pchan); -TreeElement *outliner_find_editbone(ListBase *lb, const struct EditBone *ebone); -struct ID *outliner_search_back(SpaceOops *soops, TreeElement *te, short idcode); +void outliner_build_tree( + struct Main *mainvar, + struct Scene *scene, struct ViewLayer *view_layer, + struct SpaceOops *soops, struct ARegion *ar); -void outliner_build_tree(struct Main *mainvar, struct Scene *scene, struct SpaceOops *soops); +typedef struct ObjectsSelectedData { + struct ListBase objects_selected_array; +} ObjectsSelectedData; + +TreeTraversalAction outliner_find_selected_objects(struct TreeElement *te, void *customdata); /* outliner_draw.c ---------------------------------------------- */ @@ -146,9 +211,9 @@ void restrictbutton_gr_restrict_flag(void *poin, void *poin2, int flag); /* outliner_select.c -------------------------------------------- */ eOLDrawState tree_element_type_active( - struct bContext *C, struct Scene *scene, struct SpaceOops *soops, + struct bContext *C, struct Scene *scene, struct ViewLayer *view_layer, struct SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive); -eOLDrawState tree_element_active(struct bContext *C, struct Scene *scene, SpaceOops *soops, +eOLDrawState tree_element_active(struct bContext *C, struct Scene *scene, struct ViewLayer *view_layer, SpaceOops *soops, TreeElement *te, const eOLSetState set, const bool handle_all_types); void outliner_item_do_activate_from_tree_element( @@ -158,22 +223,30 @@ int outliner_item_do_activate_from_cursor( struct bContext *C, const int mval[2], bool extend, bool recursive); +void outliner_item_select( + struct SpaceOops *soops, const struct TreeElement *te, + const bool extend, const bool toggle); + +void outliner_object_mode_toggle( + struct bContext *C, Scene *scene, ViewLayer *view_layer, + Base *base); + /* outliner_edit.c ---------------------------------------------- */ typedef void (*outliner_operation_cb)( struct bContext *C, struct ReportList *, struct Scene *scene, struct TreeElement *, struct TreeStoreElem *, TreeStoreElem *, void *); void outliner_do_object_operation_ex( - struct bContext *C, ReportList *reports, struct Scene *scene, struct SpaceOops *soops, struct ListBase *lb, - outliner_operation_cb operation_cb, bool recurse_selected); + struct bContext *C, struct ReportList *reports, struct Scene *scene, struct SpaceOops *soops, + struct ListBase *lb, outliner_operation_cb operation_cb, bool recurse_selected); void outliner_do_object_operation( - struct bContext *C, ReportList *reports, struct Scene *scene, struct SpaceOops *soops, struct ListBase *lb, - outliner_operation_cb operation_cb); + struct bContext *C, struct ReportList *reports, struct Scene *scene, struct SpaceOops *soops, + struct ListBase *lb, outliner_operation_cb operation_cb); int common_restrict_check(struct bContext *C, struct Object *ob); int outliner_has_one_flag(ListBase *lb, short flag, const int curlevel); -void outliner_set_flag(ListBase *lb, short flag, short set); +bool outliner_set_flag(ListBase *lb, short flag, short set); void object_toggle_visibility_cb( struct bContext *C, struct ReportList *reports, struct Scene *scene, @@ -186,16 +259,6 @@ void object_toggle_renderability_cb( TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); -void group_toggle_visibility_cb( - struct bContext *C, struct ReportList *reports, struct Scene *scene, - TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); -void group_toggle_selectability_cb( - struct bContext *C, struct ReportList *reports, struct Scene *scene, - TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); -void group_toggle_renderability_cb( - struct bContext *C, struct ReportList *reports, struct Scene *scene, - TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); - void item_rename_cb( struct bContext *C, struct ReportList *reports, struct Scene *scene, TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); @@ -213,9 +276,21 @@ void id_remap_cb( struct bContext *C, struct ReportList *reports, struct Scene *scene, struct TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void item_object_mode_enter_cb( + struct bContext *C, struct ReportList *reports, struct Scene *scene, + TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void item_object_mode_exit_cb( + struct bContext *C, struct ReportList *reports, struct Scene *scene, + TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); + TreeElement *outliner_dropzone_find(const struct SpaceOops *soops, const float fmval[2], const bool children); + +void outliner_set_coordinates(struct ARegion *ar, struct SpaceOops *soops); + /* ...................................................... */ +void OUTLINER_OT_highlight_update(struct wmOperatorType *ot); + void OUTLINER_OT_item_activate(struct wmOperatorType *ot); void OUTLINER_OT_item_openclose(struct wmOperatorType *ot); void OUTLINER_OT_item_rename(struct wmOperatorType *ot); @@ -235,10 +310,6 @@ void OUTLINER_OT_expanded_toggle(struct wmOperatorType *ot); void OUTLINER_OT_scroll_page(struct wmOperatorType *ot); -void OUTLINER_OT_renderability_toggle(struct wmOperatorType *ot); -void OUTLINER_OT_selectability_toggle(struct wmOperatorType *ot); -void OUTLINER_OT_visibility_toggle(struct wmOperatorType *ot); - void OUTLINER_OT_keyingset_add_selected(struct wmOperatorType *ot); void OUTLINER_OT_keyingset_remove_selected(struct wmOperatorType *ot); @@ -251,14 +322,13 @@ void OUTLINER_OT_parent_drop(struct wmOperatorType *ot); void OUTLINER_OT_parent_clear(struct wmOperatorType *ot); void OUTLINER_OT_scene_drop(struct wmOperatorType *ot); void OUTLINER_OT_material_drop(struct wmOperatorType *ot); -void OUTLINER_OT_group_link(struct wmOperatorType *ot); +void OUTLINER_OT_collection_drop(struct wmOperatorType *ot); /* outliner_tools.c ---------------------------------------------- */ void OUTLINER_OT_operation(struct wmOperatorType *ot); void OUTLINER_OT_scene_operation(struct wmOperatorType *ot); void OUTLINER_OT_object_operation(struct wmOperatorType *ot); -void OUTLINER_OT_group_operation(struct wmOperatorType *ot); void OUTLINER_OT_lib_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_remap(struct wmOperatorType *ot); @@ -267,10 +337,41 @@ 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); + /* ---------------------------------------------------------------- */ /* outliner_ops.c */ void outliner_operatortypes(void); void outliner_keymap(struct wmKeyConfig *keyconf); +/* outliner_collections.c */ + +bool outliner_is_collection_tree_element(const TreeElement *te); +struct Collection *outliner_collection_from_tree_element(const TreeElement *te); + +void OUTLINER_OT_collection_new(struct wmOperatorType *ot); +void OUTLINER_OT_collection_duplicate(struct wmOperatorType *ot); +void OUTLINER_OT_collection_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); +void OUTLINER_OT_collection_instance(struct wmOperatorType *ot); +void OUTLINER_OT_collection_exclude_set(struct wmOperatorType *ot); +void OUTLINER_OT_collection_include_set(struct wmOperatorType *ot); + +/* outliner_utils.c ---------------------------------------------- */ + +TreeElement *outliner_find_item_at_y(const SpaceOops *soops, const ListBase *tree, float view_co_y); +TreeElement *outliner_find_item_at_x_in_row(const SpaceOops *soops, const TreeElement *parent_te, float view_co_x); +TreeElement *outliner_find_tse(struct SpaceOops *soops, const TreeStoreElem *tse); +TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem); +TreeElement *outliner_find_parent_element(ListBase *lb, TreeElement *parent_te, const TreeElement *child_te); +TreeElement *outliner_find_id(struct SpaceOops *soops, ListBase *lb, const struct ID *id); +TreeElement *outliner_find_posechannel(ListBase *lb, const struct bPoseChannel *pchan); +TreeElement *outliner_find_editbone(ListBase *lb, const struct EditBone *ebone); +struct ID *outliner_search_back(SpaceOops *soops, TreeElement *te, short idcode); +bool outliner_tree_traverse(const SpaceOops *soops, ListBase *tree, int filter_te_flag, int filter_tselem_flag, + TreeTraversalFunc func, void *customdata); + + #endif /* __OUTLINER_INTERN_H__ */ diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index ad2b8008db6..316caf0e239 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -28,29 +28,392 @@ * \ingroup spoutliner */ -#include "DNA_space_types.h" +#include "MEM_guardedalloc.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DNA_group_types.h" + +#include "BLT_translation.h" + +#include "BKE_context.h" +#include "BKE_main.h" + +#include "GPU_immediate.h" #include "RNA_access.h" +#include "UI_interface.h" +#include "UI_view2d.h" + #include "WM_api.h" #include "WM_types.h" +#include "ED_screen.h" + #include "outliner_intern.h" +typedef struct OutlinerDragDropTooltip { + TreeElement *te; + void *handle; +} OutlinerDragDropTooltip; + +enum { + OUTLINER_ITEM_DRAG_CANCEL, + OUTLINER_ITEM_DRAG_CONFIRM, +}; + +static int outliner_item_drag_drop_poll(bContext *C) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + return ED_operator_outliner_active(C) && + /* Only collection display modes supported for now. Others need more design work */ + ELEM(soops->outlinevis, SO_VIEW_LAYER, SO_LIBRARIES); +} + +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 void outliner_item_drag_end(wmWindow *win, OutlinerDragDropTooltip *data) +{ + MEM_SAFE_FREE(data->te->drag_data); + + if (data->handle) { + WM_draw_cb_exit(win, data->handle); + } + + MEM_SAFE_FREE(data); +} + +static void outliner_item_drag_get_insert_data( + const SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged, + TreeElement **r_te_insert_handle, TreeElementInsertType *r_insert_type) +{ + 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]); + te_hovered = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + if (te_hovered) { + /* mouse hovers an element (ignoring x-axis), now find out how to insert the dragged item exactly */ + + if (te_hovered == te_dragged) { + *r_te_insert_handle = te_dragged; + } + else if (te_hovered != te_dragged) { + const float margin = UI_UNIT_Y * (1.0f / 4); + + *r_te_insert_handle = te_hovered; + if (view_mval[1] < (te_hovered->ys + margin)) { + if (TSELEM_OPEN(TREESTORE(te_hovered), soops)) { + /* inserting after a open item means we insert into it, but as first child */ + if (BLI_listbase_is_empty(&te_hovered->subtree)) { + *r_insert_type = TE_INSERT_INTO; + } + else { + *r_insert_type = TE_INSERT_BEFORE; + *r_te_insert_handle = te_hovered->subtree.first; + } + } + else { + *r_insert_type = TE_INSERT_AFTER; + } + } + else if (view_mval[1] > (te_hovered->ys + (3 * margin))) { + *r_insert_type = TE_INSERT_BEFORE; + } + else { + *r_insert_type = TE_INSERT_INTO; + } + } + } + else { + /* mouse doesn't hover any item (ignoring x-axis), so it's either above list bounds or below. */ + + TreeElement *first = soops->tree.first; + TreeElement *last = soops->tree.last; + + if (view_mval[1] < last->ys) { + *r_te_insert_handle = last; + *r_insert_type = TE_INSERT_AFTER; + } + else if (view_mval[1] > (first->ys + UI_UNIT_Y)) { + *r_te_insert_handle = first; + *r_insert_type = TE_INSERT_BEFORE; + } + else { + BLI_assert(0); + } + } +} + +static void outliner_item_drag_handle( + 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->reinsert_poll && + /* there is no reinsert_poll, so we do some generic checks (same types and reinsert callback is available) */ + (TREESTORE(te_dragged)->type == TREESTORE(te_insert_handle)->type) && + te_dragged->reinsert) + { + /* pass */ + } + else if (te_dragged == te_insert_handle) { + /* nothing will happen anyway, no need to do poll check */ + } + else if (!te_dragged->reinsert_poll || + !te_dragged->reinsert_poll(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; +} + +/** + * Returns true if it is a collection and empty. + */ +static bool is_empty_collection(TreeElement *te) +{ + Collection *collection = outliner_collection_from_tree_element(te); + + if (!collection) { + return false; + } + + return BLI_listbase_is_empty(&collection->gobject) && + BLI_listbase_is_empty(&collection->children); +} + +static bool outliner_item_drag_drop_apply( + Main *bmain, + Scene *scene, + SpaceOops *soops, + OutlinerDragDropTooltip *data, + const wmEvent *event) +{ + TreeElement *dragged_te = data->te; + TreeElement *insert_handle = dragged_te->drag_data->insert_handle; + TreeElementInsertType insert_type = dragged_te->drag_data->insert_type; + + if ((insert_handle == dragged_te) || !insert_handle) { + /* No need to do anything */ + } + else if (dragged_te->reinsert) { + BLI_assert(!dragged_te->reinsert_poll || dragged_te->reinsert_poll(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); + + /* If the collection was just created and you moved objects/collections inside it, + * it is strange to have it closed and we not see the newly dragged elements. */ + const bool should_open_collection = (insert_type == TE_INSERT_INTO) && is_empty_collection(insert_handle); + + dragged_te->reinsert(bmain, scene, soops, dragged_te, insert_handle, insert_type, event); + + if (should_open_collection && !is_empty_collection(insert_handle)) { + TREESTORE(insert_handle)->flag &= ~TSE_CLOSED; + } + return true; + } + + return false; +} + +static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + OutlinerDragDropTooltip *data = op->customdata; + TreeElement *te_dragged = data->te; + int retval = OPERATOR_RUNNING_MODAL; + bool redraw = false; + bool skip_rebuild = true; + + switch (event->type) { + case EVT_MODAL_MAP: + if (event->val == OUTLINER_ITEM_DRAG_CONFIRM) { + if (outliner_item_drag_drop_apply(bmain, scene, soops, data, event)) { + skip_rebuild = false; + } + retval = OPERATOR_FINISHED; + } + else if (event->val == OUTLINER_ITEM_DRAG_CANCEL) { + retval = OPERATOR_CANCELLED; + } + else { + BLI_assert(0); + } + WM_event_add_mousemove(C); /* update highlight */ + outliner_item_drag_end(CTX_wm_window(C), data); + redraw = true; + break; + case MOUSEMOVE: + outliner_item_drag_handle(soops, ar, event, te_dragged); + redraw = true; + break; + } + + if (redraw) { + if (skip_rebuild) { + ED_region_tag_redraw_no_rebuild(ar); + } + else { + ED_region_tag_redraw(ar); + } + } + + return retval; +} + +static const char *outliner_drag_drop_tooltip_get( + const TreeElement *te_float) +{ + const char *name = NULL; + + const TreeElement *te_insert = te_float->drag_data->insert_handle; + if (te_float && outliner_is_collection_tree_element(te_float)) { + if (te_insert == NULL) { + name = TIP_("Move collection"); + } + else { + switch (te_float->drag_data->insert_type) { + case TE_INSERT_BEFORE: + if (te_insert->prev && outliner_is_collection_tree_element(te_insert->prev)) { + name = TIP_("Move between collections"); + } + else { + name = TIP_("Move before collection"); + } + break; + case TE_INSERT_AFTER: + if (te_insert->next && outliner_is_collection_tree_element(te_insert->next)) { + name = TIP_("Move between collections"); + } + else { + name = TIP_("Move after collection"); + } + break; + case TE_INSERT_INTO: + name = TIP_("Move inside collection"); + break; + } + } + } + else if ((TREESTORE(te_float)->type == 0) && (te_float->idcode == ID_OB)) { + name = TIP_("Move to collection (Ctrl to link)"); + } + + return name; +} + +static void outliner_drag_drop_tooltip_cb(const wmWindow *win, void *vdata) +{ + OutlinerDragDropTooltip *data = vdata; + const char *tooltip; + + int cursorx, cursory; + int x, y; + + tooltip = outliner_drag_drop_tooltip_get(data->te); + if (tooltip == NULL) { + return; + } + + cursorx = win->eventstate->x; + cursory = win->eventstate->y; + + x = cursorx + U.widget_unit; + y = cursory - U.widget_unit; + + /* Drawing. */ + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + + const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + + glEnable(GL_BLEND); + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, tooltip, col_fg, col_bg); + glDisable(GL_BLEND); +} + +static int outliner_item_drag_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te_dragged = outliner_item_drag_element_find(soops, ar, event); + + if (!te_dragged) { + return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); + } + + OutlinerDragDropTooltip *data = MEM_mallocN(sizeof(OutlinerDragDropTooltip), __func__); + data->te = te_dragged; + + op->customdata = data; + te_dragged->drag_data = MEM_callocN(sizeof(*te_dragged->drag_data), __func__); + /* by default we don't change the item position */ + 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); + + ED_region_tag_redraw_no_rebuild(ar); + + WM_event_add_modal_handler(C, op); + + data->handle = WM_draw_cb_activate(CTX_wm_window(C), outliner_drag_drop_tooltip_cb, data); + + return OPERATOR_RUNNING_MODAL; +} + +/** + * Notes about Outliner Item Drag 'n Drop: + * Right now only collections display mode is supported. But ideally all/most modes would support this. There are + * just some open design questions that have to be answered: do we want to allow mixing order of different data types + * (like render-layers and objects)? Would that be a purely visual change or would that have any other effect? ... + */ +static void OUTLINER_OT_item_drag_drop(wmOperatorType *ot) +{ + ot->name = "Drag and Drop Item"; + ot->idname = "OUTLINER_OT_item_drag_drop"; + ot->description = "Change the hierarchical position of an item by repositioning it using drag and drop"; + + ot->invoke = outliner_item_drag_drop_invoke; + ot->modal = outliner_item_drag_drop_modal; + + ot->poll = outliner_item_drag_drop_poll; + + ot->flag = OPTYPE_UNDO; +} + /* ************************** registration **********************************/ void outliner_operatortypes(void) { + WM_operatortype_append(OUTLINER_OT_highlight_update); WM_operatortype_append(OUTLINER_OT_item_activate); WM_operatortype_append(OUTLINER_OT_select_border); WM_operatortype_append(OUTLINER_OT_item_openclose); WM_operatortype_append(OUTLINER_OT_item_rename); + WM_operatortype_append(OUTLINER_OT_item_drag_drop); WM_operatortype_append(OUTLINER_OT_operation); WM_operatortype_append(OUTLINER_OT_scene_operation); WM_operatortype_append(OUTLINER_OT_object_operation); - WM_operatortype_append(OUTLINER_OT_group_operation); WM_operatortype_append(OUTLINER_OT_lib_operation); WM_operatortype_append(OUTLINER_OT_lib_relocate); WM_operatortype_append(OUTLINER_OT_id_operation); @@ -70,10 +433,6 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_selected_toggle); WM_operatortype_append(OUTLINER_OT_expanded_toggle); - WM_operatortype_append(OUTLINER_OT_renderability_toggle); - WM_operatortype_append(OUTLINER_OT_selectability_toggle); - WM_operatortype_append(OUTLINER_OT_visibility_toggle); - WM_operatortype_append(OUTLINER_OT_keyingset_add_selected); WM_operatortype_append(OUTLINER_OT_keyingset_remove_selected); @@ -86,7 +445,48 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_parent_clear); WM_operatortype_append(OUTLINER_OT_scene_drop); WM_operatortype_append(OUTLINER_OT_material_drop); - WM_operatortype_append(OUTLINER_OT_group_link); + WM_operatortype_append(OUTLINER_OT_collection_drop); + + /* collections */ + WM_operatortype_append(OUTLINER_OT_collection_new); + WM_operatortype_append(OUTLINER_OT_collection_duplicate); + WM_operatortype_append(OUTLINER_OT_collection_delete); + WM_operatortype_append(OUTLINER_OT_collection_objects_select); + WM_operatortype_append(OUTLINER_OT_collection_objects_deselect); + WM_operatortype_append(OUTLINER_OT_collection_link); + WM_operatortype_append(OUTLINER_OT_collection_instance); + WM_operatortype_append(OUTLINER_OT_collection_exclude_set); + WM_operatortype_append(OUTLINER_OT_collection_include_set); +} + +static wmKeyMap *outliner_item_drag_drop_modal_keymap(wmKeyConfig *keyconf) +{ + static EnumPropertyItem modal_items[] = { + {OUTLINER_ITEM_DRAG_CANCEL, "CANCEL", 0, "Cancel", ""}, + {OUTLINER_ITEM_DRAG_CONFIRM, "CONFIRM", 0, "Confirm/Drop", ""}, + {0, NULL, 0, NULL, NULL} + }; + const char *map_name = "Outliner Item Drap 'n Drop Modal Map"; + + wmKeyMap *keymap = WM_modalkeymap_get(keyconf, map_name); + + /* this function is called for each spacetype, only needs to add map once */ + if (keymap && keymap->modal_items) + return NULL; + + keymap = WM_modalkeymap_add(keyconf, map_name, modal_items); + + /* items for modal map */ + WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, OUTLINER_ITEM_DRAG_CANCEL); + WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, OUTLINER_ITEM_DRAG_CANCEL); + + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, OUTLINER_ITEM_DRAG_CONFIRM); + WM_modalkeymap_add_item(keymap, RETKEY, KM_RELEASE, KM_ANY, 0, OUTLINER_ITEM_DRAG_CONFIRM); + WM_modalkeymap_add_item(keymap, PADENTER, KM_RELEASE, KM_ANY, 0, OUTLINER_ITEM_DRAG_CONFIRM); + + WM_modalkeymap_assign(keymap, "OUTLINER_OT_item_drag_drop"); + + return keymap; } void outliner_keymap(wmKeyConfig *keyconf) @@ -94,6 +494,8 @@ void outliner_keymap(wmKeyConfig *keyconf) wmKeyMap *keymap = WM_keymap_find(keyconf, "Outliner", SPACE_OUTLINER, 0); wmKeyMapItem *kmi; + WM_keymap_add_item(keymap, "OUTLINER_OT_highlight_update", MOUSEMOVE, KM_ANY, KM_ANY, 0); + WM_keymap_add_item(keymap, "OUTLINER_OT_item_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0); kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, 0, 0); @@ -123,6 +525,8 @@ void outliner_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "OUTLINER_OT_item_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "OUTLINER_OT_operation", RIGHTMOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "OUTLINER_OT_item_drag_drop", EVT_TWEAK_L, KM_ANY, 0, 0); + WM_keymap_add_item(keymap, "OUTLINER_OT_show_hierarchy", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "OUTLINER_OT_show_active", PERIODKEY, KM_PRESS, 0, 0); @@ -140,11 +544,6 @@ void outliner_keymap(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "OUTLINER_OT_selected_toggle", AKEY, KM_PRESS, 0, 0); WM_keymap_verify_item(keymap, "OUTLINER_OT_expanded_toggle", AKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_verify_item(keymap, "OUTLINER_OT_renderability_toggle", RKEY, KM_PRESS, 0, 0); - WM_keymap_verify_item(keymap, "OUTLINER_OT_selectability_toggle", SKEY, KM_PRESS, 0, 0); - WM_keymap_verify_item(keymap, "OUTLINER_OT_visibility_toggle", VKEY, KM_PRESS, 0, 0); - - /* keying sets - only for databrowse */ WM_keymap_verify_item(keymap, "OUTLINER_OT_keyingset_add_selected", KKEY, KM_PRESS, 0, 0); WM_keymap_verify_item(keymap, "OUTLINER_OT_keyingset_remove_selected", KKEY, KM_PRESS, KM_ALT, 0); @@ -154,5 +553,20 @@ void outliner_keymap(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "OUTLINER_OT_drivers_add_selected", DKEY, KM_PRESS, 0, 0); WM_keymap_verify_item(keymap, "OUTLINER_OT_drivers_delete_selected", DKEY, KM_PRESS, KM_ALT, 0); + + WM_keymap_verify_item(keymap, "OUTLINER_OT_collection_new", CKEY, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "OUTLINER_OT_collection_delete", XKEY, KM_PRESS, 0, 0); + + WM_keymap_verify_item(keymap, "OBJECT_OT_move_to_collection", MKEY, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "OBJECT_OT_link_to_collection", MKEY, KM_PRESS, KM_SHIFT, 0); + + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_hide_view_clear", HKEY, KM_PRESS, KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "select", false); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_hide_view_set", HKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "unselected", false); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_hide_view_set", HKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "unselected", true); + + outliner_item_drag_drop_modal_keymap(keyconf); } diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 5149715740f..38ae0683d4b 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -43,12 +43,17 @@ #include "BLI_utildefines.h" #include "BLI_listbase.h" +#include "BKE_armature.h" +#include "BKE_collection.h" #include "BKE_context.h" -#include "BKE_depsgraph.h" +#include "BKE_layer.h" +#include "BKE_main.h" #include "BKE_object.h" #include "BKE_scene.h" #include "BKE_sequencer.h" -#include "BKE_armature.h" +#include "BKE_workspace.h" + +#include "DEG_depsgraph.h" #include "ED_armature.h" #include "ED_object.h" @@ -68,61 +73,107 @@ #include "outliner_intern.h" -/* ****************************************************** */ -/* Outliner Selection (gray-blue highlight for rows) */ - -static int outliner_select(SpaceOops *soops, ListBase *lb, int *index, short *selecting) +static void do_outliner_activate_obdata(bContext *C, Scene *scene, ViewLayer *view_layer, Base *base) { - TreeElement *te; - TreeStoreElem *tselem; - bool changed = false; - - for (te = lb->first; te && *index >= 0; te = te->next, (*index)--) { - tselem = TREESTORE(te); - - /* if we've encountered the right item, set its 'Outliner' selection status */ - if (*index == 0) { - /* this should be the last one, so no need to do anything with index */ - if ((te->flag & TE_ICONROW) == 0) { - /* -1 value means toggle testing for now... */ - if (*selecting == -1) { - if (tselem->flag & TSE_SELECTED) - *selecting = 0; - else - *selecting = 1; - } + Main *bmain = CTX_data_main(C); + Object *obact = OBACT(view_layer); + bool use_all = false; - /* set selection */ - if (*selecting) - tselem->flag |= TSE_SELECTED; - else - tselem->flag &= ~TSE_SELECTED; + if (obact == NULL) { + ED_object_base_activate(C, base); + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + obact = base->object; + use_all = true; + } + else if (obact->data == base->object->data) { + use_all = true; + } - changed |= true; + if (use_all) { + WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL); + } + else { + Object *ob = base->object; + if (ob->type == obact->type) { + bool ok; + if (BKE_object_is_in_editmode(ob)) { + ok = ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA | EM_WAITCURSOR); + } + else { + ok = ED_object_editmode_enter_ex(CTX_data_main(C), scene, ob, EM_WAITCURSOR | EM_NO_CONTEXT); + } + if (ok) { + ED_object_base_select(base, (ob->mode & OB_MODE_EDIT) ? BA_SELECT : BA_DESELECT); + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } } - else if (TSELEM_OPEN(tselem, soops)) { - /* Only try selecting sub-elements if we haven't hit the right element yet - * - * Hack warning: - * Index must be reduced before supplying it to the sub-tree to try to do - * selection, however, we need to increment it again for the next loop to - * function correctly - */ - (*index)--; - changed |= outliner_select(soops, &te->subtree, index, selecting); - (*index)++; + } +} + +static void do_outliner_activate_pose(bContext *C, ViewLayer *view_layer, Base *base) +{ + Object *obact = OBACT(view_layer); + bool use_all = false; + + if (obact == NULL) { + ED_object_base_activate(C, base); + Scene *scene = CTX_data_scene(C); + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + obact = base->object; + use_all = true; + } + else if (obact->data == base->object->data) { + use_all = true; + } + + if (use_all) { + WM_operator_name_call(C, "OBJECT_OT_posemode_toggle", WM_OP_INVOKE_REGION_WIN, NULL); + } + else { + Object *ob = base->object; + if (ob->type == obact->type) { + struct Main *bmain = CTX_data_main(C); + bool ok = false; + if (ob->mode & OB_MODE_POSE) { + ok = ED_object_posemode_exit_ex(bmain, ob); + } + else { + ok = ED_object_posemode_enter_ex(bmain, ob); + } + if (ok) { + ED_object_base_select(base, (ob->mode & OB_MODE_POSE) ? BA_SELECT : BA_DESELECT); + + Scene *scene = CTX_data_scene(C); + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); + WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + } } } +} - return changed; +/* For draw callback to run mode switching */ +void outliner_object_mode_toggle( + bContext *C, Scene *scene, ViewLayer *view_layer, + Base *base) +{ + Object *obact = OBACT(view_layer); + if (obact->mode & OB_MODE_EDIT) { + do_outliner_activate_obdata(C, scene, view_layer, base); + } + else if (obact->mode & OB_MODE_POSE) { + do_outliner_activate_pose(C, view_layer, base); + } } /* ****************************************************** */ /* Outliner Element Selection/Activation on Click */ -static eOLDrawState tree_element_active_renderlayer( - bContext *C, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) +static eOLDrawState active_viewlayer( + bContext *C, Scene *UNUSED(scene), ViewLayer *UNUSED(sl), TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) { Scene *sce; @@ -131,12 +182,15 @@ static eOLDrawState tree_element_active_renderlayer( return OL_DRAWSEL_NONE; sce = (Scene *)tselem->id; + WorkSpace *workspace = CTX_wm_workspace(C); + ViewLayer *view_layer = te->directdata; + if (set != OL_SETSEL_NONE) { - sce->r.actlay = tselem->nr; - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, sce); + BKE_workspace_view_layer_set(workspace, view_layer, sce); + WM_event_add_notifier(C, NC_SCREEN | ND_LAYER, NULL); } else { - return sce->r.actlay == tselem->nr; + return BKE_workspace_view_layer_get(workspace, sce) == view_layer; } return OL_DRAWSEL_NONE; } @@ -146,14 +200,14 @@ static eOLDrawState tree_element_active_renderlayer( * CTRL+LMB: Select/Deselect object and all children. * CTRL+SHIFT+LMB: Add/Remove object and all children. */ -static void do_outliner_object_select_recursive(Scene *scene, Object *ob_parent, bool select) +static void do_outliner_object_select_recursive(ViewLayer *view_layer, Object *ob_parent, bool select) { Base *base; - for (base = FIRSTBASE; base; base = base->next) { + for (base = FIRSTBASE(view_layer); base; base = base->next) { Object *ob = base->object; - if ((((ob->restrictflag & OB_RESTRICT_VIEW) == 0) && BKE_object_is_child_recursive(ob_parent, ob))) { - ED_base_object_select(base, select ? BA_SELECT : BA_DESELECT); + if ((((base->flag & BASE_VISIBLED) == 0) && BKE_object_is_child_recursive(ob_parent, ob))) { + ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); } } } @@ -184,7 +238,7 @@ static void do_outliner_ebone_select_recursive(bArmature *arm, EditBone *ebone_p } static eOLDrawState tree_element_set_active_object( - bContext *C, Scene *scene, SpaceOops *soops, + bContext *C, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, TreeElement *te, const eOLSetState set, bool recursive) { TreeStoreElem *tselem = TREESTORE(te); @@ -198,7 +252,7 @@ static eOLDrawState tree_element_set_active_object( } else { ob = (Object *)outliner_search_back(soops, te, ID_OB); - if (ob == OBACT) { + if (ob == OBACT(view_layer)) { return OL_DRAWSEL_NONE; } } @@ -208,46 +262,70 @@ static eOLDrawState tree_element_set_active_object( sce = (Scene *)outliner_search_back(soops, te, ID_SCE); if (sce && scene != sce) { - ED_screen_set_scene(C, CTX_wm_screen(C), sce); + WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), sce); scene = sce; } /* find associated base in current scene */ - base = BKE_scene_base_find(scene, ob); + base = BKE_view_layer_base_find(view_layer, ob); + + if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { + if (base != NULL) { + Object *obact = OBACT(view_layer); + const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT; + if (base && !BKE_object_is_mode_compat(base->object, object_mode)) { + if (object_mode == OB_MODE_OBJECT) { + struct Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + ED_object_mode_generic_exit(bmain, depsgraph, scene, base->object); + } + if (!BKE_object_is_mode_compat(base->object, object_mode)) { + base = NULL; + } + } + } + } if (base) { if (set == OL_SETSEL_EXTEND) { /* swap select */ - if (base->flag & SELECT) - ED_base_object_select(base, BA_DESELECT); + if (base->flag & BASE_SELECTED) + ED_object_base_select(base, BA_DESELECT); else - ED_base_object_select(base, BA_SELECT); + ED_object_base_select(base, BA_SELECT); } else { /* deleselect all */ - BKE_scene_base_deselect_all(scene); - ED_base_object_select(base, BA_SELECT); + + /* Only in object mode so we can switch the active object, + * keeping all objects in the current 'mode' selected, useful for multi-pose/edit mode. + * This keeps the convention that all objects in the current mode are also selected. see T55246. */ + if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) ? (ob->mode == OB_MODE_OBJECT) : true) { + BKE_view_layer_base_deselect_all(view_layer); + } + ED_object_base_select(base, BA_SELECT); } if (recursive) { /* Recursive select/deselect for Object hierarchies */ - do_outliner_object_select_recursive(scene, ob, (ob->flag & SELECT) != 0); + do_outliner_object_select_recursive(view_layer, ob, (base->flag & BASE_SELECTED) != 0); } - if (C) { - ED_base_object_activate(C, base); /* adds notifier */ + if (set != OL_SETSEL_NONE) { + ED_object_base_activate(C, base); /* adds notifier */ + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } - } - - if (ob != scene->obedit) - ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR); + if (ob != OBEDIT_FROM_VIEW_LAYER(view_layer)) { + ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR); + } + } return OL_DRAWSEL_NORMAL; } static eOLDrawState tree_element_active_material( - bContext *C, Scene *scene, SpaceOops *soops, + bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, SpaceOops *soops, TreeElement *te, const eOLSetState set) { TreeElement *tes; @@ -256,7 +334,7 @@ static eOLDrawState tree_element_active_material( /* we search for the object parent */ ob = (Object *)outliner_search_back(soops, te, ID_OB); // note: ob->matbits can be NULL when a local object points to a library mesh. - if (ob == NULL || ob != OBACT || ob->matbits == NULL) { + if (ob == NULL || ob != OBACT(view_layer) || ob->matbits == NULL) { return OL_DRAWSEL_NONE; /* just paranoia */ } @@ -293,108 +371,21 @@ static eOLDrawState tree_element_active_material( /* Tagging object for update seems a bit stupid here, but looks like we have to do it * for render views to update. See T42973. * Note that RNA material update does it too, see e.g. rna_MaterialSlot_update(). */ - DAG_id_tag_update((ID *)ob, OB_RECALC_OB); + DEG_id_tag_update((ID *)ob, OB_RECALC_OB); WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL); } return OL_DRAWSEL_NONE; } -static eOLDrawState tree_element_active_texture( - bContext *C, Scene *scene, SpaceOops *UNUSED(soops), - TreeElement *te, const eOLSetState set) -{ - TreeElement *tep; - TreeStoreElem /* *tselem,*/ *tselemp; - Object *ob = OBACT; - SpaceButs *sbuts = NULL; - - if (ob == NULL) { - /* no active object */ - return OL_DRAWSEL_NONE; - } - - /*tselem = TREESTORE(te);*/ /*UNUSED*/ - - /* find buttons region (note, this is undefined really still, needs recode in blender) */ - /* XXX removed finding sbuts */ - - /* where is texture linked to? */ - tep = te->parent; - tselemp = TREESTORE(tep); - - if (tep->idcode == ID_WO) { - World *wrld = (World *)tselemp->id; - - if (set != OL_SETSEL_NONE) { - if (sbuts) { - // XXX sbuts->tabo = TAB_SHADING_TEX; // hack from header_buttonswin.c - // XXX sbuts->texfrom = 1; - } -// XXX extern_set_butspace(F6KEY, 0); // force shading buttons texture - wrld->texact = te->index; - } - else if (tselemp->id == (ID *)(scene->world)) { - if (wrld->texact == te->index) { - return OL_DRAWSEL_NORMAL; - } - } - } - else if (tep->idcode == ID_LA) { - Lamp *la = (Lamp *)tselemp->id; - if (set != OL_SETSEL_NONE) { - if (sbuts) { - // XXX sbuts->tabo = TAB_SHADING_TEX; // hack from header_buttonswin.c - // XXX sbuts->texfrom = 2; - } -// XXX extern_set_butspace(F6KEY, 0); // force shading buttons texture - la->texact = te->index; - } - else { - if (tselemp->id == ob->data) { - if (la->texact == te->index) { - return OL_DRAWSEL_NORMAL; - } - } - } - } - else if (tep->idcode == ID_MA) { - Material *ma = (Material *)tselemp->id; - if (set != OL_SETSEL_NONE) { - if (sbuts) { - //sbuts->tabo = TAB_SHADING_TEX; // hack from header_buttonswin.c - // XXX sbuts->texfrom = 0; - } -// XXX extern_set_butspace(F6KEY, 0); // force shading buttons texture - ma->texact = (char)te->index; - - /* also set active material */ - ob->actcol = tep->index + 1; - } - else if (tep->flag & TE_ACTIVE) { // this is active material - if (ma->texact == te->index) { - return OL_DRAWSEL_NORMAL; - } - } - } - - if (set != OL_SETSEL_NONE) { - WM_event_add_notifier(C, NC_TEXTURE, NULL); - } - - /* no active object */ - return OL_DRAWSEL_NONE; -} - - static eOLDrawState tree_element_active_lamp( - bContext *UNUSED(C), Scene *scene, SpaceOops *soops, + bContext *UNUSED(C), Scene *UNUSED(scene), ViewLayer *view_layer, SpaceOops *soops, TreeElement *te, const eOLSetState set) { Object *ob; /* we search for the object parent */ ob = (Object *)outliner_search_back(soops, te, ID_OB); - if (ob == NULL || ob != OBACT) { + if (ob == NULL || ob != OBACT(view_layer)) { /* just paranoia */ return OL_DRAWSEL_NONE; } @@ -410,7 +401,7 @@ static eOLDrawState tree_element_active_lamp( } static eOLDrawState tree_element_active_camera( - bContext *UNUSED(C), Scene *scene, SpaceOops *soops, + bContext *UNUSED(C), Scene *scene, ViewLayer *UNUSED(sl), SpaceOops *soops, TreeElement *te, const eOLSetState set) { Object *ob = (Object *)outliner_search_back(soops, te, ID_OB); @@ -423,7 +414,7 @@ static eOLDrawState tree_element_active_camera( } static eOLDrawState tree_element_active_world( - bContext *C, Scene *scene, SpaceOops *UNUSED(soops), + bContext *C, Scene *scene, ViewLayer *UNUSED(sl), SpaceOops *UNUSED(soops), TreeElement *te, const eOLSetState set) { TreeElement *tep; @@ -440,7 +431,7 @@ static eOLDrawState tree_element_active_world( if (set != OL_SETSEL_NONE) { /* make new scene active */ if (sce && scene != sce) { - ED_screen_set_scene(C, CTX_wm_screen(C), sce); + WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), sce); } } @@ -456,7 +447,7 @@ static eOLDrawState tree_element_active_world( } static eOLDrawState tree_element_active_defgroup( - bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) + bContext *C, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) { Object *ob; @@ -466,21 +457,20 @@ static eOLDrawState tree_element_active_defgroup( BLI_assert(te->index + 1 >= 0); ob->actdef = te->index + 1; - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); } else { - if (ob == OBACT) { + if (ob == OBACT(view_layer)) if (ob->actdef == te->index + 1) { return OL_DRAWSEL_NORMAL; } - } } return OL_DRAWSEL_NONE; } static eOLDrawState tree_element_active_posegroup( - bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) + bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) { Object *ob = (Object *)tselem->id; @@ -491,7 +481,7 @@ static eOLDrawState tree_element_active_posegroup( } } else { - if (ob == OBACT && ob->pose) { + if (ob == OBACT(view_layer) && ob->pose) { if (ob->pose->active_group == te->index + 1) { return OL_DRAWSEL_NORMAL; } @@ -501,7 +491,7 @@ static eOLDrawState tree_element_active_posegroup( } static eOLDrawState tree_element_active_posechannel( - bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive) + bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive) { Object *ob = (Object *)tselem->id; bArmature *arm = ob->data; @@ -535,7 +525,7 @@ static eOLDrawState tree_element_active_posechannel( } } else { - if (ob == OBACT && ob->pose) { + if (ob == OBACT(view_layer) && ob->pose) { if (pchan->bone->flag & BONE_SELECTED) { return OL_DRAWSEL_NORMAL; } @@ -545,14 +535,14 @@ static eOLDrawState tree_element_active_posechannel( } static eOLDrawState tree_element_active_bone( - bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive) + bContext *C, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive) { bArmature *arm = (bArmature *)tselem->id; Bone *bone = te->directdata; if (set != OL_SETSEL_NONE) { if (!(bone->flag & BONE_HIDDEN_P)) { - Object *ob = OBACT; + Object *ob = OBACT(view_layer); if (ob) { if (set != OL_SETSEL_EXTEND) { /* single select forces all other bones to get unselected */ @@ -581,7 +571,7 @@ static eOLDrawState tree_element_active_bone( } } else { - Object *ob = OBACT; + Object *ob = OBACT(view_layer); if (ob && ob->data == arm) { if (bone->flag & BONE_SELECTED) { @@ -594,7 +584,7 @@ static eOLDrawState tree_element_active_bone( /* ebones only draw in editmode armature */ -static void tree_element_active_ebone__sel(bContext *C, Scene *scene, bArmature *arm, EditBone *ebone, short sel) +static void tree_element_active_ebone__sel(bContext *C, Object *obedit, bArmature *arm, EditBone *ebone, short sel) { if (sel) { ebone->flag |= BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL; @@ -608,34 +598,34 @@ static void tree_element_active_ebone__sel(bContext *C, Scene *scene, bArmature if (ebone->parent && (ebone->flag & BONE_CONNECTED)) ebone->parent->flag &= ~BONE_TIPSEL; } - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, scene->obedit); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, obedit); } static eOLDrawState tree_element_active_ebone( - bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set, bool recursive) + bContext *C, TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set, bool recursive) { - BLI_assert(scene->obedit != NULL); - - bArmature *arm = scene->obedit->data; + Object *obedit = CTX_data_edit_object(C); + BLI_assert(obedit != NULL); + bArmature *arm = obedit->data; EditBone *ebone = te->directdata; eOLDrawState status = OL_DRAWSEL_NONE; if (set != OL_SETSEL_NONE) { if (set == OL_SETSEL_NORMAL) { if (!(ebone->flag & BONE_HIDDEN_A)) { - ED_armature_edit_deselect_all(scene->obedit); - tree_element_active_ebone__sel(C, scene, arm, ebone, true); + ED_armature_edit_deselect_all(obedit); + tree_element_active_ebone__sel(C, obedit, arm, ebone, true); status = OL_DRAWSEL_NORMAL; } } else if (set == OL_SETSEL_EXTEND) { if (!(ebone->flag & BONE_HIDDEN_A)) { if (!(ebone->flag & BONE_SELECTED)) { - tree_element_active_ebone__sel(C, scene, arm, ebone, true); + tree_element_active_ebone__sel(C, obedit, arm, ebone, true); status = OL_DRAWSEL_NORMAL; } else { /* entirely selected, so de-select */ - tree_element_active_ebone__sel(C, scene, arm, ebone, false); + tree_element_active_ebone__sel(C, obedit, arm, ebone, false); status = OL_DRAWSEL_NONE; } } @@ -654,7 +644,7 @@ static eOLDrawState tree_element_active_ebone( } static eOLDrawState tree_element_active_modifier( - bContext *C, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set) + bContext *C, Scene *UNUSED(scene), ViewLayer *UNUSED(sl), TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set) { if (set != OL_SETSEL_NONE) { Object *ob = (Object *)tselem->id; @@ -682,7 +672,7 @@ static eOLDrawState tree_element_active_psys( } static int tree_element_active_constraint( - bContext *C, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set) + bContext *C, Scene *UNUSED(scene), ViewLayer *UNUSED(sl), TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set) { if (set != OL_SETSEL_NONE) { Object *ob = (Object *)tselem->id; @@ -695,7 +685,7 @@ static int tree_element_active_constraint( } static eOLDrawState tree_element_active_text( - bContext *UNUSED(C), Scene *UNUSED(scene), SpaceOops *UNUSED(soops), + bContext *UNUSED(C), Scene *UNUSED(scene), ViewLayer *UNUSED(sl), SpaceOops *UNUSED(soops), TreeElement *UNUSED(te), int UNUSED(set)) { // XXX removed @@ -703,10 +693,10 @@ static eOLDrawState tree_element_active_text( } static eOLDrawState tree_element_active_pose( - bContext *C, Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set) + bContext *C, ViewLayer *view_layer, TreeElement *UNUSED(te), TreeStoreElem *tselem, const eOLSetState set) { Object *ob = (Object *)tselem->id; - Base *base = BKE_scene_base_find(scene, ob); + Base *base = BKE_view_layer_base_find(view_layer, ob); if (base == NULL) { /* Armature not instantiated in current scene (e.g. inside an appended group...). */ @@ -714,16 +704,7 @@ static eOLDrawState tree_element_active_pose( } if (set != OL_SETSEL_NONE) { - if (scene->obedit) { - ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR); - } - - if (ob->mode & OB_MODE_POSE) { - ED_object_posemode_exit(C, ob); - } - else { - ED_object_posemode_enter(C, ob); - } + do_outliner_activate_pose(C, view_layer, base); } else { if (ob->mode & OB_MODE_POSE) { @@ -795,7 +776,7 @@ static eOLDrawState tree_element_active_sequence_dup( } static eOLDrawState tree_element_active_keymap_item( - bContext *UNUSED(C), TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set) + bContext *UNUSED(C), Scene *UNUSED(scene), ViewLayer *UNUSED(sl), TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set) { wmKeyMapItem *kmi = te->directdata; @@ -811,84 +792,125 @@ static eOLDrawState tree_element_active_keymap_item( return OL_DRAWSEL_NONE; } +static eOLDrawState tree_element_active_master_collection( + bContext *C, TreeElement *UNUSED(te), const eOLSetState set) +{ + if (set == OL_SETSEL_NONE) { + ViewLayer *view_layer = CTX_data_view_layer(C); + LayerCollection *active = CTX_data_layer_collection(C); + + if (active == view_layer->layer_collections.first) { + return OL_DRAWSEL_NORMAL; + } + } + else { + ViewLayer *view_layer = CTX_data_view_layer(C); + LayerCollection *layer_collection = view_layer->layer_collections.first; + BKE_layer_collection_activate(view_layer, layer_collection); + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + } + + return OL_DRAWSEL_NONE; +} + +static eOLDrawState tree_element_active_layer_collection( + bContext *C, TreeElement *te, const eOLSetState set) +{ + if (set == OL_SETSEL_NONE) { + LayerCollection *active = CTX_data_layer_collection(C); + + if (active == te->directdata) { + return OL_DRAWSEL_NORMAL; + } + } + else { + Scene *scene = CTX_data_scene(C); + LayerCollection *layer_collection = te->directdata; + ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection); + BKE_layer_collection_activate(view_layer, layer_collection); + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + } + + return OL_DRAWSEL_NONE; +} + /* ---------------------------------------------- */ /* generic call for ID data check or make/check active in UI */ -eOLDrawState tree_element_active(bContext *C, Scene *scene, SpaceOops *soops, TreeElement *te, +eOLDrawState tree_element_active(bContext *C, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, TreeElement *te, const eOLSetState set, const bool handle_all_types) { switch (te->idcode) { /* Note: ID_OB only if handle_all_type is true, else objects are handled specially to allow multiple - * selection. See do_outliner_item_activate_from_cursor. */ + * selection. See do_outliner_item_activate. */ case ID_OB: if (handle_all_types) { - return tree_element_set_active_object(C, scene, soops, te, set, false); + return tree_element_set_active_object(C, scene, view_layer, soops, te, set, false); } break; case ID_MA: - return tree_element_active_material(C, scene, soops, te, set); + return tree_element_active_material(C, scene, view_layer, soops, te, set); case ID_WO: - return tree_element_active_world(C, scene, soops, te, set); + return tree_element_active_world(C, scene, view_layer, soops, te, set); case ID_LA: - return tree_element_active_lamp(C, scene, soops, te, set); - case ID_TE: - return tree_element_active_texture(C, scene, soops, te, set); + return tree_element_active_lamp(C, scene, view_layer, soops, te, set); case ID_TXT: - return tree_element_active_text(C, scene, soops, te, set); + return tree_element_active_text(C, scene, view_layer, soops, te, set); case ID_CA: - return tree_element_active_camera(C, scene, soops, te, set); + return tree_element_active_camera(C, scene, view_layer, soops, te, set); } return OL_DRAWSEL_NONE; } /** * Generic call for non-id data to make/check active in UI - * - * \note Context can be NULL when ``(set == OL_SETSEL_NONE)`` */ eOLDrawState tree_element_type_active( - bContext *C, Scene *scene, SpaceOops *soops, + bContext *C, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive) { switch (tselem->type) { case TSE_DEFGROUP: - return tree_element_active_defgroup(C, scene, te, tselem, set); + return tree_element_active_defgroup(C, view_layer, te, tselem, set); case TSE_BONE: - return tree_element_active_bone(C, scene, te, tselem, set, recursive); + return tree_element_active_bone(C, view_layer, te, tselem, set, recursive); case TSE_EBONE: - return tree_element_active_ebone(C, scene, te, tselem, set, recursive); + return tree_element_active_ebone(C, te, tselem, set, recursive); case TSE_MODIFIER: - return tree_element_active_modifier(C, te, tselem, set); + return tree_element_active_modifier(C, scene, view_layer, te, tselem, set); case TSE_LINKED_OB: if (set != OL_SETSEL_NONE) { - tree_element_set_active_object(C, scene, soops, te, set, false); + tree_element_set_active_object(C, scene, view_layer, soops, te, set, false); } - else if (tselem->id == (ID *)OBACT) { + else if (tselem->id == (ID *)OBACT(view_layer)) { return OL_DRAWSEL_NORMAL; } break; case TSE_LINKED_PSYS: return tree_element_active_psys(C, scene, te, tselem, set); case TSE_POSE_BASE: - return tree_element_active_pose(C, scene, te, tselem, set); + return tree_element_active_pose(C, view_layer, te, tselem, set); case TSE_POSE_CHANNEL: - return tree_element_active_posechannel(C, scene, te, tselem, set, recursive); + return tree_element_active_posechannel(C, scene, view_layer, te, tselem, set, recursive); case TSE_CONSTRAINT: - return tree_element_active_constraint(C, te, tselem, set); + return tree_element_active_constraint(C, scene, view_layer, te, tselem, set); case TSE_R_LAYER: - return tree_element_active_renderlayer(C, te, tselem, set); + return active_viewlayer(C, scene, view_layer, te, tselem, set); case TSE_POSEGRP: - return tree_element_active_posegroup(C, scene, te, tselem, set); + return tree_element_active_posegroup(C, scene, view_layer, te, tselem, set); case TSE_SEQUENCE: return tree_element_active_sequence(C, scene, te, tselem, set); case TSE_SEQUENCE_DUP: return tree_element_active_sequence_dup(scene, te, tselem, set); case TSE_KEYMAP_ITEM: - return tree_element_active_keymap_item(C, te, tselem, set); + return tree_element_active_keymap_item(C, scene, view_layer, te, tselem, set); case TSE_GP_LAYER: - //return tree_element_active_gplayer(C, scene, te, tselem, set); + //return tree_element_active_gplayer(C, scene, s, te, tselem, set); break; - + case TSE_VIEW_COLLECTION_BASE: + return tree_element_active_master_collection(C, te, set); + case TSE_LAYER_COLLECTION: + return tree_element_active_layer_collection(C, te, set); } return OL_DRAWSEL_NONE; } @@ -902,16 +924,24 @@ eOLDrawState tree_element_type_active( * Needed to run from operators accessed from a menu. */ static void do_outliner_item_activate_tree_element( - bContext *C, Scene *scene, SpaceOops *soops, + bContext *C, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, - bool extend, bool recursive) + const bool extend, const bool recursive) { - /* always makes active object, except for some specific types. - * Note about TSE_EBONE: In case of a same ID_AR datablock shared among several objects, we do not want - * to switch out of edit mode (see T48328 for details). */ - if (!ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP, TSE_EBONE)) { + /* Always makes active object, except for some specific types. */ + if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP, TSE_EBONE, TSE_LAYER_COLLECTION)) { + /* Note about TSE_EBONE: In case of a same ID_AR datablock shared among several objects, we do not want + * to switch out of edit mode (see T48328 for details). */ + } + else if (tselem->id && OB_DATA_SUPPORT_EDITMODE(te->idcode)) { + /* Support edit-mode toggle, keeping the active object as is. */ + } + else if (tselem->type == TSE_POSE_BASE) { + /* Support pose mode toggle, keeping the active object as is. */ + } + else { tree_element_set_active_object( - C, scene, soops, te, + C, scene, view_layer, soops, te, (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, recursive && tselem->type == 0); } @@ -920,100 +950,112 @@ static void do_outliner_item_activate_tree_element( /* editmode? */ if (te->idcode == ID_SCE) { if (scene != (Scene *)tselem->id) { - ED_screen_set_scene(C, CTX_wm_screen(C), (Scene *)tselem->id); + WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), (Scene *)tselem->id); } } else if (te->idcode == ID_GR) { - Group *gr = (Group *)tselem->id; - GroupObject *gob; + Collection *gr = (Collection *)tselem->id; if (extend) { int sel = BA_SELECT; - for (gob = gr->gobject.first; gob; gob = gob->next) { - if (gob->ob->flag & SELECT) { + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(gr, object) + { + Base *base = BKE_view_layer_base_find(view_layer, object); + if (base && (base->flag & BASE_SELECTED)) { sel = BA_DESELECT; break; } } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - for (gob = gr->gobject.first; gob; gob = gob->next) { - ED_base_object_select(BKE_scene_base_find(scene, gob->ob), sel); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(gr, object) + { + Base *base = BKE_view_layer_base_find(view_layer, object); + if (base) { + ED_object_base_select(base, sel); + } } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } else { - BKE_scene_base_deselect_all(scene); - - for (gob = gr->gobject.first; gob; gob = gob->next) { - if ((gob->ob->flag & SELECT) == 0) - ED_base_object_select(BKE_scene_base_find(scene, gob->ob), BA_SELECT); + BKE_view_layer_base_deselect_all(view_layer); + + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(gr, object) + { + Base *base = BKE_view_layer_base_find(view_layer, object); + /* Object may not be in this scene */ + if (base != NULL) { + if ((base->flag & BASE_SELECTED) == 0) { + ED_object_base_select(base, BA_SELECT); + } + } } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } else if (OB_DATA_SUPPORT_EDITMODE(te->idcode)) { - WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL); + Object *ob = (Object *)outliner_search_back(soops, te, ID_OB); + if ((ob != NULL) && (ob->data == tselem->id)) { + Base *base = BKE_view_layer_base_find(view_layer, ob); + if ((base != NULL) && (base->flag & BASE_VISIBLED)) { + do_outliner_activate_obdata(C, scene, view_layer, base); + } + } } else { // rest of types - tree_element_active(C, scene, soops, te, OL_SETSEL_NORMAL, false); + tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false); } } else { - tree_element_type_active( - C, scene, soops, te, tselem, - extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, - recursive); + tree_element_type_active(C, scene, view_layer, soops, te, tselem, + extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, + recursive); } } /** - * Activates tree items, also handles clicking on arrows. + * \param extend: Don't deselect other items, only modify \a te. + * \param toggle: Select \a te when not selected, deselect when selected. */ -static bool do_outliner_item_activate_from_cursor( - bContext *C, Scene *scene, ARegion *ar, SpaceOops *soops, - TreeElement *te, bool extend, bool recursive, const float mval[2]) +void outliner_item_select(SpaceOops *soops, const TreeElement *te, const bool extend, const bool toggle) { - if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { - TreeStoreElem *tselem = TREESTORE(te); - bool openclose = false; - - /* open close icon */ - if ((te->flag & TE_ICONROW) == 0) { // hidden icon, no open/close - if (mval[0] > te->xs && mval[0] < te->xs + UI_UNIT_X) - openclose = true; - } + TreeStoreElem *tselem = TREESTORE(te); + const short new_flag = toggle ? (tselem->flag ^ TSE_SELECTED) : (tselem->flag | TSE_SELECTED); - if (openclose) { - /* all below close/open? */ - if (extend) { - tselem->flag &= ~TSE_CLOSED; - outliner_set_flag(&te->subtree, TSE_CLOSED, !outliner_has_one_flag(&te->subtree, TSE_CLOSED, 1)); - } - else { - if (tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED; - else tselem->flag |= TSE_CLOSED; + if (extend == false) { + outliner_set_flag(&soops->tree, TSE_SELECTED, false); + } + tselem->flag = new_flag; +} - } +static void outliner_item_toggle_closed(TreeElement *te, const bool toggle_children) +{ + TreeStoreElem *tselem = TREESTORE(te); + if (toggle_children) { + tselem->flag &= ~TSE_CLOSED; - return true; - } - /* name and first icon */ - else if (mval[0] > te->xs + UI_UNIT_X && mval[0] < te->xend) { - do_outliner_item_activate_tree_element( - C, scene, soops, - te, tselem, - extend, recursive); - return true; - } + const bool all_opened = !outliner_has_one_flag(&te->subtree, TSE_CLOSED, 1); + outliner_set_flag(&te->subtree, TSE_CLOSED, all_opened); } - - for (te = te->subtree.first; te; te = te->next) { - if (do_outliner_item_activate_from_cursor(C, scene, ar, soops, te, extend, recursive, mval)) { - return true; - } + else { + tselem->flag ^= TSE_CLOSED; } - return false; +} + +static bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) +{ + return ((te->flag & TE_ICONROW) == 0) && (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X); +} + +static bool outliner_is_co_within_restrict_columns(const SpaceOops *soops, const ARegion *ar, float view_co_x) +{ + return ((soops->outlinevis != SO_DATA_API) && + !(soops->flag & SO_HIDE_RESTRICTCOLS) && + (view_co_x > ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX)); } /** @@ -1027,10 +1069,11 @@ void outliner_item_do_activate_from_tree_element( bool extend, bool recursive) { Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); SpaceOops *soops = CTX_wm_space_outliner(C); do_outliner_item_activate_tree_element( - C, scene, soops, + C, scene, view_layer, soops, te, tselem, extend, recursive); } @@ -1044,56 +1087,53 @@ int outliner_item_do_activate_from_cursor( bContext *C, const int mval[2], bool extend, bool recursive) { - Scene *scene = CTX_data_scene(C); ARegion *ar = CTX_wm_region(C); SpaceOops *soops = CTX_wm_space_outliner(C); TreeElement *te; - float fmval[2]; + float view_mval[2]; + bool changed = false, rebuild_tree = false; - UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &fmval[0], &fmval[1]); + UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]); - if (!ELEM(soops->outlinevis, SO_DATABLOCKS, SO_USERDEF) && - !(soops->flag & SO_HIDE_RESTRICTCOLS) && - (fmval[0] > ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX)) - { + if (outliner_is_co_within_restrict_columns(soops, ar, view_mval[0])) { return OPERATOR_CANCELLED; } - for (te = soops->tree.first; te; te = te->next) { - if (do_outliner_item_activate_from_cursor(C, scene, ar, soops, te, extend, recursive, fmval)) { - break; - } + if (!(te = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]))) { + /* skip */ } - - if (te) { - ED_undo_push(C, "Outliner click event"); + else if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) { + outliner_item_toggle_closed(te, extend); + changed = true; + rebuild_tree = true; } else { - short selecting = -1; - int row; - - /* get row number - 100 here is just a dummy value since we don't need the column */ - UI_view2d_listview_view_to_cell(&ar->v2d, 1000, UI_UNIT_Y, 0.0f, OL_Y_OFFSET, - fmval[0], fmval[1], NULL, &row); - - /* select relevant row */ - if (outliner_select(soops, &soops->tree, &row, &selecting)) { + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + /* the row may also contain children, if one is hovered we want this instead of current te */ + TreeElement *activate_te = outliner_find_item_at_x_in_row(soops, te, view_mval[0]); + TreeStoreElem *activate_tselem = TREESTORE(activate_te); - soops->storeflag |= SO_TREESTORE_REDRAW; + outliner_item_select(soops, activate_te, extend, extend); + do_outliner_item_activate_tree_element(C, scene, view_layer, soops, activate_te, activate_tselem, extend, recursive); + changed = true; + } - /* no need for undo push here, only changing outliner data which is - * scene level - campbell */ - /* ED_undo_push(C, "Outliner selection event"); */ + if (changed) { + if (rebuild_tree) { + ED_region_tag_redraw(ar); + } + else { + ED_region_tag_redraw_no_rebuild(ar); } + ED_undo_push(C, "Outliner selection change"); } - ED_region_tag_redraw(ar); - return OPERATOR_FINISHED; } /* event can enterkey, then it opens/closes */ -static int outliner_item_activate(bContext *C, wmOperator *op, const wmEvent *event) +static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { bool extend = RNA_boolean_get(op->ptr, "extend"); bool recursive = RNA_boolean_get(op->ptr, "recursive"); @@ -1106,7 +1146,7 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) ot->idname = "OUTLINER_OT_item_activate"; ot->description = "Handle mouse clicks to activate/select items"; - ot->invoke = outliner_item_activate; + ot->invoke = outliner_item_activate_invoke; ot->poll = ED_operator_outliner_active; @@ -1154,6 +1194,7 @@ static int outliner_border_select_exec(bContext *C, wmOperator *op) outliner_item_border_select(scene, &rectf, te, select); } + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); ED_region_tag_redraw(ar); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 75151325bdf..eec0f083543 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -51,12 +51,13 @@ #include "BLI_utildefines.h" #include "BKE_animsys.h" +#include "BKE_collection.h" #include "BKE_context.h" #include "BKE_constraint.h" -#include "BKE_depsgraph.h" #include "BKE_fcurve.h" -#include "BKE_group.h" +#include "BKE_layer.h" #include "BKE_library.h" +#include "BKE_library_override.h" #include "BKE_library_query.h" #include "BKE_library_remap.h" #include "BKE_main.h" @@ -64,8 +65,12 @@ #include "BKE_scene.h" #include "BKE_sequencer.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + #include "ED_armature.h" #include "ED_object.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_sequencer.h" #include "ED_undo.h" @@ -100,7 +105,8 @@ static void set_operation_types(SpaceOops *soops, ListBase *lb, for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { - if (tselem->type) { + /* 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) @@ -124,6 +130,9 @@ static void set_operation_types(SpaceOops *soops, ListBase *lb, case ID_LI: if (*idlevel == 0) *idlevel = idcode; else if (*idlevel != idcode) *idlevel = -1; + if (ELEM(*datalevel, TSE_VIEW_COLLECTION_BASE, TSE_SCENE_COLLECTION_BASE)) { + *datalevel = 0; + } break; } } @@ -191,19 +200,7 @@ static void unlink_texture_cb( MTex **mtex = NULL; int a; - if (GS(tsep->id->name) == ID_MA) { - Material *ma = (Material *)tsep->id; - mtex = ma->mtex; - } - else if (GS(tsep->id->name) == ID_LA) { - Lamp *la = (Lamp *)tsep->id; - mtex = la->mtex; - } - else if (GS(tsep->id->name) == ID_WO) { - World *wrld = (World *)tsep->id; - mtex = wrld->mtex; - } - else if (GS(tsep->id->name) == ID_LS) { + if (GS(tsep->id->name) == ID_LS) { FreestyleLineStyle *ls = (FreestyleLineStyle *)tsep->id; mtex = ls->mtex; } @@ -221,21 +218,59 @@ static void unlink_texture_cb( } } -static void unlink_group_cb( +static void unlink_collection_cb( bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *tsep, TreeStoreElem *tselem, void *UNUSED(user_data)) { - Group *group = (Group *)tselem->id; + Main *bmain = CTX_data_main(C); + Collection *collection = (Collection *)tselem->id; if (tsep) { if (GS(tsep->id->name) == ID_OB) { Object *ob = (Object *)tsep->id; ob->dup_group = NULL; + DEG_id_tag_update(&ob->id, OB_RECALC_OB); + DEG_relations_tag_update(bmain); + } + else if (GS(tsep->id->name) == ID_GR) { + Collection *parent = (Collection *)tsep->id; + id_fake_user_set(&collection->id); + BKE_collection_child_remove(bmain, parent, collection); + DEG_id_tag_update(&parent->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + } + else if (GS(tsep->id->name) == ID_SCE) { + Scene *scene = (Scene *)tsep->id; + Collection *parent = BKE_collection_master(scene); + id_fake_user_set(&collection->id); + BKE_collection_child_remove(bmain, parent, collection); + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); } } - else { - Main *bmain = CTX_data_main(C); - BKE_libblock_delete(bmain, group); +} + +static void unlink_object_cb( + bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te), + TreeStoreElem *tsep, TreeStoreElem *tselem, void *UNUSED(user_data)) +{ + Main *bmain = CTX_data_main(C); + Object *ob = (Object *)tselem->id; + + if (tsep) { + if (GS(tsep->id->name) == ID_GR) { + Collection *parent = (Collection *)tsep->id; + BKE_collection_object_remove(bmain, parent, ob, true); + DEG_id_tag_update(&parent->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + } + else if (GS(tsep->id->name) == ID_SCE) { + Scene *scene = (Scene *)tsep->id; + Collection *parent = BKE_collection_master(scene); + BKE_collection_object_remove(bmain, parent, ob, true); + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + } } } @@ -262,7 +297,7 @@ static void outliner_do_libdata_operation( for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { - if (tselem->type == 0) { + if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) { TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL; operation_cb(C, reports, scene, te, tsep, tselem, user_data); } @@ -308,7 +343,7 @@ static bool scene_cb(bContext *C, eOutliner_PropSceneOps event, TreeElement *UNU Scene *scene = (Scene *)tselem->id; if (event == OL_SCENE_OP_DELETE) { - if (ED_screen_delete_scene(C, scene)) { + if (ED_scene_delete(C, CTX_data_main(C), CTX_wm_window(C), scene)) { WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, scene); } else { @@ -359,15 +394,15 @@ void OUTLINER_OT_scene_operation(wmOperatorType *ot) /* ******************************************** */ static void object_select_cb( - bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *te, + bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) { - Base *base = (Base *)te->directdata; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = (Object *)tselem->id; + Base *base = BKE_view_layer_base_find(view_layer, ob); - if (base == NULL) base = BKE_scene_base_find(scene, (Object *)tselem->id); - if (base && ((base->object->restrictflag & OB_RESTRICT_VIEW) == 0)) { - base->flag |= SELECT; - base->object->flag |= SELECT; + if (base && ((base->flag & BASE_VISIBLED) != 0)) { + base->flag |= BASE_SELECTED; } } @@ -381,15 +416,15 @@ static void object_select_hierarchy_cb( } static void object_deselect_cb( - bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *te, + bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) { - Base *base = (Base *)te->directdata; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = (Object *)tselem->id; + Base *base = BKE_view_layer_base_find(view_layer, ob); - if (base == NULL) base = BKE_scene_base_find(scene, (Object *)tselem->id); if (base) { - base->flag &= ~SELECT; - base->object->flag &= ~SELECT; + base->flag &= ~BASE_SELECTED; } } @@ -397,31 +432,27 @@ static void object_delete_cb( bContext *C, ReportList *reports, Scene *scene, TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem, void *user_data) { - Base *base = (Base *)te->directdata; - - if (base == NULL) - base = BKE_scene_base_find(scene, (Object *)tselem->id); - if (base) { + Object *ob = (Object *)tselem->id; + if (ob) { Main *bmain = CTX_data_main(C); - if (base->object->id.tag & LIB_TAG_INDIRECT) { - BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", base->object->id.name + 2); + if (ob->id.tag & LIB_TAG_INDIRECT) { + BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", ob->id.name + 2); return; } - else if (BKE_library_ID_is_indirectly_used(bmain, base->object) && - ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) + else if (BKE_library_ID_is_indirectly_used(bmain, ob) && + ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0) { BKE_reportf(reports, RPT_WARNING, "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", - base->object->id.name + 2, scene->id.name + 2); + ob->id.name + 2, scene->id.name + 2); return; } // check also library later - if (scene->obedit == base->object) { + if (ob == CTX_data_edit_object(C)) { ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR); } - - ED_base_object_free_and_unlink(CTX_data_main(C), scene, base); + ED_object_base_free_and_unlink(CTX_data_main(C), scene, ob); /* leave for ED_outliner_id_unref to handle */ #if 0 te->directdata = NULL; @@ -454,6 +485,19 @@ static void id_local_cb( } } +static void id_static_override_cb( + bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te), + TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) +{ + if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) { + Main *bmain = CTX_data_main(C); + ID *override_id = BKE_override_static_create_from_id(bmain, tselem->id); + if (override_id != NULL) { + BKE_main_id_clear_newpoins(bmain); + } + } +} + static void id_fake_user_set_cb( bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) @@ -518,38 +562,6 @@ static void singleuser_world_cb( } } -static void group_linkobs2scene_cb( - bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) -{ - Group *group = (Group *)tselem->id; - GroupObject *gob; - Base *base; - - for (gob = group->gobject.first; gob; gob = gob->next) { - base = BKE_scene_base_find(scene, gob->ob); - if (!base) { - /* link to scene */ - base = BKE_scene_base_add(scene, gob->ob); - id_us_plus(&gob->ob->id); - } - base->object->flag |= SELECT; - base->flag |= SELECT; - } -} - -static void group_instance_cb( - bContext *C, ReportList *UNUSED(reports), Scene *scene, TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) -{ - Group *group = (Group *)tselem->id; - - Object *ob = ED_object_add_type(C, OB_EMPTY, group->id.name + 2, scene->cursor, NULL, false, scene->layact); - ob->dup_group = group; - ob->transflag |= OB_DUPLIGROUP; - id_lib_extern(&group->id); -} - /** * \param select_recurse: Set to false for operations which are already recursively operating on their children. */ @@ -567,7 +579,7 @@ void outliner_do_object_operation_ex( // when objects selected in other scenes... dunno if that should be allowed Scene *scene_owner = (Scene *)outliner_search_back(soops, te, ID_SCE); if (scene_owner && scene_act != scene_owner) { - ED_screen_set_scene(C, CTX_wm_screen(C), scene_owner); + WM_window_change_active_scene(CTX_data_main(C), C, CTX_wm_window(C), scene_owner); } /* important to use 'scene_owner' not scene_act else deleting objects can crash. * only use 'scene_act' when 'scene_owner' is NULL, which can happen when the @@ -790,12 +802,12 @@ static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem if (event == OL_MODIFIER_OP_TOGVIS) { md->mode ^= eModifierMode_Realtime; - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); } else if (event == OL_MODIFIER_OP_TOGREN) { md->mode ^= eModifierMode_Render; - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); } else if (event == OL_MODIFIER_OP_DELETE) { @@ -829,12 +841,13 @@ static Base *outline_delete_hierarchy(bContext *C, ReportList *reports, Scene *s { Base *child_base, *base_next; Object *parent; + ViewLayer *view_layer = CTX_data_view_layer(C); if (!base) { return NULL; } - for (child_base = scene->base.first; child_base; child_base = base_next) { + for (child_base = view_layer->object_bases.first; child_base; child_base = base_next) { base_next = child_base->next; for (parent = child_base->object->parent; parent && (parent != base->object); parent = parent->parent); if (parent) { @@ -857,7 +870,7 @@ static Base *outline_delete_hierarchy(bContext *C, ReportList *reports, Scene *s base->object->id.name + 2, scene->id.name + 2); return base_next; } - ED_base_object_free_and_unlink(CTX_data_main(C), scene, base); + ED_object_base_free_and_unlink(CTX_data_main(C), scene, base->object); return base_next; } @@ -865,11 +878,12 @@ static void object_delete_hierarchy_cb( bContext *C, ReportList *reports, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) { + ViewLayer *view_layer = CTX_data_view_layer(C); Base *base = (Base *)te->directdata; - Object *obedit = scene->obedit; + Object *obedit = CTX_data_edit_object(C); if (!base) { - base = BKE_scene_base_find(scene, (Object *)tselem->id); + base = BKE_view_layer_base_find(view_layer, (Object *)tselem->id); } if (base) { /* Check also library later. */ @@ -886,6 +900,7 @@ static void object_delete_hierarchy_cb( #endif } + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); } @@ -903,6 +918,8 @@ enum { OL_OP_TOGSEL, OL_OP_TOGREN, OL_OP_RENAME, + OL_OP_OBJECT_MODE_ENTER, + OL_OP_OBJECT_MODE_EXIT, }; static const EnumPropertyItem prop_object_op_types[] = { @@ -913,10 +930,9 @@ static const EnumPropertyItem prop_object_op_types[] = { {OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""}, {OL_OP_REMAP, "REMAP", 0, "Remap Users", "Make all users of selected data-blocks to use instead a new chosen one"}, - {OL_OP_TOGVIS, "TOGVIS", 0, "Toggle Visible", ""}, - {OL_OP_TOGSEL, "TOGSEL", 0, "Toggle Selectable", ""}, - {OL_OP_TOGREN, "TOGREN", 0, "Toggle Renderable", ""}, {OL_OP_RENAME, "RENAME", 0, "Rename", ""}, + {OL_OP_OBJECT_MODE_ENTER, "OBJECT_MODE_ENTER", 0, "Enter Mode", ""}, + {OL_OP_OBJECT_MODE_EXIT, "OBJECT_MODE_EXIT", 0, "Exit Mode", ""}, {0, NULL, 0, NULL, NULL} }; @@ -924,6 +940,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + wmWindow *win = CTX_wm_window(C); SpaceOops *soops = CTX_wm_space_outliner(C); int event; const char *str = NULL; @@ -938,24 +955,27 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) Scene *sce = scene; // to be able to delete, scenes are set... outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_select_cb); if (scene != sce) { - ED_screen_set_scene(C, CTX_wm_screen(C), sce); + WM_window_change_active_scene(bmain, C, win, sce); } str = "Select Objects"; + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } else if (event == OL_OP_SELECT_HIERARCHY) { Scene *sce = scene; // to be able to delete, scenes are set... outliner_do_object_operation_ex(C, op->reports, scene, soops, &soops->tree, object_select_hierarchy_cb, false); if (scene != sce) { - ED_screen_set_scene(C, CTX_wm_screen(C), sce); + WM_window_change_active_scene(bmain, C, win, sce); } str = "Select Object Hierarchy"; + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } else if (event == OL_OP_DESELECT) { outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_deselect_cb); str = "Deselect Objects"; + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } else if (event == OL_OP_DELETE) { @@ -968,8 +988,9 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) * cleanup tree here to prevent such cases. */ outliner_cleanup_tree(soops); - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); str = "Delete Objects"; + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); } else if (event == OL_OP_DELETE_HIERARCHY) { @@ -978,8 +999,9 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) /* XXX: See OL_OP_DELETE comment above. */ outliner_cleanup_tree(soops); - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); str = "Delete Object Hierarchy"; + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); } else if (event == OL_OP_REMAP) { @@ -990,25 +1012,18 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb); str = "Localized Objects"; } - else if (event == OL_OP_TOGVIS) { - outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_visibility_cb); - str = "Toggle Visibility"; - WM_event_add_notifier(C, NC_SCENE | ND_OB_VISIBLE, scene); - } - else if (event == OL_OP_TOGSEL) { - outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_selectability_cb); - str = "Toggle Selectability"; - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - } - else if (event == OL_OP_TOGREN) { - outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_renderability_cb); - str = "Toggle Renderability"; - WM_event_add_notifier(C, NC_SCENE | ND_OB_RENDER, scene); - } else if (event == OL_OP_RENAME) { outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, item_rename_cb); str = "Rename Object"; } + else if (event == OL_OP_OBJECT_MODE_ENTER) { + outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, item_object_mode_enter_cb); + str = "Enter Current Mode"; + } + else if (event == OL_OP_OBJECT_MODE_EXIT) { + outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, item_object_mode_exit_cb); + str = "Exit Current Mode"; + } else { BLI_assert(0); return OPERATOR_CANCELLED; @@ -1039,114 +1054,12 @@ void OUTLINER_OT_object_operation(wmOperatorType *ot) /* **************************************** */ -typedef enum eOutliner_PropGroupOps { - OL_GROUPOP_UNLINK = 1, - OL_GROUPOP_LOCAL, - OL_GROUPOP_LINK, - OL_GROUPOP_DELETE, - OL_GROUPOP_REMAP, - OL_GROUPOP_INSTANCE, - OL_GROUPOP_TOGVIS, - OL_GROUPOP_TOGSEL, - OL_GROUPOP_TOGREN, - OL_GROUPOP_RENAME, -} eOutliner_PropGroupOps; - -static const EnumPropertyItem prop_group_op_types[] = { - {OL_GROUPOP_UNLINK, "UNLINK", 0, "Unlink Group", ""}, - {OL_GROUPOP_LOCAL, "LOCAL", 0, "Make Local Group", ""}, - {OL_GROUPOP_LINK, "LINK", 0, "Link Group Objects to Scene", ""}, - {OL_GROUPOP_DELETE, "DELETE", 0, "Delete Group", ""}, - {OL_GROUPOP_REMAP, "REMAP", 0, "Remap Users", - "Make all users of selected data-blocks to use instead current (clicked) one"}, - {OL_GROUPOP_INSTANCE, "INSTANCE", 0, "Instance Groups in Scene", ""}, - {OL_GROUPOP_TOGVIS, "TOGVIS", 0, "Toggle Visible Group", ""}, - {OL_GROUPOP_TOGSEL, "TOGSEL", 0, "Toggle Selectable", ""}, - {OL_GROUPOP_TOGREN, "TOGREN", 0, "Toggle Renderable", ""}, - {OL_GROUPOP_RENAME, "RENAME", 0, "Rename", ""}, - {0, NULL, 0, NULL, NULL} -}; - -static int outliner_group_operation_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - int event; - - /* check for invalid states */ - if (soops == NULL) - return OPERATOR_CANCELLED; - - event = RNA_enum_get(op->ptr, "type"); - - switch (event) { - case OL_GROUPOP_UNLINK: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_group_cb, NULL); - break; - case OL_GROUPOP_LOCAL: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb, NULL); - break; - case OL_GROUPOP_LINK: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_linkobs2scene_cb, NULL); - break; - case OL_GROUPOP_INSTANCE: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_instance_cb, NULL); - /* works without this except if you try render right after, see: 22027 */ - DAG_relations_tag_update(CTX_data_main(C)); - break; - case OL_GROUPOP_DELETE: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_delete_cb, NULL); - break; - case OL_GROUPOP_REMAP: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL); - break; - case OL_GROUPOP_TOGVIS: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_toggle_visibility_cb, NULL); - break; - case OL_GROUPOP_TOGSEL: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_toggle_selectability_cb, NULL); - break; - case OL_GROUPOP_TOGREN: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_toggle_renderability_cb, NULL); - break; - case OL_GROUPOP_RENAME: - outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, item_rename_cb, NULL); - break; - default: - BLI_assert(0); - } - - ED_undo_push(C, prop_group_op_types[event - 1].name); - WM_event_add_notifier(C, NC_GROUP, NULL); - - return OPERATOR_FINISHED; -} - - -void OUTLINER_OT_group_operation(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Outliner Group Operation"; - ot->idname = "OUTLINER_OT_group_operation"; - ot->description = ""; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = outliner_group_operation_exec; - ot->poll = ED_operator_outliner_active; - - ot->flag = 0; - - ot->prop = RNA_def_enum(ot->srna, "type", prop_group_op_types, 0, "Group Operation", ""); -} - -/* **************************************** */ - typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_INVALID = 0, OUTLINER_IDOP_UNLINK, OUTLINER_IDOP_LOCAL, + OUTLINER_IDOP_STATIC_OVERRIDE, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, OUTLINER_IDOP_REMAP, @@ -1162,6 +1075,8 @@ typedef enum eOutlinerIdOpTypes { static const EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""}, {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""}, + {OUTLINER_IDOP_STATIC_OVERRIDE, "STATIC_OVERRIDE", + 0, "Add Static Override", "Add a local static override of this data-block"}, {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""}, {OUTLINER_IDOP_DELETE, "DELETE", 0, "Delete", "WARNING: no undo"}, {OUTLINER_IDOP_REMAP, "REMAP", 0, "Remap Users", @@ -1193,6 +1108,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_UNLINK: { /* unlink datablock from its parent */ + if (objectlevel) { + outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_object_cb, NULL); + + WM_event_add_notifier(C, NC_SCENE | ND_LAYER, NULL); + ED_undo_push(C, "Unlink Object"); + break; + } + switch (idlevel) { case ID_AC: outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_action_cb, NULL); @@ -1218,6 +1141,12 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL); ED_undo_push(C, "Unlink world"); break; + case ID_GR: + outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_collection_cb, NULL); + + WM_event_add_notifier(C, NC_SCENE | ND_LAYER, NULL); + ED_undo_push(C, "Unlink Collection"); + break; default: BKE_report(op->reports, RPT_WARNING, "Not yet implemented"); break; @@ -1231,6 +1160,13 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Localized Data"); break; } + case OUTLINER_IDOP_STATIC_OVERRIDE: + { + /* make local */ + outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_static_override_cb, NULL); + ED_undo_push(C, "Overrided Data"); + break; + } case OUTLINER_IDOP_SINGLE: { /* make single user */ @@ -1630,7 +1566,7 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) /* update dependencies */ if (updateDeps) { /* rebuild depsgraph for the new deps */ - DAG_relations_tag_update(CTX_data_main(C)); + DEG_relations_tag_update(CTX_data_main(C)); } return OPERATOR_FINISHED; @@ -1657,8 +1593,8 @@ void OUTLINER_OT_animdata_operation(wmOperatorType *ot) /* **************************************** */ static const EnumPropertyItem prop_constraint_op_types[] = { - {OL_CONSTRAINTOP_ENABLE, "ENABLE", ICON_RESTRICT_VIEW_OFF, "Enable", ""}, - {OL_CONSTRAINTOP_DISABLE, "DISABLE", ICON_RESTRICT_VIEW_ON, "Disable", ""}, + {OL_CONSTRAINTOP_ENABLE, "ENABLE", ICON_HIDE_OFF, "Enable", ""}, + {OL_CONSTRAINTOP_DISABLE, "DISABLE", ICON_HIDE_ON, "Disable", ""}, {OL_CONSTRAINTOP_DELETE, "DELETE", ICON_X, "Delete", ""}, {0, NULL, 0, NULL, NULL} }; @@ -1864,9 +1800,10 @@ static int do_outliner_operation_event(bContext *C, ARegion *ar, SpaceOops *soop outliner_set_flag(&soops->tree, TSE_SELECTED, 0); tselem->flag |= TSE_SELECTED; - /* redraw, same as outliner_select function */ - soops->storeflag |= SO_TREESTORE_REDRAW; - ED_region_tag_redraw(ar); + + /* Only redraw, don't rebuild here because TreeElement pointers will + * become invalid and operations will crash. */ + ED_region_tag_redraw_no_rebuild(ar); } set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -1880,7 +1817,7 @@ static int do_outliner_operation_event(bContext *C, ARegion *ar, SpaceOops *soop } } else if (objectlevel) { - WM_operator_name_call(C, "OUTLINER_OT_object_operation", WM_OP_INVOKE_REGION_WIN, NULL); + WM_menu_name_call(C, "OUTLINER_MT_object", WM_OP_INVOKE_REGION_WIN); } else if (idlevel) { if (idlevel == -1 || datalevel) { @@ -1889,7 +1826,7 @@ static int do_outliner_operation_event(bContext *C, ARegion *ar, SpaceOops *soop else { switch (idlevel) { case ID_GR: - WM_operator_name_call(C, "OUTLINER_OT_group_operation", WM_OP_INVOKE_REGION_WIN, NULL); + WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN); break; case ID_LI: WM_operator_name_call(C, "OUTLINER_OT_lib_operation", WM_OP_INVOKE_REGION_WIN, NULL); @@ -1910,8 +1847,11 @@ static int do_outliner_operation_event(bContext *C, ARegion *ar, SpaceOops *soop else if (datalevel == TSE_DRIVER_BASE) { /* do nothing... no special ops needed yet */ } - else if (ELEM(datalevel, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)) { - /*WM_operator_name_call(C, "OUTLINER_OT_renderdata_operation", WM_OP_INVOKE_REGION_WIN, NULL)*/ + else if (datalevel == TSE_LAYER_COLLECTION) { + WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN); + } + else 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); } else if (datalevel == TSE_ID_BASE) { /* do nothing... there are no ops needed here yet */ @@ -1946,6 +1886,7 @@ static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent uiBut *but = UI_context_active_but_get(C); TreeElement *te; float fmval[2]; + bool found = false; if (but) { UI_but_tooltip_timer_remove(C, but); @@ -1955,10 +1896,18 @@ static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent for (te = soops->tree.first; te; te = te->next) { if (do_outliner_operation_event(C, ar, soops, te, fmval)) { + found = true; break; } } + if (!found) { + /* Menus for clicking in empty space. */ + if (soops->outlinevis == SO_VIEW_LAYER) { + WM_menu_name_call(C, "OUTLINER_MT_collection_new", WM_OP_INVOKE_REGION_WIN); + } + } + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index b54a7705686..63312561678 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -46,6 +46,7 @@ #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_lightprobe_types.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" #include "DNA_world_types.h" @@ -63,12 +64,16 @@ #include "BKE_fcurve.h" #include "BKE_main.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_modifier.h" #include "BKE_sequencer.h" #include "BKE_idcode.h" #include "BKE_outliner_treehash.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + #include "ED_armature.h" #include "ED_screen.h" @@ -77,12 +82,19 @@ #include "RNA_access.h" +#include "UI_interface.h" + #include "outliner_intern.h" #ifdef WIN32 # include "BLI_math_base.h" /* M_PI */ #endif +/* prototypes */ +static TreeElement *outliner_add_collection_recursive( + SpaceOops *soops, Collection *collection, TreeElement *ten); +static void outliner_make_object_parent_hierarchy(ListBase *lb); + /* ********************************************************* */ /* Persistent Data */ @@ -141,6 +153,9 @@ static void outliner_storage_cleanup(SpaceOops *soops) } } } + else if (soops->treehash) { + BKE_outliner_treehash_clear_used(soops->treehash); + } } } @@ -180,16 +195,11 @@ static void check_persistent(SpaceOops *soops, TreeElement *te, ID *id, short ty /* ********************************************************* */ /* Tree Management */ -void outliner_free_tree(ListBase *lb) +void outliner_free_tree(ListBase *tree) { - while (lb->first) { - TreeElement *te = lb->first; - - outliner_free_tree(&te->subtree); - BLI_remlink(lb, te); - - if (te->flag & TE_FREE_NAME) MEM_freeN((void *)te->name); - MEM_freeN(te); + for (TreeElement *element = tree->first, *element_next; element; element = element_next) { + element_next = element->next; + outliner_free_tree_element(element, tree); } } @@ -199,103 +209,23 @@ void outliner_cleanup_tree(SpaceOops *soops) outliner_storage_cleanup(soops); } -/* Find specific item from the treestore */ -TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem) -{ - TreeElement *te, *tes; - for (te = lb->first; te; te = te->next) { - if (te->store_elem == store_elem) return te; - tes = outliner_find_tree_element(&te->subtree, store_elem); - if (tes) return tes; - } - return NULL; -} - -/* tse is not in the treestore, we use its contents to find a match */ -TreeElement *outliner_find_tse(SpaceOops *soops, const TreeStoreElem *tse) -{ - TreeStoreElem *tselem; - - if (tse->id == NULL) return NULL; - - /* check if 'tse' is in treestore */ - tselem = BKE_outliner_treehash_lookup_any(soops->treehash, tse->type, tse->nr, tse->id); - if (tselem) - return outliner_find_tree_element(&soops->tree, tselem); - - return NULL; -} - -/* Find treestore that refers to given ID */ -TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, const ID *id) -{ - for (TreeElement *te = lb->first; te; te = te->next) { - TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0) { - if (tselem->id == id) { - return te; - } - /* only deeper on scene or object */ - if (ELEM(te->idcode, ID_OB, ID_SCE) || - ((soops->outlinevis == SO_GROUPS) && (te->idcode == ID_GR))) - { - TreeElement *tes = outliner_find_id(soops, &te->subtree, id); - if (tes) { - return tes; - } - } - } - } - return NULL; -} - -TreeElement *outliner_find_posechannel(ListBase *lb, const bPoseChannel *pchan) -{ - for (TreeElement *te = lb->first; te; te = te->next) { - if (te->directdata == pchan) { - return te; - } - - TreeStoreElem *tselem = TREESTORE(te); - if (ELEM(tselem->type, TSE_POSE_BASE, TSE_POSE_CHANNEL)) { - TreeElement *tes = outliner_find_posechannel(&te->subtree, pchan); - if (tes) { - return tes; - } - } - } - return NULL; -} - -TreeElement *outliner_find_editbone(ListBase *lb, const EditBone *ebone) +/** + * Free \a element and its sub-tree and remove its link in \a parent_subtree. + * + * \note Does not remove the TreeStoreElem of \a element! + * \param parent_subtree Subtree of the parent element, so the list containing \a element. + */ +void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree) { - for (TreeElement *te = lb->first; te; te = te->next) { - if (te->directdata == ebone) { - return te; - } - - TreeStoreElem *tselem = TREESTORE(te); - if (ELEM(tselem->type, 0, TSE_EBONE)) { - TreeElement *tes = outliner_find_editbone(&te->subtree, ebone); - if (tes) { - return tes; - } - } - } - return NULL; -} + BLI_assert(BLI_findindex(parent_subtree, element) > -1); + BLI_remlink(parent_subtree, element); -ID *outliner_search_back(SpaceOops *UNUSED(soops), TreeElement *te, short idcode) -{ - TreeStoreElem *tselem; - te = te->parent; + outliner_free_tree(&element->subtree); - while (te) { - tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == idcode) return tselem->id; - te = te->parent; + if (element->flag & TE_FREE_NAME) { + MEM_freeN((void *)element->name); } - return NULL; + MEM_freeN(element); } @@ -322,98 +252,6 @@ static void outliner_add_bone(SpaceOops *soops, ListBase *lb, ID *id, Bone *curB } } -/* -------------------------------------------------------- */ - -#define LOG2I(x) (int)(log(x) / M_LN2) - -static void outliner_add_passes(SpaceOops *soops, TreeElement *tenla, ID *id, SceneRenderLayer *srl) -{ - TreeStoreElem *tselem = NULL; - TreeElement *te = NULL; - - /* log stuff is to convert bitflags (powers of 2) to small integers, - * in order to not overflow short tselem->nr */ - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_COMBINED)); - te->name = IFACE_("Combined"); - te->directdata = &srl->passflag; - - /* save cpu cycles, but we add the first to invoke an open/close triangle */ - tselem = TREESTORE(tenla); - if (tselem->flag & TSE_CLOSED) - return; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_Z)); - te->name = IFACE_("Z"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_VECTOR)); - te->name = IFACE_("Vector"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_NORMAL)); - te->name = IFACE_("Normal"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_UV)); - te->name = IFACE_("UV"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_MIST)); - te->name = IFACE_("Mist"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDEXOB)); - te->name = IFACE_("Index Object"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDEXMA)); - te->name = IFACE_("Index Material"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_RGBA)); - te->name = IFACE_("Color"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_DIFFUSE)); - te->name = IFACE_("Diffuse"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_SPEC)); - te->name = IFACE_("Specular"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_SHADOW)); - te->name = IFACE_("Shadow"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_AO)); - te->name = IFACE_("AO"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_REFLECT)); - te->name = IFACE_("Reflection"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_REFRACT)); - te->name = IFACE_("Refraction"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDIRECT)); - te->name = IFACE_("Indirect"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_ENVIRONMENT)); - te->name = IFACE_("Environment"); - te->directdata = &srl->passflag; - - te = outliner_add_element(soops, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_EMIT)); - te->name = IFACE_("Emit"); - te->directdata = &srl->passflag; -} - -#undef LOG2I - static bool outliner_animdata_test(AnimData *adt) { if (adt) @@ -424,19 +262,19 @@ static bool outliner_animdata_test(AnimData *adt) #ifdef WITH_FREESTYLE static void outliner_add_line_styles(SpaceOops *soops, ListBase *lb, Scene *sce, TreeElement *te) { - SceneRenderLayer *srl; + ViewLayer *view_layer; FreestyleLineSet *lineset; - for (srl = sce->r.layers.first; srl; srl = srl->next) { - for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) { + for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) { + for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) { FreestyleLineStyle *linestyle = lineset->linestyle; if (linestyle) { linestyle->id.tag |= LIB_TAG_DOIT; } } } - for (srl = sce->r.layers.first; srl; srl = srl->next) { - for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) { + for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) { + for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) { FreestyleLineStyle *linestyle = lineset->linestyle; if (linestyle) { if (!(linestyle->id.tag & LIB_TAG_DOIT)) @@ -451,41 +289,142 @@ static void outliner_add_line_styles(SpaceOops *soops, ListBase *lb, Scene *sce, static void outliner_add_scene_contents(SpaceOops *soops, ListBase *lb, Scene *sce, TreeElement *te) { - SceneRenderLayer *srl; - TreeElement *tenla = outliner_add_element(soops, lb, sce, te, TSE_R_LAYER_BASE, 0); - int a; + /* View layers */ + TreeElement *ten = outliner_add_element(soops, lb, sce, te, TSE_R_LAYER_BASE, 0); + ten->name = IFACE_("View Layers"); - tenla->name = IFACE_("RenderLayers"); - for (a = 0, srl = sce->r.layers.first; srl; srl = srl->next, a++) { - TreeElement *tenlay = outliner_add_element(soops, &tenla->subtree, sce, te, TSE_R_LAYER, a); - tenlay->name = srl->name; - tenlay->directdata = &srl->layflag; + ViewLayer *view_layer; + for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) { + TreeElement *tenlay = outliner_add_element(soops, &ten->subtree, sce, te, TSE_R_LAYER, 0); + tenlay->name = view_layer->name; + tenlay->directdata = view_layer; + } - if (srl->light_override) - outliner_add_element(soops, &tenlay->subtree, srl->light_override, tenlay, TSE_LINKED_LAMP, 0); - if (srl->mat_override) - outliner_add_element(soops, &tenlay->subtree, srl->mat_override, tenlay, TSE_LINKED_MAT, 0); + /* Collections */ + ten = outliner_add_element(soops, lb, &sce->id, te, TSE_SCENE_COLLECTION_BASE, 0); + ten->name = IFACE_("Scene Collection"); + outliner_add_collection_recursive(soops, sce->master_collection, ten); - outliner_add_passes(soops, tenlay, &sce->id, srl); + /* Objects */ + ten = outliner_add_element(soops, lb, sce, te, TSE_SCENE_OBJECTS_BASE, 0); + ten->name = IFACE_("Objects"); + FOREACH_SCENE_OBJECT_BEGIN(sce, ob) + { + outliner_add_element(soops, &ten->subtree, ob, NULL, 0, 0); } + FOREACH_SCENE_OBJECT_END; + outliner_make_object_parent_hierarchy(&ten->subtree); - // TODO: move this to the front? + /* Animation Data */ if (outliner_animdata_test(sce->adt)) outliner_add_element(soops, lb, sce, te, TSE_ANIM_DATA, 0); + /* Grease Pencil */ outliner_add_element(soops, lb, sce->gpd, te, 0, 0); +} - outliner_add_element(soops, lb, sce->world, te, 0, 0); +TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata) +{ + struct ObjectsSelectedData *data = customdata; + TreeStoreElem *tselem = TREESTORE(te); -#ifdef WITH_FREESTYLE - if (STREQ(sce->r.engine, RE_engine_id_BLENDER_RENDER) && (sce->r.mode & R_EDGE_FRS)) - outliner_add_line_styles(soops, lb, sce, te); -#endif + if (outliner_is_collection_tree_element(te)) { + return TRAVERSE_CONTINUE; + } + + if (tselem->type || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) { + return TRAVERSE_SKIP_CHILDS; + } + + BLI_addtail(&data->objects_selected_array, BLI_genericNodeN(te)); + + return TRAVERSE_CONTINUE; +} + +/** + * Move objects from a collection to another. + * We ignore the original object being inserted, we used it for polling only. + * Instead we move all the selected objects around. + */ +static void outliner_object_reorder( + Main *bmain, Scene *scene, + SpaceOops *soops, + TreeElement *insert_element, + TreeElement *insert_handle, TreeElementInsertType action, + const wmEvent *event) +{ + Collection *collection = outliner_collection_from_tree_element(insert_handle); + Collection *collection_ob_parent = NULL; + ID *id = insert_handle->store_elem->id; + + BLI_assert(action == TE_INSERT_INTO); + UNUSED_VARS_NDEBUG(action); + + struct ObjectsSelectedData data = { + .objects_selected_array = {NULL, NULL}, + }; + + const bool is_append = event->ctrl; + + /* Make sure we include the originally inserted element as well. */ + TREESTORE(insert_element)->flag |= TSE_SELECTED; + + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data); + LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) { + TreeElement *ten_selected = (TreeElement *)link->data; + Object *ob = (Object *)TREESTORE(ten_selected)->id; + + if (is_append) { + BKE_collection_object_add(bmain, collection, ob); + continue; + } + + /* Find parent collection of object. */ + if (ten_selected->parent) { + for (TreeElement *te_ob_parent = ten_selected->parent; te_ob_parent; te_ob_parent = te_ob_parent->parent) { + if (outliner_is_collection_tree_element(te_ob_parent)) { + collection_ob_parent = outliner_collection_from_tree_element(te_ob_parent); + break; + } + } + } + else { + collection_ob_parent = BKE_collection_master(scene); + } + + BKE_collection_object_move(bmain, scene, collection, collection_ob_parent, ob); + } + + BLI_freelistN(&data.objects_selected_array); + + DEG_relations_tag_update(bmain); + + /* TODO(sergey): Use proper flag for tagging here. */ + DEG_id_tag_update(id, 0); + + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); +} + +static bool outliner_object_reorder_poll( + const TreeElement *insert_element, + TreeElement **io_insert_handle, TreeElementInsertType *io_action) +{ + if (outliner_is_collection_tree_element(*io_insert_handle) && + (insert_element->parent != *io_insert_handle)) + { + *io_action = TE_INSERT_INTO; + return true; + } + + return false; } // can be inlined if necessary static void outliner_add_object_contents(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, Object *ob) { + te->reinsert = outliner_object_reorder; + te->reinsert_poll = outliner_object_reorder_poll; + if (outliner_animdata_test(ob->adt)) outliner_add_element(soops, &te->subtree, ob, te, TSE_ANIM_DATA, 0); @@ -719,14 +658,9 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor case ID_MA: { Material *ma = (Material *)id; - int a; if (outliner_animdata_test(ma->adt)) outliner_add_element(soops, &te->subtree, ma, te, TSE_ANIM_DATA, 0); - - for (a = 0; a < MAX_MTEX; a++) { - if (ma->mtex[a]) outliner_add_element(soops, &te->subtree, ma->mtex[a]->tex, te, 0, a); - } break; } case ID_TE: @@ -760,14 +694,9 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor case ID_LA: { Lamp *la = (Lamp *)id; - int a; if (outliner_animdata_test(la->adt)) outliner_add_element(soops, &te->subtree, la, te, TSE_ANIM_DATA, 0); - - for (a = 0; a < MAX_MTEX; a++) { - if (la->mtex[a]) outliner_add_element(soops, &te->subtree, la->mtex[a]->tex, te, 0, a); - } break; } case ID_SPK: @@ -778,17 +707,20 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor outliner_add_element(soops, &te->subtree, spk, te, TSE_ANIM_DATA, 0); break; } + case ID_LP: + { + LightProbe *prb = (LightProbe *)id; + + if (outliner_animdata_test(prb->adt)) + outliner_add_element(soops, &te->subtree, prb, te, TSE_ANIM_DATA, 0); + break; + } case ID_WO: { World *wrld = (World *)id; - int a; if (outliner_animdata_test(wrld->adt)) outliner_add_element(soops, &te->subtree, wrld, te, TSE_ANIM_DATA, 0); - - for (a = 0; a < MAX_MTEX; a++) { - if (wrld->mtex[a]) outliner_add_element(soops, &te->subtree, wrld->mtex[a]->tex, te, 0, a); - } break; } case ID_KE: @@ -882,6 +814,14 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor } break; } + case ID_GR: + { + /* Don't expand for instances, creates too many elements. */ + if (!(te->parent && te->parent->idcode == ID_OB)) { + Collection *collection = (Collection *)id; + outliner_add_collection_recursive(soops, collection, te); + } + } default: break; } @@ -905,7 +845,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i id = TREESTORE(parent)->id; } - /* One exception */ + /* exceptions */ if (type == TSE_ID_BASE) { /* pass */ } @@ -943,6 +883,9 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i else if (type == TSE_GP_LAYER) { /* pass */ } + else if (ELEM(type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { + /* pass */ + } else if (type == TSE_ID_BASE) { /* pass */ } @@ -959,8 +902,9 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i TreeStoreElem *tsepar = parent ? TREESTORE(parent) : NULL; /* ID datablock */ - if (tsepar == NULL || tsepar->type != TSE_ID_BASE) + if (tsepar == NULL || tsepar->type != TSE_ID_BASE || soops->filter_id_type) { outliner_add_id_contents(soops, te, tselem, id); + } } else if (type == TSE_ANIM_DATA) { IdAdtTemplate *iat = (IdAdtTemplate *)idv; @@ -1306,16 +1250,56 @@ static void outliner_add_seq_dup(SpaceOops *soops, Sequence *seq, TreeElement *t /* ----------------------------------------------- */ +static const char *outliner_idcode_to_plural(short idcode) +{ + const char *propname = BKE_idcode_to_name_plural(idcode); + PropertyRNA *prop = RNA_struct_type_find_property(&RNA_BlendData, propname); + return (prop) ? RNA_property_ui_name(prop) : "UNKNOWN"; +} -static void outliner_add_library_contents(Main *mainvar, SpaceOops *soops, TreeElement *te, Library *lib) +static bool outliner_library_id_show(Library *lib, ID *id, short filter_id_type) { - TreeElement *ten; + if (id->lib != lib) { + return false; + } + + if (filter_id_type == ID_GR) { + /* Don't show child collections of non-scene master collection, + * they are already shown as children. */ + Collection *collection = (Collection *)id; + bool has_non_scene_parent = false; + + for (CollectionParent *cparent = collection->parents.first; cparent; cparent = cparent->next) { + if (!(cparent->collection->flag & COLLECTION_IS_MASTER)) { + has_non_scene_parent = true; + } + } + + if (has_non_scene_parent) { + return false; + } + } + + return true; +} + +static TreeElement *outliner_add_library_contents(Main *mainvar, SpaceOops *soops, ListBase *lb, Library *lib) +{ + TreeElement *ten, *tenlib = NULL; ListBase *lbarray[MAX_LIBARRAY]; int a, tot; + short filter_id_type = (soops->filter & SO_FILTER_ID_TYPE) ? soops->filter_id_type : 0; + + if (filter_id_type) { + lbarray[0] = which_libbase(mainvar, soops->filter_id_type); + tot = 1; + } + else { + tot = set_listbasepointers(mainvar, lbarray); + } - tot = set_listbasepointers(mainvar, lbarray); for (a = 0; a < tot; a++) { - if (lbarray[a]->first) { + if (lbarray[a] && lbarray[a]->first) { ID *id = lbarray[a]->first; /* check if there's data in current lib */ @@ -1324,21 +1308,37 @@ static void outliner_add_library_contents(Main *mainvar, SpaceOops *soops, TreeE break; if (id) { - ten = outliner_add_element(soops, &te->subtree, lbarray[a], NULL, TSE_ID_BASE, 0); - ten->directdata = lbarray[a]; + if (!tenlib) { + /* Create library tree element on demand, depending if there are any datablocks. */ + if (lib) { + tenlib = outliner_add_element(soops, lb, lib, NULL, 0, 0); + } + else { + tenlib = outliner_add_element(soops, lb, mainvar, NULL, TSE_ID_BASE, 0); + tenlib->name = IFACE_("Current File"); + } + } - ten->name = BKE_idcode_to_name_plural(GS(id->name)); - if (ten->name == NULL) - ten->name = "UNKNOWN"; + /* Create datablock list parent element on demand. */ + if (filter_id_type) { + ten = tenlib; + } + else { + ten = outliner_add_element(soops, &tenlib->subtree, lbarray[a], NULL, TSE_ID_BASE, 0); + ten->directdata = lbarray[a]; + ten->name = outliner_idcode_to_plural(GS(id->name)); + } for (id = lbarray[a]->first; id; id = id->next) { - if (id->lib == lib) + if (outliner_library_id_show(lib, id, filter_id_type)) { outliner_add_element(soops, &ten->subtree, id, ten, 0, 0); + } } } } } + return tenlib; } static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops) @@ -1346,10 +1346,18 @@ static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops) TreeElement *ten; ListBase *lbarray[MAX_LIBARRAY]; int a, tot; + short filter_id_type = (soops->filter & SO_FILTER_ID_TYPE) ? soops->filter_id_type : 0; + + if (filter_id_type) { + lbarray[0] = which_libbase(mainvar, soops->filter_id_type); + tot = 1; + } + else { + tot = set_listbasepointers(mainvar, lbarray); + } - tot = set_listbasepointers(mainvar, lbarray); for (a = 0; a < tot; a++) { - if (lbarray[a]->first) { + if (lbarray[a] && lbarray[a]->first) { ID *id = lbarray[a]->first; /* check if there are any datablocks of this type which are orphans */ @@ -1360,34 +1368,203 @@ static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops) if (id) { /* header for this type of datablock */ - /* TODO's: - * - Add a parameter to BKE_idcode_to_name_plural to get a sane "user-visible" name instead? - * - Ensure that this uses nice icons for the datablock type involved instead of the dot? - */ - ten = outliner_add_element(soops, &soops->tree, lbarray[a], NULL, TSE_ID_BASE, 0); - ten->directdata = lbarray[a]; - - ten->name = BKE_idcode_to_name_plural(GS(id->name)); - if (ten->name == NULL) - ten->name = "UNKNOWN"; + if (filter_id_type) { + ten = NULL; + } + else { + ten = outliner_add_element(soops, &soops->tree, lbarray[a], NULL, TSE_ID_BASE, 0); + ten->directdata = lbarray[a]; + ten->name = outliner_idcode_to_plural(GS(id->name)); + } /* add the orphaned datablocks - these will not be added with any subtrees attached */ for (id = lbarray[a]->first; id; id = id->next) { if (ID_REAL_USERS(id) <= 0) - outliner_add_element(soops, &ten->subtree, id, ten, 0, 0); + outliner_add_element(soops, (ten) ? &ten->subtree : &soops->tree, id, ten, 0, 0); } } } } } +static void outliner_collections_reorder( + Main *bmain, + Scene *UNUSED(scene), + SpaceOops *soops, + TreeElement *insert_element, + TreeElement *insert_handle, + TreeElementInsertType action, + const wmEvent *UNUSED(event)) +{ + TreeElement *from_parent_te, *to_parent_te; + Collection *from_parent, *to_parent; + + Collection *collection = outliner_collection_from_tree_element(insert_element); + Collection *relative = NULL; + bool relative_after = false; + + from_parent_te = outliner_find_parent_element(&soops->tree, NULL, insert_element); + from_parent = (from_parent_te) ? outliner_collection_from_tree_element(from_parent_te) : NULL; + + if (ELEM(action, TE_INSERT_BEFORE, TE_INSERT_AFTER)) { + to_parent_te = outliner_find_parent_element(&soops->tree, NULL, insert_handle); + to_parent = (to_parent_te) ? outliner_collection_from_tree_element(to_parent_te) : NULL; + + relative = outliner_collection_from_tree_element(insert_handle); + relative_after = (action == TE_INSERT_AFTER); + } + else if (action == TE_INSERT_INTO) { + to_parent = outliner_collection_from_tree_element(insert_handle); + } + else { + BLI_assert(0); + return; + } + + if (!to_parent) { + return; + } + + BKE_collection_move(bmain, to_parent, from_parent, relative, relative_after, collection); + + DEG_relations_tag_update(bmain); +} + +static bool outliner_collections_reorder_poll( + const TreeElement *insert_element, + TreeElement **io_insert_handle, + TreeElementInsertType *io_action) +{ + /* Can't move master collection. */ + Collection *collection = outliner_collection_from_tree_element(insert_element); + if (collection->flag & COLLECTION_IS_MASTER) { + return false; + } + + /* Can only move into collections. */ + Collection *collection_handle = outliner_collection_from_tree_element(*io_insert_handle); + if (collection_handle == NULL) { + return false; + } + + /* We can't insert/before after master collection. */ + if (collection_handle->flag & COLLECTION_IS_MASTER) { + if (*io_action == TE_INSERT_BEFORE) { + /* can't go higher than master collection, insert into it */ + *io_action = TE_INSERT_INTO; + } + else if (*io_action == TE_INSERT_AFTER) { + *io_insert_handle = (*io_insert_handle)->subtree.last; + } + } + + return true; +} + +static void outliner_add_layer_collection_objects( + SpaceOops *soops, ListBase *tree, ViewLayer *layer, + LayerCollection *lc, TreeElement *ten) +{ + for (CollectionObject *cob = lc->collection->gobject.first; cob; cob = cob->next) { + Base *base = BKE_view_layer_base_find(layer, cob->ob); + TreeElement *te_object = outliner_add_element(soops, tree, base->object, ten, 0, 0); + te_object->directdata = base; + + if (!(base->flag & BASE_VISIBLED)) { + te_object->flag |= TE_DISABLED; + } + } +} + +static void outliner_add_layer_collections_recursive( + SpaceOops *soops, ListBase *tree, ViewLayer *layer, + ListBase *layer_collections, TreeElement *parent_ten, + const bool show_objects) +{ + for (LayerCollection *lc = layer_collections->first; lc; lc = lc->next) { + ID *id = &lc->collection->id; + TreeElement *ten = outliner_add_element(soops, tree, id, parent_ten, TSE_LAYER_COLLECTION, 0); + + ten->name = id->name + 2; + ten->directdata = lc; + ten->reinsert = outliner_collections_reorder; + ten->reinsert_poll = outliner_collections_reorder_poll; + + const bool exclude = (lc->flag & LAYER_COLLECTION_EXCLUDE) != 0; + if (exclude || + ((layer->runtime_flag & VIEW_LAYER_HAS_HIDE) && + !(lc->runtime_flag & LAYER_COLLECTION_HAS_VISIBLE_OBJECTS))) + { + ten->flag |= TE_DISABLED; + } + + outliner_add_layer_collections_recursive(soops, &ten->subtree, layer, &lc->layer_collections, ten, show_objects); + if (!exclude && show_objects) { + outliner_add_layer_collection_objects(soops, &ten->subtree, layer, lc, ten); + } + } +} + +static void outliner_add_view_layer(SpaceOops *soops, ListBase *tree, TreeElement *parent, + ViewLayer *layer, const bool show_objects) +{ + /* First layer collection is for master collection, don't show it. */ + LayerCollection *lc = layer->layer_collections.first; + if (lc == NULL) { + return; + } + + outliner_add_layer_collections_recursive(soops, tree, layer, &lc->layer_collections, parent, show_objects); + if (show_objects) { + outliner_add_layer_collection_objects(soops, tree, layer, lc, parent); + } +} + +BLI_INLINE void outliner_add_collection_init(TreeElement *te, Collection *collection) +{ + if (collection->flag & COLLECTION_IS_MASTER) { + te->name = IFACE_("Scene Collection"); + } + else { + te->name = collection->id.name + 2; + } + + te->directdata = collection; + te->reinsert = outliner_collections_reorder; + te->reinsert_poll = outliner_collections_reorder_poll; +} + +BLI_INLINE void outliner_add_collection_objects( + SpaceOops *soops, ListBase *tree, Collection *collection, TreeElement *parent) +{ + for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { + outliner_add_element(soops, tree, cob->ob, parent, 0, 0); + } +} + +static TreeElement *outliner_add_collection_recursive( + SpaceOops *soops, Collection *collection, TreeElement *ten) +{ + outliner_add_collection_init(ten, collection); + + for (CollectionChild *child = collection->children.first; child; child = child->next) { + outliner_add_element(soops, &ten->subtree, &child->collection->id, ten, 0, 0); + } + + if (soops->outlinevis != SO_SCENES) { + outliner_add_collection_objects(soops, &ten->subtree, collection, ten); + } + + return ten; +} + /* ======================================================= */ /* Generic Tree Building helpers - order these are called is top to bottom */ /* Hierarchy --------------------------------------------- */ /* make sure elements are correctly nested */ -static void outliner_make_hierarchy(ListBase *lb) +static void outliner_make_object_parent_hierarchy(ListBase *lb) { TreeElement *te, *ten, *tep; TreeStoreElem *tselem; @@ -1494,17 +1671,14 @@ static void outliner_sort(ListBase *lb) { TreeElement *te; TreeStoreElem *tselem; - int totelem = 0; te = lb->last; if (te == NULL) return; tselem = TREESTORE(te); /* sorting rules; only object lists, ID lists, or deformgroups */ - if ( ELEM(tselem->type, TSE_DEFGROUP, TSE_ID_BASE) || (tselem->type == 0 && te->idcode == ID_OB)) { - - /* count first */ - for (te = lb->first; te; te = te->next) totelem++; + if (ELEM(tselem->type, TSE_DEFGROUP, TSE_ID_BASE) || (tselem->type == 0 && te->idcode == ID_OB)) { + int totelem = BLI_listbase_count(lb); if (totelem > 1) { tTreeSort *tear = MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array"); @@ -1555,6 +1729,297 @@ static void outliner_sort(ListBase *lb) /* Filtering ----------------------------------------------- */ +typedef struct OutlinerTreeElementFocus { + TreeStoreElem *tselem; + int ys; +} OutlinerTreeElementFocus; + +/** + * Bring the outliner scrolling back to where it was in relation to the original focus element + * Caller is expected to handle redrawing of ARegion. + */ +static void outliner_restore_scrolling_position(SpaceOops *soops, ARegion *ar, OutlinerTreeElementFocus *focus) +{ + View2D *v2d = &ar->v2d; + int ytop; + + if (focus->tselem != NULL) { + outliner_set_coordinates(ar, soops); + + TreeElement *te_new = outliner_find_tree_element(&soops->tree, focus->tselem); + + if (te_new != NULL) { + int ys_new, ys_old; + + ys_new = te_new->ys; + ys_old = focus->ys; + + ytop = v2d->cur.ymax + (ys_new - ys_old) -1; + if (ytop > 0) ytop = 0; + + v2d->cur.ymax = (float)ytop; + v2d->cur.ymin = (float)(ytop - BLI_rcti_size_y(&v2d->mask)); + } + else { + return; + } + } +} + +static bool test_collection_callback(TreeElement *te) +{ + return outliner_is_collection_tree_element(te); +} + +static bool test_object_callback(TreeElement *te) +{ + TreeStoreElem *tselem = TREESTORE(te); + return ((tselem->type == 0) && (te->idcode == ID_OB)); +} + +/** + * See if TreeElement or any of its children pass the callback_test. + */ +static TreeElement *outliner_find_first_desired_element_at_y_recursive( + const SpaceOops *soops, + TreeElement *te, + const float limit, + bool (*callback_test)(TreeElement *)) +{ + if (callback_test(te)) { + return te; + } + + if (TSELEM_OPEN(te->store_elem, soops)) { + TreeElement *te_iter, *te_sub; + for (te_iter = te->subtree.first; te_iter; te_iter = te_iter->next) { + te_sub = outliner_find_first_desired_element_at_y_recursive(soops, te_iter, limit, callback_test); + if (te_sub != NULL) { + return te_sub; + } + } + } + + return NULL; +} + +/** + * Find the first element that passes a test starting from a reference vertical coordinate + * + * If the element that is in the position is not what we are looking for, keep looking for its + * children, siblings, and eventually, aunts, cousins, disntant families, ... + * + * Basically we keep going up and down the outliner tree from that point forward, until we find + * what we are looking for. If we are past the visible range and we can't find a valid element + * we return NULL. + */ +static TreeElement *outliner_find_first_desired_element_at_y( + const SpaceOops *soops, + const float view_co, + const float view_co_limit) +{ + TreeElement *te, *te_sub; + te = outliner_find_item_at_y(soops, &soops->tree, view_co); + + bool (*callback_test)(TreeElement *); + if ((soops->outlinevis == SO_VIEW_LAYER) && + (soops->filter & SO_FILTER_NO_COLLECTION)) + { + callback_test = test_object_callback; + } + else { + callback_test = test_collection_callback; + } + + while (te != NULL) { + te_sub = outliner_find_first_desired_element_at_y_recursive(soops, te, view_co_limit, callback_test); + if (te_sub != NULL) { + /* Skip the element if it was not visible to start with. */ + if (te->ys + UI_UNIT_Y > view_co_limit) { + return te_sub; + } + else { + return NULL; + } + } + + if (te->next) { + te = te->next; + continue; + } + + if (te->parent == NULL) { + break; + } + + while (te->parent) { + if (te->parent->next) { + te = te->parent->next; + break; + } + te = te->parent; + } + } + + return NULL; +} + +/** + * Store information of current outliner scrolling status to be restored later + * + * Finds the top-most collection visible in the outliner and populates the OutlinerTreeElementFocus + * struct to retrieve this element later to make sure it is in the same original position as before filtering + */ +static void outliner_store_scrolling_position(SpaceOops *soops, ARegion *ar, OutlinerTreeElementFocus *focus) +{ + TreeElement *te; + float limit = ar->v2d.cur.ymin; + + outliner_set_coordinates(ar, soops); + + te = outliner_find_first_desired_element_at_y(soops, ar->v2d.cur.ymax, limit); + + if (te != NULL) { + focus->tselem = TREESTORE(te); + focus->ys = te->ys; + } + else { + focus->tselem = NULL; + } +} + +static int outliner_exclude_filter_get(SpaceOops *soops) +{ + int exclude_filter = soops->filter & ~SO_FILTER_OB_STATE; + + if (soops->search_string[0] != 0) { + exclude_filter |= SO_FILTER_SEARCH; + } + else { + exclude_filter &= ~SO_FILTER_SEARCH; + } + + /* Let's have this for the collection options at first. */ + if (!SUPPORT_FILTER_OUTLINER(soops)) { + return (exclude_filter & SO_FILTER_SEARCH); + } + + if (soops->filter & SO_FILTER_NO_OBJECT) { + exclude_filter |= SO_FILTER_OB_TYPE; + } + + switch (soops->filter_state) { + case SO_FILTER_OB_VISIBLE: + exclude_filter |= SO_FILTER_OB_STATE_VISIBLE; + break; + case SO_FILTER_OB_SELECTED: + exclude_filter |= SO_FILTER_OB_STATE_SELECTED; + break; + case SO_FILTER_OB_ACTIVE: + exclude_filter |= SO_FILTER_OB_STATE_ACTIVE; + break; + } + + return exclude_filter; +} + +static bool outliner_element_visible_get(ViewLayer *view_layer, TreeElement *te, const int exclude_filter) +{ + if ((exclude_filter & SO_FILTER_ANY) == 0) { + return true; + } + + TreeStoreElem *tselem = TREESTORE(te); + if ((tselem->type == 0) && (te->idcode == ID_OB)) { + if ((exclude_filter & SO_FILTER_OB_TYPE) == SO_FILTER_OB_TYPE) { + return false; + } + + Object *ob = (Object *)tselem->id; + Base *base = (Base *)te->directdata; + BLI_assert((base == NULL) || (base->object == ob)); + + if (exclude_filter & SO_FILTER_OB_TYPE) { + switch (ob->type) { + case OB_MESH: + if (exclude_filter & SO_FILTER_NO_OB_MESH) { + return false; + } + break; + case OB_ARMATURE: + if (exclude_filter & SO_FILTER_NO_OB_ARMATURE) { + return false; + } + break; + case OB_EMPTY: + if (exclude_filter & SO_FILTER_NO_OB_EMPTY) { + return false; + } + break; + case OB_LAMP: + if (exclude_filter & SO_FILTER_NO_OB_LAMP) { + return false; + } + break; + case OB_CAMERA: + if (exclude_filter & SO_FILTER_NO_OB_CAMERA) { + return false; + } + break; + default: + if (exclude_filter & SO_FILTER_NO_OB_OTHERS) { + return false; + } + break; + } + } + + if (exclude_filter & SO_FILTER_OB_STATE) { + if (base == NULL) { + base = BKE_view_layer_base_find(view_layer, ob); + + if (base == NULL) { + return false; + } + } + + if (exclude_filter & SO_FILTER_OB_STATE_VISIBLE) { + if ((base->flag & BASE_VISIBLED) == 0) { + return false; + } + } + else if (exclude_filter & SO_FILTER_OB_STATE_SELECTED) { + if ((base->flag & BASE_SELECTED) == 0) { + return false; + } + } + else { + BLI_assert(exclude_filter & SO_FILTER_OB_STATE_ACTIVE); + if (base != BASACT(view_layer)) { + return false; + } + } + } + + if ((te->parent != NULL) && + (TREESTORE(te->parent)->type == 0) && (te->parent->idcode == ID_OB)) + { + if (exclude_filter & SO_FILTER_NO_CHILDREN) { + return false; + } + } + } + else if (te->parent != NULL && + TREESTORE(te->parent)->type == 0 && te->parent->idcode == ID_OB) + { + if (exclude_filter & SO_FILTER_NO_OB_CONTENT) { + return false; + } + } + + return true; +} + static bool outliner_filter_has_name(TreeElement *te, const char *name, int flags) { int fn_flag = 0; @@ -1565,30 +2030,24 @@ static bool outliner_filter_has_name(TreeElement *te, const char *name, int flag return fnmatch(name, te->name, fn_flag) == 0; } -static int outliner_filter_tree(SpaceOops *soops, ListBase *lb) +static int outliner_filter_subtree( + SpaceOops *soops, ViewLayer *view_layer, ListBase *lb, const char *search_string, const int exclude_filter) { - TreeElement *te, *ten; + TreeElement *te, *te_next; TreeStoreElem *tselem; - char search_buff[sizeof(((struct SpaceOops *)NULL)->search_string) + 2]; - char *search_string; - /* although we don't have any search string, we return true - * since the entire tree is ok then... - */ - if (soops->search_string[0] == 0) - return 1; + for (te = lb->first; te; te = te_next) { + te_next = te->next; - if (soops->search_flags & SO_FIND_COMPLETE) { - search_string = soops->search_string; - } - else { - /* Implicitly add heading/trailing wildcards if needed. */ - BLI_strncpy_ensure_pad(search_buff, soops->search_string, '*', sizeof(search_buff)); - search_string = search_buff; - } - - for (te = lb->first; te; te = ten) { - ten = te->next; + if ((outliner_element_visible_get(view_layer, te, exclude_filter) == false)) { + outliner_free_tree_element(te, lb); + continue; + } + else if ((exclude_filter & SO_FILTER_SEARCH) == 0) { + /* Filter subtree too. */ + outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter); + continue; + } if (!outliner_filter_has_name(te, search_string, soops->search_flags)) { /* item isn't something we're looking for, but... @@ -1602,12 +2061,10 @@ static int outliner_filter_tree(SpaceOops *soops, ListBase *lb) /* flag as not a found item */ tselem->flag &= ~TSE_SEARCHMATCH; - if ((!TSELEM_OPEN(tselem, soops)) || outliner_filter_tree(soops, &te->subtree) == 0) { - outliner_free_tree(&te->subtree); - BLI_remlink(lb, te); - - if (te->flag & TE_FREE_NAME) MEM_freeN((void *)te->name); - MEM_freeN(te); + if ((!TSELEM_OPEN(tselem, soops)) || + outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter) == 0) + { + outliner_free_tree_element(te, lb); } } else { @@ -1617,7 +2074,7 @@ static int outliner_filter_tree(SpaceOops *soops, ListBase *lb) tselem->flag |= TSE_SEARCHMATCH; /* filter subtree too */ - outliner_filter_tree(soops, &te->subtree); + outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter); } } @@ -1625,14 +2082,36 @@ static int outliner_filter_tree(SpaceOops *soops, ListBase *lb) return (BLI_listbase_is_empty(lb) == false); } +static void outliner_filter_tree(SpaceOops *soops, ViewLayer *view_layer) +{ + char search_buff[sizeof(((struct SpaceOops *)NULL)->search_string) + 2]; + char *search_string; + + const int exclude_filter = outliner_exclude_filter_get(soops); + + if (exclude_filter == 0) { + return; + } + + if (soops->search_flags & SO_FIND_COMPLETE) { + search_string = soops->search_string; + } + else { + /* Implicitly add heading/trailing wildcards if needed. */ + BLI_strncpy_ensure_pad(search_buff, soops->search_string, '*', sizeof(search_buff)); + search_string = search_buff; + } + + outliner_filter_subtree(soops, view_layer, &soops->tree, search_string, exclude_filter); +} + /* ======================================================= */ /* Main Tree Building API */ /* Main entry point for building the tree data-structure that the outliner represents */ // TODO: split each mode into its own function? -void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) +void outliner_build_tree(Main *mainvar, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, ARegion *ar) { - Base *base; TreeElement *te = NULL, *ten; TreeStoreElem *tselem; int show_opened = !soops->treestore || !BLI_mempool_len(soops->treestore); /* on first view, we open scenes */ @@ -1640,7 +2119,7 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) /* Are we looking for something - we want to tag parents to filter child matches * - NOT in datablocks view - searching all datablocks takes way too long to be useful * - this variable is only set once per tree build */ - if (soops->search_string[0] != 0 && soops->outlinevis != SO_DATABLOCKS) + if (soops->search_string[0] != 0 && soops->outlinevis != SO_DATA_API) soops->search_flags |= SO_SEARCH_RECURSIVE; else soops->search_flags &= ~SO_SEARCH_RECURSIVE; @@ -1650,8 +2129,12 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) BKE_outliner_treehash_rebuild_from_treestore(soops->treehash, soops->treestore); } - if (soops->tree.first && (soops->storeflag & SO_TREESTORE_REDRAW)) + if (ar->do_draw & RGN_DRAW_NO_REBUILD) { return; + } + + OutlinerTreeElementFocus focus; + outliner_store_scrolling_position(soops, ar, &focus); outliner_free_tree(&soops->tree); outliner_storage_cleanup(soops); @@ -1661,20 +2144,18 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) Library *lib; /* current file first - mainvar provides tselem with unique pointer - not used */ - ten = outliner_add_element(soops, &soops->tree, mainvar, NULL, TSE_ID_BASE, 0); - ten->name = IFACE_("Current File"); - - tselem = TREESTORE(ten); - if (!tselem->used) - tselem->flag &= ~TSE_CLOSED; - - outliner_add_library_contents(mainvar, soops, ten, NULL); + ten = outliner_add_library_contents(mainvar, soops, &soops->tree, NULL); + if (ten) { + tselem = TREESTORE(ten); + if (!tselem->used) + tselem->flag &= ~TSE_CLOSED; + } for (lib = mainvar->library.first; lib; lib = lib->id.next) { - ten = outliner_add_element(soops, &soops->tree, lib, NULL, 0, 0); - lib->id.newid = (ID *)ten; - - outliner_add_library_contents(mainvar, soops, ten, lib); + ten = outliner_add_library_contents(mainvar, soops, &soops->tree, lib); + if (ten) { + lib->id.newid = (ID *)ten; + } } /* make hierarchy */ @@ -1694,9 +2175,10 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) } else { /* Else, make a new copy of the libtree for our parent. */ - TreeElement *dupten = outliner_add_element(soops, &par->subtree, lib, NULL, 0, 0); - outliner_add_library_contents(mainvar, soops, dupten, lib); - dupten->parent = par; + TreeElement *dupten = outliner_add_library_contents(mainvar, soops, &par->subtree, lib); + if (dupten) { + dupten->parent = par; + } } } ten = nten; @@ -1706,80 +2188,18 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) lib->id.newid = NULL; } - else if (soops->outlinevis == SO_ALL_SCENES) { + else if (soops->outlinevis == SO_SCENES) { Scene *sce; for (sce = mainvar->scene.first; sce; sce = sce->id.next) { te = outliner_add_element(soops, &soops->tree, sce, NULL, 0, 0); tselem = TREESTORE(te); - if (sce == scene && show_opened) - tselem->flag &= ~TSE_CLOSED; - for (base = sce->base.first; base; base = base->next) { - ten = outliner_add_element(soops, &te->subtree, base->object, te, 0, 0); - ten->directdata = base; + if (sce == scene && show_opened) { + tselem->flag &= ~TSE_CLOSED; } - outliner_make_hierarchy(&te->subtree); - /* clear id.newid, to prevent objects be inserted in wrong scenes (parent in other scene) */ - for (base = sce->base.first; base; base = base->next) base->object->id.newid = NULL; - } - } - else if (soops->outlinevis == SO_CUR_SCENE) { - - outliner_add_scene_contents(soops, &soops->tree, scene, NULL); - for (base = scene->base.first; base; base = base->next) { - ten = outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0); - ten->directdata = base; - } - outliner_make_hierarchy(&soops->tree); - } - else if (soops->outlinevis == SO_VISIBLE) { - for (base = scene->base.first; base; base = base->next) { - if (base->lay & scene->lay) - outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0); + outliner_make_object_parent_hierarchy(&te->subtree); } - outliner_make_hierarchy(&soops->tree); - } - else if (soops->outlinevis == SO_GROUPS) { - Group *group; - GroupObject *go; - - for (group = mainvar->group.first; group; group = group->id.next) { - if (group->gobject.first) { - te = outliner_add_element(soops, &soops->tree, group, NULL, 0, 0); - - for (go = group->gobject.first; go; go = go->next) { - ten = outliner_add_element(soops, &te->subtree, go->ob, te, 0, 0); - ten->directdata = NULL; /* eh, why? */ - } - outliner_make_hierarchy(&te->subtree); - /* clear id.newid, to prevent objects be inserted in wrong scenes (parent in other scene) */ - for (go = group->gobject.first; go; go = go->next) go->ob->id.newid = NULL; - } - } - } - else if (soops->outlinevis == SO_SAME_TYPE) { - Object *ob = OBACT; - if (ob) { - for (base = scene->base.first; base; base = base->next) { - if (base->object->type == ob->type) { - ten = outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0); - ten->directdata = base; - } - } - outliner_make_hierarchy(&soops->tree); - } - } - else if (soops->outlinevis == SO_SELECTED) { - for (base = scene->base.first; base; base = base->next) { - if (base->lay & scene->lay) { - if (base->flag & SELECT) { - ten = outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0); - ten->directdata = base; - } - } - } - outliner_make_hierarchy(&soops->tree); } else if (soops->outlinevis == SO_SEQUENCE) { Sequence *seq; @@ -1805,7 +2225,7 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) seq = seq->next; } } - else if (soops->outlinevis == SO_DATABLOCKS) { + else if (soops->outlinevis == SO_DATA_API) { PointerRNA mainptr; RNA_main_pointer_create(mainvar, &mainptr); @@ -1817,32 +2237,36 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) tselem->flag &= ~TSE_CLOSED; } } - else if (soops->outlinevis == SO_USERDEF) { - PointerRNA userdefptr; - - RNA_pointer_create(NULL, &RNA_UserPreferences, &U, &userdefptr); - - ten = outliner_add_element(soops, &soops->tree, (void *)&userdefptr, NULL, TSE_RNA_STRUCT, -1); - - if (show_opened) { - tselem = TREESTORE(ten); - tselem->flag &= ~TSE_CLOSED; - } - } else if (soops->outlinevis == SO_ID_ORPHANS) { outliner_add_orphaned_datablocks(mainvar, soops); } - else { - ten = outliner_add_element(soops, &soops->tree, OBACT, NULL, 0, 0); - if (ten) ten->directdata = BASACT; + else if (soops->outlinevis == SO_VIEW_LAYER) { + if (soops->filter & SO_FILTER_NO_COLLECTION) { + /* Show objects in the view layer. */ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + TreeElement *te_object = outliner_add_element(soops, &soops->tree, base->object, NULL, 0, 0); + te_object->directdata = base; + } + + outliner_make_object_parent_hierarchy(&soops->tree); + } + else { + /* Show collections in the view layer. */ + ten = outliner_add_element(soops, &soops->tree, scene, NULL, TSE_VIEW_COLLECTION_BASE, 0); + ten->name = IFACE_("Scene Collection"); + TREESTORE(ten)->flag &= ~TSE_CLOSED; + + bool show_objects = !(soops->filter & SO_FILTER_NO_OBJECT); + outliner_add_view_layer(soops, &ten->subtree, ten, view_layer, show_objects); + } } if ((soops->flag & SO_SKIP_SORT_ALPHA) == 0) { outliner_sort(&soops->tree); } - outliner_filter_tree(soops, &soops->tree); + + outliner_filter_tree(soops, view_layer); + outliner_restore_scrolling_position(soops, ar, &focus); BKE_main_id_clear_newpoins(mainvar); } - - diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c new file mode 100644 index 00000000000..6b7035dd326 --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -0,0 +1,246 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_outliner/outliner_utils.c + * \ingroup spoutliner + */ + +#include "BLI_utildefines.h" + +#include "DNA_action_types.h" +#include "DNA_space_types.h" + +#include "BKE_outliner_treehash.h" + +#include "ED_armature.h" + +#include "UI_interface.h" + +#include "outliner_intern.h" + +/** + * Try to find an item under y-coordinate \a view_co_y (view-space). + * \note Recursive + */ +TreeElement *outliner_find_item_at_y(const SpaceOops *soops, const ListBase *tree, float view_co_y) +{ + for (TreeElement *te_iter = tree->first; te_iter; te_iter = te_iter->next) { + if (view_co_y < (te_iter->ys + UI_UNIT_Y)) { + if (view_co_y >= te_iter->ys) { + /* co_y is inside this element */ + return te_iter; + } + else if (TSELEM_OPEN(te_iter->store_elem, soops)) { + /* co_y is lower than current element, possibly inside children */ + TreeElement *te_sub = outliner_find_item_at_y(soops, &te_iter->subtree, view_co_y); + if (te_sub) { + return te_sub; + } + } + } + } + + return NULL; +} + +/** + * Collapsed items can show their children as click-able icons. This function tries to find + * such an icon that represents the child item at x-coordinate \a view_co_x (view-space). + * + * \return a hovered child item or \a parent_te (if no hovered child found). + */ +TreeElement *outliner_find_item_at_x_in_row(const SpaceOops *soops, const TreeElement *parent_te, float view_co_x) +{ + if (!TSELEM_OPEN(TREESTORE(parent_te), soops)) { /* if parent_te is opened, it doesn't show childs in row */ + /* no recursion, items can only display their direct children in the row */ + for (TreeElement *child_te = parent_te->subtree.first; + child_te && view_co_x >= child_te->xs; /* don't look further if co_x is smaller than child position*/ + child_te = child_te->next) + { + if ((child_te->flag & TE_ICONROW) && (view_co_x > child_te->xs) && (view_co_x < child_te->xend)) { + return child_te; + } + } + } + + /* return parent if no child is hovered */ + return (TreeElement *)parent_te; +} + +/* Find specific item from the treestore */ +TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem) +{ + TreeElement *te, *tes; + for (te = lb->first; te; te = te->next) { + if (te->store_elem == store_elem) return te; + tes = outliner_find_tree_element(&te->subtree, store_elem); + if (tes) return tes; + } + return NULL; +} + +/* Find parent element of te */ +TreeElement *outliner_find_parent_element(ListBase *lb, TreeElement *parent_te, const TreeElement *child_te) +{ + TreeElement *te; + for (te = lb->first; te; te = te->next) { + if (te == child_te) { + return parent_te; + } + + TreeElement *find_te = outliner_find_parent_element(&te->subtree, te, child_te); + if (find_te) { + return find_te; + } + } + return NULL; +} + +/* tse is not in the treestore, we use its contents to find a match */ +TreeElement *outliner_find_tse(SpaceOops *soops, const TreeStoreElem *tse) +{ + TreeStoreElem *tselem; + + if (tse->id == NULL) return NULL; + + /* check if 'tse' is in treestore */ + tselem = BKE_outliner_treehash_lookup_any(soops->treehash, tse->type, tse->nr, tse->id); + if (tselem) + return outliner_find_tree_element(&soops->tree, tselem); + + return NULL; +} + +/* Find treestore that refers to given ID */ +TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, const ID *id) +{ + for (TreeElement *te = lb->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); + if (tselem->type == 0) { + if (tselem->id == id) { + return te; + } + } + + TreeElement *tes = outliner_find_id(soops, &te->subtree, id); + if (tes) { + return tes; + } + } + return NULL; +} + +TreeElement *outliner_find_posechannel(ListBase *lb, const bPoseChannel *pchan) +{ + for (TreeElement *te = lb->first; te; te = te->next) { + if (te->directdata == pchan) { + return te; + } + + TreeStoreElem *tselem = TREESTORE(te); + if (ELEM(tselem->type, TSE_POSE_BASE, TSE_POSE_CHANNEL)) { + TreeElement *tes = outliner_find_posechannel(&te->subtree, pchan); + if (tes) { + return tes; + } + } + } + return NULL; +} + +TreeElement *outliner_find_editbone(ListBase *lb, const EditBone *ebone) +{ + for (TreeElement *te = lb->first; te; te = te->next) { + if (te->directdata == ebone) { + return te; + } + + TreeStoreElem *tselem = TREESTORE(te); + if (ELEM(tselem->type, 0, TSE_EBONE)) { + TreeElement *tes = outliner_find_editbone(&te->subtree, ebone); + if (tes) { + return tes; + } + } + } + return NULL; +} + +ID *outliner_search_back(SpaceOops *UNUSED(soops), TreeElement *te, short idcode) +{ + TreeStoreElem *tselem; + te = te->parent; + + while (te) { + tselem = TREESTORE(te); + if (tselem->type == 0 && te->idcode == idcode) return tselem->id; + te = te->parent; + } + return NULL; +} + +/** + * Iterate over all tree elements (pre-order traversal), executing \a func callback for + * each tree element matching the optional filters. + * + * \param filter_te_flag: If not 0, only TreeElements with this flag will be visited. + * \param filter_tselem_flag: Same as \a filter_te_flag, but for the TreeStoreElem. + * \param func: Custom callback to execute for each visited item. + */ +bool outliner_tree_traverse(const SpaceOops *soops, ListBase *tree, int filter_te_flag, int filter_tselem_flag, + TreeTraversalFunc func, void *customdata) +{ + for (TreeElement *te = tree->first, *te_next; te; te = te_next) { + TreeTraversalAction func_retval = TRAVERSE_CONTINUE; + /* in case te is freed in callback */ + TreeStoreElem *tselem = TREESTORE(te); + ListBase subtree = te->subtree; + te_next = te->next; + + if (filter_te_flag && (te->flag & filter_te_flag) == 0) { + /* skip */ + } + else if (filter_tselem_flag && (tselem->flag & filter_tselem_flag) == 0) { + /* skip */ + } + else { + func_retval = func(te, customdata); + } + /* Don't access te or tselem from now on! Might've been freed... */ + + if (func_retval == TRAVERSE_BREAK) { + return false; + } + + if (func_retval == TRAVERSE_SKIP_CHILDS) { + /* skip */ + } + else if (!outliner_tree_traverse(soops, &subtree, filter_te_flag, filter_tselem_flag, func, customdata)) { + return false; + } + } + + return true; +} diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 0cf4fd09555..71ae7eeeb3d 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -39,6 +39,7 @@ #include "BLI_mempool.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_screen.h" #include "BKE_scene.h" #include "BKE_outliner_treehash.h" @@ -47,6 +48,7 @@ #include "ED_screen.h" #include "WM_api.h" +#include "WM_message.h" #include "WM_types.h" #include "BIF_gl.h" @@ -102,10 +104,14 @@ static int outliner_parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *e if (GS(id->name) == ID_OB) { /* Ensure item under cursor is valid drop target */ TreeElement *te = outliner_dropzone_find(soops, fmval, true); + TreeStoreElem *tselem = te ? TREESTORE(te) : NULL; - if (te && te->idcode == ID_OB && TREESTORE(te)->type == 0) { + if (!te) { + /* pass */ + } + else if (te->idcode == ID_OB && tselem->type == 0) { Scene *scene; - ID *te_id = TREESTORE(te)->id; + ID *te_id = tselem->id; /* check if dropping self or parent */ if (te_id == id || (Object *)te_id == ((Object *)id)->parent) @@ -118,9 +124,19 @@ static int outliner_parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *e * element for object it means that all displayed objects belong to * active scene and parenting them is allowed (sergey) */ - if (!scene || BKE_scene_base_find(scene, (Object *)id)) { + if (!scene) { return 1; } + else { + for (ViewLayer *view_layer = scene->view_layers.first; + view_layer; + view_layer = view_layer->next) + { + if (BKE_view_layer_base_find(view_layer, (Object *)id)) { + return 1; + } + } + } } } } @@ -143,7 +159,7 @@ static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent * UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - if (!ELEM(soops->outlinevis, SO_ALL_SCENES, SO_CUR_SCENE, SO_VISIBLE, SO_GROUPS)) { + if (!ELEM(soops->outlinevis, SO_VIEW_LAYER)) { return false; } @@ -156,7 +172,7 @@ static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent * switch (te->idcode) { case ID_SCE: - return (ELEM(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)); + return (ELEM(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER)); case ID_OB: return (ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE)); /* Other codes to ignore? */ @@ -230,7 +246,7 @@ static void outliner_material_drop_copy(wmDrag *drag, wmDropBox *drop) RNA_string_set(drop->ptr, "material", id->name + 2); } -static int outliner_group_link_poll(bContext *C, wmDrag *drag, const wmEvent *event) +static int outliner_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); SpaceOops *soops = CTX_wm_space_outliner(C); @@ -239,19 +255,19 @@ static int outliner_group_link_poll(bContext *C, wmDrag *drag, const wmEvent *ev if (drag->type == WM_DRAG_ID) { ID *id = drag->poin; - if (GS(id->name) == ID_OB) { + if (ELEM(GS(id->name), ID_OB, ID_GR)) { /* Ensure item under cursor is valid drop target */ TreeElement *te = outliner_dropzone_find(soops, fmval, true); - return (te && te->idcode == ID_GR && TREESTORE(te)->type == 0); + return (te && outliner_is_collection_tree_element(te)); } } return 0; } -static void outliner_group_link_copy(wmDrag *drag, wmDropBox *drop) +static void outliner_collection_drop_copy(wmDrag *drag, wmDropBox *drop) { ID *id = drag->poin; - RNA_string_set(drop->ptr, "object", id->name + 2); + RNA_string_set(drop->ptr, "child", id->name + 2); } /* region dropbox definition */ @@ -263,7 +279,7 @@ static void outliner_dropboxes(void) WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", outliner_parent_clear_poll, outliner_parent_clear_copy); WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", outliner_scene_drop_poll, outliner_scene_drop_copy); WM_dropbox_add(lb, "OUTLINER_OT_material_drop", outliner_material_drop_poll, outliner_material_drop_copy); - WM_dropbox_add(lb, "OUTLINER_OT_group_link", outliner_group_link_poll, outliner_group_link_copy); + WM_dropbox_add(lb, "OUTLINER_OT_collection_drop", outliner_collection_drop_poll, outliner_collection_drop_copy); } static void outliner_main_region_draw(const bContext *C, ARegion *ar) @@ -292,7 +308,9 @@ static void outliner_main_region_free(ARegion *UNUSED(ar)) } -static void outliner_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn) +static void outliner_main_region_listener( + bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, + wmNotifier *wmn, const Scene *UNUSED(scene)) { /* context changes */ switch (wmn->category) { @@ -308,7 +326,9 @@ static void outliner_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(s case ND_RENDER_OPTIONS: case ND_SEQUENCER: case ND_LAYER: + case ND_LAYER_CONTENT: case ND_WORLD: + case ND_SCENEBROWSE: ED_region_tag_redraw(ar); break; } @@ -392,10 +412,33 @@ static void outliner_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(s if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) ED_region_tag_redraw(ar); break; + case NC_SCREEN: + if (ELEM(wmn->data, ND_LAYER)) { + ED_region_tag_redraw(ar); + } + break; } } +static void outliner_main_region_message_subscribe( + const struct bContext *UNUSED(C), + struct WorkSpace *UNUSED(workspace), struct Scene *UNUSED(scene), + struct bScreen *UNUSED(screen), struct ScrArea *sa, struct ARegion *ar, + struct wmMsgBus *mbus) +{ + SpaceOops *soops = sa->spacedata.first; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { + .owner = ar, + .user_data = ar, + .notify = ED_region_do_msg_notify_tag_redraw, + }; + + if (ELEM(soops->outlinevis, SO_VIEW_LAYER, SO_SCENES)) { + WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw); + } +} + /* ************************ header outliner area region *********************** */ @@ -414,7 +457,9 @@ static void outliner_header_region_free(ARegion *UNUSED(ar)) { } -static void outliner_header_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn) +static void outliner_header_region_listener( + bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, + wmNotifier *wmn, const Scene *UNUSED(scene)) { /* context changes */ switch (wmn->category) { @@ -431,20 +476,21 @@ static void outliner_header_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED /* ******************** default callbacks for outliner space ***************** */ -static SpaceLink *outliner_new(const bContext *UNUSED(C)) +static SpaceLink *outliner_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *ar; SpaceOops *soutliner; soutliner = MEM_callocN(sizeof(SpaceOops), "initoutliner"); soutliner->spacetype = SPACE_OUTLINER; + soutliner->filter_id_type = ID_GR; /* header */ ar = MEM_callocN(sizeof(ARegion), "header for outliner"); BLI_addtail(&soutliner->regionbase, ar); ar->regiontype = RGN_TYPE_HEADER; - ar->alignment = RGN_ALIGN_BOTTOM; + ar->alignment = RGN_ALIGN_TOP; /* main region */ ar = MEM_callocN(sizeof(ARegion), "main region for outliner"); @@ -547,6 +593,7 @@ void ED_spacetype_outliner(void) art->draw = outliner_main_region_draw; art->free = outliner_main_region_free; art->listener = outliner_main_region_listener; + art->message_subscribe = outliner_main_region_message_subscribe; BLI_addhead(&st->regiontypes, art); /* regions: header */ |