diff --git a/Cargo.toml b/Cargo.toml index 3b615848ab9e..3282422590c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,11 +31,11 @@ egui_extras = { git = "https://github.com/emilk/egui", rev = "940b896cbba39eb047 egui_glow = { git = "https://github.com/emilk/egui", rev = "940b896cbba39eb04757b975f81d9b80e7e198ed" } egui-wgpu = { git = "https://github.com/emilk/egui", rev = "940b896cbba39eb04757b975f81d9b80e7e198ed" } -# eframe = { path = "../egui/crates/eframe" } -# egui = { path = "../egui/crates/egui" } -# egui_extras = { path = "../egui/crates/egui_extras" } -# egui_glow = { path = "../egui/crates/egui_glow" } -# egui-wgpu = { path = "../egui/crates/egui-wgpu" } +# eframe = { path = "../../egui/crates/eframe" } +# egui = { path = "../../egui/crates/egui" } +# egui_extras = { path = "../../egui/crates/egui_extras" } +# egui_glow = { path = "../../egui/crates/egui_glow" } +# egui-wgpu = { path = "../../egui/crates/egui-wgpu" } # Because gltf hasn't published a new version: https://github.com/gltf-rs/gltf/issues/357 gltf = { git = "https://github.com/rerun-io/gltf", rev = "3c14ded73755d1ce9e47010edb06db63cb7e2cca" } diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs index 8793b1474423..916e6c08f67d 100644 --- a/crates/re_viewer/src/app.rs +++ b/crates/re_viewer/src/app.rs @@ -189,20 +189,8 @@ impl App { pub fn promise_exists(&mut self, name: impl AsRef) -> bool { self.pending_promises.contains_key(name.as_ref()) } -} - -impl eframe::App for App { - fn save(&mut self, storage: &mut dyn eframe::Storage) { - eframe::set_value(storage, eframe::APP_KEY, &self.state); - } - - fn update(&mut self, egui_ctx: &egui::Context, frame: &mut eframe::Frame) { - #[cfg(not(target_arch = "wasm32"))] - if self.ctrl_c.load(std::sync::atomic::Ordering::SeqCst) { - frame.close(); - return; - } + fn check_keyboard_shortcuts(&mut self, egui_ctx: &egui::Context, frame: &mut eframe::Frame) { if egui_ctx .input_mut() .consume_shortcut(&kb_shortcuts::RESET_VIEWER) @@ -218,6 +206,29 @@ impl eframe::App for App { self.state.profiler.start(); } + if !frame.is_web() { + egui::gui_zoom::zoom_with_keyboard_shortcuts( + egui_ctx, + frame.info().native_pixels_per_point, + ); + } + } +} + +impl eframe::App for App { + fn save(&mut self, storage: &mut dyn eframe::Storage) { + eframe::set_value(storage, eframe::APP_KEY, &self.state); + } + + fn update(&mut self, egui_ctx: &egui::Context, frame: &mut eframe::Frame) { + #[cfg(not(target_arch = "wasm32"))] + if self.ctrl_c.load(std::sync::atomic::Ordering::Relaxed) { + frame.close(); + return; + } + + self.check_keyboard_shortcuts(egui_ctx, frame); + self.state.cache.new_frame(); if let Some(rx) = &mut self.rx { @@ -527,24 +538,47 @@ fn top_panel(egui_ctx: &egui::Context, frame: &mut eframe::Frame, app: &mut App) } }; + let gui_zoom = if let Some(native_pixels_per_point) = frame.info().native_pixels_per_point { + native_pixels_per_point / egui_ctx.pixels_per_point() + } else { + 1.0 + }; + + // On Mac, we share the same space as the native red/yellow/green close/minimize/maximize buttons. + // This means we need to make room for them. + let native_buttons_size_in_native_scale = egui::vec2(64.0, 24.0); // source: I measured /emilk + + let bar_height = if crate::FULLSIZE_CONTENT { + // Use more vertical space when zoomed in… + let bar_height = native_buttons_size_in_native_scale.y; + + // …but never shrink below the native button height when zoomed out. + bar_height.max(gui_zoom * native_buttons_size_in_native_scale.y) + } else { + egui_ctx.style().spacing.interact_size.y + }; + egui::TopBottomPanel::top("top_bar") .frame(panel_frame) + .exact_height(bar_height) .show(egui_ctx, |ui| { egui::menu::bar(ui, |ui| { - #[cfg(target_os = "macos")] - if crate::native::FULLSIZE_CONTENT { - // We use up the same row as the native red/yellow/green close/minimize/maximize buttons. - // This means we need to make room for them: - ui.add_space(64.0); - - // …and match their height: - ui.set_min_size(egui::vec2(ui.available_width(), 24.0)); + ui.set_height(bar_height); + + if crate::FULLSIZE_CONTENT { + // Always use the same width measured in native GUI coordinates: + ui.add_space(gui_zoom * native_buttons_size_in_native_scale.x); } + #[cfg(not(target_arch = "wasm32"))] ui.menu_button("File", |ui| { file_menu(ui, app, frame); }); + ui.menu_button("View", |ui| { + view_menu(ui, app, frame); + }); + ui.menu_button("Recordings", |ui| { recordings_menu(ui, app); }); @@ -634,7 +668,8 @@ fn file_saver_progress_ui(egui_ctx: &egui::Context, app: &mut App) { } } -fn file_menu(ui: &mut egui::Ui, app: &mut App, _frame: &mut eframe::Frame) { +#[cfg(not(target_arch = "wasm32"))] +fn file_menu(ui: &mut egui::Ui, app: &mut App, frame: &mut eframe::Frame) { // TODO(emilk): support saving data on web #[cfg(not(target_arch = "wasm32"))] { @@ -646,7 +681,7 @@ fn file_menu(ui: &mut egui::Ui, app: &mut App, _frame: &mut eframe::Frame) { ui.spinner(); }); ui.horizontal(|ui| { - let _ = ui.button("Save time selection…"); + let _ = ui.button("Save Time Selection…"); ui.spinner(); }); }); @@ -715,7 +750,7 @@ fn file_menu(ui: &mut egui::Ui, app: &mut App, _frame: &mut eframe::Frame) { #[cfg(not(target_arch = "wasm32"))] if ui .button("Load") - .on_hover_text("Load a Rerun data file (.rrd)") + .on_hover_text("Load a Rerun Data File (.rrd)") .clicked() { if let Some(path) = rfd::FileDialog::new() @@ -728,37 +763,43 @@ fn file_menu(ui: &mut egui::Ui, app: &mut App, _frame: &mut eframe::Frame) { } } - ui.menu_button("Advanced", |ui| { - ui.set_min_width(180.0); + #[cfg(not(target_arch = "wasm32"))] + if ui.button("Quit").clicked() { + frame.close(); + } +} - if ui - .add( - egui::Button::new("Reset viewer") - .shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::RESET_VIEWER)), - ) - .on_hover_text("Reset the viewer to how it looked the first time you ran it") - .clicked() - { - app.reset(ui.ctx()); - ui.close_menu(); - } +fn view_menu(ui: &mut egui::Ui, app: &mut App, frame: &mut eframe::Frame) { + ui.set_min_width(180.0); - #[cfg(all(feature = "puffin", not(target_arch = "wasm32")))] - if ui - .add( - egui::Button::new("Profile viewer") - .shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::SHOW_PROFILER)), - ) - .on_hover_text("Starts a profiler, showing what makes the viewer run slow") - .clicked() - { - app.state.profiler.start(); - } - }); + // On the web the browser controls the zoom + if !frame.is_web() { + egui::gui_zoom::zoom_menu_buttons(ui, frame.info().native_pixels_per_point); + ui.separator(); + } - #[cfg(not(target_arch = "wasm32"))] - if ui.button("Quit").clicked() { - _frame.close(); + if ui + .add( + egui::Button::new("Reset Viewer") + .shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::RESET_VIEWER)), + ) + .on_hover_text("Reset the viewer to how it looked the first time you ran it") + .clicked() + { + app.reset(ui.ctx()); + ui.close_menu(); + } + + #[cfg(all(feature = "puffin", not(target_arch = "wasm32")))] + if ui + .add( + egui::Button::new("Profile Viewer") + .shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::SHOW_PROFILER)), + ) + .on_hover_text("Starts a profiler, showing what makes the viewer run slow") + .clicked() + { + app.state.profiler.start(); } } @@ -771,6 +812,11 @@ fn recordings_menu(ui: &mut egui::Ui, app: &mut App) { .sorted_by_key(|log_db| log_db.recording_info().map(|ri| ri.started)) .collect_vec(); + if log_dbs.is_empty() { + ui.weak("(empty)"); + return; + } + ui.style_mut().wrap = Some(false); for log_db in log_dbs { let info = if let Some(rec_info) = log_db.recording_info() { diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs index fa04a97cf53e..44d841cb4428 100644 --- a/crates/re_viewer/src/lib.rs +++ b/crates/re_viewer/src/lib.rs @@ -42,6 +42,12 @@ pub use web::start; // --------------------------------------------------------------------------- +/// If true, we fill the entire window, except for the close/maximize/minimize buttons in the top-left. +/// See +pub const FULLSIZE_CONTENT: bool = cfg!(target_os = "macos"); + +// --------------------------------------------------------------------------- + /// Profiling macro for feature "puffin" #[doc(hidden)] #[macro_export] diff --git a/crates/re_viewer/src/native.rs b/crates/re_viewer/src/native.rs index 255fc43d893e..29a0d6297085 100644 --- a/crates/re_viewer/src/native.rs +++ b/crates/re_viewer/src/native.rs @@ -7,10 +7,6 @@ use crate::DesignTokens; #[cfg(not(any(feature = "glow", feature = "wgpu")))] compile_error!("You must enable either the 'glow' or 'wgpu' feature of re_viewer."); -/// If true, we fill the entire window, except for the close/maximize/minimize buttons in the top-left. -/// See -pub const FULLSIZE_CONTENT: bool = cfg!(target_os = "macos"); - type AppCreator = Box, DesignTokens) -> Box>; @@ -34,7 +30,7 @@ pub fn run_native_app(app_creator: AppCreator) { default_theme: eframe::Theme::Dark, #[cfg(target_os = "macos")] - fullsize_content: FULLSIZE_CONTENT, + fullsize_content: crate::FULLSIZE_CONTENT, #[cfg(feature = "wgpu")] wgpu_options: crate::wgpu_options(), diff --git a/crates/re_viewer/src/ui/view3d/scene.rs b/crates/re_viewer/src/ui/view3d/scene.rs index 5a39d7208f2d..672fcd1c85c3 100644 --- a/crates/re_viewer/src/ui/view3d/scene.rs +++ b/crates/re_viewer/src/ui/view3d/scene.rs @@ -401,10 +401,10 @@ impl Scene { let hover_size_boost = 1.5; const HOVER_COLOR: [u8; 4] = [255, 200, 200, 255]; - let viewport_area = viewport_size.x * viewport_size.y; + let viewport_area = (viewport_size.x * viewport_size.y).at_least(1.0); // Size of a ui point (in meters), when projected out one meter: - let point_size_at_one_meter = eye.fov_y / viewport_size.y; + let point_size_at_one_meter = eye.fov_y / viewport_size.y.at_least(1.0); let eye_camera_plane = macaw::Plane3::from_normal_point(eye.forward_in_world(), eye.pos_in_world());