diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 16bbbb9566e..1500ae13462 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -410,6 +410,10 @@ class OUTLINER_PT_filter(Panel): row = sub.row() row.label(icon='EMPTY_DATA') row.prop(space, "use_filter_object_empty", text="Empties") + row = sub.row() + if bpy.data.libraries: + row.label(icon='LIBRARY_DATA_OVERRIDE') + row.prop(space, "use_filter_lib_override", text="Library Overrides") if ( bpy.data.curves or diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 13edabd4cb7..c9a9c26e222 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -95,6 +95,10 @@ struct IDOverrideLibraryProperty *BKE_lib_override_library_property_get( struct IDOverrideLibrary *override, const char *rna_path, bool *r_created); void BKE_lib_override_library_property_delete(struct IDOverrideLibrary *override, struct IDOverrideLibraryProperty *override_property); +bool BKE_lib_override_rna_property_find(struct PointerRNA *idpoin, + const struct IDOverrideLibraryProperty *library_prop, + struct PointerRNA *r_override_poin, + struct PropertyRNA **r_override_prop); struct IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_find( struct IDOverrideLibraryProperty *override_property, @@ -131,7 +135,7 @@ bool BKE_lib_override_library_status_check_local(struct Main *bmain, struct ID * bool BKE_lib_override_library_status_check_reference(struct Main *bmain, struct ID *local); bool BKE_lib_override_library_operations_create(struct Main *bmain, struct ID *local); -void BKE_lib_override_library_main_operations_create(struct Main *bmain, const bool force_auto); +bool BKE_lib_override_library_main_operations_create(struct Main *bmain, const bool force_auto); void BKE_lib_override_library_id_reset(struct Main *bmain, struct ID *id_root); void BKE_lib_override_library_id_hierarchy_reset(struct Main *bmain, struct ID *id_root); diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h index 58872d6be58..b13a0580a55 100644 --- a/source/blender/blenkernel/BKE_undo_system.h +++ b/source/blender/blenkernel/BKE_undo_system.h @@ -96,6 +96,12 @@ typedef struct UndoStep { /* Over alloc 'type->struct_size'. */ } UndoStep; +typedef enum UndoPushReturn { + UNDO_PUSH_RET_FAILURE = 0, + UNDO_PUSH_RET_SUCCESS = (1 << 0), + UNDO_PUSH_RET_OVERRIDE_CHANGED = (1 << 1), +} UndoPushReturn; + typedef void (*UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref); typedef struct UndoType { @@ -172,11 +178,11 @@ UndoStep *BKE_undosys_step_push_init_with_type(UndoStack *ustack, const UndoType *ut); UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, struct bContext *C, const char *name); -bool BKE_undosys_step_push_with_type(UndoStack *ustack, - struct bContext *C, - const char *name, - const UndoType *ut); -bool BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name); +UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, + struct bContext *C, + const char *name, + const UndoType *ut); +UndoPushReturn BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name); UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index cabc80d4024..cb41a4728ef 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -57,6 +57,8 @@ #include "RNA_access.h" #include "RNA_types.h" +#include "atomic_ops.h" + #define OVERRIDE_AUTO_CHECK_DELAY 0.2 /* 200ms between auto-override checks. */ //#define DEBUG_OVERRIDE_TIMEIT @@ -982,6 +984,23 @@ IDOverrideLibraryProperty *BKE_lib_override_library_property_get(IDOverrideLibra return op; } +/** + * Get the RNA-property matching the \a library_prop override property. Used for UI to query + * additional data about the overriden property (e.g. UI name). + * + * \param idpoin: Pointer to the override ID. + * \param library_prop: The library override property to find the matching RNA property for. + */ +bool BKE_lib_override_rna_property_find(PointerRNA *idpoin, + const IDOverrideLibraryProperty *library_prop, + PointerRNA *r_override_poin, + PropertyRNA **r_override_prop) +{ + BLI_assert(RNA_struct_is_ID(idpoin->type) && ID_IS_OVERRIDE_LIBRARY(idpoin->data)); + return RNA_path_resolve_property( + idpoin, library_prop->rna_path, r_override_poin, r_override_prop); +} + void lib_override_library_property_copy(IDOverrideLibraryProperty *op_dst, IDOverrideLibraryProperty *op_src) { @@ -1370,19 +1389,20 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local) * since it has to go over all properties in depth (all overridable ones at least). * Generating differential values and applying overrides are much cheaper. * - * \return true if a new overriding op was created, or some local data was reset. */ + * \return true if any library operation was created. + */ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) { BLI_assert(local->override_library != NULL); const bool is_template = (local->override_library->reference == NULL); - bool ret = false; + bool created = false; if (!is_template) { /* Do not attempt to generate overriding rules from an empty place-holder generated by link * code when it cannot find the actual library/ID. Much better to keep the local data-block as * is in the file in that case, until broken lib is fixed. */ if (ID_MISSING(local->override_library->reference)) { - return ret; + return created; } if (GS(local->name) == ID_OB) { @@ -1412,14 +1432,16 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) local->override_library, RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE, &report_flags); + if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) { - ret = true; + created = true; } + #ifndef NDEBUG if (report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) { printf("We did restore some properties of %s from its reference.\n", local->name); } - if (ret) { + if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) { printf("We did generate library override rules for %s\n", local->name); } else { @@ -1427,19 +1449,28 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) } #endif } - return ret; + return created; } +struct LibOverrideOpCreateData { + Main *bmain; + bool changed; +}; + static void lib_override_library_operations_create_cb(TaskPool *__restrict pool, void *taskdata) { - Main *bmain = BLI_task_pool_user_data(pool); + struct LibOverrideOpCreateData *create_data = BLI_task_pool_user_data(pool); ID *id = taskdata; - BKE_lib_override_library_operations_create(bmain, id); + if (BKE_lib_override_library_operations_create(create_data->bmain, id)) { + /* Technically no need for atomic, all jobs write the same value and we only care if one did + * it. But play safe and avoid implicit assumptions. */ + atomic_fetch_and_or_uint8((uint8_t *)&create_data->changed, true); + } } /** Check all overrides from given \a bmain and create/update overriding operations as needed. */ -void BKE_lib_override_library_main_operations_create(Main *bmain, const bool force_auto) +bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool force_auto) { ID *id; @@ -1464,7 +1495,8 @@ void BKE_lib_override_library_main_operations_create(Main *bmain, const bool for } } - TaskPool *task_pool = BLI_task_pool_create(bmain, TASK_PRIORITY_HIGH); + struct LibOverrideOpCreateData create_pool_data = {.bmain = bmain, .changed = false}; + TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH); FOREACH_MAIN_ID_BEGIN (bmain, id) { if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && @@ -1503,6 +1535,8 @@ void BKE_lib_override_library_main_operations_create(Main *bmain, const bool for #ifdef DEBUG_OVERRIDE_TIMEIT TIMEIT_END_AVERAGED(BKE_lib_override_library_main_operations_create); #endif + + return create_pool_data.changed; } static bool lib_override_library_id_reset_do(Main *bmain, ID *id_root) diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index b687254fd69..078c93532d9 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -342,9 +342,10 @@ static bool undosys_stack_push_main(UndoStack *ustack, const char *name, struct CLOG_INFO(&LOG, 1, "'%s'", name); bContext *C_temp = CTX_create(); CTX_data_main_set(C_temp, bmain); - bool ok = BKE_undosys_step_push_with_type(ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE); + UndoPushReturn ret = BKE_undosys_step_push_with_type( + ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE); CTX_free(C_temp); - return ok; + return (ret & UNDO_PUSH_RET_SUCCESS); } void BKE_undosys_stack_init_from_main(UndoStack *ustack, struct Main *bmain) @@ -495,18 +496,21 @@ UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, bContext *C, const char /** * \param C: Can be NULL from some callers if their encoding function doesn't need it */ -bool BKE_undosys_step_push_with_type(UndoStack *ustack, - bContext *C, - const char *name, - const UndoType *ut) +UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, + bContext *C, + const char *name, + const UndoType *ut) { UNDO_NESTED_ASSERT(false); undosys_stack_validate(ustack, false); bool is_not_empty = ustack->step_active != NULL; + UndoPushReturn retval = UNDO_PUSH_RET_FAILURE; /* Might not be final place for this to be called - probably only want to call it from some * undo handlers, not all of them? */ - BKE_lib_override_library_main_operations_create(G_MAIN, false); + if (BKE_lib_override_library_main_operations_create(G_MAIN, false)) { + retval |= UNDO_PUSH_RET_OVERRIDE_CHANGED; + } /* Remove all undos after (also when 'ustack->step_active == NULL'). */ while (ustack->steps.last != ustack->step_active) { @@ -558,7 +562,7 @@ bool BKE_undosys_step_push_with_type(UndoStack *ustack, if (!undosys_step_encode(C, G_MAIN, ustack, us)) { MEM_freeN(us); undosys_stack_validate(ustack, true); - return false; + return retval; } ustack->step_active = us; BLI_addtail(&ustack->steps, us); @@ -589,10 +593,10 @@ bool BKE_undosys_step_push_with_type(UndoStack *ustack, } undosys_stack_validate(ustack, true); - return true; + return (retval | UNDO_PUSH_RET_SUCCESS); } -bool BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name) +UndoPushReturn BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name) { UNDO_NESTED_ASSERT(false); const UndoType *ut = ustack->step_init ? ustack->step_init->type : diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 88493de5076..7dc2c9e86ee 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3421,7 +3421,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } case SPACE_OUTLINER: { SpaceOutliner *space_outliner = (SpaceOutliner *)sl; - space_outliner->filter &= ~(SO_FILTER_UNUSED_1 | SO_FILTER_UNUSED_5 | + space_outliner->filter &= ~(SO_FILTER_CLEARED_1 | SO_FILTER_UNUSED_5 | SO_FILTER_OB_STATE_SELECTABLE); space_outliner->storeflag &= ~(SO_TREESTORE_UNUSED_1); break; diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 152bbc96281..ab515c0c3a8 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -493,7 +493,7 @@ static bool parent_clear_poll(bContext *C, case ID_OB: return ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE); case ID_GR: - return event->shift; + return event->shift || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE); default: return true; } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 9223da136e1..8c6e4329577 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -2180,6 +2180,10 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.icon = ICON_MODIFIER_DATA; data.drag_id = tselem->id; break; + case TSE_LIBRARY_OVERRIDE_BASE: + case TSE_LIBRARY_OVERRIDE: + data.icon = ICON_LIBRARY_DATA_OVERRIDE; + break; case TSE_LINKED_OB: data.icon = ICON_OBJECT_DATA; break; diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index ef152c473f4..8170f1006a5 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -331,7 +331,8 @@ static void do_item_rename(ARegion *region, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_SCENE_COLLECTION_BASE, - TSE_VIEW_COLLECTION_BASE)) { + TSE_VIEW_COLLECTION_BASE, + TSE_LIBRARY_OVERRIDE_BASE)) { BKE_report(reports, RPT_WARNING, "Cannot edit builtin name"); } else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 56eedcd3748..11894548016 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -66,6 +66,7 @@ #include "BKE_idtype.h" #include "BKE_layer.h" #include "BKE_lib_id.h" +#include "BKE_lib_override.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_outliner_treehash.h" @@ -640,6 +641,30 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, } } +static void outliner_add_library_override_contents(SpaceOutliner *soops, TreeElement *te, ID *id) +{ + if (!id->override_library) { + return; + } + + PointerRNA idpoin; + RNA_id_pointer_create(id, &idpoin); + + PointerRNA override_ptr; + PropertyRNA *override_prop; + int index = 0; + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { + if (!BKE_lib_override_rna_property_find(&idpoin, op, &override_ptr, &override_prop)) { + BLI_assert(false); + continue; + } + + TreeElement *ten = outliner_add_element( + soops, &te->subtree, id, te, TSE_LIBRARY_OVERRIDE, index++); + ten->name = RNA_property_ui_name(override_prop); + } +} + /* Can be inlined if necessary. */ static void outliner_add_id_contents(SpaceOutliner *space_outliner, TreeElement *te, @@ -903,6 +928,17 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, default: break; } + + const bool lib_overrides_visible = !SUPPORT_FILTER_OUTLINER(space_outliner) || + ((space_outliner->filter & SO_FILTER_NO_LIB_OVERRIDE) == 0); + + if (lib_overrides_visible && ID_IS_OVERRIDE_LIBRARY(id)) { + TreeElement *ten = outliner_add_element( + space_outliner, &te->subtree, id, te, TSE_LIBRARY_OVERRIDE_BASE, 0); + + ten->name = IFACE_("Library Overrides"); + outliner_add_library_override_contents(space_outliner, ten, id); + } } /** diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index c7c207caca0..06946e4c90b 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -112,6 +112,12 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), /* context changes */ switch (wmn->category) { + case NC_WM: + switch (wmn->data) { + case ND_LIB_OVERRIDE_CHANGED: + ED_region_tag_redraw(region); + break; + } case NC_SCENE: switch (wmn->data) { case ND_OB_ACTIVE: @@ -152,8 +158,6 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), case NC_OBJECT: switch (wmn->data) { case ND_TRANSFORM: - /* transform doesn't change outliner data */ - break; case ND_BONE_ACTIVE: case ND_BONE_SELECT: case ND_DRAW: diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 2328f7d5256..533416bf85e 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -145,12 +145,14 @@ void ED_undo_push(bContext *C, const char *str) } } + UndoPushReturn push_retval; + /* Only apply limit if this is the last undo step. */ if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) { BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, steps - 1, 0); } - BKE_undosys_step_push(wm->undo_stack, C, str); + push_retval = BKE_undosys_step_push(wm->undo_stack, C, str); if (U.undomemory != 0) { const size_t memory_limit = (size_t)U.undomemory * 1024 * 1024; @@ -160,6 +162,10 @@ void ED_undo_push(bContext *C, const char *str) if (CLOG_CHECK(&LOG, 1)) { BKE_undosys_print(wm->undo_stack); } + + if (push_retval & UNDO_PUSH_RET_OVERRIDE_CHANGED) { + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); + } } /** diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h index d561b489ae4..95d5a2ba73c 100644 --- a/source/blender/makesdna/DNA_outliner_types.h +++ b/source/blender/makesdna/DNA_outliner_types.h @@ -117,6 +117,8 @@ enum { #define TSE_SCENE_OBJECTS_BASE 41 #define TSE_GPENCIL_EFFECT_BASE 42 #define TSE_GPENCIL_EFFECT 43 +#define TSE_LIBRARY_OVERRIDE_BASE 44 +#define TSE_LIBRARY_OVERRIDE 45 /* Check whether given TreeStoreElem should have a real ID in its ->id member. */ #define TSE_IS_REAL_ID(_tse) \ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index f27a89a3d83..03f5d3f8d47 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -305,8 +305,9 @@ typedef enum eSpaceOutliner_Flag { /* SpaceOutliner.filter */ typedef enum eSpaceOutliner_Filter { - SO_FILTER_SEARCH = (1 << 0), /* Run-time flag. */ - SO_FILTER_UNUSED_1 = (1 << 1), /* cleared */ + SO_FILTER_SEARCH = (1 << 0), /* Run-time flag. */ + SO_FILTER_CLEARED_1 = (1 << 1), + SO_FILTER_NO_LIB_OVERRIDE = SO_FILTER_CLEARED_1, /* re-use */ SO_FILTER_NO_OBJECT = (1 << 2), SO_FILTER_NO_OB_CONTENT = (1 << 3), /* Not only mesh, but modifiers, constraints, ... */ SO_FILTER_NO_CHILDREN = (1 << 4), @@ -339,7 +340,7 @@ typedef enum eSpaceOutliner_Filter { #define SO_FILTER_ANY \ (SO_FILTER_NO_OB_CONTENT | SO_FILTER_NO_CHILDREN | SO_FILTER_OB_TYPE | SO_FILTER_OB_STATE | \ - SO_FILTER_NO_COLLECTION) + SO_FILTER_NO_COLLECTION | SO_FILTER_NO_LIB_OVERRIDE) /* SpaceOutliner.filter_state */ typedef enum eSpaceOutliner_StateFilter { diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 548a1cae610..a5aedad20f0 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3442,6 +3442,13 @@ static void rna_def_space_outliner(BlenderRNA *brna) RNA_def_property_enum_items(prop, rna_enum_id_type_items); RNA_def_property_ui_text(prop, "Filter by Type", "Data-block type to show"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); + + prop = RNA_def_property(srna, "use_filter_lib_override", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "filter", SO_FILTER_NO_LIB_OVERRIDE); + RNA_def_property_ui_text(prop, + "Show Library Overrides", + "For libraries with overrides created, show the overriden values"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL); } static void rna_def_space_view3d_shading(BlenderRNA *brna) diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index a2cc246e21e..448166170a9 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -312,6 +312,7 @@ typedef struct wmNotifier { #define ND_JOB (5 << 16) #define ND_UNDO (6 << 16) #define ND_XR_DATA_CHANGED (7 << 16) +#define ND_LIB_OVERRIDE_CHANGED (8 << 16) /* NC_SCREEN */ #define ND_LAYOUTBROWSE (1 << 16)