Skip to content

Commit

Permalink
Show download sizes of in the example page (#4841)
Browse files Browse the repository at this point in the history
### What
* Closes #4724


![image](https://github.com/rerun-io/rerun/assets/1148717/623f1d47-5f5d-4597-b117-00763cdf151c)

### 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):
* Using newly built examples:
[app.rerun.io](https://app.rerun.io/pr/4841/index.html)
* Using examples from latest `main` build:
[app.rerun.io](https://app.rerun.io/pr/4841/index.html?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[app.rerun.io](https://app.rerun.io/pr/4841/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [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/4841)
- [Docs
preview](https://rerun.io/preview/7bf9d8177bdcf7abf61948aa6e431254cccc613e/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/7bf9d8177bdcf7abf61948aa6e431254cccc613e/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 Jan 17, 2024
1 parent fb50414 commit 8e4e232
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 45 deletions.
30 changes: 22 additions & 8 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ criterion = "0.5"
crossbeam = "0.8"
directories-next = "2"
document-features = "0.2"
ehttp = "0.3.1"
ehttp = "0.4.0"
enumset = "1.0.12"
env_logger = { version = "0.10", default-features = false }
ewebsock = "0.4.0"
Expand Down
3 changes: 2 additions & 1 deletion crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ impl App {
}

pub fn set_examples_manifest_url(&mut self, url: String) {
self.state.set_examples_manifest_url(url);
self.state
.set_examples_manifest_url(&self.re_ui.egui_ctx, url);
}

pub fn build_info(&self) -> &re_build_info::BuildInfo {
Expand Down
4 changes: 2 additions & 2 deletions crates/re_viewer/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ pub struct AppState {
}

impl AppState {
pub fn set_examples_manifest_url(&mut self, url: String) {
self.welcome_screen.set_examples_manifest_url(url);
pub fn set_examples_manifest_url(&mut self, egui_ctx: &egui::Context, url: String) {
self.welcome_screen.set_examples_manifest_url(egui_ctx, url);
}

pub fn app_options(&self) -> &AppOptions {
Expand Down
125 changes: 94 additions & 31 deletions crates/re_viewer/src/ui/welcome_screen/example_page.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use egui::{NumExt as _, Ui};

use ehttp::{fetch, Request};
use poll_promise::Promise;

use re_log_types::LogMsg;
use re_smart_channel::ReceiveSet;
use re_viewer_context::SystemCommandSender;
Expand Down Expand Up @@ -46,13 +46,24 @@ const THUMBNAIL_RADIUS: f32 = 4.0;
/// For layout purposes, each example spans multiple cells in the grid. This structure is used to
/// track the rectangle that spans the block of cells used for the corresponding example, so hover/
/// click can be detected.
#[derive(Debug)]
struct ExampleDescLayout {
desc: ExampleDesc,
rect: egui::Rect,

/// We do an async HEAD request to get the size of the RRD file
/// so we can show it to the user.
rrd_byte_size_promise: Promise<Option<u64>>,
}

impl ExampleDescLayout {
fn new(egui_ctx: &egui::Context, desc: ExampleDesc) -> Self {
ExampleDescLayout {
rrd_byte_size_promise: load_file_size(egui_ctx, desc.rrd_url.clone()),
desc,
rect: egui::Rect::NOTHING,
}
}

/// Saves the top left corner of the hover/click area for this example.
fn set_top_left(&mut self, pos: egui::Pos2) {
self.rect.min = pos;
Expand All @@ -74,15 +85,6 @@ impl ExampleDescLayout {
}
}

impl From<ExampleDesc> for ExampleDescLayout {
fn from(desc: ExampleDesc) -> Self {
ExampleDescLayout {
desc,
rect: egui::Rect::NOTHING,
}
}
}

type ManifestJson = Vec<ExampleDesc>;
type Manifest = Vec<ExampleDescLayout>;
type ManifestPromise = Promise<Result<Manifest, LoadError>>;
Expand All @@ -103,16 +105,67 @@ impl std::fmt::Display for LoadError {
}
}

fn load_manifest(url: String) -> ManifestPromise {
fn load_manifest(egui_ctx: &egui::Context, url: String) -> ManifestPromise {
let (sender, promise) = Promise::new();
let egui_ctx = egui_ctx.clone(); // So we can wake up the ui thread

fetch(Request::get(url), move |response| match response {
Ok(response) => sender.send(
serde_json::from_slice::<ManifestJson>(&response.bytes)
.map(|examples| examples.into_iter().map(ExampleDescLayout::from).collect())
.map_err(LoadError::Deserialize),
),
Err(err) => sender.send(Err(LoadError::Fetch(err))),
fetch(Request::get(url), move |response| {
match response {
Ok(response) => sender.send(
serde_json::from_slice::<ManifestJson>(&response.bytes)
.map(|examples| {
examples
.into_iter()
.map(|example| ExampleDescLayout::new(&egui_ctx, example))
.collect()
})
.map_err(LoadError::Deserialize),
),
Err(err) => sender.send(Err(LoadError::Fetch(err))),
}
egui_ctx.request_repaint();
});

promise
}

/// Do a HEAD request to get the size of a file.
///
/// In case of an error, it is logged as DEBUG and
/// the promise is resolved to `None`.
fn load_file_size(egui_ctx: &egui::Context, url: String) -> Promise<Option<u64>> {
let (sender, promise) = Promise::new();
let egui_ctx = egui_ctx.clone(); // So we can wake up the ui thread

let request = Request {
method: "HEAD".into(),
..Request::get(url.clone())
};

fetch(request, move |response| {
match response {
Ok(response) => {
if response.ok {
let headers = &response.headers;
let content_length = headers
.get("content-length")
.or_else(|| headers.get("x-goog-stored-content-length"))
.and_then(|s| s.parse::<u64>().ok());
sender.send(content_length);
} else {
re_log::debug!(
"Failed to load file size of {url:?}: {} {}",
response.status,
response.status_text
);
}
}
Err(err) => {
re_log::debug!("Failed to load file size of {url:?}: {err}");
sender.send(None);
}
}
egui_ctx.request_repaint();
});

promise
Expand Down Expand Up @@ -166,10 +219,10 @@ impl Default for ExamplePage {
}

impl ExamplePage {
pub fn set_manifest_url(&mut self, url: String) {
pub fn set_manifest_url(&mut self, egui_ctx: &egui::Context, url: String) {
if self.manifest_url != url {
self.manifest_url = url.clone();
self.examples = Some(load_manifest(url));
self.examples = Some(load_manifest(egui_ctx, url));
}
}

Expand All @@ -182,7 +235,7 @@ impl ExamplePage {
) -> WelcomeScreenResponse {
let examples = self
.examples
.get_or_insert_with(|| load_manifest(self.manifest_url.clone()));
.get_or_insert_with(|| load_manifest(ui.ctx(), self.manifest_url.clone()));

let Some(examples) = examples.ready_mut() else {
ui.spinner();
Expand Down Expand Up @@ -278,7 +331,7 @@ impl ExamplePage {
ui.vertical(|ui| {
example_description(
ui,
&example.desc,
example,
example.hovered(ui, self.id),
);

Expand Down Expand Up @@ -374,17 +427,27 @@ fn example_thumbnail(
}
}

fn example_description(ui: &mut Ui, example: &ExampleDesc, hovered: bool) {
ui.label(
egui::RichText::new(example.title.clone())
.strong()
.line_height(Some(22.0))
.text_style(re_ui::ReUi::welcome_screen_body()),
);
fn example_description(ui: &mut Ui, example: &ExampleDescLayout, hovered: bool) {
let desc = &example.desc;

let title = egui::RichText::new(desc.title.clone())
.strong()
.line_height(Some(22.0))
.text_style(re_ui::ReUi::welcome_screen_body());

ui.horizontal(|ui| {
ui.label(title);

if let Some(Some(size)) = example.rrd_byte_size_promise.ready().cloned() {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
ui.label(re_format::format_bytes(size as f64));
});
}
});

ui.add_space(4.0);

let mut desc_text = egui::RichText::new(example.description.clone()).line_height(Some(19.0));
let mut desc_text = egui::RichText::new(desc.description.clone()).line_height(Some(19.0));
if hovered {
desc_text = desc_text.strong();
}
Expand Down
4 changes: 2 additions & 2 deletions crates/re_viewer/src/ui/welcome_screen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ impl Default for WelcomeScreen {
}

impl WelcomeScreen {
pub fn set_examples_manifest_url(&mut self, url: String) {
self.example_page.set_manifest_url(url);
pub fn set_examples_manifest_url(&mut self, egui_ctx: &egui::Context, url: String) {
self.example_page.set_manifest_url(egui_ctx, url);
}

/// Welcome screen shown in place of the viewport when no data is loaded.
Expand Down
1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ skip = [
{ name = "cargo_metadata" }, # Older version used by ply-rs. It's small, and it's build-time only!
{ name = "cfg_aliases" }, # Tiny macro-only crate. Winit and other use older version than us.
{ name = "core-graphics" }, # old version via arboard
{ name = "ehttp" }, # Will go away with next egui update
{ name = "foreign-types-shared" }, # used for cocoa bindings. wgpu uses newer than eframe.
{ name = "foreign-types" }, # used for cocoa bindings. wgpu uses newer than eframe.
{ name = "hashbrown" }, # Old version used by polar-rs
Expand Down

0 comments on commit 8e4e232

Please sign in to comment.