-
Notifications
You must be signed in to change notification settings - Fork 324
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement tooltips that show the type of an output port on hover. (en…
…so-org/ide#1312) Original commit: enso-org/ide@f03a652
- Loading branch information
1 parent
421c788
commit 93f8503
Showing
20 changed files
with
820 additions
and
165 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
ide/src/rust/ensogl/lib/core/src/animation/frp/animation/hysteretic.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.