Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement "Open file" dialog on Web #3068

Merged
merged 4 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions crates/re_ui/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ pub trait UICommandSender {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, strum_macros::EnumIter)]
pub enum UICommand {
// Listed in the order they show up in the command palette by default!
#[cfg(not(target_arch = "wasm32"))]
Open,
#[cfg(not(target_arch = "wasm32"))]
Save,
Expand Down Expand Up @@ -84,7 +83,6 @@ impl UICommand {
"Save data for the current loop selection to a Rerun data file (.rrd)",
),

#[cfg(not(target_arch = "wasm32"))]
UICommand::Open => ("Open…", "Open a Rerun Data File (.rrd)"),

UICommand::CloseCurrentRecording => (
Expand Down Expand Up @@ -190,7 +188,6 @@ impl UICommand {
UICommand::Save => Some(cmd(Key::S)),
#[cfg(not(target_arch = "wasm32"))]
UICommand::SaveSelection => Some(cmd_alt(Key::S)),
#[cfg(not(target_arch = "wasm32"))]
UICommand::Open => Some(cmd(Key::O)),
UICommand::CloseCurrentRecording => None,

Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ egui-wgpu.workspace = true
image = { workspace = true, default-features = false, features = ["png"] }
itertools = { workspace = true }
once_cell = { workspace = true }
poll-promise = "0.2"
poll-promise = { version = "0.2", features = ["web"] }
rfd.workspace = true
ron = "0.8.0"
serde = { version = "1", features = ["derive"] }
Expand Down
89 changes: 76 additions & 13 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use re_smart_channel::{Receiver, SmartChannelSource};
use re_ui::{toasts, UICommand, UICommandSender};
use re_viewer_context::{
command_channel, AppOptions, CommandReceiver, CommandSender, ComponentUiRegistry,
DynSpaceViewClass, PlayState, SpaceViewClassRegistry, SpaceViewClassRegistryError,
StoreContext, SystemCommand, SystemCommandSender,
DynSpaceViewClass, FileContents, PlayState, SpaceViewClassRegistry,
SpaceViewClassRegistryError, StoreContext, SystemCommand, SystemCommandSender,
};

use crate::{
Expand Down Expand Up @@ -93,6 +93,9 @@ pub struct App {

rx: Receiver<LogMsg>,

#[cfg(target_arch = "wasm32")]
open_file_promise: Option<poll_promise::Promise<Option<FileContents>>>,

/// What is serialized
pub(crate) state: AppState,

Expand Down Expand Up @@ -201,6 +204,8 @@ impl App {
text_log_rx,
component_ui_registry: re_data_ui::create_component_ui_registry(),
rx,
#[cfg(target_arch = "wasm32")]
open_file_promise: Default::default(),
state,
background_tasks: Default::default(),
store_hub: Some(StoreHub::new()),
Expand Down Expand Up @@ -274,12 +279,13 @@ impl App {

fn run_pending_ui_commands(
&mut self,
frame: &mut eframe::Frame,
egui_ctx: &egui::Context,
app_blueprint: &AppBlueprint<'_>,
store_context: Option<&StoreContext<'_>>,
frame: &mut eframe::Frame,
) {
while let Some(cmd) = self.command_receiver.recv_ui() {
self.run_ui_command(cmd, app_blueprint, store_context, frame);
self.run_ui_command(frame, egui_ctx, app_blueprint, store_context, cmd);
}
}

Expand All @@ -297,8 +303,9 @@ impl App {
SystemCommand::CloseRecordingId(recording_id) => {
store_hub.remove_recording_id(&recording_id);
}

#[cfg(not(target_arch = "wasm32"))]
SystemCommand::LoadRrd(path) => {
SystemCommand::LoadRrdPath(path) => {
let with_notification = true;
if let Some(rrd) = crate::loading::load_file_path(&path, with_notification) {
let store_id = rrd.store_dbs().next().map(|db| db.store_id().clone());
Expand All @@ -308,6 +315,18 @@ impl App {
}
}
}

SystemCommand::LoadRrdContents(FileContents { file_name, bytes }) => {
let bytes: &[u8] = &bytes;
if let Some(rrd) = crate::loading::load_file_contents(&file_name, bytes) {
let store_id = rrd.store_dbs().next().map(|db| db.store_id().clone());
store_hub.add_bundle(rrd);
if let Some(store_id) = store_id {
store_hub.set_recording_id(store_id);
}
}
}

SystemCommand::ResetViewer => self.reset(store_hub, egui_ctx),
SystemCommand::UpdateBlueprint(blueprint_id, updates) => {
let blueprint_db = store_hub.store_db_mut(&blueprint_id);
Expand All @@ -325,10 +344,11 @@ impl App {

fn run_ui_command(
&mut self,
cmd: UICommand,
_frame: &mut eframe::Frame,
_egui_ctx: &egui::Context,
app_blueprint: &AppBlueprint<'_>,
store_context: Option<&StoreContext<'_>>,
_frame: &mut eframe::Frame,
cmd: UICommand,
) {
match cmd {
#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -347,9 +367,18 @@ impl App {
UICommand::Open => {
if let Some(rrd_file) = open_rrd_dialog() {
self.command_sender
.send_system(SystemCommand::LoadRrd(rrd_file));
.send_system(SystemCommand::LoadRrdPath(rrd_file));
}
}
#[cfg(target_arch = "wasm32")]
UICommand::Open => {
let egui_ctx = _egui_ctx.clone();
self.open_file_promise = Some(poll_promise::Promise::spawn_async(async move {
let file = async_open_rrd_dialog().await;
egui_ctx.request_repaint(); // Wake ui thread
file
}));
}
UICommand::CloseCurrentRecording => {
let cur_rec = store_context
.and_then(|ctx| ctx.recording)
Expand Down Expand Up @@ -919,6 +948,17 @@ impl eframe::App for App {
self.ram_limit_warner.update();
}

#[cfg(target_arch = "wasm32")]
if let Some(promise) = &self.open_file_promise {
if let Some(contents_opt) = promise.ready() {
if let Some(contents) = contents_opt {
self.command_sender
.send_system(SystemCommand::LoadRrdContents(contents.clone()));
}
self.open_file_promise = None;
}
}

#[cfg(not(target_arch = "wasm32"))]
if self.screenshotter.is_screenshotting() {
// Make screenshots high-quality by pretending we have a high-dpi display, whether we do or not:
Expand Down Expand Up @@ -1002,7 +1042,7 @@ impl eframe::App for App {
self.command_sender.send_ui(cmd);
}

self.run_pending_ui_commands(&app_blueprint, store_context.as_ref(), frame);
self.run_pending_ui_commands(frame, egui_ctx, &app_blueprint, store_context.as_ref());

self.run_pending_system_commands(&mut store_hub, egui_ctx);

Expand Down Expand Up @@ -1138,14 +1178,37 @@ fn file_saver_progress_ui(egui_ctx: &egui::Context, background_tasks: &mut Backg
}

#[cfg(not(target_arch = "wasm32"))]
use std::path::PathBuf;
#[cfg(not(target_arch = "wasm32"))]
fn open_rrd_dialog() -> Option<PathBuf> {
fn open_rrd_dialog() -> Option<std::path::PathBuf> {
rfd::FileDialog::new()
.add_filter("rerun data file", &["rrd"])
.add_filter("Rerun data file", &["rrd"])
.pick_file()
}

#[cfg(target_arch = "wasm32")]
async fn async_open_rrd_dialog() -> Option<FileContents> {
let res = rfd::AsyncFileDialog::new()
.add_filter("Rerun data file", &["rrd"])
.pick_file()
.await;

match res {
Some(file) => Some({
let file_name = file.file_name();
re_log::debug!("Reading {file_name}…");
let bytes = file.read().await;
re_log::debug!(
"{file_name} was {}",
re_format::format_bytes(bytes.len() as _)
);
FileContents {
file_name,
bytes: bytes.into(),
}
}),
None => None,
}
}

#[cfg(not(target_arch = "wasm32"))]
fn save(
app: &mut App,
Expand Down
6 changes: 2 additions & 4 deletions crates/re_viewer/src/ui/recordings_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ pub fn recordings_panel_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) {
ui,
"Recordings",
Some("These are the Recordings currently loaded in the Viewer"),
|_ui| {
#[cfg(not(target_arch = "wasm32"))]
add_button_ui(ctx, _ui);
|ui| {
add_button_ui(ctx, ui);
},
);
});
Expand Down Expand Up @@ -149,7 +148,6 @@ fn recording_ui(
.show(ui)
}

#[cfg(not(target_arch = "wasm32"))]
fn add_button_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) {
use re_ui::UICommandSender;

Expand Down
4 changes: 2 additions & 2 deletions crates/re_viewer/src/ui/rerun_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ impl App {

ui.add_space(spacing);

UICommand::Open.menu_button_ui(ui, &self.command_sender);

#[cfg(not(target_arch = "wasm32"))]
{
UICommand::Open.menu_button_ui(ui, &self.command_sender);

self.save_buttons_ui(ui, _store_context);

UICommand::CloseCurrentRecording.menu_button_ui(ui, &self.command_sender);
Expand Down
7 changes: 3 additions & 4 deletions crates/re_viewer/src/ui/wait_screen_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ fn welcome_ui_impl(
fn onboarding_content_ui(
re_ui: &ReUi,
ui: &mut Ui,
_command_sender: &re_viewer_context::CommandSender,
command_sender: &re_viewer_context::CommandSender,
) {
let column_spacing = 15.0;
let stability_adjustment = 1.0; // minimize jitter with sizing and scroll bars
Expand Down Expand Up @@ -198,12 +198,11 @@ fn onboarding_content_ui(
url_large_text_button(re_ui, ui, "Rust", RUST_QUICKSTART);
});

#[cfg(not(target_arch = "wasm32"))]
{
use re_ui::UICommandSender;
use re_ui::UICommandSender as _;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that still needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the trait import is needed for send_ui. I added the as _ because of https://github.com/rerun-io/rerun/blob/main/CODE_STYLE.md#style

ui.horizontal(|ui| {
if large_text_button(ui, "Open file…").clicked() {
_command_sender.send_ui(re_ui::UICommand::Open);
command_sender.send_ui(re_ui::UICommand::Open);
}
button_centered_label(ui, "Or drop a file anywhere!");
});
Expand Down
12 changes: 11 additions & 1 deletion crates/re_viewer_context/src/command_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@ use re_ui::{UICommand, UICommandSender};

// ----------------------------------------------------------------------------

/// The contents of as file
#[derive(Clone)]
pub struct FileContents {
pub file_name: String,
pub bytes: std::sync::Arc<[u8]>,
}

/// Commands used by internal system components
// TODO(jleibs): Is there a better crate for this?
pub enum SystemCommand {
/// Load an RRD by Filename
#[cfg(not(target_arch = "wasm32"))]
LoadRrd(std::path::PathBuf),
LoadRrdPath(std::path::PathBuf),

/// Load an RRD by content
LoadRrdContents(FileContents),

/// Reset the `Viewer` to the default state
ResetViewer,
Expand Down
3 changes: 2 additions & 1 deletion crates/re_viewer_context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ pub use annotations::{AnnotationMap, Annotations, ResolvedAnnotationInfo, MISSIN
pub use app_options::AppOptions;
pub use caches::{Cache, Caches};
pub use command_sender::{
command_channel, CommandReceiver, CommandSender, SystemCommand, SystemCommandSender,
command_channel, CommandReceiver, CommandSender, FileContents, SystemCommand,
SystemCommandSender,
};
pub use component_ui_registry::{ComponentUiRegistry, UiVerbosity};
pub use item::{resolve_mono_instance_path, resolve_mono_instance_path_item, Item, ItemCollection};
Expand Down