Skip to content
This repository has been archived by the owner on Dec 28, 2021. It is now read-only.

Implement tooltips that show the type of an output port on hover. #1312

Merged
merged 13 commits into from
Mar 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
unnecessary library imports when selecting hints from node searcher. This
makes the generated textual code easier to read and reduces likelihood of
accidental name collision.
- [Hovering over an output port shows a pop-up with the result type of a node]
[1312]. This allows discovering the result type of a node which can help with
debugging and development.
- [Visualizations can define context for preprocessor evaluation][1291]. Users
can now decide what module's context should be used for visualization
preprocessor. This allows providing visualization with standard library
Expand Down Expand Up @@ -195,6 +198,7 @@ you can find their release notes
https://www.youtube.com/watch?v=BYUAL4ksEgY&ab_channel=Enso
[podcast-future-of-enso]:
https://www.youtube.com/watch?v=rF8DuJPOfTs&t=1863s&ab_channel=Enso
[1312]: https://github.com/enso-org/ide/pull/1312

<br/>

Expand Down
198 changes: 198 additions & 0 deletions src/rust/ensogl/lib/components/src/label.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
//! Label component. Appears as text with background.

use crate::prelude::*;

use enso_frp as frp;
use enso_frp;
use ensogl_core::application::Application;
use ensogl_core::data::color;
use ensogl_core::display::shape::*;
use ensogl_core::display::traits::*;
use ensogl_core::display;
use ensogl_text as text;

use ensogl_theme::component::label as theme;



// ==========================
// === Shapes Definitions ===
// ==========================

mod background {
use super::*;

ensogl_core::define_shape_system! {
(style:Style,bg_color:Vector4) {

let width = Var::<Pixels>::from("input_size.x");
let height = Var::<Pixels>::from("input_size.y");
let padding = style.get_number_or(theme::padding, 0.0);
let width = width - padding.px() * 2.0;
let height = height - padding.px() * 2.0;
let radius = &height / 2.0;
let shape = Rect((&width,&height)).corners_radius(&radius);
let shape = shape.fill(Var::<color::Rgba>::from(bg_color.clone()));


// === Shadow ===
let alpha = Var::<f32>::from(format!("({0}.w)",bg_color));
let border_size_f = 16.0;
let shaow_size = style.get_number_or(theme::shadow::size,0.0);
let shadow_size = shaow_size.px();
let shadow_width = &width + &shadow_size * 2.0;
let shadow_height = &height + &shadow_size * 2.0;
let shadow_radius = &shadow_height / 2.0;
let shadow = Rect((shadow_width,shadow_height)).corners_radius(shadow_radius);
let base_color = color::Rgba::from(style.get_color(theme::shadow));
let base_color = Var::<color::Rgba>::from(base_color);
let base_color = base_color.multiply_alpha(&alpha);
let fading_color = color::Rgba::from(style.get_color(theme::shadow::fading));
let fading_color = Var::<color::Rgba>::from(fading_color);
let fading_color = fading_color.multiply_alpha(&alpha);
let exponent = style.get_number_or(theme::shadow::exponent,2.0);
let shadow_color = color::LinearGradient::new()
.add(0.0,fading_color.into_linear())
.add(1.0,base_color.into_linear());
let shadow_color = color::SdfSampler::new(shadow_color)
.max_distance(border_size_f)
.slope(color::Slope::Exponent(exponent));
let shadow = shadow.fill(shadow_color);

(shadow+shape).into()
}
}
}



// ===========
// === FRP ===
// ===========

ensogl_core::define_endpoints! {
Input {
set_content(String),
set_opacity(f32)
}
Output {
size (Vector2)
}
}



// =============
// === Model ===
// =============

#[derive(Clone,Debug)]
struct Model {
background : background::View,
label : text::Area,
display_object : display::object::Instance,
app : Application,
style : StyleWatch,
}

