Skip to content

Commit 556c0b2

Browse files
authored
Show connection status in top bar (#4443)
### 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)
1 parent 2d5eaa2 commit 556c0b2

File tree

6 files changed

+168
-181
lines changed

6 files changed

+168
-181
lines changed

crates/re_viewer/src/app.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ impl App {
678678

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

681-
crate::ui::top_panel(app_blueprint, store_context, ui, self, gpu_resource_stats);
681+
crate::ui::top_panel(self, app_blueprint, store_context, gpu_resource_stats, ui);
682682

683683
self.memory_panel_ui(ui, gpu_resource_stats, store_stats);
684684

@@ -731,10 +731,11 @@ impl App {
731731
render_ctx.before_submit();
732732
}
733733
} else {
734-
// This is part of the loading vs. welcome screen UI logic. The loading screen
735-
// is displayed when no app ID is set. This is e.g. the initial state for the
736-
// web demos.
737-
crate::ui::loading_ui(ui, &self.rx);
734+
// There's nothing to show.
735+
// We get here when
736+
// A) there is nothing loaded
737+
// B) we decided not to show the welcome screen, presumably because data is expected at any time now.
738+
// The user can see the connection status in the top bar.
738739
}
739740
});
740741
}
@@ -941,12 +942,13 @@ impl App {
941942
}
942943
}
943944

944-
/// This function will create an empty blueprint whenever the welcome screen should be
945-
/// displayed.
946-
///
947-
/// The welcome screen can be displayed only when a blueprint is available (and no recording is
948-
/// loaded). This function implements the heuristic which determines when the welcome screen
945+
/// This function implements a heuristic which determines when the welcome screen
949946
/// should show up.
947+
///
948+
/// Why not always show it when no data is loaded?
949+
/// Because sometimes we expect data to arrive at any moment,
950+
/// and showing the wlecome screen for a few frames will just be an annoying flash
951+
/// in the users face.
950952
fn should_show_welcome_screen(&mut self, store_hub: &StoreHub) -> bool {
951953
// Don't show the welcome screen if we have actual data to display.
952954
if store_hub.current_recording().is_some() || store_hub.selected_application_id().is_some()

crates/re_viewer/src/ui/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ pub use recordings_panel::recordings_panel_ui;
1717

1818
pub(crate) use {
1919
self::mobile_warning_ui::mobile_warning_ui, self::top_panel::top_panel,
20-
self::welcome_screen::loading_ui, self::welcome_screen::WelcomeScreen,
20+
self::welcome_screen::WelcomeScreen,
2121
};

crates/re_viewer/src/ui/recordings_panel.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ fn loading_receivers_ui(
6969
| SmartChannelSource::Sdk
7070
| SmartChannelSource::WsClient { .. }
7171
| SmartChannelSource::TcpServer { .. } => {
72-
// TODO(#3046): show these in status bar
72+
// These show up in the top panel - see `top_panel.rs`.
7373
continue;
7474
}
7575
};

crates/re_viewer/src/ui/top_panel.rs

+147-54
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
use egui::NumExt as _;
2+
use itertools::Itertools;
23
use re_format::format_number;
34
use re_renderer::WgpuResourcePoolStatistics;
5+
use re_smart_channel::{ReceiveSet, SmartChannelSource};
46
use re_ui::UICommand;
57
use re_viewer_context::StoreContext;
68

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

911
pub fn top_panel(
12+
app: &mut App,
1013
app_blueprint: &AppBlueprint<'_>,
1114
store_context: Option<&StoreContext<'_>>,
12-
ui: &mut egui::Ui,
13-
app: &mut App,
1415
gpu_resource_stats: &WgpuResourcePoolStatistics,
16+
ui: &mut egui::Ui,
1517
) {
1618
re_tracing::profile_function!();
1719

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

29-
top_bar_ui(app_blueprint, store_context, ui, app, gpu_resource_stats);
31+
top_bar_ui(app, app_blueprint, store_context, ui, gpu_resource_stats);
3032
})
3133
.response;
3234

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

