diff --git a/source/blender/editors/include/ED_view3d.hh b/source/blender/editors/include/ED_view3d.hh index b11b795bdaa..372281ab678 100644 --- a/source/blender/editors/include/ED_view3d.hh +++ b/source/blender/editors/include/ED_view3d.hh @@ -1303,6 +1303,27 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, /* `view3d_view.cc` */ +/** + * Exit 'local view' of given View3D editor, if it is active and there is nothing to display in it + * anymore. + * + * \param depsgraph Optional, only required for #frame_selected. + * \param frame_selected Frame the newly out-of-local view to show currently visible selected + * objects. Will only do something if a valid #depsgraph pointer is also provided. + * \param smooth_viewtx Smooth transition time (in milliseconds) between current view and final + * view, if changes are hapenning. Currently only used if #frame_selected is enabled. + * + * \return `true` if the local view was actually exited. + */ +bool ED_localview_exit_if_empty(const Depsgraph *depsgraph, + Scene *scene, + ViewLayer *view_layer, + wmWindowManager *wm, + wmWindow *win, + View3D *v3d, + ScrArea *area, + bool frame_selected = true, + int smooth_viewtx = 0); /** * See if current UUID is valid, otherwise set a valid UUID to v3d, * Try to keep the same UUID previously used to allow users to quickly toggle back and forth. diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index a3799e7c6a0..9236b80a46b 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -1112,6 +1112,9 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) case ND_LAYER_CONTENT: ED_region_tag_redraw(region); WM_gizmomap_tag_refresh(gzmap); + if (v3d->localvd && v3d->localvd->runtime.flag & V3D_RUNTIME_LOCAL_MAYBE_EMPTY) { + ED_area_tag_refresh(area); + } break; case ND_LAYER: if (wmn->reference) { @@ -1315,6 +1318,11 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) break; case NC_ID: if (ELEM(wmn->action, NA_RENAME, NA_EDITED, NA_ADDED, NA_REMOVED)) { + if (ELEM(wmn->action, NA_EDITED, NA_REMOVED) && v3d->localvd && + v3d->localvd->runtime.flag & V3D_RUNTIME_LOCAL_MAYBE_EMPTY) + { + ED_area_tag_refresh(area); + } ED_region_tag_redraw(region); WM_gizmomap_tag_refresh(gzmap); } @@ -1976,9 +1984,20 @@ static void space_view3d_listener(const wmSpaceTypeListenerParams *params) static void space_view3d_refresh(const bContext *C, ScrArea *area) { - UNUSED_VARS(C); View3D *v3d = (View3D *)area->spacedata.first; MEM_SAFE_FREE(v3d->runtime.local_stats); + + if (v3d->localvd && v3d->localvd->runtime.flag & V3D_RUNTIME_LOCAL_MAYBE_EMPTY) { + ED_localview_exit_if_empty(CTX_data_ensure_evaluated_depsgraph(C), + CTX_data_scene(C), + CTX_data_view_layer(C), + CTX_wm_manager(C), + CTX_wm_window(C), + v3d, + CTX_wm_area(C), + true, + 300); + } } static void view3d_id_remap_v3d_ob_centers(View3D *v3d, @@ -2033,6 +2052,9 @@ static void view3d_id_remap(ScrArea *area, if (view3d->localvd != nullptr) { /* Object centers in local-view aren't used, see: #52663 */ view3d_id_remap_v3d(area, slink, view3d->localvd, mappings, true); + /* Remapping is potentially modifying ID pointers, and there is a local View3D, mark it for a + * check for emptiness. */ + view3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY; } BKE_viewer_path_id_remap(&view3d->viewer_path, mappings); } @@ -2045,6 +2067,13 @@ static void view3d_foreach_id(SpaceLink *space_link, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->ob_center, IDWALK_CB_DIRECT_WEAK_LINK); if (v3d->localvd) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_DIRECT_WEAK_LINK); + + /* If potentially modifying ID pointers, and there is a local View3D, mark it for a check for + * emptiness. */ + const int flags = BKE_lib_query_foreachid_process_flags_get(data); + if ((flags & IDWALK_READONLY) == 0) { + v3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY; + } } BKE_viewer_path_foreach_id(data, &v3d->viewer_path); } diff --git a/source/blender/editors/space_view3d/view3d_view.cc b/source/blender/editors/space_view3d/view3d_view.cc index 1eb58382367..cca7a8ef552 100644 --- a/source/blender/editors/space_view3d/view3d_view.cc +++ b/source/blender/editors/space_view3d/view3d_view.cc @@ -989,7 +989,7 @@ static void view3d_localview_exit(const Depsgraph *depsgraph, continue; } - if (frame_selected) { + if (frame_selected && depsgraph) { Object *camera_old_rv3d, *camera_new_rv3d; camera_old_rv3d = (rv3d->persp == RV3D_CAMOB) ? camera_old : nullptr; @@ -1018,6 +1018,34 @@ static void view3d_localview_exit(const Depsgraph *depsgraph, } } +bool ED_localview_exit_if_empty(const Depsgraph *depsgraph, + Scene *scene, + ViewLayer *view_layer, + wmWindowManager *wm, + wmWindow *win, + View3D *v3d, + ScrArea *area, + const bool frame_selected, + const int smooth_viewtx) +{ + if (v3d->localvd == nullptr) { + return false; + } + + v3d->localvd->runtime.flag &= ~V3D_RUNTIME_LOCAL_MAYBE_EMPTY; + + BKE_view_layer_synced_ensure(scene, view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { + if (base->local_view_bits & v3d->local_view_uid) { + return false; + } + } + + view3d_localview_exit( + depsgraph, wm, win, scene, view_layer, area, frame_selected, smooth_viewtx); + return true; +} + static int localview_exec(bContext *C, wmOperator *op) { const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); @@ -1108,6 +1136,19 @@ static int localview_remove_from_exec(bContext *C, wmOperator *op) } } + /* If some object was removed from the local view, exit the local view if it is now empty. */ + if (changed) { + ED_localview_exit_if_empty(CTX_data_ensure_evaluated_depsgraph(C), + scene, + view_layer, + CTX_wm_manager(C), + CTX_wm_window(C), + v3d, + CTX_wm_area(C), + true, + WM_operator_smooth_viewtx_get(op)); + } + if (changed) { DEG_tag_on_visible_update(bmain, false); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); diff --git a/source/blender/editors/util/ed_util.cc b/source/blender/editors/util/ed_util.cc index cd5dc783ba7..6e739c6a9b6 100644 --- a/source/blender/editors/util/ed_util.cc +++ b/source/blender/editors/util/ed_util.cc @@ -29,6 +29,7 @@ #include "BKE_object.hh" #include "BKE_packedFile.h" #include "BKE_paint.hh" +#include "BKE_scene.hh" #include "BKE_screen.hh" #include "BKE_undo_system.hh" @@ -47,6 +48,7 @@ #include "ED_sculpt.hh" #include "ED_space_api.hh" #include "ED_util.hh" +#include "ED_view3d.hh" #include "GPU_immediate.hh" @@ -71,6 +73,21 @@ void ED_editors_init_for_undo(Main *bmain) BKE_texpaint_slots_refresh_object(scene, ob); ED_paint_proj_mesh_data_check(*scene, *ob, nullptr, nullptr, nullptr, nullptr); } + + /* UI Updates. */ + /* Flag local View3D's to check and exit if they are empty. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = reinterpret_cast(sl); + if (v3d->localvd) { + v3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY; + } + } + } + } + } } } diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index ee6ff29d04b..f0a07ab8b80 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -389,6 +389,8 @@ enum { V3D_RUNTIME_XR_SESSION_ROOT = (1 << 0), /** Some operators override the depth buffer for dedicated occlusion operations. */ V3D_RUNTIME_DEPTHBUF_OVERRIDDEN = (1 << 1), + /** Local view may have become empty, and may need to be exited. */ + V3D_RUNTIME_LOCAL_MAYBE_EMPTY = (1 << 2), }; /** #RegionView3D::persp */ diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index f3611a3668e..a6bb3e19526 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -452,6 +452,21 @@ static void wm_file_read_setup_wm_finalize(bContext *C, /* Else just using the new WM read from file, nothing to do. */ BLI_assert(wm_setup_data->old_wm == nullptr); MEM_delete(wm_setup_data); + + /* UI Updates. */ + /* Flag local View3D's to check and exit if they are empty. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = reinterpret_cast(sl); + if (v3d->localvd) { + v3d->localvd->runtime.flag |= V3D_RUNTIME_LOCAL_MAYBE_EMPTY; + } + } + } + } + } } /** \} */