Outliner: Properties editor sync on selection

When outliner datablocks are selected, switch to the corresponding tab
for that datablock in properties editors. Only properties editors
that share an edge with the outliner will change tabs.

Additionally, when modifiers, constraints, and shader effects are
selected from the outliner, the panel will be expanded in all properties
editors.

Part of T77408

Manifest Task: https://developer.blender.org/T63991

Differential Revision: https://developer.blender.org/D8638
This commit is contained in:
Nathan Craddock 2020-10-28 11:43:10 -06:00
parent 5af8fc8192
commit 0e47e57eb7
Notes: blender-bot 2023-02-14 02:52:35 +01:00
Referenced by issue #94079, Outliner: Object selected/deselected when clicking with RMB on it's object data [`Sync Selection` option causes this]
Referenced by issue #93799, Outliner: Remap Users crash (for `ID Type` `Object`)
Referenced by issue #82668, Properties editor "forgets" selected tab and always resets to Object properties tab when switching between objects or editors
Referenced by issue #77408, Continued Outliner Improvements Design
Referenced by issue #63991, Outliner/Properties syncing
13 changed files with 265 additions and 16 deletions

View File

@ -183,6 +183,8 @@ bool BKE_constraint_remove_ex(ListBase *list,
bool clear_dep);
bool BKE_constraint_remove(ListBase *list, struct bConstraint *con);
void BKE_constraint_panel_expand(struct bConstraint *con);
/* Constraints + Proxies function prototypes */
void BKE_constraints_proxylocal_extract(struct ListBase *dst, struct ListBase *src);
bool BKE_constraints_proxylocked_owner(struct Object *ob, struct bPoseChannel *pchan);

View File

@ -249,6 +249,7 @@ typedef struct GpencilModifierTypeInfo {
void BKE_gpencil_modifier_init(void);
void BKE_gpencil_modifierType_panel_id(GpencilModifierType type, char *r_idname);
void BKE_gpencil_modifier_panel_expand(struct GpencilModifierData *md);
const GpencilModifierTypeInfo *BKE_gpencil_modifier_get_info(GpencilModifierType type);
struct GpencilModifierData *BKE_gpencil_modifier_new(int type);
void BKE_gpencil_modifier_free_ex(struct GpencilModifierData *md, const int flag);

View File

@ -396,6 +396,7 @@ const ModifierTypeInfo *BKE_modifier_get_info(ModifierType type);
/* For modifier UI panels. */
void BKE_modifier_type_panel_id(ModifierType type, char *r_idname);
void BKE_modifier_panel_expand(struct ModifierData *md);
/* Modifier utility calls, do call through type pointer and return
* default values if pointer is optional.

View File

@ -151,6 +151,7 @@ typedef struct ShaderFxTypeInfo {
void BKE_shaderfx_init(void);
void BKE_shaderfxType_panel_id(ShaderFxType type, char *r_idname);
void BKE_shaderfx_panel_expand(struct ShaderFxData *fx);
const ShaderFxTypeInfo *BKE_shaderfx_get_info(ShaderFxType type);
struct ShaderFxData *BKE_shaderfx_new(int type);
void BKE_shaderfx_free_ex(struct ShaderFxData *fx, const int flag);

View File

@ -5406,6 +5406,11 @@ bool BKE_constraint_remove_ex(ListBase *list, Object *ob, bConstraint *con, bool
return false;
}
void BKE_constraint_panel_expand(bConstraint *con)
{
con->ui_expand_flag |= (1 << 0);
}
/* ......... */
/* Creates a new constraint, initializes its data, and returns it */

View File

@ -411,6 +411,11 @@ void BKE_gpencil_modifierType_panel_id(GpencilModifierType type, char *r_idname)
strcat(r_idname, mti->name);
}
void BKE_gpencil_modifier_panel_expand(GpencilModifierData *md)
{
md->ui_expand_flag |= (1 << 0);
}
/**
* Generic grease pencil modifier copy data.
* \param md_src: Source modifier data

View File

@ -130,6 +130,11 @@ void BKE_modifier_type_panel_id(ModifierType type, char *r_idname)
strcat(r_idname, mti->name);
}
void BKE_modifier_panel_expand(ModifierData *md)
{
md->ui_expand_flag |= (1 << 0);
}
/***/
ModifierData *BKE_modifier_new(int type)