impl Model {
fn new(app: Application) -> Self {
let app = app.clone_ref();
let scene = app.display.scene();
let logger = Logger::new("TextLabel");
let display_object = display::object::Instance::new(&logger);
let label = app.new_view::<text::Area>();
let background = background::View::new(&logger);

// FIXME[MM/WD]: Depth sorting of labels to in front of everything else in the scene.
// Temporary solution. The depth management needs to allow defining relative position of
// the text and background and let the whole component to be set to am an arbitrary layer.
label.remove_from_scene_layer_DEPRECATED(&scene.layers.main);
label.add_to_scene_layer_DEPRECATED(&scene.layers.tooltip_text);
scene.layers.tooltip_background.add_exclusive(&background);

display_object.add_child(&background);
display_object.add_child(&label);

let style = StyleWatch::new(&app.display.scene().style_sheet);

Model { label, display_object, background, app, style }
}

pub fn height(&self) -> f32 {
self.style.get_number_or(theme::height, 0.0)
}

fn set_width(&self, width:f32) -> Vector2 {
let padding = self.style.get_number_or(theme::padding,0.0);
let text_size = self.style.get_number_or(theme::text::size,0.0);
let text_offset = self.style.get_number_or(theme::text::offset,0.0);
let height = self.height();
let size = Vector2(width * 1.25,height);
let padded_size = size + Vector2(padding,padding) * 2.0;
self.background.size.set(padded_size);
let text_origin = Vector2(padding / 2.0 + text_offset - size.x/2.0, text_size /2.0);
self.label.set_position_xy(text_origin);
padded_size
}

fn set_content(&self, t:&str) -> Vector2 {
self.label.set_content(t);
self.set_width(self.label.width.value())
}

fn set_opacity(&self, value:f32) {
let text_color_path = theme::text;
let text_color = self.style.get_color(text_color_path).multiply_alpha(value);
let text_color = color::Rgba::from(text_color);
self.label.frp.set_color_all.emit(text_color);
self.label.frp.set_default_color.emit(text_color);

let bg_color_path = theme::background;
let bg_color = self.style.get_color(bg_color_path).multiply_alpha(value);
let bg_color = color::Rgba::from(bg_color);
self.background.bg_color.set(bg_color.into())
}
}



// =======================
// === Label Component ===
// =======================

#[allow(missing_docs)]
#[derive(Clone,CloneRef,Debug)]
pub struct Label {
model : Rc<Model>,
pub frp : Rc<Frp>,
}

impl Label {
/// Constructor.
pub fn new(app:Application) -> Self {
let frp = Rc::new(Frp::new());
let model = Rc::new(Model::new(app.clone_ref()));
Label {frp,model}.init()
}

fn init(self) -> Self {
let frp = &self.frp;
let network = &frp.network;
let model = &self.model;

frp::extend! { network
frp.source.size <+ frp.set_content.map(f!((t)
model.set_content(t)
));

eval frp.set_opacity((value) model.set_opacity(*value));
}

self
}
}

impl display::Object for Label {
fn display_object(&self) -> &display::object::Instance { &self.model.display_object }
}
1 change: 1 addition & 0 deletions src/rust/ensogl/lib/components/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

pub mod drop_down_menu;
pub mod list_view;
pub mod label;
pub mod toggle_button;

/// Commonly used types and functions.
Expand Down
2 changes: 2 additions & 0 deletions src/rust/ensogl/lib/core/src/animation/frp/animation.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! FRP bindings to the animation engine.

pub mod hysteretic;

use crate::prelude::*;

use crate::animation::physics::inertia;
Expand Down
113 changes: 113 additions & 0 deletions src/rust/ensogl/lib/core/src/animation/frp/animation/hysteretic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! Animation that has a delayed onset and offset.
//!
//! The basic idea behind this animation is, that it changes between two states: of (0.0) and on
//! (1.0), but only after a delay (`st`/`et` start delay, end delay). If the animation gets
//! canceled before the delay has passed, nothing happens, and the delay resets.
//!
//! This can be used to hide state changes that are only happening for a short duration. For example
//! consider a UI element that is shown when hovering ofer an icon. When only moving the mouse
//! cursor past the icon we want to avoid the pop-up appearing and disappearing right away. The
//! `HystereticAnimation` allows this by setting a `start_delay_duration` long enough to avoid
//! triggering during the time it takes to move past the icon. Thus, when the cursor moves past the
//! icon nothing is visible to the user, but if the mosque cursor stays on the icon longer than
//! `start_delay_duration`, the animation starts, and the pop-up becomes visible. In reverse, when
//! moving the cursor between multiple icons, the pop-up should not permanently start and disappear
//! and re-appear. Thus setting a `end_delay_duration` will avoid the pop-up from disappearing, if
//! the time the cursor is between icons is less than the `end_delay_duration`. Instead, the hiding
//! will only start iof the cursos has left any icon triggering the pop-up for longer than the
//! `end_delay_duration`.
//!
use crate::prelude::*;

use crate::Animation;
use crate::Easing;

use enso_frp as frp;



