Skip to content

Commit b54f8b9

Browse files
committed
Title bar: View menu (zoom, fit all, isolation, layouts) + Help links + phosphor icons
1 parent 3ccb689 commit b54f8b9

4 files changed

Lines changed: 318 additions & 35 deletions

File tree

crates/renzora/src/core/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,24 @@ pub struct OpenCodeEditorFile {
824824
pub path: std::path::PathBuf,
825825
}
826826

827+
/// One-shot: request a viewport camera operation from the View menu.
828+
///
829+
/// Consumed by the camera controller in `renzora_camera`.
830+
#[derive(Resource, Clone, Copy, Debug)]
831+
pub enum CameraViewRequest {
832+
ZoomIn,
833+
ZoomOut,
834+
ResetZoom,
835+
FrameAll,
836+
}
837+
838+
/// Toggle: when active, only the selected entity (and its ancestors/descendants)
839+
/// remain visible in the viewport. Toggled from the View menu.
840+
#[derive(Resource, Default)]
841+
pub struct IsolationMode {
842+
pub active: bool,
843+
}
844+
827845
/// Tracks whether a UI text input has keyboard focus.
828846
///
829847
/// When `true`, keyboard shortcuts should not fire so typing is not interrupted.

crates/renzora_camera/src/lib.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ impl Plugin for CameraPlugin {
172172
handle_view_angle_keys,
173173
focus_selected,
174174
frame_all,
175+
handle_camera_view_request,
175176
camera_to_cursor,
176177
camera_controller,
177178
apply_nav_overlay,
@@ -374,6 +375,56 @@ fn frame_all(
374375
pivot_lock.0 = true;
375376
}
376377

378+
/// Consume one-shot `CameraViewRequest`s from the View menu (Zoom In/Out,
379+
/// Reset Zoom, Fit All) and apply them to the orbit camera.
380+
fn handle_camera_view_request(
381+
mut commands: Commands,
382+
request: Option<Res<renzora::core::CameraViewRequest>>,
383+
play_mode: Option<Res<renzora::core::PlayModeState>>,
384+
mut orbit: ResMut<OrbitCameraState>,
385+
mut pivot_lock: ResMut<PivotLock>,
386+
meshes: Query<&GlobalTransform, (With<Mesh3d>, Without<EditorCamera>, Without<PlayModeCamera>)>,
387+
) {
388+
let Some(request) = request else { return };
389+
if play_mode.as_ref().map_or(false, |pm| pm.is_in_play_mode()) {
390+
commands.remove_resource::<renzora::core::CameraViewRequest>();
391+
return;
392+
}
393+
match *request {
394+
renzora::core::CameraViewRequest::ZoomIn => {
395+
let delta = orbit.distance * 0.2;
396+
orbit.zoom(delta);
397+
}
398+
renzora::core::CameraViewRequest::ZoomOut => {
399+
let delta = -orbit.distance * 0.2;
400+
orbit.zoom(delta);
401+
}
402+
renzora::core::CameraViewRequest::ResetZoom => {
403+
orbit.distance = OrbitCameraState::default().distance;
404+
}
405+
renzora::core::CameraViewRequest::FrameAll => {
406+
let mut count = 0u32;
407+
let mut centroid = Vec3::ZERO;
408+
for gt in &meshes {
409+
centroid += gt.translation();
410+
count += 1;
411+
}
412+
if count > 0 {
413+
centroid /= count as f32;
414+
let mut max_dist = 1.0f32;
415+
for gt in &meshes {
416+
let d = gt.translation().distance(centroid);
417+
if d > max_dist { max_dist = d; }
418+
}
419+
orbit.focus = centroid;
420+
orbit.distance = (max_dist * 2.5).max(3.0);
421+
pivot_lock.0 = true;
422+
}
423+
}
424+
}
425+
commands.remove_resource::<renzora::core::CameraViewRequest>();
426+
}
427+
377428
/// Move the camera's orbit pivot to the point under the mouse cursor (ground
378429
/// plane intersection). Keeps the camera's world position unchanged — only
379430
/// the pivot/distance/yaw/pitch are recomputed.

crates/renzora_editor/src/lib.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,8 @@ impl Plugin for RenzoraEditorPlugin {
381381
.init_resource::<ShortcutRegistry>()
382382
.init_resource::<EditorActionHooks>()
383383
.init_resource::<MaterialThumbnailRegistry>()
384-
.init_resource::<ModelThumbnailRegistry>();
384+
.init_resource::<ModelThumbnailRegistry>()
385+
.init_resource::<renzora::core::IsolationMode>();
385386

386387
register_builtin_tools(
387388
&mut app.world_mut().resource_mut::<ToolbarRegistry>(),
@@ -435,6 +436,10 @@ impl Plugin for RenzoraEditorPlugin {
435436
sync_active_tool_to_gizmo_mode.run_if(in_state(SplashState::Editor)),
436437
)
437438
.add_systems(Update, apply_vsync_setting)
439+
.add_systems(
440+
Update,
441+
apply_isolation_mode.run_if(in_state(SplashState::Editor)),
442+
)
438443
;
439444
}
440445
}
@@ -459,6 +464,73 @@ fn apply_vsync_setting(
459464
}
460465
}
461466

