Workspaces: Option to pin scene to a workspace

Adds a "Pin Scene" option to the workspace. When activated, the workspace will
remember the scene that was last activated in it, so that when switching back
to this workspace, the same scene will be reactivated. This is important for a
VSE workflow, so that users can switch between different workspaces displaying
a scene and thus a timeline for a specific task.

The option can be found in the Properties, Workspace tab. D11890 additionally
adds an icon for this to the scene switcher in the topbar.

The workspace data contains a pointer to the scene which is a UI to scene data
relation. When appending a workspace, the pointer is cleared.

Differential Revision: https://developer.blender.org/D9140

Reviewed by: Brecht Van Lommel, Bastien Montagne (no final accept, but was fine
with the general design earlier)
This commit is contained in:
Julian Eisel 2022-07-07 17:51:18 +02:00
parent f9a805164a
commit e0cc86978c
Notes: blender-bot 2023-02-14 06:46:23 +01:00
Referenced by issue #68321, GPencil: New Workflow for Storyboarding
8 changed files with 114 additions and 2 deletions

View File

@ -28,6 +28,8 @@ class WORKSPACE_PT_main(WorkSpaceButtonsPanel, Panel):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(workspace, "use_pin_scene")
layout.prop(workspace, "object_mode", text="Mode")

View File

