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

Introduce a welcome screen when no recording is loaded #2982

Merged
merged 24 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
Binary file added crates/re_ui/data/icons/external_link.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion crates/re_ui/examples/re_ui_example.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use re_ui::{toasts, CommandPalette, UICommand, UICommandSender};
use re_ui::{toasts, CommandPalette, ReUi, UICommand, UICommandSender};

/// Sender that queues up the execution of a command.
pub struct CommandSender(std::sync::mpsc::Sender<UICommand>);
Expand Down Expand Up @@ -493,6 +493,11 @@ impl egui_tiles::Behavior<Tab> for MyTileTreeBehavior {
ui.label("Hover me for a tooltip")
.on_hover_text("This is a tooltip");

ui.label(
egui::RichText::new("Welcome to the ReUi example")
.text_style(ReUi::welcome_screen_h1()),
);

Default::default()
}

Expand Down
17 changes: 17 additions & 0 deletions crates/re_ui/src/design_tokens.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(clippy::unwrap_used)] // fixed json file

use crate::ReUi;
use egui::Color32;

/// The look and feel of the UI.
Expand Down Expand Up @@ -79,6 +80,22 @@ fn apply_design_tokens(ctx: &egui::Context) -> DesignTokens {
// egui_style.spacing.interact_size.y = font_size;
}

// fonts used in the welcome screen
// TODO(ab): font sizes should come from design tokens
egui_style
.text_styles
.insert(ReUi::welcome_screen_h1(), egui::FontId::proportional(42.0));
egui_style
.text_styles
.insert(ReUi::welcome_screen_h2(), egui::FontId::proportional(24.0)); //TODO(ab): thin variant
egui_style
.text_styles
.insert(ReUi::welcome_screen_h3(), egui::FontId::proportional(18.0));
egui_style.text_styles.insert(
ReUi::welcome_screen_body(),
egui::FontId::proportional(14.0),
);

