Original commit: 570d3759fb
This commit is contained in:
Michael Mauderer 2020-12-22 22:19:31 +01:00 committed by GitHub
parent 063c028b4c
commit f7ea27b0cd
9 changed files with 230 additions and 23 deletions

View File

@ -136,7 +136,7 @@ commands.build.rust = async function(argv) {
console.log('Checking the resulting WASM size.')
let stats = fss.statSync(paths.dist.wasm.mainOptGz)
let limit = 4.27
let limit = 4.28
let size = Math.round(100 * stats.size / 1024 / 1024) / 100
if (size > limit) {
throw(`Output file size exceeds the limit (${size}MB > ${limit}MB).`)

View File

@ -157,6 +157,11 @@ pub trait ShapeOps : Sized where for<'t> &'t Self : IntoOwned<Owned=Self> {
fn shrink<T:Into<Var<Pixels>>>(&self, value:T) -> Shrink<Self> {
Shrink(self, value.into())
}
/// Repeats the shape with the given tile size.
fn repeat<T:Into<Var<Vector2<Pixels>>>>(&self, value:T) -> Repeat<Self> {
Repeat(self, value)
}
}
macro_rules! define_shape_operator {

View File

@ -124,4 +124,5 @@ define_modifiers! {
PixelSnap pixel_snap (child) ()
Grow grow (child) (value:f32)
Shrink shrink (child) (value:f32)
Repeat repeat (child) (tile_size:Vector2<Pixels>)
}

View File

@ -30,6 +30,10 @@ BoundingBox inverse (BoundingBox a) {
return BoundingBox(0.0,0.0,0.0,0.0);
}
BoundingBox infinite () {
return BoundingBox(0.0,0.0,0.0,0.0);
}
BoundingBox unify (BoundingBox a, BoundingBox b) {
float min_x = min(a.min_x,b.min_x);
float max_x = max(a.max_x,b.max_x);
@ -95,6 +99,8 @@ Sdf grow (Sdf a, float size) {
return Sdf(a.distance - size);
}
// ================
// === BoundSdf ===
// ================
@ -290,6 +296,14 @@ Shape set_color(Shape shape, Srgba t) {
return shape;
}
Shape withInfiniteBounds (Shape s) {
Id id = s.id;
Color color = s.color;
BoundSdf sdf = s.sdf;
sdf.bounds = infinite();
return shape(id, sdf, color);
}
// ===========
@ -322,3 +336,7 @@ vec2 scale (vec2 position, float value) {
vec2 cartesian2polar (vec2 position) {
return vec2(length(position), atan(position.y, position.x));
}
vec2 repeat (vec2 position, vec2 tile_size) {
return mod(position-tile_size/2.0, tile_size);
}

View File

@ -298,6 +298,20 @@ impl Canvas {
let value = value.into();
self.grow(num, s, -value)
}
/// Repeat the shape with the given tile size.
pub fn repeat<T:Into<Var<Vector2<Pixels>>>>
(&mut self, num:usize, s:Shape, tile_size:T) -> Shape {
self.if_not_defined(num, |this| {
let value:Glsl = tile_size.into().glsl();
let repeat = iformat!("position = mod(position,{value});");
let expr = iformat!("return withInfiniteBounds({s.getter()});");
this.add_current_function_code_line(repeat);
let mut shape = this.new_shape_from_expr(num,&expr);
shape.add_ids(&s.ids);
shape
})
}
}

View File

@ -8,21 +8,26 @@ pub mod action_bar;
pub mod expression;
pub mod input;
pub mod output;
#[deny(missing_docs)]
pub mod error;
pub use expression::Expression;
pub use error::Error;
use crate::prelude::*;
use enso_frp as frp;
use enso_frp;
use ensogl::Animation;
use ensogl::application::Application;
use ensogl::data::color;
use ensogl::display::shape::*;
use ensogl::display::traits::*;
use ensogl::display;
use ensogl::Animation;
use ensogl::gui::component;
use ensogl::gui::component::ShapeView;
use ensogl::gui::text;
use ensogl_text::Text;
use ensogl_text::style::Size as TextSize;
use ensogl_theme;
use crate::Type;
@ -44,6 +49,14 @@ pub const PADDING : f32 = 40.0;
pub const RADIUS : f32 = 14.0;
pub const SHADOW_SIZE : f32 = 10.0;
const INFINITE : f32 = 99999.0;
const ERROR_PATTERN_STRIPE_WIDTH : f32 = 10.0;
const ERROR_PATTERN_STRIPE_ANGLE : f32 = 45.0;
const ERROR_PATTERN_REPEAT_TILE_SIZE : (f32,f32) = (15.0,15.0);
const ERROR_BORDER_WIDTH : f32 = 10.0;
const TEXT_SIZE : f32 = 12.0;
// =============
@ -55,7 +68,7 @@ pub mod shape {
use super::*;
ensogl::define_shape_system! {
(style:Style, selection:f32, bg_color:Vector4 ) {
(style:Style, selection:f32, bg_color:Vector4) {
use ensogl_theme::graph_editor::node as node_theme;
let bg_color = Var::<color::Rgba>::from(bg_color);
@ -66,7 +79,7 @@ pub mod shape {
let width = width - PADDING.px() * 2.0;
let height = height - PADDING.px() * 2.0;
let radius = RADIUS.px();
let shape = Rect((&width,&height)).corners_radius(radius);
let shape = Rect((&width,&height)).corners_radius(&radius);
let shape = shape.fill(bg_color);
@ -110,6 +123,18 @@ pub mod shape {
let select = select.fill(color::Rgba::from(sel_color));
// === Error Pattern Alternative ===
// TODO: Remove once the error indicator design is finalised.
// let repeat = Var::<Vector2<Pixels>>::from((10.px(), 10.px()));
// let error_width = Var::<Pixels>::from(5.px());
//
// let stripe_red = Rect((error_width, 99999.px()));
// let pattern = stripe_red.repeat(repeat).rotate(45.0.radians());
// let mask = Rect((&width,&height)).corners_radius(&radius);
// let pattern1 = mask.intersection(pattern).fill(color::Rgba::red());
// let out = select + shadow + shape + pattern1;
// === Final Shape ===
let out = select + shadow + shape;
@ -137,7 +162,43 @@ pub mod drag_area {
}
}
// =======================
// === Error Indicator ===
// =======================
pub mod error_shape {
use super::*;
ensogl::define_shape_system! {
(style:Style,color_rgba:Vector4<f32>) {
let width = Var::<Pixels>::from("input_size.x");
let height = Var::<Pixels>::from("input_size.y");
let zoom = Var::<f32>::from("1.0/zoom()");
let width = width - PADDING.px() * 2.0;
let height = height - PADDING.px() * 2.0;
let radius = RADIUS.px();
let (repeat_x,repeat_y) = ERROR_PATTERN_REPEAT_TILE_SIZE;
let repeat = Var::<Vector2<Pixels>>::from((repeat_x.px(),repeat_y.px()));
let error_width = Var::<Pixels>::from(zoom * ERROR_PATTERN_STRIPE_WIDTH);
let stripe_red = Rect((error_width,INFINITE.px()));
let stripe_angle_rad = ERROR_PATTERN_STRIPE_ANGLE.radians();
let pattern = stripe_red.repeat(repeat).rotate(stripe_angle_rad);
let pattern_width = ERROR_BORDER_WIDTH.px();
let mask = Rect((&width,&height)).corners_radius(&radius);
let mask = mask.grow(pattern_width);
let pattern = mask.intersection(pattern).fill(color_rgba);
pattern.into()
}
}
}
// ==============
// === Crumbs ===
// ==============
#[derive(Clone,Copy,Debug)]
pub enum Endpoint { Input, Output }
@ -180,6 +241,7 @@ ensogl::define_endpoints! {
set_disabled (bool),
set_input_connected (span_tree::Crumbs,Option<Type>,bool),
set_expression (Expression),
set_error (Option<Error>),
/// Set the expression USAGE type. This is not the definition type, which can be set with
/// `set_expression` instead. In case the usage type is set to None, ports still may be
/// colored if the definition type was present.
@ -273,15 +335,20 @@ impl Deref for Node {
#[derive(Clone,CloneRef,Debug)]
#[allow(missing_docs)]
pub struct NodeModel {
pub app : Application,
pub display_object : display::object::Instance,
pub logger : Logger,
pub main_area : component::ShapeView<shape::Shape>,
pub drag_area : component::ShapeView<drag_area::Shape>,
pub input : input::Area,
pub output : output::Area,
pub visualization : visualization::Container,
pub action_bar : action_bar::ActionBar,
pub app : Application,
pub display_object : display::object::Instance,
pub logger : Logger,
pub main_area : ShapeView<shape::Shape>,
pub drag_area : ShapeView<drag_area::Shape>,
pub error_indicator : ShapeView<error_shape::Shape>,
// TODO: This extra text field should not be required after #1026 has been finished.
// Instead we should get the error content as normal node output that is visible in the
// visualisation. Alternatively it might be extended to use a preview of the new information.
pub error_text : text::Area,
pub input : input::Area,
pub output : output::Area,
pub visualization : visualization::Container,
pub action_bar : action_bar::ActionBar,
}
impl NodeModel {
@ -292,10 +359,14 @@ impl NodeModel {
edge::depth_sort_hack_1(scene);
output::area::depth_sort_hack(&scene);
let main_logger = Logger::sub(&logger,"main_area");
let drag_logger = Logger::sub(&logger,"drag_area");
let main_area = component::ShapeView::<shape::Shape>::new(&main_logger,scene);
let drag_area = component::ShapeView::<drag_area::Shape>::new(&drag_logger,scene);
let main_logger = Logger::sub(&logger,"main_area");
let drag_logger = Logger::sub(&logger,"drag_area");
let error_indicator_logger = Logger::sub(&logger,"error_indicator");
let error_indicator = ShapeView::<error_shape::Shape>::new(&error_indicator_logger,scene);
let main_area = ShapeView::<shape::Shape>::new(&main_logger,scene);
let drag_area = ShapeView::<drag_area::Shape>::new(&drag_logger,scene);
let error_text = app.new_view::<text::Area>();
edge::depth_sort_hack_2(scene);
input::area::depth_sort_hack(scene); // FIXME hack for sorting
@ -303,6 +374,12 @@ impl NodeModel {
let display_object = display::object::Instance::new(&logger);
display_object.add_child(&drag_area);
display_object.add_child(&main_area);
display_object.add_child(&error_indicator);
display_object.add_child(&error_text);
error_text.set_default_color.emit(color::Rgba::red());
error_text.set_default_text_size(TextSize::from(TEXT_SIZE));
// Disable shadows to allow interaction with the output port.
let shape_system = scene.shapes.shape_system(PhantomData::<shape::Shape>);
@ -325,8 +402,8 @@ impl NodeModel {
display_object.add_child(&output);
let app = app.clone_ref();
Self {app,display_object,logger,main_area,drag_area,output,input,visualization,action_bar}
.init()
Self {app,display_object,logger,main_area,drag_area,output,input,visualization,action_bar,
error_indicator,error_text}.init()
}
pub fn get_crumbs_by_id(&self, id:ast::Id) -> Option<Crumbs> {
@ -366,9 +443,14 @@ impl NodeModel {
let padded_size = size + Vector2(PADDING,PADDING) * 2.0;
self.main_area.shape.sprite.size.set(padded_size);
self.drag_area.shape.sprite.size.set(padded_size);
self.error_indicator.shape.sprite.size.set(padded_size);
self.main_area.mod_position(|t| t.x = width/2.0);
self.drag_area.mod_position(|t| t.x = width/2.0);
self.error_indicator.set_position_x(width/2.0);
self.error_text.set_position_y(height + TEXT_SIZE);
self.error_text.set_position_x(CORNER_RADIUS);
let action_bar_width = ACTION_BAR_WIDTH;
self.action_bar.mod_position(|t| {
t.x = width + CORNER_RADIUS + action_bar_width / 2.0;
@ -380,6 +462,12 @@ impl NodeModel {
pub fn visualization(&self) -> &visualization::Container {
&self.visualization
}
fn set_error(&self, error:Option<&Error>) {
let message = error.map(|t| t.message.clone()).unwrap_or_default();
self.error_text.set_content.emit(message);
}
}
impl Node {
@ -390,9 +478,10 @@ impl Node {
let model = Rc::new(NodeModel::new(app,registry));
let selection = Animation::<f32>::new(network);
let bg_color_anim = color::Animation::new(network);
let style = StyleWatch::new(&app.display.scene().style_sheet);
let action_bar = &model.action_bar.frp;
let bg_color_anim = color::Animation::new(network);
let error_color_anim = color::Animation::new(network);
let style = StyleWatch::new(&app.display.scene().style_sheet);
let action_bar = &model.action_bar.frp;
frp::extend! { network
@ -450,6 +539,25 @@ impl Node {
eval out.hover ((t) action_bar.set_visibility(t));
// === Set Node Error ===
error_color_anim.target <+ frp.set_error.map(f!((error) {
model.set_error(error.as_ref());
match error.as_ref() {
Some(error) => {
let error_data:visualization::Data = error.clone().into();
model.visualization.frp.set_data.emit(error_data);
color::Lcha::from(color::Rgba::red())
},
None => color::Lcha::transparent(),
}
}));
eval error_color_anim.value((value)
model.error_indicator.shape.color_rgba.set(color::Rgba::from(value).into());
);
// === Color Handling ===
bg_color <- frp.set_disabled.map(f!([model,style](disabled) {
@ -464,6 +572,7 @@ impl Node {
);
}
frp.set_error.emit(None);
frp.set_disabled.emit(false);
Self {frp,model}
}

View File

@ -0,0 +1,35 @@
//! Contains a struct definition for error information on nodes.
use crate::prelude::*;
use crate::component::node::visualization;
// =============
// === Error ===
// =============
/// Error information to be displayed on a node.
///
/// Note:
/// Tis is a dummy implementation that can and should be extended once we have the proper
/// error information from the language server.
/// See [issue #1026](https://github.com/enso-org/ide/issues/1026) for more information.
#[derive(Clone,Debug)]
pub struct Error {
/// The error message to show on the node.
pub message:String,
}
impl From<Error> for visualization::Data {
fn from(error:Error) -> visualization::Data {
let content = serde_json::Value::String(error.message).into();
visualization::Data::Json {content}
}
}
impl From<String> for Error {
fn from(message:String) -> Error {
Self{message}
}
}

View File

@ -362,6 +362,8 @@ ensogl::define_endpoints! {
stop_editing(),
/// Remove all nodes from the graph.
collapse_selected_nodes(),
/// Indicate whether this node had an error or not.
set_node_error_status(NodeId,Option<node::error::Error>),
// === Visualization ===
@ -2424,11 +2426,26 @@ fn new_graph_editor(app:&Application) -> GraphEditor {
set_node_expression_string <- inputs.set_node_expression.map(|(id,expr)| (*id,expr.code.clone()));
out.source.node_expression_set <+ set_node_expression_string;
}
// === Set Node Error ===
frp::extend! { network
eval inputs.set_node_error_status([model]((node_id, error)) {
if let Some(node) = model.nodes.get_cloned_ref(node_id) {
node.set_error.emit(error)
}
});
}
// ==================
// === Move Nodes ===
// ==================
frp::extend! { network
mouse_pos <- mouse.position.map(|p| Vector2(p.x,p.y));

View File

@ -105,15 +105,23 @@ fn init(app:&Application) {
let node1_id = graph_editor.add_node();
let node2_id = graph_editor.add_node();
let node3_id = graph_editor.add_node();
graph_editor.frp.set_node_position.emit((node1_id,Vector2(-150.0,50.0)));
graph_editor.frp.set_node_position.emit((node2_id,Vector2(50.0,50.0)));
graph_editor.frp.set_node_position.emit((node3_id,Vector2(150.0,250.0)));
let expression_1 = expression_mock();
graph_editor.frp.set_node_expression.emit((node1_id,expression_1.clone()));
let expression_2 = expression_mock3();
graph_editor.frp.set_node_expression.emit((node2_id,expression_2.clone()));
let expression_3 = expression_mock2();
graph_editor.frp.set_node_expression.emit((node3_id,expression_3));
let error = "Runtime Error".to_string().into();
graph_editor.frp.set_node_error_status.emit((node3_id,Some(error)));
// === Connections ===