@ -67,6 +67,8 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data)
{
WorkSpace *workspace = (WorkSpace *)id;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, workspace->pin_scene, IDWALK_CB_NOP);
LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER);
}
@ -120,6 +122,15 @@ static void workspace_blend_read_lib(BlendLibReader *reader, ID *id)
WorkSpace *workspace = (WorkSpace *)id;
Main *bmain = BLO_read_lib_get_main(reader);
/* Do not keep the scene reference when appending a workspace. Setting a scene for a workspace is
* a convenience feature, but the workspace should never truly depend on scene data. */
if (ID_IS_LINKED(id)) {
workspace->pin_scene = NULL;
}
else {
BLO_read_id_address(reader, NULL, &workspace->pin_scene);
}
/* Restore proper 'parent' pointers to relevant data, and clean up unused/invalid entries. */
LISTBASE_FOREACH_MUTABLE (WorkSpaceDataRelation *, relation, &workspace->hook_layout_relations) {
relation->parent = NULL;

View File

@ -2732,6 +2732,8 @@ void blo_lib_link_restore(Main *oldmain,
LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) {
lib_link_workspace_layout_restore(id_map, newmain, layout);
}
workspace->pin_scene = restore_pointer_by_name(
id_map, (ID *)workspace->pin_scene, USER_IGNORE);
}
LISTBASE_FOREACH (wmWindow *, win, &curwm->windows) {
@ -2745,6 +2747,7 @@ void blo_lib_link_restore(Main *oldmain,
if (win->scene == NULL) {
win->scene = curscene;
}
win->unpinned_scene = restore_pointer_by_name(id_map, (ID *)win->unpinned_scene, USER_IGNORE);
if (BKE_view_layer_find(win->scene, win->view_layer_name) == NULL) {
STRNCPY(win->view_layer_name, cur_view_layer->name);
}

View File

@ -54,17 +54,87 @@ WorkSpace *ED_workspace_add(Main *bmain, const char *name)
return BKE_workspace_add(bmain, name);
}
static void workspace_exit(WorkSpace *workspace, wmWindow *win)
{
/* Scene pinning: Store whatever scene was active when leaving the workspace. It's reactivated
* when the workspace gets reactivated as well. */
if (workspace->flags & WORKSPACE_USE_PIN_SCENE) {
workspace->pin_scene = WM_window_get_active_scene(win);
}
else {
/* The active scene may have been changed. So also always update the unpinned scene to the
* latest when leaving a workspace that has no scene pinning. */
win->unpinned_scene = WM_window_get_active_scene(win);
}
}
/**
* State changes (old workspace to new workspace):
* 1) unpinned -> pinned
* * Store current scene as the unpinned one (done in #workspace_exit()).
* * Change the current scene to the pinned one.
* 2) pinned -> pinned
* * Change the current scene to the new pinned one.
* 3) pinned -> unpinned
* * Change current scene back to the unpinned one
* 4) unpinned -> unpinned
* * Make sure the unpinned scene is active.
*
* Note that the pin scene must also be updated when leaving a workspace with a pinned scene.
* That's done separately via workspace_exit() above.
*/
static void workspace_scene_pinning_update(WorkSpace *workspace_new,
const WorkSpace *workspace_old,
bContext *C)
{
wmWindow *win = CTX_wm_window(C);
Main *bmain = CTX_data_main(C);
Scene *active_scene = WM_window_get_active_scene(win);
const bool is_new_pinned = (workspace_new->flags & WORKSPACE_USE_PIN_SCENE);
const bool is_old_pinned = (workspace_old->flags & WORKSPACE_USE_PIN_SCENE);
/* State changes 1 and 2. */
if (is_new_pinned) {
if (workspace_new->pin_scene && (workspace_new->pin_scene != active_scene)) {
WM_window_set_active_scene(bmain, C, win, workspace_new->pin_scene);
workspace_new->pin_scene = NULL;
}
}
/* State change 3 - Changing from workspace with pinned scene to unpinned scene. */
else if (is_old_pinned) {
if (win->unpinned_scene) {
WM_window_set_active_scene(bmain, C, win, win->unpinned_scene);
}
else {
/* When leaving a workspace where the pinning was just enabled, the unpinned scene wasn't set
* yet. */
win->unpinned_scene = active_scene;
}
}
else {
/* When leaving a workspace where the pinning was just disabled, we still want to restore the
* unpinned scene. */
if (win->unpinned_scene) {
WM_window_set_active_scene(bmain, C, win, win->unpinned_scene);
}
}
BLI_assert(WM_window_get_active_scene(win));
}
/**
* Changes the object mode (if needed) to the one set in \a workspace_new.
* Object mode is still stored on object level. In future it should all be workspace level instead.
*/
static void workspace_change_update(WorkSpace *workspace_new,
const WorkSpace *workspace_old,
WorkSpace *workspace_old,
bContext *C,
wmWindowManager *wm)
{
workspace_scene_pinning_update(workspace_new, workspace_old, C);
/* needs to be done before changing mode! (to ensure right context) */
UNUSED_VARS(workspace_old, workspace_new, C, wm);
UNUSED_VARS(wm);
#if 0
Object *ob_act = CTX_data_active_object(C) eObjectMode mode_old = workspace_old->object_mode;
eObjectMode mode_new = workspace_new->object_mode;
@ -113,6 +183,8 @@ bool ED_workspace_change(WorkSpace *workspace_new, bContext *C, wmWindowManager
return false;
}
workspace_exit(workspace_old, win);
screen_change_prepare(screen_old, screen_new, bmain, C, win);
if (screen_new == NULL) {
@ -143,6 +215,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo
WorkSpace *workspace_new = ED_workspace_add(bmain, workspace_old->id.name + 2);
workspace_new->flags = workspace_old->flags;
workspace_new->pin_scene = workspace_old->pin_scene;
workspace_new->object_mode = workspace_old->object_mode;
workspace_new->order = workspace_old->order;
BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids);

View File

@ -239,6 +239,9 @@ typedef struct wmWindow {
struct Scene *new_scene;
/** Active view layer displayed in this window. */
char view_layer_name[64];
/** The workspace may temporarily override the window's scene with scene pinning. This is the
* "overriden" or "default" scene to restore when entering a workspace with no scene pinned. */
struct Scene *unpinned_scene;
struct WorkSpaceInstanceHook *workspace_hook;

View File

@ -123,6 +123,10 @@ typedef struct WorkSpace {
/** List of #bToolRef */
ListBase tools;
/** Optional, scene to switch to when enabling this workspace (NULL to disable). Cleared on
* link/append. */
struct Scene *pin_scene;
char _pad[4];
int object_mode;
@ -195,6 +199,7 @@ typedef struct WorkSpaceInstanceHook {
typedef enum eWorkSpaceFlags {
WORKSPACE_USE_FILTER_BY_ORIGIN = (1 << 1),
WORKSPACE_USE_PIN_SCENE = (1 << 2),
} eWorkSpaceFlags;
#ifdef __cplusplus

View File

@ -413,6 +413,13 @@ static void rna_def_workspace(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Object Mode", "Switch to this object mode when activating the workspace");
prop = RNA_def_property(srna, "use_pin_scene", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", WORKSPACE_USE_PIN_SCENE);
RNA_def_property_ui_text(prop,
"Pin Scene",
"Remember the last used scene for the workspace and switch to it "
"whenever this workspace is activated again");
/* Flags */
prop = RNA_def_property(srna, "use_filter_by_owner", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);

View File

@ -81,6 +81,8 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data)
if (BKE_lib_query_foreachid_iter_stop(data)) {
return;
}
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win->unpinned_scene, IDWALK_CB_NOP);
}
if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) {
@ -224,6 +226,7 @@ static void lib_link_workspace_instance_hook(BlendLibReader *reader,
{
WorkSpace *workspace = BKE_workspace_active_get(hook);
BLO_read_id_address(reader, id->lib, &workspace);
BKE_workspace_active_set(hook, workspace);
}
@ -239,6 +242,11 @@ static void window_manager_blend_read_lib(BlendLibReader *reader, ID *id)
/* deprecated, but needed for versioning (will be NULL'ed then) */
BLO_read_id_address(reader, NULL, &win->screen);
/* The unpinned scene is a UI->Scene-data pointer, and should be NULL'ed on linking (like
* WorkSpace.pin_scene). But the WindowManager ID (owning the window) is never linked. */
BLI_assert(!ID_IS_LINKED(id));
BLO_read_id_address(reader, id->lib, &win->unpinned_scene);
LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) {
BKE_screen_area_blend_read_lib(reader, &wm->id, area);
}