let panel_bg_color = get_aliased_color(&json, "{Alias.Color.Surface.Default.value}");
// let floating_color = get_aliased_color(&json, "{Alias.Color.Surface.Floating.value}");
let floating_color = Color32::from_gray(38); // TODO(emilk): change the content of the design_tokens.json origin instead
Expand Down
24 changes: 24 additions & 0 deletions crates/re_ui/src/icons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ pub const RESET: Icon = Icon::new("reset", include_bytes!("../data/icons/reset.p

pub const CLOSE: Icon = Icon::new("close", include_bytes!("../data/icons/close.png"));

/// Used for HTTP URLs that leads out of the app.
///
/// Remember to also use `.on_hover_cursor(egui::CursorIcon::PointingHand)`
/// and `.on_hover_text(url)`.
pub const EXTERNAL_LINK: Icon = Icon::new(
abey79 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

We should use this icon on the "Help" button in the rerun menu too, and maybe elsewhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

Noted, will do a follow-up PR

"external_link",
include_bytes!("../data/icons/external_link.png"),
);

pub const SPACE_VIEW_TEXT: Icon = Icon::new(
"spaceview_text",
include_bytes!("../data/icons/spaceview_text.png"),
Expand Down Expand Up @@ -97,3 +106,18 @@ pub const SPACE_VIEW_UNKNOWN: Icon = Icon::new(
);

pub const CONTAINER: Icon = Icon::new("container", include_bytes!("../data/icons/container.png"));

pub const WELCOME_SCREEN_CONFIGURE: Icon = Icon::new(
"welcome_screen_configure",
include_bytes!("../data/images/welcome_screen_configure.png"),
);

pub const WELCOME_SCREEN_LIVE_DATA: Icon = Icon::new(
"welcome_screen_live_data",
include_bytes!("../data/images/welcome_screen_live_data.png"),
);

pub const WELCOME_SCREEN_RECORDED_DATA: Icon = Icon::new(
"welcome_screen_recorded_data",
include_bytes!("../data/images/welcome_screen_recorded_data.png"),
);
21 changes: 21 additions & 0 deletions crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,27 @@ impl ReUi {
}
}

/// Welcome screen big title
#[inline]
pub fn welcome_screen_h1() -> egui::TextStyle {
egui::TextStyle::Name("welcome-screen-h1".into())
}

#[inline]
pub fn welcome_screen_h2() -> egui::TextStyle {
egui::TextStyle::Name("welcome-screen-h2".into())
}

#[inline]
pub fn welcome_screen_h3() -> egui::TextStyle {
egui::TextStyle::Name("welcome-screen-h3".into())
}

#[inline]
pub fn welcome_screen_body() -> egui::TextStyle {
egui::TextStyle::Name("welcome-screen-body".into())
}

/// Margin on all sides of views.
pub fn view_padding() -> f32 {
12.0
Expand Down
132 changes: 96 additions & 36 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use web_time::Instant;
use re_data_store::store_db::StoreDb;
use re_log_types::{LogMsg, StoreKind};
use re_renderer::WgpuResourcePoolStatistics;
use re_smart_channel::Receiver;
use re_smart_channel::{Receiver, SmartChannelSource};
use re_ui::{toasts, UICommand, UICommandSender};
use re_viewer_context::{
command_channel, AppOptions, CommandReceiver, CommandSender, ComponentUiRegistry,
Expand Down Expand Up @@ -47,6 +47,8 @@ pub struct StartupOptions {
/// Set the screen resolution in logical points.
#[cfg(not(target_arch = "wasm32"))]
pub resolution_in_points: Option<[f32; 2]>,

pub skip_welcome_screen: bool,
}

impl Default for StartupOptions {
Expand All @@ -60,6 +62,8 @@ impl Default for StartupOptions {

#[cfg(not(target_arch = "wasm32"))]
resolution_in_points: None,

skip_welcome_screen: false,
}
}
}
Expand Down Expand Up @@ -199,7 +203,7 @@ impl App {
rx,
state,
background_tasks: Default::default(),
store_hub: Some(StoreHub::default()),
store_hub: Some(StoreHub::new()),
toasts: toasts::Toasts::new(),
memory_panel: Default::default(),
memory_panel_open: false,
Expand Down Expand Up @@ -564,43 +568,56 @@ impl App {
self.style_panel_ui(egui_ctx, ui);

if let Some(store_view) = store_context {
// TODO(jleibs): We don't necessarily want to show the wait
// screen just because we're missing a recording. If we've
// loaded a blueprint, we can still show the empty layouts or
// static data, but we need to jump through some hoops to
// handle a missing `RecordingConfig` in this case.
if let Some(store_db) = store_view.recording {
// TODO(andreas): store the re_renderer somewhere else.
let egui_renderer = {
let render_state = frame.wgpu_render_state().unwrap();
&mut render_state.renderer.write()
};
if let Some(render_ctx) = egui_renderer
.callback_resources
.get_mut::<re_renderer::RenderContext>()
{
render_ctx.begin_frame();

self.state.show(
app_blueprint,
ui,
render_ctx,
store_db,
store_view,
&self.re_ui,
&self.component_ui_registry,
&self.space_view_class_registry,
&self.rx,
&self.command_sender,
);

render_ctx.before_submit();
}
static EMPTY_STORE_DB: once_cell::sync::Lazy<StoreDb> =
once_cell::sync::Lazy::new(|| {
StoreDb::new(re_log_types::StoreId::from_string(
StoreKind::Recording,
"<EMPTY>".to_owned(),
))
});

// We want the regular UI as soon as a blueprint is available (or, rather, an
// app ID is set). If no recording is available, we use a default, empty one.
// Note that EMPTY_STORE_DB is *not* part of the list of available recordings
// (StoreContext::alternate_recordings), which means that it's not displayed in
// the recordings UI.
let store_db = if let Some(store_db) = store_view.recording {
store_db
} else {
crate::ui::wait_screen_ui(ui, &self.rx);
&EMPTY_STORE_DB
};

// TODO(andreas): store the re_renderer somewhere else.
let egui_renderer = {
let render_state = frame.wgpu_render_state().unwrap();
&mut render_state.renderer.write()
};
if let Some(render_ctx) = egui_renderer
.callback_resources
.get_mut::<re_renderer::RenderContext>()
{
render_ctx.begin_frame();

self.state.show(
app_blueprint,
ui,
render_ctx,
store_db,
store_view,
&self.re_ui,
&self.component_ui_registry,
&self.space_view_class_registry,
&self.rx,
&self.command_sender,
);

render_ctx.before_submit();
}
} else {
crate::ui::wait_screen_ui(ui, &self.rx);
// This is part of the loading vs. welcome screen UI logic. The loading screen
// is displayed when no app ID is set. This is e.g. the initial state for the
// web demos.
crate::ui::loading_ui(ui, &self.rx);
}
});
}
Expand Down Expand Up @@ -824,6 +841,45 @@ impl App {
}
}
}

/// This function will create an empty blueprint whenever the welcome screen should be
/// displayed.
///
/// The welcome screen can be displayed only when a blueprint is available (and no recording is
/// loaded). This function implements the heuristic which determines when the welcome screen
/// should show up.
fn handle_default_blueprint(&mut self, store_hub: &mut StoreHub) {
if store_hub.current_recording().is_some()
|| store_hub.selected_application_id().is_some()
|| self.startup_options.skip_welcome_screen
{
return;
}

// Here, we use the type of Receiver as a proxy for which kind of workflow the viewer is
// being used in.
let welcome = match self.rx.source() {
// These source are typically "finite". We want the loading screen so long as data is
// coming in.
SmartChannelSource::Files { .. } | SmartChannelSource::RrdHttpStream { .. } => {
!self.rx.is_connected()
abey79 marked this conversation as resolved.
Show resolved Hide resolved
}
// The workflows associated with these sources typically do not require showing the
// welcome screen until after some recording have been loaded and then closed.
SmartChannelSource::RrdWebEventListener
| SmartChannelSource::Sdk
| SmartChannelSource::WsClient { .. } => false,
// This might be the trickiest case. When running the bare executable, we want to show
// the welcome screen (default, "new user" workflow). There are other case using Tcp
// where it's not the case, including Python/C++ SDKs and possibly other, advanced used,
// scenarios. In this cases, `--skip-welcome-screen` should be used.
SmartChannelSource::TcpServer { .. } => true,
};

if welcome {
store_hub.set_app_id(StoreHub::welcome_screen_app_id());
}
}
}

impl eframe::App for App {
Expand Down Expand Up @@ -925,6 +981,10 @@ impl eframe::App for App {

file_saver_progress_ui(egui_ctx, &mut self.background_tasks); // toasts for background file saver

// Heuristic to set the app_id to the welcome screen blueprint.
// Must be called before `read_context` below.
self.handle_default_blueprint(&mut store_hub);

let store_context = store_hub.read_context();

let app_blueprint = AppBlueprint::new(store_context.as_ref(), egui_ctx);
Expand Down
9 changes: 8 additions & 1 deletion crates/re_viewer/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,17 @@ impl AppState {
..Default::default()
};

let show_welcome =
store_context.blueprint.app_id() == Some(&StoreHub::welcome_screen_app_id());

egui::CentralPanel::default()
.frame(viewport_frame)
.show_inside(ui, |ui| {
viewport.viewport_ui(ui, &mut ctx);
if show_welcome {
crate::ui::welcome_ui(re_ui, ui, rx, command_sender);
} else {
viewport.viewport_ui(ui, &mut ctx);
}
});

// If the viewport was user-edited, then disable auto space views
Expand Down
Loading