View File

@ -175,6 +175,11 @@ void BKE_shaderfxType_panel_id(ShaderFxType type, char *r_idname)
strcat(r_idname, fxi->name);
}
void BKE_shaderfx_panel_expand(ShaderFxData *fx)
{
fx->ui_expand_flag |= (1 << 0);
}
void BKE_shaderfx_copydata_generic(const ShaderFxData *fx_src, ShaderFxData *fx_dst)
{
const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx_src->type);

View File

@ -26,6 +26,7 @@
extern "C" {
#endif
struct bContext;
struct SpaceProperties;
int ED_buttons_tabs_list(struct SpaceProperties *sbuts, short *context_tabs_array);
@ -35,6 +36,8 @@ void ED_buttons_search_string_set(struct SpaceProperties *sbuts, const char *val
int ED_buttons_search_string_length(struct SpaceProperties *sbuts);
const char *ED_buttons_search_string_get(struct SpaceProperties *sbuts);
void ED_buttons_set_context(const struct bContext *C, PointerRNA *ptr, const int context);
#ifdef __cplusplus
}
#endif

View File

@ -199,6 +199,7 @@ int ED_region_global_size_y(void);
void ED_area_update_region_sizes(struct wmWindowManager *wm,
struct wmWindow *win,
struct ScrArea *area);
bool ED_area_has_shared_border(struct ScrArea *a, struct ScrArea *b);
ScrArea *ED_screen_areas_iter_first(const struct wmWindow *win, const bScreen *screen);
ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area);

View File