4750
fn top_bar_ui(
51+
app: &mut App,
4852
app_blueprint: &AppBlueprint<'_>,
4953
store_context: Option<&StoreContext<'_>>,
5054
ui: &mut egui::Ui,
51-
app: &mut App,
5255
gpu_resource_stats: &WgpuResourcePoolStatistics,
5356
) {
5457
app.rerun_menu_button_ui(store_context, ui);
@@ -76,58 +79,11 @@ fn top_bar_ui(
7679
ui.add_space(extra_margin);
7780
}
7881

79-
let mut selection_panel_expanded = app_blueprint.selection_panel_expanded;
80-
if app
81-
.re_ui()
82-
.medium_icon_toggle_button(
83-
ui,
84-
&re_ui::icons::RIGHT_PANEL_TOGGLE,
85-
&mut selection_panel_expanded,
86-
)
87-
.on_hover_text(format!(
88-
"Toggle Selection View{}",
89-
UICommand::ToggleSelectionPanel.format_shortcut_tooltip_suffix(ui.ctx())
90-
))
91-
.clicked()
92-
{
93-
app_blueprint.toggle_selection_panel(&app.command_sender);
94-
}
95-
96-
let mut time_panel_expanded = app_blueprint.time_panel_expanded;
97-
if app
98-
.re_ui()
99-
.medium_icon_toggle_button(
100-
ui,
101-
&re_ui::icons::BOTTOM_PANEL_TOGGLE,
102-
&mut time_panel_expanded,
103-
)
104-
.on_hover_text(format!(
105-
"Toggle Timeline View{}",
106-
UICommand::ToggleTimePanel.format_shortcut_tooltip_suffix(ui.ctx())
107-
))
108-
.clicked()
109-
{
110-
app_blueprint.toggle_time_panel(&app.command_sender);
111-
}
82+
panel_buttons_r2l(app, app_blueprint, ui);
11283

113-
let mut blueprint_panel_expanded = app_blueprint.blueprint_panel_expanded;
114-
if app
115-
.re_ui()
116-
.medium_icon_toggle_button(
117-
ui,
118-
&re_ui::icons::LEFT_PANEL_TOGGLE,
119-
&mut blueprint_panel_expanded,
120-
)
121-
.on_hover_text(format!(
122-
"Toggle Blueprint View{}",
123-
UICommand::ToggleBlueprintPanel.format_shortcut_tooltip_suffix(ui.ctx())
124-
))
125-
.clicked()
126-
{
127-
app_blueprint.toggle_blueprint_panel(&app.command_sender);
128-
}
84+
connection_status_ui(ui, app.msg_receive_set());
12985

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

96+
fn connection_status_ui(ui: &mut egui::Ui, rx: &ReceiveSet<re_log_types::LogMsg>) {
97+
let sources = rx
98+
.sources()
99+
.into_iter()
100+
.filter(|source| {
101+
match source.as_ref() {
102+
SmartChannelSource::File(_) | SmartChannelSource::RrdHttpStream { .. } => {
103+
false // These show up in the recordings panel as a "Loading…" in `recordings_panel.rs`
104+
}
105+
106+
re_smart_channel::SmartChannelSource::RrdWebEventListener
107+
| re_smart_channel::SmartChannelSource::Sdk
108+
| re_smart_channel::SmartChannelSource::WsClient { .. }
109+
| re_smart_channel::SmartChannelSource::TcpServer { .. } => true,
110+
}
111+
})
112+
.collect_vec();
113+
114+
match sources.len() {
115+
0 => return,
116+
1 => {
117+
source_label(ui, sources[0].as_ref());
118+
}
119+
n => {
120+
// In practice we never get here
121+
ui.label(format!("{n} sources connected"))
122+
.on_hover_ui(|ui| {
123+
ui.vertical(|ui| {
124+
for source in &sources {
125+
source_label(ui, source.as_ref());
126+
}
127+
});
128+
});
129+
}
130+
}
131+
132+
fn source_label(ui: &mut egui::Ui, source: &SmartChannelSource) -> egui::Response {
133+
let response = ui.label(status_string(source));
134+
135+
let tooltip = match source {
136+
SmartChannelSource::File(_)
137+
| SmartChannelSource::RrdHttpStream { .. }
138+
| SmartChannelSource::RrdWebEventListener
139+
| SmartChannelSource::Sdk
140+
| SmartChannelSource::WsClient { .. } => None,
141+
142+
SmartChannelSource::TcpServer { .. } => {
143+
Some("Waiting for an SDK to connect".to_owned())
144+
}
145+
};
146+
147+
if let Some(tooltip) = tooltip {
148+
response.on_hover_text(tooltip)
149+
} else {
150+
response
151+
}
152+
}
153+
154+
fn status_string(source: &SmartChannelSource) -> String {
155+
match source {
156+
re_smart_channel::SmartChannelSource::File(path) => {
157+
format!("Loading {}…", path.display())
158+
}
159+
re_smart_channel::SmartChannelSource::RrdHttpStream { url } => {
160+
format!("Loading {url}…")
161+
}
162+
re_smart_channel::SmartChannelSource::RrdWebEventListener => {
163+
"Waiting for logging data…".to_owned()
164+
}
165+
re_smart_channel::SmartChannelSource::Sdk => {
166+
"Waiting for logging data from SDK".to_owned()
167+
}
168+
re_smart_channel::SmartChannelSource::WsClient { ws_server_url } => {
169+
// TODO(emilk): it would be even better to know whether or not we are connected, or are attempting to connect
170+
format!("Waiting for data from {ws_server_url}")
171+
}
172+
re_smart_channel::SmartChannelSource::TcpServer { port } => {
173+
format!("Listening on TCP port {port}")
174+
}
175+
}
176+
}
177+
}
178+
179+
/// Lay out the panel button right-to-left
180+
fn panel_buttons_r2l(app: &App, app_blueprint: &AppBlueprint<'_>, ui: &mut egui::Ui) {
181+
let mut selection_panel_expanded = app_blueprint.selection_panel_expanded;
182+
if app
183+
.re_ui()
184+
.medium_icon_toggle_button(
185+
ui,
186+
&re_ui::icons::RIGHT_PANEL_TOGGLE,
187+
&mut selection_panel_expanded,
188+
)
189+
.on_hover_text(format!(
190+
"Toggle Selection View{}",
191+
UICommand::ToggleSelectionPanel.format_shortcut_tooltip_suffix(ui.ctx())
192+
))
193+
.clicked()
194+
{
195+
app_blueprint.toggle_selection_panel(&app.command_sender);
196+
}
197+
198+
let mut time_panel_expanded = app_blueprint.time_panel_expanded;
199+
if app
200+
.re_ui()
201+
.medium_icon_toggle_button(
202+
ui,
203+
&re_ui::icons::BOTTOM_PANEL_TOGGLE,
204+
&mut time_panel_expanded,
205+
)
206+
.on_hover_text(format!(
207+
"Toggle Timeline View{}",
208+
UICommand::ToggleTimePanel.format_shortcut_tooltip_suffix(ui.ctx())
209+
))
210+
.clicked()
211+
{
212+
app_blueprint.toggle_time_panel(&app.command_sender);
213+
}
214+
215+
let mut blueprint_panel_expanded = app_blueprint.blueprint_panel_expanded;
216+
if app
217+
.re_ui()
218+
.medium_icon_toggle_button(
219+
ui,
220+
&re_ui::icons::LEFT_PANEL_TOGGLE,
221+
&mut blueprint_panel_expanded,
222+
)
223+
.on_hover_text(format!(
224+
"Toggle Blueprint View{}",
225+
UICommand::ToggleBlueprintPanel.format_shortcut_tooltip_suffix(ui.ctx())
226+
))
227+
.clicked()
228+
{
229+
app_blueprint.toggle_blueprint_panel(&app.command_sender);
230+
}
231+
}
232+
140233
/// Shows clickable website link as an image (text doesn't look as nice)
141234
fn website_link_ui(ui: &mut egui::Ui) {
142235
let desired_height = ui.max_rect().height();

0 commit comments

Comments
 (0)