Cleaning the code, adding batched node position notifications to graph editor. (https://github.com/enso-org/ide/pull/508)

Original commit: 1b05c19aa8
This commit is contained in:
Wojciech Daniło 2020-06-01 21:01:43 +02:00 committed by GitHub
parent 48eeef9873
commit 4a07474ea2
4 changed files with 86 additions and 59 deletions

View File

@ -108,14 +108,14 @@ where Color<D> : HasComponents<ComponentsRepr=(f32,f32,f32,f32)> {
}
impl<D> Into<Vector3<f32>> for &Color<D>
where Color<D> : Copy + HasComponents<ComponentsRepr=(f32,f32,f32)> {
where Color<D> : Copy + HasComponents<ComponentsRepr=(f32,f32,f32)> {
fn into(self) -> Vector3<f32> {
Into::<Vector3<f32>>::into((*self).into_components())
}
}
impl<D> Into<Vector4<f32>> for &Color<D>
where Color<D> : Copy + HasComponents<ComponentsRepr=(f32,f32,f32,f32)> {
where Color<D> : Copy + HasComponents<ComponentsRepr=(f32,f32,f32,f32)> {
fn into(self) -> Vector4<f32> {
Into::<Vector4<f32>>::into((*self).into_components())
}

View File

@ -432,7 +432,7 @@ macro_rules! define_vector {
/// Smart constructor.
#[allow(non_snake_case)]
pub fn $name<T>($($field:T),*) -> $name<T> {
pub const fn $name<T>($($field:T),*) -> $name<T> {
$name {$($field),*}
}

View File

@ -15,6 +15,17 @@ use ensogl::gui::component;
// =================
// === Constants ===
// =================
const DEFAULT_SIZE : V2 = V2(16.0,16.0);
const DEFAULT_RADIUS : f32 = 8.0;
const DEFAULT_COLOR_LAB : V3 = V3(1.0,0.0,0.0);
const DEFAULT_COLOR_ALPHA : f32 = 0.2;
// ==================
// === StyleValue ===
// ==================
@ -58,7 +69,7 @@ impl<T> StyleValue<T> {
// === Style ===
// =============
macro_rules! define_style {( $($field:ident : $field_type:ty),* $(,)? ) => {
macro_rules! define_style {( $( $(#$meta:tt)* $field:ident : $field_type:ty),* $(,)? ) => {
/// Set of cursor style parameters. You can construct this object in FRP network, merge it using
/// its `Semigroup` instance, and finally pass to the cursor to apply the style. Please note
/// that cursor does not implement any complex style management (like pushing or popping a style
@ -66,7 +77,7 @@ macro_rules! define_style {( $($field:ident : $field_type:ty),* $(,)? ) => {
/// it in FRP.
#[derive(Debug,Clone,Default)]
pub struct Style {
$($field : $field_type),*
$($(#$meta)? $field : Option<$field_type>),*
}
impl PartialSemigroup<&Style> for Style {
@ -84,12 +95,15 @@ macro_rules! define_style {( $($field:ident : $field_type:ty),* $(,)? ) => {
};}
define_style! {
host : Option<display::object::Instance>,
size : Option<StyleValue<Vector2<f32>>>,
offset : Option<StyleValue<Vector2<f32>>>,
color : Option<StyleValue<color::Lcha>>,
radius : Option<StyleValue<f32>>,
press : Option<StyleValue<f32>>,
/// Host defines an object which the cursor position is bound to. It is used to implement
/// label selection. After setting the host to the label, cursor will not follow mouse anymore,
/// it will inherit its position from the label instead.
host : display::object::Instance,
size : StyleValue<Vector2<f32>>,
offset : StyleValue<Vector2<f32>>,
color : StyleValue<color::Lcha>,
radius : StyleValue<f32>,
press : StyleValue<f32>,
}
@ -117,8 +131,9 @@ impl Style {
}
pub fn new_box_selection(size:Vector2<f32>) -> Self {
let offset = Some(StyleValue::new_no_animation(-size / 2.0));
let size = Some(StyleValue::new_no_animation(size.abs() + Vector2::new(16.0,16.0)));
let def_size = Vector2::new(DEFAULT_SIZE.x,DEFAULT_SIZE.y);
let offset = Some(StyleValue::new_no_animation(-size / 2.0));
let size = Some(StyleValue::new_no_animation(size.abs() + def_size));
Self {size,offset,..default()}
}
@ -166,12 +181,13 @@ pub mod shape {
) {
let width : Var<Distance<Pixels>> = "input_size.x".into();
let height : Var<Distance<Pixels>> = "input_size.y".into();
let press_diff = 2.px() * &press;
let radius = 1.px() * radius - &press_diff;
let selection_width = 1.px() * &selection_size.x();
let selection_height = 1.px() * &selection_size.y();
let width = (&width - &press_diff * 2.0) + selection_width.abs();
let height = (&height - &press_diff * 2.0) + selection_height.abs();
let press_side_shrink = 2.px();
let press_diff = press_side_shrink * &press;
let radius = 1.px() * radius - &press_diff;
let selection_width = 1.px() * &selection_size.x();
let selection_height = 1.px() * &selection_size.y();
let width = (&width - &press_diff * 2.0) + selection_width.abs();
let height = (&height - &press_diff * 2.0) + selection_height.abs();
let cursor = Rect((width,height))
.corners_radius(radius)
.fill("srgba(input_color)");
@ -243,7 +259,7 @@ impl CursorModel {
let logger = Logger::new("cursor");
let frp = FrpInputs::new(network);
let view = component::ShapeView::<shape::Shape>::new(&logger,&scene);
let style = Rc::new(RefCell::new(Style::default()));
let style = default();
let shape_system = scene.shapes.shape_system(PhantomData::<shape::Shape>);
shape_system.shape_system.set_pointer_events(false);
@ -279,16 +295,16 @@ impl Cursor {
// === Animations ===
//
// The following animators are used for smooth cursor transitions. There are two components
// The following animators are used for smooth cursor transitions. There are two of them
// with a non-obvious behavior, namely the `host_follow_weight` and `host_attached_weight`.
// The mouse position can be in three stages:
// The mouse position is driven by three factors:
//
// - Real-time cursor mode.
// Cursor follows the system mouse position.
//
// - Host-follow mode.
// Cursor follows the host using dynamic simulator. The `host_follow_weight` variable is
// a weight between real-time mode and this one.
// Cursor follows the host using dynamic inertia simulator. The `host_follow_weight`
// variable is a weight between real-time mode and this one.
//
// - Host-attached mode.
// Cursor follows the host without any delay. The `host_attached_weight` variable is a
@ -307,17 +323,11 @@ impl Cursor {
let host_follow_weight = Animation :: <f32> :: new(&network);
let host_attached_weight = Tween :: new(&network);
let default_size = V2(16.0,16.0);
let default_radius = 8.0;
let default_lab = V3(1.0,0.0,0.0);
let default_alpha = 0.2;
host_attached_weight.set_duration(300.0);
color_lab.set_target_value(default_lab);
color_alpha.set_target_value(default_alpha);
radius.set_target_value(default_radius);
size.set_target_value(default_size);
color_lab.set_target_value(DEFAULT_COLOR_LAB);
color_alpha.set_target_value(DEFAULT_COLOR_ALPHA);
radius.set_target_value(DEFAULT_RADIUS);
size.set_target_value(DEFAULT_SIZE);
frp::extend! { network
eval press.value ((v) model.view.shape.press.set(*v));
@ -344,8 +354,8 @@ impl Cursor {
match &new_style.color {
None => {
color_lab.set_target_value(default_lab);
color_alpha.set_target_value(default_alpha);
color_lab.set_target_value(DEFAULT_COLOR_LAB);
color_alpha.set_target_value(DEFAULT_COLOR_ALPHA);
}
Some(new_color) => {
let lab = color::Laba::from(new_color.value);
@ -359,7 +369,7 @@ impl Cursor {
}
match &new_style.size {
None => size.set_target_value(default_size),
None => size.set_target_value(DEFAULT_SIZE),
Some(new_size) => {
size.set_target_value(V2::new(new_size.value.x,new_size.value.y));
if !new_size.animate { size.skip() }
@ -375,7 +385,7 @@ impl Cursor {
}
match &new_style.radius {
None => radius.set_target_value(default_radius),
None => radius.set_target_value(DEFAULT_RADIUS),
Some(new_radius) => {
radius.set_target_value(new_radius.value);
if !new_radius.animate { radius.skip() }

View File

@ -211,9 +211,6 @@ impl<K,V,S> SharedHashMap<K,V,S> {
#[derive(Debug,Clone,CloneRef)]
pub struct Frp {
pub inputs : FrpInputs,
@ -478,19 +475,20 @@ macro_rules! generate_frp_outputs {
generate_frp_outputs! {
node_added : NodeId,
node_removed : NodeId,
node_selected : NodeId,
node_deselected : NodeId,
node_position_set : (NodeId,Position),
node_expression_set : (NodeId,node::Expression),
node_added : NodeId,
node_removed : NodeId,
node_selected : NodeId,
node_deselected : NodeId,
node_position_set : (NodeId,Position),
node_position_set_batched : (NodeId,Position),
node_expression_set : (NodeId,node::Expression),
edge_added : EdgeId,
edge_removed : EdgeId,
edge_source_set : (EdgeId,EdgeTarget),
edge_target_set : (EdgeId,EdgeTarget),
edge_source_unset : EdgeId,
edge_target_unset : EdgeId,
edge_added : EdgeId,
edge_removed : EdgeId,
edge_source_set : (EdgeId,EdgeTarget),
edge_target_set : (EdgeId,EdgeTarget),
edge_source_unset : EdgeId,
edge_target_unset : EdgeId,
some_edge_targets_detached : (),
all_edge_targets_attached : (),
@ -1042,6 +1040,14 @@ impl GraphEditorModel {
}
}
pub fn node_position(&self, node_id:impl Into<NodeId>) -> Position {
let node_id = node_id.into();
self.nodes.get_cloned_ref(&node_id).map(|node| {
let v_pos = node.position();
frp::Position::new(v_pos.x, v_pos.y)
}).unwrap_or_default()
}
pub fn node_pos_mod
(&self, node_id:impl Into<NodeId>, pos_diff:impl Into<Position>) -> (NodeId,Position) {
let node_id = node_id.into();
@ -1397,7 +1403,8 @@ fn new_graph_editor(world:&World) -> GraphEditor {
outputs.node_added <+ new_node;
node_with_position <- add_node_at_cursor.map3(&new_node,&mouse.position,|_,id,pos| (*id,*pos));
outputs.node_position_set <+ node_with_position;
outputs.node_position_set <+ node_with_position;
outputs.node_position_set_batched <+ node_with_position;
cursor_style <- all
[ cursor_selection
@ -1481,16 +1488,26 @@ fn new_graph_editor(world:&World) -> GraphEditor {
node_drag <- mouse.translation.gate(&touch.nodes.is_down);
was_selected <- touch.nodes.down.map(f!((id) model.nodes.selected.contains(id)));
tx_sel_nodes <- any (node_drag, inputs.translate_selected_nodes);
non_selected_drag <- tx_sel_nodes.map2(&touch.nodes.down,|_,id|*id).gate_not(&was_selected);
selected_drag <= tx_sel_nodes.map(f_!(model.nodes.selected.keys())).gate(&was_selected);
non_selected_drag <- tx_sel_nodes.map2(&touch.nodes.down,|_,id|vec![*id]).gate_not(&was_selected);
selected_drag <- tx_sel_nodes.map(f_!(model.nodes.selected.keys())).gate(&was_selected);
nodes_to_drag <- any (non_selected_drag, selected_drag);
nodes_new_pos <- nodes_to_drag.map2(&tx_sel_nodes,f!((id,tx) model.node_pos_mod(id,tx)));
outputs.node_position_set <+ nodes_new_pos;
node_to_drag <= nodes_to_drag;
node_new_pos <- node_to_drag.map2(&tx_sel_nodes,f!((id,tx) model.node_pos_mod(id,tx)));
outputs.node_position_set <+ node_new_pos;
was_drag_false <- touch.nodes.down.constant(false);
was_drag_true <- node_drag.constant(true);
was_drag <- any (was_drag_false,was_drag_true);
drag_finish <- touch.nodes.up.gate(&was_drag);
dragged_node <= nodes_to_drag.sample(&drag_finish);
dragged_node_pos <- dragged_node.map(f!([model] (id) (*id,model.node_position(id))));
outputs.node_position_set_batched <+ dragged_node_pos;
// === Set Node Position ===
outputs.node_position_set <+ inputs.set_node_position;
outputs.node_position_set <+ inputs.set_node_position;
outputs.node_position_set_batched <+ inputs.set_node_position;
eval outputs.node_position_set (((id,pos)) model.set_node_position(id,pos));