From 0b4bf473d18a1194078dff76f5f6d056027abee7 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Fri, 12 Feb 2021 12:38:06 +0000 Subject: [PATCH] Add API to mark node VCS status. (https://github.com/enso-org/ide/pull/1160) Original commit: https://github.com/enso-org/ide/commit/3c8b4b37adadcd25432e4b3ab5b87dc6cc83f78f --- gui/CHANGELOG.md | 2 + gui/src/rust/ensogl/lib/theme/src/lib.rs | 5 + .../view/graph-editor/src/component/node.rs | 17 +- .../graph-editor/src/component/node/vcs.rs | 200 ++++++++++++++++++ gui/src/rust/ide/view/graph-editor/src/lib.rs | 15 ++ .../ide/view/src/debug_scenes/interface.rs | 37 ++++ 6 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 gui/src/rust/ide/view/graph-editor/src/component/node/vcs.rs diff --git a/gui/CHANGELOG.md b/gui/CHANGELOG.md index 0ba013f1803..8041a8205b6 100644 --- a/gui/CHANGELOG.md +++ b/gui/CHANGELOG.md @@ -36,12 +36,14 @@ read the notes of the `Enso 2.0.0-alpha.1` release. - [Added the ability to reposition visualisations.][1096] There is now an icon in the visualization action bar that allows dragging the visualization. Once the visualization has been moved, there appears another icon that will reset the position to the original position. +- [There is now an API to show VCS status for node][1160]. - [A shortcut for reloading visualization files.][1190] The visible visualizations must be switched to another and switched back to see the effect. [1096]: https://github.com/enso-org/ide/pull/1172 [1098]: https://github.com/enso-org/ide/pull/1098 [1181]: https://github.com/enso-org/ide/pull/1181 +[1160]: https://github.com/enso-org/ide/pull/1160 [1190]: https://github.com/enso-org/ide/pull/1190
diff --git a/gui/src/rust/ensogl/lib/theme/src/lib.rs b/gui/src/rust/ensogl/lib/theme/src/lib.rs index ddb9dacac8a..78e48e6c27d 100644 --- a/gui/src/rust/ensogl/lib/theme/src/lib.rs +++ b/gui/src/rust/ensogl/lib/theme/src/lib.rs @@ -199,6 +199,11 @@ define_themes! { [light:0, dark:1] hovered = Lcha(0.0,0.0,0.0,0.45) , Lcha(1.0,0.0,0.0,0.7); } } + vcs { + unchanged = Lcha::transparent(), Lcha::transparent(); + added = Lcha::green(0.8,1.0), Lcha::green(0.8,1.0); + edited = Lcha::yellow(0.9,1.0), Lcha::yellow(0.9,1.0); + } } visualization { background = Lcha(0.98,0.014,0.18,1.0) , Lcha(0.2,0.014,0.18,1.0); diff --git a/gui/src/rust/ide/view/graph-editor/src/component/node.rs b/gui/src/rust/ide/view/graph-editor/src/component/node.rs index e159ea47265..f518f8c995f 100644 --- a/gui/src/rust/ide/view/graph-editor/src/component/node.rs +++ b/gui/src/rust/ide/view/graph-editor/src/component/node.rs @@ -10,6 +10,8 @@ pub mod input; pub mod output; #[deny(missing_docs)] pub mod error; +#[deny(missing_docs)] +pub mod vcs; pub use expression::Expression; pub use error::Error; @@ -246,8 +248,9 @@ ensogl::define_endpoints! { /// 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. - set_expression_usage_type (Crumbs,Option), + set_expression_usage_type (Crumbs,Option), set_output_expression_visibility (bool), + set_vcs_status (Option), } Output { /// Press event. Emitted when user clicks on non-active part of the node, like its @@ -350,6 +353,7 @@ pub struct NodeModel { pub output : output::Area, pub visualization : visualization::Container, pub action_bar : action_bar::ActionBar, + pub vcs_indicator : vcs::StatusIndicator, } impl NodeModel { @@ -381,12 +385,14 @@ impl NodeModel { let main_area = shape::View::new(&main_logger); let drag_area = drag_area::View::new(&drag_logger); let error_text = app.new_view::(); + let vcs_indicator = vcs::StatusIndicator::new(app); 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); + display_object.add_child(&vcs_indicator); error_text.set_default_color.emit(color::Rgba::red()); error_text.set_default_text_size(TextSize::from(TEXT_SIZE)); @@ -411,7 +417,7 @@ impl NodeModel { let app = app.clone_ref(); Self {app,display_object,logger,main_area,drag_area,output,input,visualization,action_bar, - error_indicator,error_text}.init() + error_indicator,error_text,vcs_indicator}.init() } pub fn get_crumbs_by_id(&self, id:ast::Id) -> Option { @@ -452,10 +458,13 @@ impl NodeModel { self.main_area.size.set(padded_size); self.drag_area.size.set(padded_size); self.error_indicator.size.set(padded_size); + self.vcs_indicator.set_size(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.vcs_indicator.set_position_x(width/2.0); + self.error_text.set_position_y(height + TEXT_SIZE); self.error_text.set_position_x(CORNER_RADIUS); @@ -584,6 +593,10 @@ impl Node { eval bg_color_anim.value ((c) model.main_area.bg_color.set(color::Rgba::from(c).into()) ); + + + // === VCS Handling === + model.vcs_indicator.frp.set_status <+ frp.set_vcs_status; } frp.set_error.emit(None); diff --git a/gui/src/rust/ide/view/graph-editor/src/component/node/vcs.rs b/gui/src/rust/ide/view/graph-editor/src/component/node/vcs.rs new file mode 100644 index 00000000000..8388c5daedf --- /dev/null +++ b/gui/src/rust/ide/view/graph-editor/src/component/node/vcs.rs @@ -0,0 +1,200 @@ +//! Functionality related to visualising the version control system status of a node. + +use crate::component::node as node; +use crate::prelude::*; + +use enso_frp as frp; +use ensogl::application::Application; +use ensogl::data::color; +use ensogl::display::shape::*; +use ensogl::display; + + + +// ============== +// === Status === +// ============== + +/// The version control system status of a node. +#[derive(Debug,Copy,Clone)] +#[allow(missing_docs)] +pub enum Status { + Unchanged, + Added, + Edited, +} + +impl Status { + fn get_highlight_color_from_style(self, style:&StyleWatch) -> color::Lcha { + match self { + Status::Unchanged => style.get_color(ensogl_theme::graph_editor::node::vcs::unchanged), + Status::Added => style.get_color(ensogl_theme::graph_editor::node::vcs::added), + Status::Edited => style.get_color(ensogl_theme::graph_editor::node::vcs::edited), + } + } +} + +impl Default for Status { + fn default() -> Self { + Status::Unchanged + } +} + + + +// ======================= +// === Indicator Shape === +// ======================= + +/// Shape used in the status indicator. Appears as a colored border surrounding the node. +mod status_indicator_shape { + use super::*; + + const INDICATOR_WIDTH_OUTER : f32 = 15.0; + const INDICATOR_WIDTH_INNER : f32 = 10.0; + + ensogl::define_shape_system! { + (style:Style,color_rgba:Vector4) { + let width = Var::::from("input_size.x"); + let height = Var::::from("input_size.y"); + let width = width - node::PADDING.px() * 2.0; + let height = height - node::PADDING.px() * 2.0; + let radius = node::RADIUS.px(); + + let base = Rect((&width,&height)).corners_radius(&radius); + let outer = base.grow(INDICATOR_WIDTH_OUTER.px()); + let inner = base.grow(INDICATOR_WIDTH_INNER.px()); + + (outer-inner).fill(color_rgba).into() + } + } +} + + + +// ============================== +// === Status Indicator Model === +// ============================== + +/// Internal data of `StatusIndicator`. +#[derive(Clone,CloneRef,Debug)] +struct StatusIndicatorModel { + shape : status_indicator_shape::View, + root : display::object::Instance, +} + +impl StatusIndicatorModel { + fn new(logger: &Logger) -> Self { + let shape = status_indicator_shape::View::new(logger); + let root = display::object::Instance::new(&logger); + root.add_child(&shape); + StatusIndicatorModel{shape, root} + } + + fn hide(&self) { + self.shape.unset_parent(); + } + + fn show(&self) { + self.root.add_child(&self.shape); + } + + fn set_visibility(&self, visibility:bool) { + if visibility { + self.show() + } else { + self.hide() + } + } +} + +impl display::Object for StatusIndicatorModel { + fn display_object(&self) -> &display::object::Instance { + &self.root + } +} + + + +// ======================= +// === StatusIndicator === +// ======================= + +ensogl::define_endpoints! { + Input { + set_status (Option), + set_size (Vector2), + set_visibility (bool), + } + Output { + status (Option), + } +} + +#[derive(Clone,CloneRef,Debug)] +#[allow(missing_docs)] +pub struct StatusIndicator { + model : Rc, + pub frp : Frp, +} + +impl StatusIndicator { + /// Constructor. + pub fn new(app:&Application) -> Self { + let logger = Logger::new("status_indicator"); + let model = Rc::new(StatusIndicatorModel::new(&logger)); + let frp = Frp::new(); + Self {frp,model}.init_frp(app) + } + + fn init_frp(self, app:&Application) -> Self { + let frp = &self.frp; + let model = &self.model; + let network = &frp.network; + let indicator_color = color::Animation::new(network); + + // FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape + // system (#795) + let styles = StyleWatch::new(&app.display.scene().style_sheet); + + frp::extend! { network + frp.source.status <+ frp.input.set_status; + + status_color <- frp.set_status.unwrap().map(f!([styles](status) + status.get_highlight_color_from_style(&styles) + )); + indicator_color.target <+ status_color; + + eval indicator_color.value ((c) + model.shape.color_rgba.set(color::Rgba::from(c).into()) + ); + + eval frp.input.set_size ((size) + model.shape.size.set(*size); + ); + + has_status <- frp.status.map(|status| status.is_some()); + visible <- and(&frp.input.set_visibility,&has_status); + eval visible ([model](visible) model.set_visibility(*visible)); + }; + + frp.set_status.emit(None); + frp.set_visibility.emit(true); + self + } + + +} + +impl display::Object for StatusIndicator { + fn display_object(&self) -> &display::object::Instance { + &self.model.display_object() + } +} + +impl Deref for StatusIndicator { + type Target = Frp; + fn deref(&self) -> &Self::Target { + &self.frp + } +} diff --git a/gui/src/rust/ide/view/graph-editor/src/lib.rs b/gui/src/rust/ide/view/graph-editor/src/lib.rs index a427952a9f5..f7db9259507 100644 --- a/gui/src/rust/ide/view/graph-editor/src/lib.rs +++ b/gui/src/rust/ide/view/graph-editor/src/lib.rs @@ -408,6 +408,11 @@ ensogl::define_endpoints! { debug_set_test_visualization_data_for_selected_node(), + // === VCS Status === + + set_node_vcs_status ((NodeId,Option)), + + set_detached_edge_targets (EdgeEndpoint), set_detached_edge_sources (EdgeEndpoint), set_edge_source ((EdgeId,EdgeEndpoint)), @@ -2794,6 +2799,16 @@ fn new_graph_editor(app:&Application) -> GraphEditor { + // ================ + // === Node VCS === + // ================ + + eval inputs.set_node_vcs_status(((node_id,status)) + model.with_node(*node_id, |node| node.set_vcs_status.emit(status)) + ); + + + // ================== // === Edge Binds === // ================== diff --git a/gui/src/rust/ide/view/src/debug_scenes/interface.rs b/gui/src/rust/ide/view/src/debug_scenes/interface.rs index c2a428bc139..3b1e6795821 100644 --- a/gui/src/rust/ide/view/src/debug_scenes/interface.rs +++ b/gui/src/rust/ide/view/src/debug_scenes/interface.rs @@ -130,6 +130,29 @@ fn init(app:&Application) { graph_editor.frp.connect_nodes.emit((src,tgt)); + // === VCS === + + let dummy_node_added_id = graph_editor.add_node(); + let dummy_node_edited_id = graph_editor.add_node(); + let dummy_node_unchanged_id = graph_editor.add_node(); + + graph_editor.frp.set_node_position.emit((dummy_node_added_id,Vector2(-450.0,50.0))); + graph_editor.frp.set_node_position.emit((dummy_node_edited_id,Vector2(-450.0,125.0))); + graph_editor.frp.set_node_position.emit((dummy_node_unchanged_id,Vector2(-450.0,200.0))); + + let dummy_node_added_expr = expression_mock_string("This node was added."); + let dummy_node_edited_expr = expression_mock_string("This node was edited."); + let dummy_node_unchanged_expr = expression_mock_string("This node was not changed."); + + graph_editor.frp.set_node_expression.emit((dummy_node_added_id,dummy_node_added_expr)); + graph_editor.frp.set_node_expression.emit((dummy_node_edited_id,dummy_node_edited_expr)); + graph_editor.frp.set_node_expression.emit((dummy_node_unchanged_id,dummy_node_unchanged_expr)); + + graph_editor.frp.set_node_vcs_status.emit((dummy_node_added_id,Some(vcs::Status::Edited))); + graph_editor.frp.set_node_vcs_status.emit((dummy_node_edited_id,Some(vcs::Status::Added))); + graph_editor.frp.set_node_vcs_status.emit((dummy_node_unchanged_id,Some(vcs::Status::Unchanged))); + + // === Types (Port Coloring) === let mut dummy_type_generator = DummyTypeGenerator::default(); @@ -215,6 +238,7 @@ fn init(app:&Application) { // ============= use crate::graph_editor::component::node::Expression; +use crate::graph_editor::component::node::vcs; use ast::crumbs::*; use ast::crumbs::PatternMatchCrumb::*; @@ -223,6 +247,19 @@ use ensogl_text_msdf_sys::run_once_initialized; use span_tree::traits::*; +pub fn expression_mock_string(label:&str) -> Expression { + let pattern = Some(label.to_string()); + let code = format!("\"{}\"", label); + let parser = Parser::new_or_panic(); + let parameters = vec![]; + let ast = parser.parse_line(&code).unwrap(); + let invocation_info = span_tree::generate::context::CalledMethodInfo {parameters}; + let ctx = span_tree::generate::MockContext::new_single(ast.id.unwrap(),invocation_info); + let output_span_tree = span_tree::SpanTree::default(); + let input_span_tree = span_tree::SpanTree::new(&ast,&ctx).unwrap(); + Expression {pattern,code,input_span_tree,output_span_tree} +} + pub fn expression_mock() -> Expression { let pattern = Some("var1".to_string()); let code = "[1,2,3]".to_string();