Skip to content

Commit

Permalink
Implement loading spinner for visualisations. (#6512)
Browse files Browse the repository at this point in the history
Fixes #5088. Adds a ensoGL spinner for visualizations waiting on data.

https://user-images.githubusercontent.com/1428930/236801655-67a0ffed-da5d-4e27-8797-cd8126cb86d9.mp4

# Important Notes
This spinner will not show up for the duration where visualizations are processing data on the frontend. If this is a concern, visualization need to implement heir own loading spinner, or we need to provide a unified API for them to keep the spinner visible.
  • Loading branch information
MichaelMauderer authored May 12, 2023
1 parent 4b7afbf commit 9e71fea
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
belongs to the node.][6487].
- [List Editor Widget][6470]. Now you can edit lists by clicking buttons on
nodes or by dragging the elements.
- [Visualisations now show a loading spinner while waiting on data.][6512].
- [Fixed text visualisations which were being cut off at the last line.][6421]
- [Fixed a bug where, when scrolling or dragging on a full-screen visualization,
the view of the graph changed as well.][6530]
Expand Down Expand Up @@ -227,6 +228,7 @@
[6487]: https://github.com/enso-org/enso/pull/6487
[6341]: https://github.com/enso-org/enso/pull/6341
[6470]: https://github.com/enso-org/enso/pull/6470
[6512]: https://github.com/enso-org/enso/pull/6512

#### Enso Standard Library

Expand Down
12 changes: 12 additions & 0 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 app/gui/view/documentation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl Model {
}