467+
/// View menu Isolation Mode: when active, hide all mesh entities that aren't
468+
/// the current selection (or its ancestors/descendants). On deactivate,
469+
/// restore the entities we hid back to `Visibility::Inherited`.
470+
fn apply_isolation_mode(
471+
iso: Res<renzora::core::IsolationMode>,
472+
selection: Res<EditorSelection>,
473+
children_q: Query<&Children>,
474+
parent_q: Query<&ChildOf>,
475+
mut visibility_q: Query<
476+
(Entity, &mut Visibility),
477+
(
478+
With<Mesh3d>,
479+
Without<renzora::core::EditorCamera>,
480+
Without<renzora::core::HideInHierarchy>,
481+
),
482+
>,
483+
mut hidden_entities: Local<Vec<Entity>>,
484+
mut last_active: Local<bool>,
485+
) {
486+
if iso.active == *last_active {
487+
return;
488+
}
489+
*last_active = iso.active;
490+
491+
if iso.active {
492+
let mut keep: std::collections::HashSet<Entity> = std::collections::HashSet::new();
493+
for sel in selection.get_all() {
494+
keep.insert(sel);
495+
let mut cur = sel;
496+
while let Ok(child_of) = parent_q.get(cur) {
497+
let parent = child_of.parent();
498+
keep.insert(parent);
499+
cur = parent;
500+
}
501+
collect_descendants(sel, &children_q, &mut keep);
502+
}
503+
hidden_entities.clear();
504+
for (entity, mut vis) in &mut visibility_q {
505+
if !keep.contains(&entity) && *vis != Visibility::Hidden {
506+
*vis = Visibility::Hidden;
507+
hidden_entities.push(entity);
508+
}
509+
}
510+
} else {
511+
let drained: Vec<Entity> = hidden_entities.drain(..).collect();
512+
for entity in drained {
513+
if let Ok((_, mut vis)) = visibility_q.get_mut(entity) {
514+
*vis = Visibility::Inherited;
515+
}
516+
}
517+
}
518+
}
519+
520+
fn collect_descendants(
521+
entity: Entity,
522+
children_q: &Query<&Children>,
523+
out: &mut std::collections::HashSet<Entity>,
524+
) {
525+
if let Ok(children) = children_q.get(entity) {
526+
for child in children.iter() {
527+
if out.insert(child) {
528+
collect_descendants(child, children_q, out);
529+
}
530+
}
531+
}
532+
}
533+
462534
/// Observer: show toast notifications when scripts are hot-reloaded.
463535
fn show_script_reload_toasts(
464536
trigger: On<renzora::ScriptsReloaded>,
@@ -841,6 +913,10 @@ pub fn editor_ui_system(world: &mut World) {
841913
)
842914
})
843915
.unwrap_or((false, false));
916+
let isolation_active = world
917+
.get_resource::<renzora::core::IsolationMode>()
918+
.map(|m| m.active)
919+
.unwrap_or(false);
844920
let title_action = renzora_ui::title_bar::render_title_bar(
845921
&ctx,
846922
&theme,
@@ -852,6 +928,7 @@ pub fn editor_ui_system(world: &mut World) {
852928
&mut window_queue,
853929
can_undo,
854930
can_redo,
931+
isolation_active,
855932
);
856933

857934
// 6. Document tabs (below title bar)
@@ -1166,6 +1243,25 @@ pub fn editor_ui_system(world: &mut World) {
11661243
}
11671244
}
11681245
TitleBarAction::ResetLayout => reset_layout(world),
1246+
TitleBarAction::ZoomIn => {
1247+
world.insert_resource(renzora::core::CameraViewRequest::ZoomIn);
1248+
}
1249+
TitleBarAction::ZoomOut => {
1250+
world.insert_resource(renzora::core::CameraViewRequest::ZoomOut);
1251+
}
1252+
TitleBarAction::ResetZoom => {
1253+
world.insert_resource(renzora::core::CameraViewRequest::ResetZoom);
1254+
}
1255+
TitleBarAction::FrameAll => {
1256+
world.insert_resource(renzora::core::CameraViewRequest::FrameAll);
1257+
}
1258+
TitleBarAction::ToggleIsolation => {
1259+
let mut iso = world
1260+
.remove_resource::<renzora::core::IsolationMode>()
1261+
.unwrap_or_default();
1262+
iso.active = !iso.active;
1263+
world.insert_resource(iso);
1264+
}
11691265
TitleBarAction::None => {}
11701266
}
11711267

0 commit comments

Comments
 (0)