From d489042bb6539597df46b69352dff3eb756e0186 Mon Sep 17 00:00:00 2001 From: Clement Rey Date: Wed, 23 Oct 2024 17:23:25 +0200 Subject: [PATCH] implement Import support --- crates/viewer/re_ui/src/command.rs | 9 ++- crates/viewer/re_viewer/src/app.rs | 81 ++++++++++++++++++-- crates/viewer/re_viewer/src/ui/rerun_menu.rs | 1 + 3 files changed, 84 insertions(+), 7 deletions(-) diff --git a/crates/viewer/re_ui/src/command.rs b/crates/viewer/re_ui/src/command.rs index f997ef5243074..611ce3c1c691b 100644 --- a/crates/viewer/re_ui/src/command.rs +++ b/crates/viewer/re_ui/src/command.rs @@ -14,6 +14,7 @@ pub trait UICommandSender { pub enum UICommand { // Listed in the order they show up in the command palette by default! Open, + Import, SaveRecording, SaveRecordingSelection, SaveBlueprint, @@ -111,7 +112,8 @@ impl UICommand { Self::SaveBlueprint => ("Save blueprint…", "Save the current viewer setup as a Rerun blueprint file (.rbl)"), - Self::Open => ("Open…", "Open any supported files (.rrd, images, meshes, …)"), + Self::Open => ("Open…", "Open any supported files (.rrd, images, meshes, …) in a new recording"), + Self::Import => ("Import…", "Import any supported files (.rrd, images, meshes, …) in the current recording"), Self::CloseCurrentRecording => ( "Close current recording", @@ -271,6 +273,10 @@ impl UICommand { KeyboardShortcut::new(Modifiers::COMMAND, key) } + fn cmd_shift(key: Key) -> KeyboardShortcut { + KeyboardShortcut::new(Modifiers::COMMAND.plus(Modifiers::SHIFT), key) + } + fn cmd_alt(key: Key) -> KeyboardShortcut { KeyboardShortcut::new(Modifiers::COMMAND.plus(Modifiers::ALT), key) } @@ -284,6 +290,7 @@ impl UICommand { Self::SaveRecordingSelection => Some(cmd_alt(Key::S)), Self::SaveBlueprint => None, Self::Open => Some(cmd(Key::O)), + Self::Import => Some(cmd_shift(Key::O)), Self::CloseCurrentRecording => None, Self::CloseAllRecordings => None, diff --git a/crates/viewer/re_viewer/src/app.rs b/crates/viewer/re_viewer/src/app.rs index 571f26c1dcc86..8d04ebe075dc5 100644 --- a/crates/viewer/re_viewer/src/app.rs +++ b/crates/viewer/re_viewer/src/app.rs @@ -146,6 +146,13 @@ const MIN_ZOOM_FACTOR: f32 = 0.2; #[cfg(not(target_arch = "wasm32"))] const MAX_ZOOM_FACTOR: f32 = 5.0; +#[cfg(target_arch = "wasm32")] +struct PendingFilePromise { + recommended_application_id: Option, + recommended_recording_id: Option, + promise: poll_promise::Promise>, +} + /// The Rerun Viewer as an [`eframe`] application. pub struct App { build_info: re_build_info::BuildInfo, @@ -169,7 +176,7 @@ pub struct App { rx: ReceiveSet, #[cfg(target_arch = "wasm32")] - open_files_promise: Option>>, + open_files_promise: Option, /// What is serialized pub(crate) state: AppState, @@ -564,6 +571,18 @@ impl App { store_context: Option<&StoreContext<'_>>, cmd: UICommand, ) { + let active_application_id = store_context + .and_then(|ctx| { + ctx.hub + .active_app() + // Don't redirect data to the welcome screen. + .filter(|&app_id| app_id != &StoreHub::welcome_screen_app_id()) + }) + .cloned(); + let active_recording_id = store_context + .and_then(|ctx| ctx.hub.active_recording_id()) + .cloned(); + match cmd { UICommand::SaveRecording => { if let Err(err) = save_recording(self, store_context, None) { @@ -602,12 +621,57 @@ impl App { #[cfg(target_arch = "wasm32")] UICommand::Open => { let egui_ctx = egui_ctx.clone(); - self.open_files_promise = Some(poll_promise::Promise::spawn_local(async move { + + // Open: we want to try and load into a new dedicated recording. + let recommended_application_id = None; + let recommended_recording_id = None; + let promise = poll_promise::Promise::spawn_local(async move { let file = async_open_rrd_dialog().await; egui_ctx.request_repaint(); // Wake ui thread file - })); + }); + + self.open_files_promise = Some(PendingFilePromise { + recommended_application_id, + recommended_recording_id, + promise, + }); } + + #[cfg(not(target_arch = "wasm32"))] + UICommand::Import => { + for file_path in open_file_dialog_native() { + self.command_sender + .send_system(SystemCommand::LoadDataSource(DataSource::FilePath( + FileSource::FileDialog { + recommended_application_id: active_application_id.clone(), + recommended_recording_id: active_recording_id.clone(), + }, + file_path, + ))); + } + } + #[cfg(target_arch = "wasm32")] + UICommand::Import => { + let egui_ctx = egui_ctx.clone(); + + // Import: we want to try and load into the current recording. + let recommended_application_id = active_application_id; + let recommended_recording_id = active_recording_id; + + let promise = poll_promise::Promise::spawn_local(async move { + let file = async_open_rrd_dialog().await; + egui_ctx.request_repaint(); // Wake ui thread + file + }); + + self.open_files_promise = Some(PendingFilePromise { + recommended_application_id, + recommended_recording_id, + promise, + }); + } + UICommand::CloseCurrentRecording => { let cur_rec = store_context.map(|ctx| ctx.recording.store_id()); if let Some(cur_rec) = cur_rec { @@ -1586,14 +1650,19 @@ impl eframe::App for App { } #[cfg(target_arch = "wasm32")] - if let Some(promise) = &self.open_files_promise { + if let Some(PendingFilePromise { + recommended_application_id, + recommended_recording_id, + promise, + }) = &self.open_files_promise + { if let Some(files) = promise.ready() { for file in files { self.command_sender .send_system(SystemCommand::LoadDataSource(DataSource::FileContents( FileSource::FileDialog { - recommended_application_id: None, - recommended_recording_id: None, + recommended_application_id: recommended_application_id.clone(), + recommended_recording_id: recommended_recording_id.clone(), }, file.clone(), ))); diff --git a/crates/viewer/re_viewer/src/ui/rerun_menu.rs b/crates/viewer/re_viewer/src/ui/rerun_menu.rs index 5d75274b4a496..9038872d3770e 100644 --- a/crates/viewer/re_viewer/src/ui/rerun_menu.rs +++ b/crates/viewer/re_viewer/src/ui/rerun_menu.rs @@ -37,6 +37,7 @@ impl App { ui.add_space(SPACING); UICommand::Open.menu_button_ui(ui, &self.command_sender); + UICommand::Import.menu_button_ui(ui, &self.command_sender); self.save_buttons_ui(ui, _store_context);