// ===========
// === Frp ===
// ===========

crate::define_endpoints! {
Input {
/// Trigger start of animation towards start state (0.0). Will be delayed by onset time.
to_start(),
/// Trigger start of animation towards end state (1.0). Will be delayed by onset time.
to_end(),
}
Output {
/// Represents the numeric state of the animation in the range 0..1.
value(f32),
/// Triggered when the state reaches 1.0.
on_end(),
/// Triggered when the state reaches 0.0.
on_start(),
}
}



// ===========================
// === HystereticAnimation ===
// ===========================

/// Animation that has a delayed onset and offset.
#[derive(CloneRef,Debug,Shrinkwrap)]
pub struct HystereticAnimation {
#[allow(missing_docs)]
pub frp : FrpEndpoints,
}

impl HystereticAnimation {
#[allow(missing_docs)]
pub fn new(network:&frp::Network, start_delay_duration:f32, end_delay_duration:f32) -> Self {
let frp = Frp::extend(network);
let start_delay = Easing::new(network);
let end_delay = Easing::new(network);
start_delay.set_duration(start_delay_duration);
end_delay.set_duration(end_delay_duration);

let transition = Animation::<f32>::new(network);

frp::extend! { network

during_transition <- any_mut();

on_end <- frp.to_end.constant(());
on_end_while_active <- on_end.gate(&during_transition);
on_end_while_inactive <- on_end.gate_not(&during_transition);

on_start <- frp.to_start.constant(());
on_start_while_active <- on_start.gate(&during_transition);
on_start_while_inactive <- on_start.gate_not(&during_transition);

start_delay.target <+ on_start_while_inactive.constant(1.0);
end_delay.target <+ on_end_while_inactive.constant(1.0);

start_delay.stop_and_rewind <+ on_end.constant(0.0);
end_delay.stop_and_rewind <+ on_start.constant(0.0);

offset_start <- end_delay.on_end.map(|t| t.is_normal()).on_true();
onset_start <- start_delay.on_end.map(|t| t.is_normal()).on_true();

onset_end <- transition.value.map(|t| (t - 1.0).abs() < std::f32::EPSILON).on_true();
offset_end <- transition.value.map(|t| t.abs() < std::f32::EPSILON).on_true();

transition.target <+ onset_start.constant(1.0);
transition.target <+ offset_start.constant(0.0);
transition.target <+ on_end_while_active.constant(0.0);
transition.target <+ on_start_while_active.constant(1.0);

during_transition <+ bool(&onset_end,&onset_start);
during_transition <+ bool(&offset_end,&offset_start);

frp.source.value <+ transition.value;
frp.source.on_end <+ onset_end;
frp.source.on_start <+ offset_end;
}

HystereticAnimation{frp}
}
}
13 changes: 13 additions & 0 deletions src/rust/ensogl/lib/core/src/data/color/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,16 @@ impl<C:Default> Default for Alpha<C> {
Self {alpha,opaque}
}
}

impl<C> Alpha<C> {
/// Return the color with a multiplied alpha channel.
pub fn multiply_alpha(self, alpha:f32) -> Color<Self> {
let alpha = self.alpha * alpha;
Color(Alpha{alpha, opaque: self.opaque })
}
MichaelMauderer marked this conversation as resolved.
Show resolved Hide resolved

/// Modify the color's alpha channel.
pub fn mod_alpha<F:FnOnce(&mut f32)>(&mut self, f:F) {
f(&mut self.alpha)
}
}
4 changes: 2 additions & 2 deletions src/rust/ensogl/lib/core/src/data/color/gradient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl<Color> LinearGradient<Color> {
}

impls! { [Color] From<&LinearGradient<Color>> for Glsl
where [Color:Copy + RefInto<Glsl>] {
where [Color:RefInto<Glsl>] {
|t| {
let args = t.control_points.iter().map(|control_point| {
let offset = control_point.offset.glsl();
Expand All @@ -82,7 +82,7 @@ pub const DEFAULT_DISTANCE_GRADIENT_MAX_DISTANCE : f32 = 10.0;
/// A gradient which transforms a linear gradient to a gradient along the signed distance field.
/// The slope parameter modifies how fast the gradient values are changed, allowing for nice,
/// smooth transitions.
#[derive(Copy,Clone,Debug)]
#[derive(Clone,Debug)]
pub struct SdfSampler<Gradient> {
/// The distance from the shape border at which the gradient should start.
pub min_distance : f32,
Expand Down
Loading