@ -1871,6 +1871,11 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar
area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE;
}
bool ED_area_has_shared_border(struct ScrArea *a, struct ScrArea *b)
{
return area_getorientation(a, b) != -1;
}
/* called in screen_refresh, or screens_init, also area size changes */
void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area)
{

View File

@ -56,6 +56,7 @@
#include "RNA_access.h"
#include "ED_buttons.h"
#include "ED_physics.h"
#include "ED_screen.h"
@ -512,11 +513,11 @@ static bool buttons_context_linestyle_pinnable(const bContext *C, ViewLayer *vie
}
#endif
static bool buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int flag)
static bool buttons_context_path(
const bContext *C, SpaceProperties *sbuts, ButsContextPath *path, int mainb, int flag)
{
/* Note we don't use CTX_data here, instead we get it from the window.
* Otherwise there is a loop reading the context that we are setting. */
SpaceProperties *sbuts = CTX_wm_space_properties(C);
wmWindow *window = CTX_wm_window(C);
Scene *scene = WM_window_get_active_scene(window);
ViewLayer *view_layer = WM_window_get_active_view_layer(window);
@ -660,14 +661,14 @@ void buttons_context_compute(const bContext *C, SpaceProperties *sbuts)
int flag = 0;
/* Set scene path. */
buttons_context_path(C, path, BCONTEXT_SCENE, pflag);
buttons_context_path(C, sbuts, path, BCONTEXT_SCENE, pflag);
buttons_texture_context_compute(C, sbuts);
/* for each context, see if we can compute a valid path to it, if
* this is the case, we know we have to display the button */
for (int i = 0; i < BCONTEXT_TOT; i++) {
if (buttons_context_path(C, path, i, pflag)) {
if (buttons_context_path(C, sbuts, path, i, pflag)) {
flag |= (1 << i);
/* setting icon for data context */
@ -713,7 +714,7 @@ void buttons_context_compute(const bContext *C, SpaceProperties *sbuts)
}
}
buttons_context_path(C, path, sbuts->mainb, pflag);
buttons_context_path(C, sbuts, path, sbuts->mainb, pflag);
if (!(flag & (1 << sbuts->mainb))) {
if (flag & (1 << BCONTEXT_OBJECT)) {
@ -734,6 +735,39 @@ void buttons_context_compute(const bContext *C, SpaceProperties *sbuts)
sbuts->pathflag = flag;
}
static bool is_pointer_in_path(ButsContextPath *path, PointerRNA *ptr)
{
for (int i = 0; i < path->len; ++i) {
if (ptr->owner_id == path->ptr[i].owner_id) {
return true;
}
}
return false;
}
void ED_buttons_set_context(const bContext *C, PointerRNA *ptr, const int context)
{
ScrArea *active_area = CTX_wm_area(C);
bScreen *screen = CTX_wm_screen(C);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
/* Only update for properties editors that are visible and share a border. */
if (area->spacetype != SPACE_PROPERTIES) {
continue;
}
if (!ED_area_has_shared_border(active_area, area)) {
continue;
}
SpaceProperties *sbuts = (SpaceProperties *)area->spacedata.first;
ButsContextPath path;
if (buttons_context_path(C, sbuts, &path, context, 0) && is_pointer_in_path(&path, ptr)) {
sbuts->mainbuser = context;
sbuts->mainb = sbuts->mainbuser;
}
}
}
/************************* Context Callback ************************/
const char *buttons_context_dir[] = {

View File

@ -27,12 +27,16 @@
#include "DNA_armature_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_light_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_shader_fx_types.h"
#include "DNA_world_types.h"
#include "BLI_listbase.h"
@ -40,21 +44,27 @@
#include "BKE_armature.h"
#include "BKE_collection.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_sequencer.h"
#include "BKE_shader_fx.h"
#include "BKE_workspace.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "ED_armature.h"
#include "ED_buttons.h"
#include "ED_gpencil.h"
#include "ED_object.h"
#include "ED_outliner.h"
@ -769,15 +779,26 @@ static eOLDrawState tree_element_active_psys(bContext *C,
}
static int tree_element_active_constraint(bContext *C,
Scene *UNUSED(scene),
ViewLayer *UNUSED(sl),
TreeElement *UNUSED(te),
Scene *scene,
ViewLayer *view_layer,
TreeElement *te,
TreeStoreElem *tselem,
const eOLSetState set)
{
if (set != OL_SETSEL_NONE) {
Object *ob = (Object *)tselem->id;
/* Activate the parent bone if this is a bone constraint. */
te = te->parent;
while (te) {
tselem = TREESTORE(te);
if (tselem->type == TSE_POSE_CHANNEL) {
tree_element_active_posechannel(C, scene, view_layer, ob, te, tselem, set, false);
return OL_DRAWSEL_NONE;
}
te = te->parent;
}
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
}
@ -1009,6 +1030,7 @@ eOLDrawState tree_element_type_active(bContext *C,
case TSE_POSE_CHANNEL:
return tree_element_active_posechannel(
C, tvc->scene, tvc->view_layer, tvc->ob_pose, te, tselem, set, recursive);
case TSE_CONSTRAINT_BASE:
case TSE_CONSTRAINT:
return tree_element_active_constraint(C, tvc->scene, tvc->view_layer, te, tselem, set);
case TSE_R_LAYER:
@ -1049,6 +1071,169 @@ bPoseChannel *outliner_find_parent_bone(TreeElement *te, TreeElement **r_bone_te
return NULL;
}
static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreElem *tselem)
{
PointerRNA ptr = {0};
int context = 0;
/* ID Types */
if (tselem->type == 0) {
RNA_id_pointer_create(tselem->id, &ptr);
switch (te->idcode) {
case ID_SCE:
context = BCONTEXT_SCENE;
break;
case ID_OB:
context = BCONTEXT_OBJECT;
break;
case ID_ME:
case ID_CU:
case ID_MB:
case ID_IM:
case ID_LT:
case ID_LA:
case ID_CA:
case ID_KE:
case ID_SPK:
case ID_AR:
case ID_GD:
case ID_LP:
case ID_HA:
case ID_PT:
case ID_VO:
context = BCONTEXT_DATA;
break;
case ID_MA:
context = BCONTEXT_MATERIAL;
break;
case ID_WO:
context = BCONTEXT_WORLD;
break;
}
}
else {
switch (tselem->type) {
case TSE_DEFGROUP_BASE:
case TSE_DEFGROUP:
RNA_id_pointer_create(tselem->id, &ptr);
context = BCONTEXT_DATA;
break;
case TSE_CONSTRAINT_BASE:
case TSE_CONSTRAINT: {
TreeElement *bone_te = NULL;
bPoseChannel *pchan = outliner_find_parent_bone(te, &bone_te);
if (pchan) {
RNA_pointer_create(TREESTORE(bone_te)->id, &RNA_PoseBone, pchan, &ptr);
context = BCONTEXT_BONE_CONSTRAINT;
}
else {
RNA_id_pointer_create(tselem->id, &ptr);
context = BCONTEXT_CONSTRAINT;
}
/* Expand the selected constraint in the properties editor. */
if (tselem->type != TSE_CONSTRAINT_BASE) {
BKE_constraint_panel_expand(te->directdata);
}
break;
}
case TSE_MODIFIER_BASE:
case TSE_MODIFIER:
RNA_id_pointer_create(tselem->id, &ptr);
context = BCONTEXT_MODIFIER;
if (tselem->type != TSE_MODIFIER_BASE) {
Object *ob = (Object *)tselem->id;
if (ob->type == OB_GPENCIL) {
BKE_gpencil_modifier_panel_expand(te->directdata);
}
else {
BKE_modifier_panel_expand(te->directdata);
}
}
break;
case TSE_GPENCIL_EFFECT_BASE:
case TSE_GPENCIL_EFFECT:
RNA_id_pointer_create(tselem->id, &ptr);
context = BCONTEXT_SHADERFX;
if (tselem->type != TSE_GPENCIL_EFFECT_BASE) {
BKE_shaderfx_panel_expand(te->directdata);
}
break;
case TSE_BONE: {
bArmature *arm = (bArmature *)tselem->id;
Bone *bone = te->directdata;
RNA_pointer_create(&arm->id, &RNA_Bone, bone, &ptr);
context = BCONTEXT_BONE;
break;
}
case TSE_EBONE: {
bArmature *arm = (bArmature *)tselem->id;
EditBone *ebone = te->directdata;
RNA_pointer_create(&arm->id, &RNA_EditBone, ebone, &ptr);
context = BCONTEXT_BONE;
break;
}
case TSE_POSE_CHANNEL: {
Object *ob = (Object *)tselem->id;
bArmature *arm = ob->data;
bPoseChannel *pchan = te->directdata;
RNA_pointer_create(&arm->id, &RNA_PoseBone, pchan, &ptr);
context = BCONTEXT_BONE;
break;
}
case TSE_POSE_BASE: {
Object *ob = (Object *)tselem->id;
bArmature *arm = ob->data;
RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr);
context = BCONTEXT_DATA;
break;
}
case TSE_R_LAYER_BASE:
case TSE_R_LAYER: {
ViewLayer *view_layer = te->directdata;
RNA_pointer_create(tselem->id, &RNA_ViewLayer, view_layer, &ptr);
context = BCONTEXT_VIEW_LAYER;
break;
}
case TSE_POSEGRP_BASE:
case TSE_POSEGRP: {
Object *ob = (Object *)tselem->id;
bArmature *arm = ob->data;
RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr);
context = BCONTEXT_DATA;
break;
}
case TSE_LINKED_PSYS: {
Object *ob = (Object *)tselem->id;
ParticleSystem *psys = psys_get_current(ob);
RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &ptr);
context = BCONTEXT_PARTICLE;
break;
}
case TSE_GP_LAYER:
RNA_id_pointer_create(tselem->id, &ptr);
context = BCONTEXT_DATA;
break;
}
}
if (ptr.data) {
ED_buttons_set_context(C, &ptr, context);
}
}
/* ================================================ */
/**
@ -1073,14 +1258,8 @@ static void do_outliner_item_activate_tree_element(bContext *C,
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. */
/* 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 (do_activate_data) {
tree_element_set_active_object(C,
@ -1155,6 +1334,8 @@ static void do_outliner_item_activate_tree_element(bContext *C,
extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL,
recursive);
}
outliner_set_properties_tab(C, te, tselem);
}
/* Select the item using the set flags */