mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 11:52:59 +03:00
Fix visualisation FRP bugs. (#6831)
Fixes * Empty Visualization when opening a full-screen visualization directly without opening the visualization before. #6770 https://github.com/enso-org/enso/assets/1428930/5812ed03-652c-4a27-8e33-b85512ca11b6 * Empty visualization when opening the full-screen visualization before the data for the visualization has arrived. #6561 https://github.com/enso-org/enso/assets/1428930/d8e58f2d-f1b6-4b70-84fa-e917f6c0af1f * Visualization is reset to default when reconnecting nodes #6673 https://github.com/enso-org/enso/assets/1428930/ac6cf79a-7147-4f13-9045-52599fb39900 * Redundant internal open/lose events caused by logic loops around the show/hide button, as well as many redundant layer setting/unsetting issues internal to the visualization code. Generally improves the logic around the visualization API by avoiding decentralized logic in different places and removing old code that is no longer needed.
This commit is contained in:
parent
db96bd2e2c
commit
72b202b7d0
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4347,6 +4347,7 @@ dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bimap",
|
||||
"bitflags 2.2.1",
|
||||
"derivative",
|
||||
"engine-protocol",
|
||||
"enso-config",
|
||||
"enso-frp",
|
||||
|
@ -186,12 +186,12 @@ impl Visualization {
|
||||
eval view.visualization_preprocessor_changed (((node, preprocessor)) model.visualization_preprocessor_changed(*node, preprocessor.clone_ref()));
|
||||
eval view.set_node_error_status (((node, error)) model.error_on_node_changed(*node, error));
|
||||
|
||||
update <- source::<(ViewNodeId, visualization_view::Data)>();
|
||||
set_data <- source::<(ViewNodeId, visualization_view::Data)>();
|
||||
error_update <- source::<(ViewNodeId, visualization_view::Data)>();
|
||||
visualization_failure <- source::<ViewNodeId>();
|
||||
error_vis_failure <- source::<ViewNodeId>();
|
||||
|
||||
view.set_visualization_data <+ update;
|
||||
view.set_visualization_data <+ set_data;
|
||||
view.set_error_visualization_data <+ error_update;
|
||||
view.disable_visualization <+ visualization_failure;
|
||||
|
||||
@ -199,7 +199,7 @@ impl Visualization {
|
||||
}
|
||||
|
||||
Self { model, _network: network }
|
||||
.spawn_visualization_handler(notifications, manager, update, visualization_failure)
|
||||
.spawn_visualization_handler(notifications, manager, set_data, visualization_failure)
|
||||
.spawn_visualization_handler(
|
||||
error_notifications,
|
||||
error_manager,
|
||||
@ -213,7 +213,7 @@ impl Visualization {
|
||||
self,
|
||||
notifier: impl Stream<Item = manager::Notification> + Unpin + 'static,
|
||||
manager: Rc<Manager>,
|
||||
update_endpoint: frp::Source<(ViewNodeId, visualization_view::Data)>,
|
||||
set_data_endpoint: frp::Source<(ViewNodeId, visualization_view::Data)>,
|
||||
failure_endpoint: frp::Source<ViewNodeId>,
|
||||
) -> Self {
|
||||
let weak = Rc::downgrade(&self.model);
|
||||
@ -221,7 +221,7 @@ impl Visualization {
|
||||
info!("Received update for visualization: {notification:?}");
|
||||
match notification {
|
||||
manager::Notification::ValueUpdate { target, data, .. } => {
|
||||
model.handle_value_update(&update_endpoint, target, data);
|
||||
model.handle_value_update(&set_data_endpoint, target, data);
|
||||
}
|
||||
manager::Notification::FailedToAttach { visualization, error } => {
|
||||
error!("Visualization {} failed to attach: {error}.", visualization.id);
|
||||
|
@ -12,6 +12,7 @@ analytics = { path = "../../analytics" }
|
||||
ast = { path = "../../language/ast/impl" }
|
||||
base64 = "0.13"
|
||||
bimap = { version = "0.4.0" }
|
||||
derivative = "2.2.0"
|
||||
engine-protocol = { path = "../../controller/engine-protocol" }
|
||||
enso-config = { path = "../../config" }
|
||||
enso-frp = { path = "../../../../lib/rust/frp" }
|
||||
|
@ -259,6 +259,7 @@ ensogl::define_endpoints_2! {
|
||||
select (),
|
||||
deselect (),
|
||||
enable_visualization (),
|
||||
enable_fullscreen_visualization (),
|
||||
disable_visualization (),
|
||||
set_visualization (Option<visualization::Definition>),
|
||||
set_disabled (bool),
|
||||
@ -318,12 +319,6 @@ ensogl::define_endpoints_2! {
|
||||
freeze (bool),
|
||||
hover (bool),
|
||||
error (Option<Error>),
|
||||
/// Whether visualization was permanently enabled (e.g. by pressing the button).
|
||||
visualization_enabled (bool),
|
||||
/// Visualization can be visible even when it is not enabled, e.g. when showing preview.
|
||||
/// Visualization can be invisible even when enabled, e.g. when the node has an error.
|
||||
visualization_visible (bool),
|
||||
visualization_path (Option<visualization::Path>),
|
||||
expression_label_visible (bool),
|
||||
/// The [`display::object::Model::position`] of the Node. Emitted when the Display Object
|
||||
/// hierarchy is updated (see: [`ensogl_core::display::object::Instance::update`]).
|
||||
@ -607,12 +602,6 @@ impl NodeModel {
|
||||
size
|
||||
}
|
||||
|
||||
#[profile(Debug)]
|
||||
#[allow(missing_docs)] // FIXME[everyone] All pub functions should have docs.
|
||||
pub fn visualization(&self) -> &visualization::Container {
|
||||
&self.visualization
|
||||
}
|
||||
|
||||
#[profile(Debug)]
|
||||
fn set_error(&self, error: Option<&Error>) {
|
||||
if let Some(error) = error {
|
||||
@ -753,7 +742,6 @@ impl Node {
|
||||
|
||||
// === Action Bar ===
|
||||
|
||||
let visualization_button_state = action_bar.action_visibility.clone_ref();
|
||||
out.context_switch <+ action_bar.action_context_switch;
|
||||
out.skip <+ action_bar.action_skip;
|
||||
out.freeze <+ action_bar.action_freeze;
|
||||
@ -790,7 +778,10 @@ impl Node {
|
||||
hover_onset_delay.set_delay(VIS_PREVIEW_ONSET_MS);
|
||||
hover_onset_delay.set_duration(0.0);
|
||||
|
||||
let visualization = &model.visualization.frp;
|
||||
|
||||
frp::extend! { network
|
||||
enabled <- bool(&input.disable_visualization, &input.enable_visualization);
|
||||
|
||||
out.error <+ input.set_error;
|
||||
is_error_set <- input.set_error.map(
|
||||
@ -806,11 +797,23 @@ impl Node {
|
||||
}
|
||||
));
|
||||
|
||||
eval input.set_visualization ((t) model.visualization.frp.set_visualization.emit(t));
|
||||
visualization_enabled_frp <- bool(&input.disable_visualization,&input.enable_visualization);
|
||||
eval visualization_enabled_frp ((enabled)
|
||||
model.action_bar.set_action_visibility_state(enabled)
|
||||
);
|
||||
viz_enabled <- enabled && no_error_set;
|
||||
visualization.set_view_state <+ viz_enabled.on_true().constant(visualization::ViewState::Enabled);
|
||||
visualization.set_view_state <+ viz_enabled.on_false().constant(visualization::ViewState::Disabled);
|
||||
|
||||
// Integration between visualization and action bar.
|
||||
visualization.set_visualization <+ input.set_visualization;
|
||||
is_enabled <- visualization.view_state.map(|state|{
|
||||
matches!(state,visualization::ViewState::Enabled)
|
||||
});
|
||||
action_bar.set_action_visibility_state <+ is_enabled;
|
||||
button_set_to_true <- action_bar.user_action_visibility.on_true();
|
||||
button_set_to_true_without_error <- button_set_to_true.gate_not(&is_error_set);
|
||||
button_set_to_true_with_error <- button_set_to_true.gate(&is_error_set);
|
||||
visualization.set_view_state <+ button_set_to_true_without_error.constant(visualization::ViewState::Enabled);
|
||||
action_bar.set_action_visibility_state <+ button_set_to_true_with_error.constant(false);
|
||||
|
||||
visualization.set_view_state <+ action_bar.user_action_visibility.on_false().constant(visualization::ViewState::Disabled);
|
||||
|
||||
// Show preview visualisation after some delay, depending on whether we show an error
|
||||
// or are in quick preview mode. Also, omit the preview if we don't have an
|
||||
@ -840,38 +843,10 @@ impl Node {
|
||||
hide_preview <+ editing_finished;
|
||||
preview_enabled <- bool(&hide_preview, &input.show_preview);
|
||||
preview_visible <- hover_preview_visible || preview_enabled;
|
||||
preview_visible <- preview_visible.on_change();
|
||||
|
||||
// If the preview is visible while the visualization button is disabled, clicking the
|
||||
// visualization button hides the preview and keeps the visualization button disabled.
|
||||
vis_button_on <- visualization_button_state.filter(|e| *e).constant(());
|
||||
vis_button_off <- visualization_button_state.filter(|e| !*e).constant(());
|
||||
visualization_on <- vis_button_on.gate_not(&preview_visible);
|
||||
vis_button_on_while_preview_visible <- vis_button_on.gate(&preview_visible);
|
||||
hide_preview <+ vis_button_on_while_preview_visible;
|
||||
hide_preview <+ vis_button_off;
|
||||
action_bar.set_action_visibility_state <+
|
||||
vis_button_on_while_preview_visible.constant(false);
|
||||
visualization_enabled <- bool(&vis_button_off, &visualization_on);
|
||||
|
||||
visualization_visible <- visualization_enabled || preview_visible;
|
||||
visualization_visible <- visualization_visible && no_error_set;
|
||||
visualization_visible_on_change <- visualization_visible.on_change();
|
||||
out.visualization_visible <+ visualization_visible_on_change;
|
||||
out.visualization_enabled <+ visualization_enabled;
|
||||
eval visualization_visible_on_change ((is_visible)
|
||||
model.visualization.frp.set_visibility(is_visible)
|
||||
);
|
||||
out.visualization_path <+ model.visualization.frp.visualisation.all_with(&init,|def_opt,_| {
|
||||
def_opt.as_ref().map(|def| def.signature.path.clone_ref())
|
||||
});
|
||||
|
||||
// Ensure the preview is visible above all other elements, but the normal visualisation
|
||||
// is below nodes.
|
||||
layer_on_hover <- hover_preview_visible.on_false().map(|_| visualization::Layer::Default);
|
||||
layer_on_not_hover <- hover_preview_visible.on_true().map(|_| visualization::Layer::Front);
|
||||
layer <- any(layer_on_hover,layer_on_not_hover);
|
||||
model.visualization.frp.set_layer <+ layer;
|
||||
vis_preview_visible <- preview_visible && no_error_set;
|
||||
vis_preview_visible <- vis_preview_visible.on_change();
|
||||
visualization.set_view_state <+ vis_preview_visible.on_true().constant(visualization::ViewState::Preview);
|
||||
visualization.set_view_state <+ vis_preview_visible.on_false().constant(visualization::ViewState::Disabled);
|
||||
|
||||
update_error <- all(input.set_error,preview_visible);
|
||||
eval update_error([model]((error,visible)){
|
||||
@ -883,6 +858,10 @@ impl Node {
|
||||
});
|
||||
|
||||
eval error_color_anim.value ((value) model.set_error_color(value));
|
||||
visualization.set_view_state <+ input.set_error.is_some().constant(visualization::ViewState::Disabled);
|
||||
|
||||
enable_fullscreen <- frp.enable_fullscreen_visualization.gate(&no_error_set);
|
||||
visualization.set_view_state <+ enable_fullscreen.constant(visualization::ViewState::Fullscreen);
|
||||
|
||||
}
|
||||
|
||||
@ -949,16 +928,14 @@ impl Node {
|
||||
// === Type Labels ===
|
||||
|
||||
model.output.set_type_label_visibility
|
||||
<+ visualization_visible.not().and(&no_error_set);
|
||||
<+ visualization.visible.not().and(&no_error_set);
|
||||
|
||||
|
||||
// === Bounding Box ===
|
||||
|
||||
let visualization_size = &model.visualization.frp.size;
|
||||
// Visualization can be enabled and not visible when the node has an error.
|
||||
visualization_enabled_and_visible <- visualization_enabled && visualization_visible;
|
||||
bbox_input <- all4(
|
||||
&out.position,&new_size,&visualization_enabled_and_visible,visualization_size);
|
||||
&out.position,&new_size,&visualization.visible,visualization_size);
|
||||
out.bounding_box <+ bbox_input.map(|(a,b,c,d)| bounding_box(*a,*b,c.then(|| *d)));
|
||||
|
||||
inner_bbox_input <- all2(&out.position,&new_size);
|
||||
@ -997,6 +974,11 @@ impl Node {
|
||||
color::Lcha::transparent()
|
||||
}
|
||||
}
|
||||
|
||||
/// FRP API of the visualization container attached to this node.
|
||||
pub fn visualization(&self) -> &visualization::container::Frp {
|
||||
&self.model().visualization.frp
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for Node {
|
||||
|
@ -70,6 +70,7 @@ ensogl::define_endpoints! {
|
||||
Input {
|
||||
set_size (Vector2),
|
||||
set_visibility (bool),
|
||||
/// Set whether the `visibility` icon should be toggled on or off.
|
||||
set_action_visibility_state (bool),
|
||||
set_action_skip_state (bool),
|
||||
set_action_freeze_state (bool),
|
||||
@ -86,6 +87,9 @@ ensogl::define_endpoints! {
|
||||
mouse_over (),
|
||||
mouse_out (),
|
||||
action_visibility (bool),
|
||||
/// The last visibility selection by the user. Ignores changes to the
|
||||
/// visibility chooser icon made through the input API.
|
||||
user_action_visibility (bool),
|
||||
action_context_switch (bool),
|
||||
action_freeze (bool),
|
||||
action_skip (bool),
|
||||
@ -412,6 +416,7 @@ impl ActionBar {
|
||||
// === Icon Actions ===
|
||||
|
||||
frp.source.action_visibility <+ model.icons.visibility.state;
|
||||
frp.source.user_action_visibility <+ model.icons.visibility.last_user_state;
|
||||
frp.source.action_skip <+ model.icons.skip.state;
|
||||
frp.source.action_freeze <+ model.icons.freeze.state;
|
||||
disable_context_button_clicked <- model.icons.context_switch.disable_button.is_pressed.on_true();
|
||||
|
@ -1,4 +1,12 @@
|
||||
//! This module defines the `Container` struct and related functionality.
|
||||
//! This module defines the `Container` struct and related functionality. This represent the view
|
||||
//! a visualisation in the graph editor and includes a visual box that contains the visualisation,
|
||||
//! and action bar that allows setting the visualisation type.
|
||||
//!
|
||||
//! The `[Container]` struct is responsible for managing the visualisation and action bar and
|
||||
//! providing a unified interface to the graph editor. This includes ensuring that the visualisation
|
||||
//! is correctly positioned, sized and layouted in its different [ViewState]s (which include the
|
||||
//! `Enabled`, `Fullscreen` and `Preview` states). Importantly, this also includes EnsoGL layer
|
||||
//! management to ensure correct occlusion of the visualisation with respect to other scene objects.
|
||||
|
||||
// FIXME There is a serious performance problem in this implementation. It assumes that the
|
||||
// FIXME visualization is a child of the container. However, this is very inefficient. Consider a
|
||||
@ -24,6 +32,7 @@ use ensogl::data::color::Rgba;
|
||||
use ensogl::display;
|
||||
use ensogl::display::scene;
|
||||
use ensogl::display::scene::Scene;
|
||||
use ensogl::display::DomScene;
|
||||
use ensogl::display::DomSymbol;
|
||||
use ensogl::system::web;
|
||||
use ensogl::Animation;
|
||||
@ -125,29 +134,59 @@ pub mod background {
|
||||
// === Frp ===
|
||||
// ===========
|
||||
|
||||
ensogl::define_endpoints! {
|
||||
/// Indicates the visibility state of the visualisation.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Derivative)]
|
||||
#[derivative(Default)]
|
||||
pub enum ViewState {
|
||||
/// Visualisation is permanently enabled and visible in the graph editor. It is attached to a
|
||||
/// single node and can be moved and interacted with when selected.
|
||||
Enabled,
|
||||
/// Visualisation is disabled and hidden in the graph editor.
|
||||
#[derivative(Default)]
|
||||
Disabled,
|
||||
/// Visualisation is temporarily enabled and visible in the graph editor. It should be placed
|
||||
/// above other scene elements to allow quick inspection.
|
||||
Preview,
|
||||
/// Visualisation is enabled and visible in the graph editor in fullscreen mode. It occludes
|
||||
/// the whole graph and can be interacted with.
|
||||
Fullscreen,
|
||||
}
|
||||
|
||||
impl ViewState {
|
||||
/// Indicates whether the visualisation is visible in the graph editor. It is always visible
|
||||
/// when not disabled.
|
||||
pub fn is_visible(&self) -> bool {
|
||||
!matches!(self, ViewState::Disabled)
|
||||
}
|
||||
|
||||
/// Indicates whether the visualisation is fullscreen mode.
|
||||
pub fn is_fullscreen(&self) -> bool {
|
||||
matches!(self, ViewState::Fullscreen)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ensogl::define_endpoints_2! {
|
||||
Input {
|
||||
set_visibility (bool),
|
||||
toggle_visibility (),
|
||||
set_view_state (ViewState),
|
||||
set_visualization (Option<visualization::Definition>),
|
||||
cycle_visualization (),
|
||||
set_data (visualization::Data),
|
||||
set_data (Option<visualization::Data>),
|
||||
select (),
|
||||
deselect (),
|
||||
set_size (Vector2),
|
||||
enable_fullscreen (),
|
||||
disable_fullscreen (),
|
||||
set_vis_input_type (Option<enso::Type>),
|
||||
set_layer (visualization::Layer),
|
||||
}
|
||||
|
||||
Output {
|
||||
preprocessor (PreprocessorConfiguration),
|
||||
visualisation (Option<visualization::Definition>),
|
||||
visualization_path (Option<visualization::Path>),
|
||||
size (Vector2),
|
||||
is_selected (bool),
|
||||
vis_input_type (Option<enso::Type>),
|
||||
fullscreen (bool),
|
||||
visible (bool),
|
||||
vis_input_type (Option<enso::Type>)
|
||||
view_state (ViewState),
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,8 +200,7 @@ ensogl::define_endpoints! {
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct View {
|
||||
display_object: display::object::Instance,
|
||||
|
||||
display_object: display::object::Instance,
|
||||
background: background::View,
|
||||
overlay: overlay::View,
|
||||
background_dom: DomSymbol,
|
||||
@ -277,7 +315,6 @@ pub struct ContainerModel {
|
||||
scene: Scene,
|
||||
view: View,
|
||||
fullscreen_view: fullscreen::Panel,
|
||||
is_fullscreen: Rc<Cell<bool>>,
|
||||
registry: visualization::Registry,
|
||||
size: Rc<Cell<Vector2>>,
|
||||
action_bar: ActionBar,
|
||||
@ -294,7 +331,6 @@ impl ContainerModel {
|
||||
let view = View::new(scene.clone_ref());
|
||||
let fullscreen_view = fullscreen::Panel::new(scene);
|
||||
let scene = scene.clone_ref();
|
||||
let is_fullscreen = default();
|
||||
let size = default();
|
||||
let action_bar = ActionBar::new(app, registry.clone_ref());
|
||||
view.add_child(&action_bar);
|
||||
@ -307,7 +343,6 @@ impl ContainerModel {
|
||||
scene,
|
||||
view,
|
||||
fullscreen_view,
|
||||
is_fullscreen,
|
||||
registry,
|
||||
size,
|
||||
action_bar,
|
||||
@ -318,21 +353,16 @@ impl ContainerModel {
|
||||
fn init(self) -> Self {
|
||||
self.display_object.add_child(&self.drag_root);
|
||||
self.scene.layers.above_nodes.add(&self.action_bar);
|
||||
|
||||
self.update_shape_sizes();
|
||||
self.update_shape_sizes(ViewState::default());
|
||||
self.init_corner_roundness();
|
||||
// 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
|
||||
}
|
||||
|
||||
/// Indicates whether the visualization container is visible and active.
|
||||
/// Note: can't be called `is_visible` due to a naming conflict with `display::object::class`.
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.view.has_parent()
|
||||
fn set_visualization_layer(&self, layer: visualization::Layer) {
|
||||
if let Some(vis) = self.visualization.borrow().as_ref() {
|
||||
vis.set_layer.emit(layer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,54 +370,60 @@ impl ContainerModel {
|
||||
// === Private API ===
|
||||
|
||||
impl ContainerModel {
|
||||
fn set_visibility(&self, visibility: bool) {
|
||||
fn apply_view_state(&self, view_state: ViewState) {
|
||||
// This is a workaround for #6600. It ensures the action bar is removed
|
||||
// and receive no further mouse events.
|
||||
if visibility {
|
||||
if view_state.is_visible() {
|
||||
self.view.add_child(&self.action_bar);
|
||||
} else {
|
||||
self.action_bar.unset_parent();
|
||||
}
|
||||
|
||||
// Show or hide the visualization.
|
||||
if visibility {
|
||||
if view_state.is_visible() {
|
||||
self.drag_root.add_child(&self.view);
|
||||
self.show_visualisation();
|
||||
} else {
|
||||
self.drag_root.remove_child(&self.view);
|
||||
}
|
||||
|
||||
match view_state {
|
||||
ViewState::Enabled => self.enable_default_view(),
|
||||
ViewState::Disabled => {}
|
||||
ViewState::Preview => self.enable_preview(),
|
||||
ViewState::Fullscreen => self.enable_fullscreen(),
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_fullscreen(&self) {
|
||||
self.is_fullscreen.set(true);
|
||||
fn set_vis_parents(&self, parent: &dyn display::Object, dom_parent: &DomScene) {
|
||||
if let Some(viz) = &*self.visualization.borrow() {
|
||||
self.fullscreen_view.add_child(viz);
|
||||
parent.add_child(viz);
|
||||
if let Some(dom) = viz.root_dom() {
|
||||
self.scene.dom.layers.fullscreen_vis.manage(dom);
|
||||
dom_parent.manage(dom);
|
||||
}
|
||||
viz.inputs.activate.emit(());
|
||||
}
|
||||
}
|
||||
|
||||
fn disable_fullscreen(&self) {
|
||||
self.is_fullscreen.set(false);
|
||||
if let Some(viz) = &*self.visualization.borrow() {
|
||||
self.view.add_child(viz);
|
||||
if let Some(dom) = viz.root_dom() {
|
||||
self.scene.dom.layers.back.manage(dom);
|
||||
}
|
||||
viz.inputs.deactivate.emit(());
|
||||
}
|
||||
fn enable_fullscreen(&self) {
|
||||
self.set_visualization_layer(visualization::Layer::Fullscreen);
|
||||
self.set_vis_parents(&self.fullscreen_view, &self.scene.dom.layers.fullscreen_vis)
|
||||
}
|
||||
|
||||
fn toggle_visibility(&self) {
|
||||
self.set_visibility(!self.is_active())
|
||||
fn enable_default_view(&self) {
|
||||
self.set_visualization_layer(visualization::Layer::Default);
|
||||
self.set_vis_parents(&self.view, &self.scene.dom.layers.back)
|
||||
}
|
||||
|
||||
fn enable_preview(&self) {
|
||||
self.set_visualization_layer(visualization::Layer::Front);
|
||||
self.set_vis_parents(&self.view, &self.scene.dom.layers.front);
|
||||
}
|
||||
|
||||
fn set_visualization(
|
||||
&self,
|
||||
visualization: visualization::Instance,
|
||||
preprocessor: &frp::Any<PreprocessorConfiguration>,
|
||||
view_state: ViewState,
|
||||
) {
|
||||
let size = self.size.get();
|
||||
visualization.frp.set_size.emit(size);
|
||||
@ -399,31 +435,27 @@ impl ContainerModel {
|
||||
vis_preprocessor_change <- visualization.on_preprocessor_change.map(|x| x.clone());
|
||||
preprocessor <+ vis_preprocessor_change;
|
||||
}
|
||||
preprocessor.emit(visualization.on_preprocessor_change.value());
|
||||
if self.is_fullscreen.get() {
|
||||
self.fullscreen_view.add_child(&visualization)
|
||||
} else {
|
||||
self.view.add_child(&visualization);
|
||||
}
|
||||
self.visualization.replace(Some(visualization));
|
||||
self.visualization.replace(Some(visualization.clone_ref()));
|
||||
self.vis_frp_connection.replace(Some(vis_frp_connection));
|
||||
self.apply_view_state(view_state);
|
||||
preprocessor.emit(visualization.on_preprocessor_change.value());
|
||||
}
|
||||
|
||||
fn set_visualization_data(&self, data: &visualization::Data) {
|
||||
self.visualization.borrow().for_each_ref(|vis| vis.send_data.emit(data))
|
||||
}
|
||||
|
||||
fn update_shape_sizes(&self) {
|
||||
fn update_shape_sizes(&self, view_state: ViewState) {
|
||||
let size = self.size.get();
|
||||
self.set_size(size);
|
||||
self.update_layout(size, view_state);
|
||||
}
|
||||
|
||||
fn set_size(&self, size: impl Into<Vector2>) {
|
||||
fn update_layout(&self, size: impl Into<Vector2>, view_state: ViewState) {
|
||||
let dom = self.view.background_dom.dom();
|
||||
let bg_dom = self.fullscreen_view.background_dom.dom();
|
||||
let size = size.into();
|
||||
self.size.set(size);
|
||||
if self.is_fullscreen.get() {
|
||||
if view_state.is_fullscreen() {
|
||||
self.view.overlay.set_size(Vector2(0.0, 0.0));
|
||||
dom.set_style_or_warn("width", "0");
|
||||
dom.set_style_or_warn("height", "0");
|
||||
@ -461,16 +493,6 @@ impl ContainerModel {
|
||||
self.view.background.roundness.set(value);
|
||||
}
|
||||
|
||||
fn show_visualisation(&self) {
|
||||
if let Some(vis) = self.visualization.borrow().as_ref() {
|
||||
if self.is_fullscreen.get() {
|
||||
self.fullscreen_view.add_child(vis);
|
||||
} else {
|
||||
self.view.add_child(vis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if given mouse-event-target means this visualization.
|
||||
fn is_this_target(&self, target: scene::PointerTargetId) -> bool {
|
||||
self.view.overlay.is_this_target(target)
|
||||
@ -526,7 +548,9 @@ impl Container {
|
||||
}
|
||||
|
||||
fn init(self, app: &Application) -> Self {
|
||||
let frp = &self.frp;
|
||||
let frp = &self.frp.private;
|
||||
let input = &frp.input;
|
||||
let output = &frp.output;
|
||||
let network = &self.frp.network;
|
||||
let model = &self.model;
|
||||
let scene = &self.model.scene;
|
||||
@ -536,30 +560,27 @@ impl Container {
|
||||
let selection = Animation::new(network);
|
||||
|
||||
frp::extend! { network
|
||||
eval frp.set_visibility ((v) model.set_visibility(*v));
|
||||
eval_ frp.toggle_visibility (model.toggle_visibility());
|
||||
eval input.set_view_state((state) model.apply_view_state(*state));
|
||||
output.view_state <+ input.set_view_state.on_change();
|
||||
output.fullscreen <+ output.view_state.map(|state| state.is_fullscreen()).on_change();
|
||||
output.visible <+ output.view_state.map(|state| state.is_visible()).on_change();
|
||||
output.size <+ input.set_size.on_change();
|
||||
|
||||
visualisation_uninitialised <- frp.set_visualization.map(|t| t.is_none());
|
||||
default_visualisation <- visualisation_uninitialised.on_true().map(|_| {
|
||||
visualisation_not_selected <- input.set_visualization.map(|t| t.is_none());
|
||||
input_type_not_set <- input.set_vis_input_type.is_some().not();
|
||||
uninitialised <- visualisation_not_selected && input_type_not_set;
|
||||
set_default_visualisation <- uninitialised.on_change().on_true().map(|_| {
|
||||
Some(visualization::Registry::default_visualisation())
|
||||
});
|
||||
vis_input_type <- frp.set_vis_input_type.on_change();
|
||||
vis_input_type <- vis_input_type.gate(&visualisation_uninitialised).unwrap();
|
||||
default_visualisation_for_type <- vis_input_type.map(f!((tp) {
|
||||
vis_input_type_changed <- input.set_vis_input_type.on_change();
|
||||
vis_input_type_changed_without_selection <-
|
||||
vis_input_type_changed.gate(&visualisation_not_selected).unwrap();
|
||||
set_default_visualisation_for_type <- vis_input_type_changed_without_selection.map(f!((tp) {
|
||||
registry.default_visualization_for_type(tp)
|
||||
}));
|
||||
default_visualisation <- any(&default_visualisation, &default_visualisation_for_type);
|
||||
set_default_visualisation <- any(
|
||||
&set_default_visualisation, &set_default_visualisation_for_type);
|
||||
|
||||
eval frp.set_data ((t) model.set_visualization_data(t));
|
||||
frp.source.size <+ frp.set_size;
|
||||
frp.source.visible <+ frp.set_visibility;
|
||||
frp.source.visible <+ frp.toggle_visibility.map(f!((()) model.is_active()));
|
||||
eval frp.set_layer ([model](l) {
|
||||
if let Some(vis) = model.visualization.borrow().as_ref() {
|
||||
vis.set_layer.emit(l)
|
||||
}
|
||||
model.view.set_layer(*l);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -569,15 +590,17 @@ impl Container {
|
||||
selected_definition <- action_bar.visualisation_selection.map(f!([registry](path)
|
||||
path.as_ref().and_then(|path| registry.definition_from_path(path))
|
||||
));
|
||||
action_bar.set_vis_input_type <+ frp.set_vis_input_type;
|
||||
frp.source.vis_input_type <+ frp.set_vis_input_type;
|
||||
action_bar.hide_icons <+ selected_definition.constant(());
|
||||
output.vis_input_type <+ input.set_vis_input_type;
|
||||
let chooser = &model.action_bar.visualization_chooser();
|
||||
chooser.frp.set_vis_input_type <+ input.set_vis_input_type;
|
||||
}
|
||||
|
||||
|
||||
// === Cycling Visualizations ===
|
||||
|
||||
frp::extend! { network
|
||||
vis_after_cycling <- frp.cycle_visualization.map3(&frp.visualisation,&frp.vis_input_type,
|
||||
vis_after_cycling <- input.cycle_visualization.map3(&output.visualisation, &output.vis_input_type,
|
||||
f!(((),vis,input_type) model.next_visualization(vis,input_type))
|
||||
);
|
||||
}
|
||||
@ -587,19 +610,19 @@ impl Container {
|
||||
|
||||
frp::extend! { network
|
||||
vis_definition_set <- any(
|
||||
frp.set_visualization,
|
||||
input.set_visualization,
|
||||
selected_definition,
|
||||
vis_after_cycling,
|
||||
default_visualisation);
|
||||
set_default_visualisation);
|
||||
new_vis_definition <- vis_definition_set.on_change();
|
||||
let preprocessor = &frp.source.preprocessor;
|
||||
frp.source.visualisation <+ new_vis_definition.map(f!(
|
||||
[model,action_bar,app,preprocessor](vis_definition) {
|
||||
let preprocessor = &output.preprocessor;
|
||||
output.visualisation <+ new_vis_definition.map2(&output.view_state, f!(
|
||||
[model,action_bar,app,preprocessor](vis_definition, view_state) {
|
||||
|
||||
if let Some(definition) = vis_definition {
|
||||
match definition.new_instance(&app) {
|
||||
Ok(vis) => {
|
||||
model.set_visualization(vis,&preprocessor);
|
||||
model.set_visualization(vis,&preprocessor, *view_state);
|
||||
let path = Some(definition.signature.path.clone());
|
||||
action_bar.set_selected_visualization.emit(path);
|
||||
},
|
||||
@ -611,21 +634,24 @@ impl Container {
|
||||
vis_definition.clone()
|
||||
}));
|
||||
|
||||
output.visualization_path <+ output.visualisation.map(|definition| {
|
||||
definition.as_ref().map(|def| def.signature.path.clone_ref())
|
||||
});
|
||||
}
|
||||
|
||||
// === Visualisation Loading Spinner ===
|
||||
|
||||
eval_ frp.source.visualisation ( model.view.show_waiting_screen() );
|
||||
eval_ frp.set_data ( model.view.disable_waiting_screen() );
|
||||
|
||||
frp::extend! { network
|
||||
eval_ output.visualisation ( model.view.show_waiting_screen() );
|
||||
eval_ input.set_data ( model.view.disable_waiting_screen() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// === Selecting Visualization ===
|
||||
|
||||
frp::extend! { network
|
||||
mouse_down_target <- scene.mouse.frp_deprecated.down.map(f_!(scene.mouse.target.get()));
|
||||
selected_by_click <= mouse_down_target.map(f!([model] (target){
|
||||
selected_by_click <= mouse_down_target.map2(&output.view_state, f!([model] (target,view_state){
|
||||
let vis = &model.visualization;
|
||||
let activate = || vis.borrow().as_ref().map(|v| v.activate.clone_ref());
|
||||
let deactivate = || vis.borrow().as_ref().map(|v| v.deactivate.clone_ref());
|
||||
@ -634,7 +660,7 @@ impl Container {
|
||||
activate.emit(());
|
||||
return Some(true);
|
||||
}
|
||||
} else if !model.is_fullscreen.get() {
|
||||
} else if !view_state.is_fullscreen() {
|
||||
if let Some(deactivate) = deactivate() {
|
||||
deactivate.emit(());
|
||||
return Some(false);
|
||||
@ -645,34 +671,28 @@ impl Container {
|
||||
selection_after_click <- selected_by_click.map(|sel| if *sel {1.0} else {0.0});
|
||||
selection.target <+ selection_after_click;
|
||||
eval selection.value ((selection) model.view.background.selection.set(*selection));
|
||||
|
||||
selected_by_going_fullscreen <- bool(&frp.disable_fullscreen,&frp.enable_fullscreen);
|
||||
selected <- any(selected_by_click,selected_by_going_fullscreen);
|
||||
|
||||
is_selected_changed <= selected.map2(&frp.output.is_selected, |&new,&old| {
|
||||
(new != old).as_some(new)
|
||||
});
|
||||
frp.source.is_selected <+ is_selected_changed;
|
||||
is_selected <- selected_by_click || output.fullscreen;
|
||||
output.is_selected <+ is_selected.on_change();
|
||||
}
|
||||
|
||||
|
||||
// === Fullscreen View ===
|
||||
|
||||
frp::extend! { network
|
||||
eval_ frp.enable_fullscreen (model.enable_fullscreen());
|
||||
eval_ frp.disable_fullscreen (model.disable_fullscreen());
|
||||
fullscreen_enabled_weight <- frp.enable_fullscreen.constant(1.0);
|
||||
fullscreen_disabled_weight <- frp.disable_fullscreen.constant(0.0);
|
||||
fullscreen_weight <- any(fullscreen_enabled_weight,fullscreen_disabled_weight);
|
||||
frp.source.size <+ frp.set_size;
|
||||
enable_fullscreen <- output.fullscreen.on_true();
|
||||
disable_fullscreen <- output.fullscreen.on_false();
|
||||
|
||||
_eval <- fullscreen_weight.all_with3(&frp.size,scene_shape,
|
||||
f!([model] (weight,viz_size,scene_size) {
|
||||
fullscreen_enabled_weight <- enable_fullscreen.constant(1.0);
|
||||
fullscreen_disabled_weight <- disable_fullscreen.constant(0.0);
|
||||
fullscreen_weight <- any(fullscreen_enabled_weight,fullscreen_disabled_weight);
|
||||
|
||||
_eval <- fullscreen_weight.all_with4(&output.size,scene_shape,&output.view_state,
|
||||
f!([model] (weight,viz_size,scene_size,view_state) {
|
||||
let weight_inv = 1.0 - weight;
|
||||
let scene_size : Vector2 = scene_size.into();
|
||||
let current_size = viz_size * weight_inv + scene_size * *weight;
|
||||
model.set_corner_roundness(weight_inv);
|
||||
model.set_size(current_size);
|
||||
model.update_layout(current_size,*view_state);
|
||||
|
||||
let m1 = model.scene.layers.panel.camera().inversed_view_matrix();
|
||||
let m2 = model.scene.layers.viz.camera().view_matrix();
|
||||
@ -683,6 +703,16 @@ impl Container {
|
||||
let current_pos = pp * weight_inv;
|
||||
model.fullscreen_view.set_position(current_pos);
|
||||
}));
|
||||
|
||||
|
||||
// === Data Update ===
|
||||
|
||||
data <- input.set_data.unwrap();
|
||||
has_data <- input.set_data.is_some();
|
||||
reset_data <- data.sample(&new_vis_definition).gate(&has_data);
|
||||
data_update <- any(&data,&reset_data);
|
||||
eval data_update ((t) model.set_visualization_data(t));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -709,8 +739,8 @@ impl Container {
|
||||
//
|
||||
// This is not optimal the optimal solution to this problem, as it also means that we have
|
||||
// an animation on an invisible component running.
|
||||
frp.set_size.emit(Vector2(DEFAULT_SIZE.0, DEFAULT_SIZE.1));
|
||||
frp.set_visualization.emit(None);
|
||||
self.frp.public.set_size(Vector2(DEFAULT_SIZE.0, DEFAULT_SIZE.1));
|
||||
self.frp.public.set_visualization(None);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -432,6 +432,11 @@ impl ActionBar {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Visualization Chooser component getter.
|
||||
pub fn visualization_chooser(&self) -> &VisualizationChooser {
|
||||
&self.model.visualization_chooser
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for ActionBar {
|
||||
|
@ -9,39 +9,6 @@ use ensogl::display;
|
||||
use ensogl::display::scene::Scene;
|
||||
use ensogl::display::DomSymbol;
|
||||
use ensogl::system::web;
|
||||
use ensogl_hardcoded_theme as theme;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Shapes ===
|
||||
// ==============
|
||||
|
||||
/// Container background shape definition.
|
||||
///
|
||||
/// Provides a backdrop and outline for visualisations. Can indicate the selection status of the
|
||||
/// container.
|
||||
/// TODO : We do not use backgrounds because otherwise they would overlap JS
|
||||
/// visualizations. Instead we added a HTML background to the `View`.
|
||||
/// This should be further investigated while fixing rust visualization displaying. (#526)
|
||||
pub mod background {
|
||||
use super::*;
|
||||
|
||||
ensogl::shape! {
|
||||
alignment = center;
|
||||
(style:Style,selected:f32,radius:f32,roundness:f32) {
|
||||
let width : Var<Pixels> = "input_size.x".into();
|
||||
let height : Var<Pixels> = "input_size.y".into();
|
||||
let radius = 1.px() * &radius;
|
||||
let color_path = theme::graph_editor::visualization::background;
|
||||
let color_bg = style.get_color(color_path);
|
||||
let corner_radius = &radius * &roundness;
|
||||
let background = Rect((&width,&height)).corners_radius(corner_radius);
|
||||
let background = background.fill(color_bg);
|
||||
background.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -54,9 +21,9 @@ pub mod background {
|
||||
#[allow(missing_docs)]
|
||||
pub struct Panel {
|
||||
display_object: display::object::Instance,
|
||||
// Note: We use a HTML background, because a EnsoGL background would be
|
||||
// overlapping the JS visualization.
|
||||
pub background_dom: DomSymbol,
|
||||
// TODO: See TODO above.
|
||||
// background : background::View,
|
||||
}
|
||||
|
||||
impl Panel {
|
||||
@ -76,9 +43,6 @@ impl Panel {
|
||||
|
||||
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");
|
||||
|
@ -16,6 +16,8 @@ pub enum Layer {
|
||||
Default,
|
||||
/// Display the visualisation over the scene.
|
||||
Front,
|
||||
/// Display the visualisation in fullscreen mode.
|
||||
Fullscreen,
|
||||
}
|
||||
|
||||
impl Layer {
|
||||
@ -24,6 +26,7 @@ impl Layer {
|
||||
match self {
|
||||
Layer::Default => scene.dom.layers.back.manage(dom),
|
||||
Layer::Front => scene.dom.layers.front.manage(dom),
|
||||
Layer::Fullscreen => scene.dom.layers.fullscreen_vis.manage(dom),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1596,6 +1596,7 @@ impl GraphEditorModelWithNetwork {
|
||||
let touch = &self.touch_state;
|
||||
let model = &self.model;
|
||||
let NodeCreationContext { pointer_style, output_press, input_press, output } = ctx;
|
||||
let visualisation = node.visualization();
|
||||
|
||||
if let Some(network) = self.network.upgrade_or_warn() {
|
||||
frp::new_bridge_network! { [network, node_network] graph_node_bridge
|
||||
@ -1697,8 +1698,8 @@ impl GraphEditorModelWithNetwork {
|
||||
|
||||
// === Visualizations ===
|
||||
|
||||
visualization_shown <- node.visualization_visible.gate(&node.visualization_visible);
|
||||
visualization_hidden <- node.visualization_visible.gate_not(&node.visualization_visible);
|
||||
visualization_shown <- visualisation.visible.on_true();
|
||||
visualization_hidden <- visualisation.visible.on_false();
|
||||
|
||||
let vis_is_selected = node_model.visualization.frp.is_selected.clone_ref();
|
||||
|
||||
@ -1711,7 +1712,7 @@ impl GraphEditorModelWithNetwork {
|
||||
node_model.visualization.frp.preprocessor.map(move |preprocessor| {
|
||||
(node_id,preprocessor.clone())
|
||||
});
|
||||
output.visualization_preprocessor_changed <+ preprocessor_changed.gate(&node.visualization_visible);
|
||||
output.visualization_preprocessor_changed <+ preprocessor_changed;
|
||||
|
||||
|
||||
metadata <- any(...);
|
||||
@ -1729,7 +1730,7 @@ impl GraphEditorModelWithNetwork {
|
||||
|
||||
init <- source::<()>();
|
||||
enabled_visualization_path <- init.all_with3(
|
||||
&node.visualization_enabled, &node.visualization_path,
|
||||
&visualisation.visible, &visualisation.visualization_path,
|
||||
move |_init, is_enabled, path| (node_id, is_enabled.and_option(path.clone()))
|
||||
);
|
||||
output.enabled_visualization_path <+ enabled_visualization_path;
|
||||
@ -2004,17 +2005,20 @@ impl GraphEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_visualization_fullscreen(&self, node_id: impl Into<NodeId>) {
|
||||
fn enable_visualization_fullscreen(&self, node_id: impl Into<NodeId>) -> bool {
|
||||
let node_id = node_id.into();
|
||||
if let Some(node) = self.nodes.get_cloned_ref(&node_id) {
|
||||
node.model().visualization.frp.enable_fullscreen.emit(());
|
||||
node.frp().enable_fullscreen_visualization();
|
||||
node.visualization().fullscreen.value()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn disable_visualization_fullscreen(&self, node_id: impl Into<NodeId>) {
|
||||
let node_id = node_id.into();
|
||||
if let Some(node) = self.nodes.get_cloned_ref(&node_id) {
|
||||
node.model().visualization.frp.disable_fullscreen.emit(());
|
||||
node.model().visualization.frp.set_view_state(visualization::ViewState::Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3581,7 +3585,7 @@ fn new_graph_editor(app: &Application) -> GraphEditor {
|
||||
viz_tgt_nodes_off <- viz_tgt_nodes.map(f!([model](node_ids) {
|
||||
node_ids.iter().cloned().filter(|node_id| {
|
||||
model.nodes.get_cloned_ref(node_id)
|
||||
.map(|node| !node.visualization_enabled.value())
|
||||
.map(|node| !node.visualization().visible.value())
|
||||
.unwrap_or_default()
|
||||
}).collect_vec()
|
||||
}));
|
||||
@ -3597,7 +3601,9 @@ fn new_graph_editor(app: &Application) -> GraphEditor {
|
||||
eval viz_enable ((id) model.enable_visualization(id));
|
||||
eval viz_disable ((id) model.disable_visualization(id));
|
||||
eval viz_preview_disable ((id) model.disable_visualization(id));
|
||||
eval viz_fullscreen_on ((id) model.enable_visualization_fullscreen(id));
|
||||
fullscreen_vis_was_enabled <- viz_fullscreen_on.map(f!((id)
|
||||
model.enable_visualization_fullscreen(id).then(|| *id))
|
||||
).unwrap();
|
||||
|
||||
viz_fs_to_close <- out.visualization_fullscreen.sample(&inputs.close_fullscreen_visualization);
|
||||
eval viz_fs_to_close ([model](vis) {
|
||||
@ -3607,7 +3613,7 @@ fn new_graph_editor(app: &Application) -> GraphEditor {
|
||||
}
|
||||
});
|
||||
|
||||
out.visualization_fullscreen <+ viz_fullscreen_on.map(|id| Some(*id));
|
||||
out.visualization_fullscreen <+ fullscreen_vis_was_enabled.map(|id| Some(*id));
|
||||
out.visualization_fullscreen <+ inputs.close_fullscreen_visualization.constant(None);
|
||||
|
||||
out.is_fs_visualization_displayed <+ out.visualization_fullscreen.map(Option::is_some);
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Options intended to be common for all developers.
|
||||
|
||||
wasm-size-limit: 15.91 MiB
|
||||
wasm-size-limit: 15.92 MiB
|
||||
|
||||
required-versions:
|
||||
# NB. The Rust version is pinned in rust-toolchain.toml.
|
||||
|
@ -54,7 +54,7 @@ pub trait ColorableShape: ShapeWithDefaultableData {
|
||||
// === Frp ===
|
||||
// ===========
|
||||
|
||||
ensogl_core::define_endpoints! {
|
||||
ensogl_core::define_endpoints_2! {
|
||||
Input {
|
||||
set_visibility (bool),
|
||||
set_color_scheme (ColorScheme),
|
||||
@ -65,7 +65,11 @@ ensogl_core::define_endpoints! {
|
||||
set_read_only (bool),
|
||||
}
|
||||
Output {
|
||||
/// Current state of the button, as visible in the scene,
|
||||
state (bool),
|
||||
/// Last state of the button from a user interaction.
|
||||
/// This ignores state changes based on the `set_state` input.
|
||||
last_user_state (bool),
|
||||
visible (bool),
|
||||
mouse_over (),
|
||||
mouse_out (),
|
||||
@ -232,6 +236,8 @@ impl<Shape: ColorableShape + 'static> ToggleButton<Shape> {
|
||||
fn init_frp(self, app: &Application, tooltip_style: tooltip::Style) -> Self {
|
||||
let network = &self.frp.network;
|
||||
let frp = &self.frp;
|
||||
let input = &self.frp.private.input;
|
||||
let output = &self.frp.private.output;
|
||||
let model = &self.model;
|
||||
let color = color::Animation::new(network);
|
||||
let icon = &model.icon.events_deprecated;
|
||||
@ -249,36 +255,37 @@ impl<Shape: ColorableShape + 'static> ToggleButton<Shape> {
|
||||
|
||||
// === Input Processing ===
|
||||
|
||||
eval frp.set_size ((size) model.icon.set_size(*size););
|
||||
eval input.set_size ((size) model.icon.set_size(*size););
|
||||
|
||||
|
||||
// === State ===
|
||||
|
||||
clicked <- icon.mouse_down_primary.gate_not(&frp.set_read_only);
|
||||
toggle <- any_(frp.toggle, clicked);
|
||||
frp.source.state <+ frp.state.not().sample(&toggle);
|
||||
frp.source.state <+ frp.set_state;
|
||||
clicked <- icon.mouse_down_primary.gate_not(&input.set_read_only);
|
||||
toggle <- any_(input.toggle, clicked);
|
||||
output.state <+ output.state.not().sample(&toggle);
|
||||
output.state <+ input.set_state;
|
||||
output.last_user_state <+ output.state.sample(&clicked);
|
||||
|
||||
|
||||
// === Mouse Interactions ===
|
||||
|
||||
frp.source.mouse_over <+ icon.mouse_over;
|
||||
frp.source.mouse_out <+ icon.mouse_out;
|
||||
frp.source.is_hovered <+ bool(&icon.mouse_out, &icon.mouse_over);
|
||||
frp.source.is_pressed <+ bool(&icon.mouse_up_primary, &icon.mouse_down_primary);
|
||||
output.mouse_over <+ icon.mouse_over;
|
||||
output.mouse_out <+ icon.mouse_out;
|
||||
output.is_hovered <+ bool(&icon.mouse_out, &icon.mouse_over);
|
||||
output.is_pressed <+ bool(&icon.mouse_up_primary, &icon.mouse_down_primary);
|
||||
|
||||
|
||||
// === Color ===
|
||||
|
||||
invisible <- frp.set_visibility.on_false().constant(0.0);
|
||||
invisible <- input.set_visibility.on_false().constant(0.0);
|
||||
color.target_alpha <+ invisible;
|
||||
|
||||
frp.source.visible <+ frp.set_visibility;
|
||||
output.visible <+ input.set_visibility;
|
||||
|
||||
button_state <- all_with4(&frp.visible,&frp.state,&frp.is_hovered,&frp.is_pressed,
|
||||
button_state <- all_with4(&output.visible,&output.state,&output.is_hovered,&output.is_pressed,
|
||||
|a,b,c,d| ButtonState::new(*a,*b,*c,*d));
|
||||
|
||||
color_target <- all_with(&frp.set_color_scheme,&button_state,
|
||||
color_target <- all_with(&input.set_color_scheme,&button_state,
|
||||
|colors,state| colors.query(*state));
|
||||
|
||||
color.target <+ color_target;
|
||||
@ -287,7 +294,7 @@ impl<Shape: ColorableShape + 'static> ToggleButton<Shape> {
|
||||
|
||||
// === Tooltip ===
|
||||
|
||||
tooltip <- frp.is_hovered.map(move |is_hovered| {
|
||||
tooltip <- output.is_hovered.map(move |is_hovered| {
|
||||
if *is_hovered {
|
||||
tooltip_style.clone()
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user