/// Load an HTML file into the documentation view when user is waiting for data to be received.
/// TODO(#184315201): This should be replaced with a EnsoGL spinner.
/// TODO(#5214): This should be replaced with a EnsoGL spinner.
fn load_waiting_screen(&self) {
let spinner = include_str!("../assets/spinner.html");
self.inner_dom.dom().set_inner_html(spinner)
Expand Down
75 changes: 55 additions & 20 deletions app/gui/view/graph-editor/src/component/visualization/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::visualization;
use action_bar::ActionBar;
use enso_frp as frp;
use ensogl::application::Application;
use ensogl::data::color::Rgba;
use ensogl::display;
use ensogl::display::scene;
use ensogl::display::scene::Scene;
Expand Down Expand Up @@ -162,10 +163,11 @@ ensogl::define_endpoints! {
pub struct View {
display_object: display::object::Instance,

background: background::View,
overlay: overlay::View,
background_dom: DomSymbol,
scene: Scene,
background: background::View,
overlay: overlay::View,
background_dom: DomSymbol,
scene: Scene,
loading_spinner: ensogl_component::spinner::View,
}

impl View {
Expand All @@ -176,16 +178,38 @@ impl View {
let overlay = overlay::View::new();
display_object.add_child(&background);
display_object.add_child(&overlay);
let div = web::document.create_div_or_panic();
let background_dom = DomSymbol::new(&div);
display_object.add_child(&background_dom);
let loading_spinner = ensogl_component::spinner::View::new();

ensogl::shapes_order_dependencies! {
scene => {
background -> overlay;
background -> ensogl_component::spinner;
}
};

Self { display_object, background, overlay, background_dom, scene, loading_spinner }.init()
}

fn set_layer(&self, layer: visualization::Layer) {
layer.apply_for_html_component(&self.scene, &self.background_dom);
}

fn show_waiting_screen(&self) {
self.add_child(&self.loading_spinner);
}

fn disable_waiting_screen(&self) {
self.loading_spinner.unset_parent();
}

fn init_background(&self) {
let background = &self.background_dom;
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
// system (#795)
let styles = StyleWatch::new(&scene.style_sheet);
let styles = StyleWatch::new(&self.scene.style_sheet);
let bg_color =
styles.get_color(ensogl_hardcoded_theme::graph_editor::visualization::background);
let bg_hex = format!(
Expand All @@ -196,29 +220,29 @@ impl View {
bg_color.alpha
);

let div = web::document.create_div_or_panic();
let background_dom = DomSymbol::new(&div);
// TODO : We added a HTML background to the `View`, because "shape" background was
// overlapping the JS visualization. This should be further investigated
// while fixing rust visualization displaying. (#796)
background_dom.dom().set_style_or_warn("width", "0");
background_dom.dom().set_style_or_warn("height", "0");
background_dom.dom().set_style_or_warn("z-index", "1");
background_dom.dom().set_style_or_warn("overflow-y", "auto");
background_dom.dom().set_style_or_warn("overflow-x", "auto");
background_dom.dom().set_style_or_warn("background", bg_hex);
background_dom.dom().set_style_or_warn("border-radius", "14px");
shadow::add_to_dom_element(&background_dom, &styles);
display_object.add_child(&background_dom);

Self { display_object, background, overlay, background_dom, scene }.init()
background.dom().set_style_or_warn("width", "0");
background.dom().set_style_or_warn("height", "0");
background.dom().set_style_or_warn("z-index", "1");
background.dom().set_style_or_warn("overflow-y", "auto");
background.dom().set_style_or_warn("overflow-x", "auto");
background.dom().set_style_or_warn("background", bg_hex);
background.dom().set_style_or_warn("border-radius", "14px");
shadow::add_to_dom_element(background, &styles);
}

fn set_layer(&self, layer: visualization::Layer) {
layer.apply_for_html_component(&self.scene, &self.background_dom);
fn init_spinner(&self) {
let spinner = &self.loading_spinner;
spinner.scale.set(5.0);
spinner.rgba.set(Rgba::black().into())
}

fn init(self) -> Self {
self.init_background();
self.init_spinner();
self.show_waiting_screen();
self.set_layer(visualization::Layer::Default);
self.scene.layers.viz.add(&self);
self
Expand Down Expand Up @@ -300,6 +324,8 @@ impl ContainerModel {
// FIXME: These 2 lines fix a bug with display objects visible on stage.
self.set_visibility(true);
self.set_visibility(false);

self.view.show_waiting_screen();
self
}

Expand Down Expand Up @@ -409,6 +435,7 @@ impl ContainerModel {
self.view.background.radius.set(CORNER_RADIUS);
self.view.overlay.set_size(size);
self.view.background.set_size(size + 2.0 * Vector2(PADDING, PADDING));
self.view.loading_spinner.set_size(size + 2.0 * Vector2(PADDING, PADDING));
dom.set_style_or_warn("width", format!("{}px", size[0]));
dom.set_style_or_warn("height", format!("{}px", size[1]));
bg_dom.set_style_or_warn("width", "0");
Expand Down Expand Up @@ -584,9 +611,17 @@ impl Container {
}
vis_definition.clone()
}));


// === Visualisation Loading Spinner ===

eval_ frp.source.visualisation ( model.view.show_waiting_screen() );
eval_ frp.set_data ( model.view.disable_waiting_screen() );

}



// === Selecting Visualization ===

frp::extend! { network
Expand Down
1 change: 1 addition & 0 deletions lib/rust/ensogl/component/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ ensogl-shadow = { path = "shadow" }
ensogl-text = { path = "text" }
ensogl-tooltip = { path = "tooltip" }
ensogl-toggle-button = { path = "toggle-button" }
ensogl-spinner = { path = "spinner" }
15 changes: 15 additions & 0 deletions lib/rust/ensogl/component/spinner/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "ensogl-spinner"
version = "0.1.0"
authors = ["Enso Team <[email protected]>"]
edition = "2021"

[lib]
crate-type = ["rlib", "cdylib"]

[dependencies]
enso-frp = { path = "../../../frp" }
enso-prelude = { path = "../../../prelude" }
enso-shapely = { path = "../../../shapely" }
enso-types = { path = "../../../types" }
ensogl-core = { path = "../../core" }
58 changes: 58 additions & 0 deletions lib/rust/ensogl/component/spinner/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! An animated spinner component that can be used to indicate that a process
//! is running.
use ensogl_core::display::shape::*;
use ensogl_core::prelude::*;

use ensogl_core::display::IntoGlsl;



// ===============
// === Spinner ===
// ===============

const ANIMATION_SPEED: f32 = 0.001;
const SHAPE_RADIUS: f32 = 1.0;
const SHAPE_OFFSET: f32 = 2.0;
const ANIMATION_OFFSET_MS: f32 = 100.0;

/// Convert a time value to an alpha value for the spinner animation. The
/// animation is a sine wave that oscillates between 0 and 1.
fn time_to_alpha<F1: Into<Var<f32>>, F2: Into<Var<f32>>, F3: Into<Var<f32>>>(
time: F1,
offset: F2,
scale: F3,
) -> Var<f32> {
let time = time.into();
let offset = offset.into();
let scale = scale.into();
Var::from(0.5) + ((time + offset) * scale).sin() / 2.0
}

ensogl_core::shape! {
alignment = center;
(style: Style, scale: f32, rgba: Vector4<f32>) {
let time = &Var::<f32>::from("input_time");
let radius = (&scale * SHAPE_RADIUS).px();
let offset = (&scale * (SHAPE_RADIUS * 2.0 + SHAPE_OFFSET)).px();
let dot1 = Circle(&radius).translate_x(-&offset);
let dot2 = Circle(&radius);
let dot3 = Circle(&radius).translate_x(offset);
let dot3_anim_start = 0.0;
let dot2_anim_start = dot3_anim_start + ANIMATION_OFFSET_MS;
let dot1_anim_start = dot2_anim_start + ANIMATION_OFFSET_MS;
let dot1_alpha = rgba.w() * time_to_alpha(time, dot1_anim_start, ANIMATION_SPEED);
let dot2_alpha = rgba.w() * time_to_alpha(time, dot2_anim_start, ANIMATION_SPEED);
let dot3_alpha = rgba.w() * time_to_alpha(time, dot3_anim_start, ANIMATION_SPEED);
let rgb = rgba.xyz();
let color1 = format!("srgba({}.x,{}.y,{}.z,{})", rgb, rgb, rgb, dot1_alpha.glsl());
let color2 = format!("srgba({}.x,{}.y,{}.z,{})", rgb, rgb, rgb, dot2_alpha.glsl());
let color3 = format!("srgba({}.x,{}.y,{}.z,{})", rgb, rgb, rgb, dot3_alpha.glsl());
let dot1 = dot1.fill(color1);
let dot2 = dot2.fill(color2);
let dot3 = dot3.fill(color3);
let shape = dot1 + dot2 + dot3;
shape.into()
}
}
1 change: 1 addition & 0 deletions lib/rust/ensogl/component/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub use ensogl_scroll_area as scroll_area;
pub use ensogl_scrollbar as scrollbar;
pub use ensogl_selector as selector;
pub use ensogl_shadow as shadow;
pub use ensogl_spinner as spinner;
pub use ensogl_text as text;
pub use ensogl_toggle_button as toggle_button;
pub use ensogl_tooltip as tooltip;

0 comments on commit 9e71fea

Please sign in to comment.