Skip to content

Commit

Permalink
Show connection status in top bar (#4443)
Browse files Browse the repository at this point in the history
### What
* Closes #3046

This replaces the loading screen with a connection status that's always
shown in the top panel.

<img width="325" alt="image"
src="https://github.com/rerun-io/rerun/assets/1148717/42a4ab7b-53d9-4f98-b8b8-6fea37d2540d">


### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
  * Full build: [app.rerun.io](https://app.rerun.io/pr/4443/index.html)
* Partial build:
[app.rerun.io](https://app.rerun.io/pr/4443/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
- Useful for quick testing when changes do not affect examples in any
way
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG

- [PR Build Summary](https://build.rerun.io/pr/4443)
- [Docs
preview](https://rerun.io/preview/426dea12d09df75a35829394c210a346d6de8e6c/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/426dea12d09df75a35829394c210a346d6de8e6c/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
  • Loading branch information
emilk authored Dec 6, 2023
1 parent 2d5eaa2 commit 556c0b2
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 181 deletions.
22 changes: 12 additions & 10 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ impl App {

crate::ui::mobile_warning_ui(&self.re_ui, ui);

crate::ui::top_panel(app_blueprint, store_context, ui, self, gpu_resource_stats);
crate::ui::top_panel(self, app_blueprint, store_context, gpu_resource_stats, ui);

self.memory_panel_ui(ui, gpu_resource_stats, store_stats);

Expand Down Expand Up @@ -731,10 +731,11 @@ impl App {
render_ctx.before_submit();
}
} else {
// 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);
// There's nothing to show.
// We get here when
// A) there is nothing loaded
// B) we decided not to show the welcome screen, presumably because data is expected at any time now.
// The user can see the connection status in the top bar.
}
});
}
Expand Down Expand Up @@ -941,12 +942,13 @@ 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
/// This function implements a heuristic which determines when the welcome screen
/// should show up.
///
/// Why not always show it when no data is loaded?
/// Because sometimes we expect data to arrive at any moment,
/// and showing the wlecome screen for a few frames will just be an annoying flash
/// in the users face.
fn should_show_welcome_screen(&mut self, store_hub: &StoreHub) -> bool {
// Don't show the welcome screen if we have actual data to display.
if store_hub.current_recording().is_some() || store_hub.selected_application_id().is_some()
Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ pub use recordings_panel::recordings_panel_ui;

pub(crate) use {
self::mobile_warning_ui::mobile_warning_ui, self::top_panel::top_panel,
self::welcome_screen::loading_ui, self::welcome_screen::WelcomeScreen,
self::welcome_screen::WelcomeScreen,
};
2 changes: 1 addition & 1 deletion crates/re_viewer/src/ui/recordings_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn loading_receivers_ui(
| SmartChannelSource::Sdk
| SmartChannelSource::WsClient { .. }
| SmartChannelSource::TcpServer { .. } => {
// TODO(#3046): show these in status bar
// These show up in the top panel - see `top_panel.rs`.
continue;
}
};
Expand Down
201 changes: 147 additions & 54 deletions crates/re_viewer/src/ui/top_panel.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use egui::NumExt as _;
use itertools::Itertools;
use re_format::format_number;
use re_renderer::WgpuResourcePoolStatistics;
use re_smart_channel::{ReceiveSet, SmartChannelSource};
use re_ui::UICommand;
use re_viewer_context::StoreContext;

use crate::{app_blueprint::AppBlueprint, App};

pub fn top_panel(
app: &mut App,
app_blueprint: &AppBlueprint<'_>,
store_context: Option<&StoreContext<'_>>,
ui: &mut egui::Ui,
app: &mut App,
gpu_resource_stats: &WgpuResourcePoolStatistics,
ui: &mut egui::Ui,
) {
re_tracing::profile_function!();

Expand All @@ -26,10 +28,11 @@ pub fn top_panel(
ui.set_height(top_bar_style.height);
ui.add_space(top_bar_style.indent);

top_bar_ui(app_blueprint, store_context, ui, app, gpu_resource_stats);
top_bar_ui(app, app_blueprint, store_context, ui, gpu_resource_stats);
})
.response;

// React to dragging and double-clicking the top bar:
#[cfg(not(target_arch = "wasm32"))]
if !re_ui::NATIVE_WINDOW_BAR {
let title_bar_response = _response.interact(egui::Sense::click());
Expand All @@ -45,10 +48,10 @@ pub fn top_panel(
}

fn top_bar_ui(
app: &mut App,
app_blueprint: &AppBlueprint<'_>,
store_context: Option<&StoreContext<'_>>,
ui: &mut egui::Ui,
app: &mut App,
gpu_resource_stats: &WgpuResourcePoolStatistics,
) {
app.rerun_menu_button_ui(store_context, ui);
Expand Down Expand Up @@ -76,58 +79,11 @@ fn top_bar_ui(
ui.add_space(extra_margin);
}

let mut selection_panel_expanded = app_blueprint.selection_panel_expanded;
if app
.re_ui()
.medium_icon_toggle_button(
ui,
&re_ui::icons::RIGHT_PANEL_TOGGLE,
&mut selection_panel_expanded,
)
.on_hover_text(format!(
"Toggle Selection View{}",
UICommand::ToggleSelectionPanel.format_shortcut_tooltip_suffix(ui.ctx())
))
.clicked()
{
app_blueprint.toggle_selection_panel(&app.command_sender);
}

let mut time_panel_expanded = app_blueprint.time_panel_expanded;
if app
.re_ui()
.medium_icon_toggle_button(
ui,
&re_ui::icons::BOTTOM_PANEL_TOGGLE,
&mut time_panel_expanded,
)
.on_hover_text(format!(
"Toggle Timeline View{}",
UICommand::ToggleTimePanel.format_shortcut_tooltip_suffix(ui.ctx())
))
.clicked()
{
app_blueprint.toggle_time_panel(&app.command_sender);
}
panel_buttons_r2l(app, app_blueprint, ui);

let mut blueprint_panel_expanded = app_blueprint.blueprint_panel_expanded;
if app
.re_ui()
.medium_icon_toggle_button(
ui,
&re_ui::icons::LEFT_PANEL_TOGGLE,
&mut blueprint_panel_expanded,
)
.on_hover_text(format!(
"Toggle Blueprint View{}",
UICommand::ToggleBlueprintPanel.format_shortcut_tooltip_suffix(ui.ctx())
))
.clicked()
{
app_blueprint.toggle_blueprint_panel(&app.command_sender);
}
connection_status_ui(ui, app.msg_receive_set());

if cfg!(debug_assertions) && app.app_options().show_metrics {
if cfg!(debug_assertions) {
ui.vertical_centered(|ui| {
ui.style_mut().wrap = Some(false);
ui.add_space(6.0); // TODO(emilk): in egui, add a proper way of centering a single widget in a UI.
Expand All @@ -137,6 +93,143 @@ fn top_bar_ui(
});
}

fn connection_status_ui(ui: &mut egui::Ui, rx: &ReceiveSet<re_log_types::LogMsg>) {
let sources = rx
.sources()
.into_iter()
.filter(|source| {
match source.as_ref() {
SmartChannelSource::File(_) | SmartChannelSource::RrdHttpStream { .. } => {
false // These show up in the recordings panel as a "Loading…" in `recordings_panel.rs`
}

re_smart_channel::SmartChannelSource::RrdWebEventListener
| re_smart_channel::SmartChannelSource::Sdk
| re_smart_channel::SmartChannelSource::WsClient { .. }
| re_smart_channel::SmartChannelSource::TcpServer { .. } => true,
}
})
.collect_vec();

match sources.len() {
0 => return,
1 => {
source_label(ui, sources[0].as_ref());
}
n => {
// In practice we never get here
ui.label(format!("{n} sources connected"))
.on_hover_ui(|ui| {
ui.vertical(|ui| {
for source in &sources {
source_label(ui, source.as_ref());
}
});
});
}
}

fn source_label(ui: &mut egui::Ui, source: &SmartChannelSource) -> egui::Response {
let response = ui.label(status_string(source));

let tooltip = match source {
SmartChannelSource::File(_)
| SmartChannelSource::RrdHttpStream { .. }
| SmartChannelSource::RrdWebEventListener
| SmartChannelSource::Sdk
| SmartChannelSource::WsClient { .. } => None,

SmartChannelSource::TcpServer { .. } => {
Some("Waiting for an SDK to connect".to_owned())
}
};

if let Some(tooltip) = tooltip {
response.on_hover_text(tooltip)
} else {
response
}
}

fn status_string(source: &SmartChannelSource) -> String {
match source {
re_smart_channel::SmartChannelSource::File(path) => {
format!("Loading {}…", path.display())
}
re_smart_channel::SmartChannelSource::RrdHttpStream { url } => {
format!("Loading {url}…")
}
re_smart_channel::SmartChannelSource::RrdWebEventListener => {
"Waiting for logging data…".to_owned()
}
re_smart_channel::SmartChannelSource::Sdk => {
"Waiting for logging data from SDK".to_owned()
}
re_smart_channel::SmartChannelSource::WsClient { ws_server_url } => {
// TODO(emilk): it would be even better to know whether or not we are connected, or are attempting to connect
format!("Waiting for data from {ws_server_url}")
}
re_smart_channel::SmartChannelSource::TcpServer { port } => {
format!("Listening on TCP port {port}")
}
}
}
}

/// Lay out the panel button right-to-left
fn panel_buttons_r2l(app: &App, app_blueprint: &AppBlueprint<'_>, ui: &mut egui::Ui) {
let mut selection_panel_expanded = app_blueprint.selection_panel_expanded;
if app
.re_ui()
.medium_icon_toggle_button(
ui,
&re_ui::icons::RIGHT_PANEL_TOGGLE,
&mut selection_panel_expanded,
)
.on_hover_text(format!(
"Toggle Selection View{}",
UICommand::ToggleSelectionPanel.format_shortcut_tooltip_suffix(ui.ctx())
))
.clicked()
{
app_blueprint.toggle_selection_panel(&app.command_sender);
}

let mut time_panel_expanded = app_blueprint.time_panel_expanded;
if app
.re_ui()
.medium_icon_toggle_button(
ui,
&re_ui::icons::BOTTOM_PANEL_TOGGLE,
&mut time_panel_expanded,
)
.on_hover_text(format!(
"Toggle Timeline View{}",
UICommand::ToggleTimePanel.format_shortcut_tooltip_suffix(ui.ctx())
))
.clicked()
{
app_blueprint.toggle_time_panel(&app.command_sender);
}

let mut blueprint_panel_expanded = app_blueprint.blueprint_panel_expanded;
if app
.re_ui()
.medium_icon_toggle_button(
ui,
&re_ui::icons::LEFT_PANEL_TOGGLE,
&mut blueprint_panel_expanded,
)
.on_hover_text(format!(
"Toggle Blueprint View{}",
UICommand::ToggleBlueprintPanel.format_shortcut_tooltip_suffix(ui.ctx())
))
.clicked()
{
app_blueprint.toggle_blueprint_panel(&app.command_sender);
}
}

/// Shows clickable website link as an image (text doesn't look as nice)
fn website_link_ui(ui: &mut egui::Ui) {
let desired_height = ui.max_rect().height();
Expand Down
Loading

0 comments on commit 556c0b2

Please sign in to comment.