From 384df369238ccf703217396468ec9f43d837fad2 Mon Sep 17 00:00:00 2001 From: Felix Rech Date: Mon, 22 Mar 2021 19:43:14 +0100 Subject: [PATCH 1/5] Add a theming API for visualizations --- .../foreign/java_script/binding.rs | 59 ++++++++++++++++++- .../foreign/java_script/instance.rs | 4 +- .../foreign/java_script/visualization.js | 1 + 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs index 7a91b2af81..283a1ca6d3 100644 --- a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs +++ b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs @@ -4,8 +4,13 @@ use crate::prelude::*; use crate::component::visualization::foreign::java_script::PreprocessorCallback; use crate::component::visualization::instance::PreprocessorConfiguration; +use crate::component::type_coloring; +use crate::Type; +use crate::display::style; use ensogl::display::DomSymbol; +use ensogl::display::shape::StyleWatch; +use ensogl::data::color; use fmt::Formatter; use wasm_bindgen::prelude::*; use web_sys::HtmlDivElement; @@ -60,6 +65,7 @@ pub fn js_class() -> JsValue { pub struct JsConsArgs { #[wasm_bindgen(skip)] pub root : HtmlDivElement, + theme : JsTheme, #[wasm_bindgen(skip)] pub set_preprocessor : Box, } @@ -72,10 +78,12 @@ impl Debug for JsConsArgs { impl JsConsArgs { /// Constructor. - pub fn new(root:DomSymbol, closure:F) -> Self { + pub fn new + (root:DomSymbol, styles:StyleWatch, closure:F) -> Self { let set_preprocessor = Box::new(closure); + let theme = JsTheme {styles}; let root = root.dom().clone(); - JsConsArgs {root,set_preprocessor} + JsConsArgs {root,theme,set_preprocessor} } } @@ -86,6 +94,11 @@ impl JsConsArgs { self.root.clone().into() } + /// Getter for the theming API that we expose to JS visualizations + pub fn theme(&self) -> JsTheme { + self.theme.clone().into() + } + /// Helper method to emit an preprocessor change event from the visualisation. pub fn emit_preprocessor_change(&self, code:Option, module:Option){ let closure = &self.set_preprocessor; @@ -93,3 +106,45 @@ impl JsConsArgs { (*closure)(preprocessor_config); } } + +/// The theming API that we expose to JS visualizations +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct JsTheme { + styles: StyleWatch +} + +#[allow(non_snake_case)] +#[wasm_bindgen] +impl JsTheme { + /// Takes a qualified type name and returns the color that is used in the GUI for that type. + pub fn getColorForType(&self, tp_name: &str) -> String { + let tp = Type::from(tp_name.to_string()); + let lcha = type_coloring::compute(&tp,&self.styles); + format_lcha(lcha) + } + + /// Takes a qualified type name and returns the color that should be used for foreground + /// (e.g. text) that is shown on top of the background color returned by getColorForType. + pub fn getForegroundColorForType(&self, _tp_name: &str) -> String { + "white".to_string() + } + + /// Queries style sheet value for a value. + pub fn get(&self, path: &str) -> Option { + if let style::Data::Color(lcha) = self.styles.get(path)? { + Some(format_lcha(lcha)) + } else { + None + } + } +} + +fn format_lcha(lcha: color::Lcha) -> String { + let rgba = color::Rgba::from(lcha); + format!("rgba({:.0}, {:.0}, {:.0}, {})", + (rgba.red * 255.0).round(), + (rgba.green * 255.0).round(), + (rgba.blue * 255.0).round(), + rgba.alpha) +} diff --git a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs index 7a5214e3ed..ec920b20bb 100644 --- a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs +++ b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs @@ -159,7 +159,9 @@ impl InstanceModel { let logger = Logger::new("Instance"); let root_node = Self::create_root(scene,&logger)?; let (preprocessor_change,closure) = Self::preprocessor_change_callback(); - let init_data = JsConsArgs::new(root_node.clone_ref(), closure); + let init_data = JsConsArgs::new(root_node.clone_ref(), + StyleWatch::new(&scene.style_sheet), + closure); let object = Self::instantiate_class_with_args(class,init_data)?; let on_data_received = get_method(object.as_ref(),method::ON_DATA_RECEIVED).ok(); let on_data_received = Rc::new(on_data_received); diff --git a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js index e64cd77f36..95100458a8 100644 --- a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js +++ b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js @@ -19,6 +19,7 @@ export class Visualization { this.__preprocessorModule__ = null this.dom = api.root() + this.theme = api.theme() this.__api__ = api } From 3a59c67d1840a9c10fdefb27704267fd779a5bf9 Mon Sep 17 00:00:00 2001 From: Felix Rech Date: Mon, 22 Mar 2021 22:40:27 +0100 Subject: [PATCH 2/5] Fix CI warnings --- CHANGELOG.md | 2 ++ .../src/component/visualization/foreign/java_script/binding.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8188c4deeb..0136e5bcef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,8 @@ a project contain syntax errors. - [Users can opt out of anonymous data gathering.][1328] This can be done with the `--no-data-gathering` command-line flag during the startup of the IDE. +- [Provide a theming API for JavaScript visualizations][1358]. This API can be + used to obtain the colors associated with a type or a class GUI elements. #### EnsoGL (rendering engine) diff --git a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs index 283a1ca6d3..299c77c733 100644 --- a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs +++ b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs @@ -96,7 +96,7 @@ impl JsConsArgs { /// Getter for the theming API that we expose to JS visualizations pub fn theme(&self) -> JsTheme { - self.theme.clone().into() + self.theme.clone() } /// Helper method to emit an preprocessor change event from the visualisation. From 7e8ecb199687746e3481d918ef4b67b06be179cb Mon Sep 17 00:00:00 2001 From: Felix Rech Date: Tue, 23 Mar 2021 13:38:51 +0100 Subject: [PATCH 3/5] Refine the theming API for visualisations --- CHANGELOG.md | 6 +- .../foreign/java_script/binding.rs | 113 ++++++++++-------- .../foreign/java_script/instance.rs | 5 +- 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0136e5bcef..aaeafd1118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,8 +45,10 @@ a project contain syntax errors. - [Users can opt out of anonymous data gathering.][1328] This can be done with the `--no-data-gathering` command-line flag during the startup of the IDE. -- [Provide a theming API for JavaScript visualizations][1358]. This API can be - used to obtain the colors associated with a type or a class GUI elements. +- [Provide a theming API for JavaScript visualizations][1358]. It is now + possible to use the Enso theming engine while developing custom + visualizations in JavaScript. You can query it for all IDE colors, including + the colors used to represent types. #### EnsoGL (rendering engine) diff --git a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs index 299c77c733..7a9d858813 100644 --- a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs +++ b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs @@ -6,17 +6,17 @@ use crate::component::visualization::foreign::java_script::PreprocessorCallback; use crate::component::visualization::instance::PreprocessorConfiguration; use crate::component::type_coloring; use crate::Type; -use crate::display::style; use ensogl::display::DomSymbol; use ensogl::display::shape::StyleWatch; use ensogl::data::color; +use ensogl::display::style::data::DataMatch; +use ensogl_theme; use fmt::Formatter; use wasm_bindgen::prelude::*; use web_sys::HtmlDivElement; - // ================= // === Constants === // ================= @@ -55,9 +55,70 @@ pub fn js_class() -> JsValue { -// ===================== -// === Rust Bindings === -// ===================== +// ============= +// === Theme === +// ============= + +/// The theming API that we expose to JS visualizations +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct JsTheme { + styles: StyleWatch +} + +/// A color in RGBA representation. Can be passed to JavaScript. Implements the `From` trait for +/// convertion from `color::Lcha`. +#[wasm_bindgen] +#[derive(Debug, Copy, Clone)] +pub struct JsColor { + /// The red part as a float between 0 and 1 + pub red: f32, + /// The green part as a float between 0 and 1 + pub green: f32, + /// The blue part as a float between 0 and 1 + pub blue: f32, + /// The opacity as a float between 0 and 1 + pub alpha: f32 +} + +impl From for JsColor { + fn from(lcha: color::Lcha) -> Self { + let rgba = color::Rgba::from(lcha); + JsColor { + red: rgba.red, + green: rgba.green, + blue: rgba.blue, + alpha: rgba.alpha + } + } +} + +#[allow(non_snake_case)] +#[wasm_bindgen] +impl JsTheme { + /// Takes a qualified type name and returns the color that is used in the GUI for that type. + pub fn getColorForType(&self, tp_name: &str) -> JsColor { + let tp = Type::from(tp_name.to_string()); + type_coloring::compute(&tp,&self.styles).into() + } + + /// Takes a qualified type name and returns the color that should be used for foreground + /// (e.g. text) that is shown on top of the background color returned by getColorForType. + pub fn getForegroundColorForType(&self, _tp_name: &str) -> JsColor { + self.styles.get_color(ensogl_theme::code::types::selected).into() + } + + /// Queries style sheet value for a value. + pub fn get(&self, path: &str) -> Option { + Some(self.styles.get(path).color()?.into()) + } +} + + + +// ======================== +// === Constructor Args === +// ======================== /// Data that is passed into the javascript Visualization baseclass. #[allow(missing_docs)] @@ -106,45 +167,3 @@ impl JsConsArgs { (*closure)(preprocessor_config); } } - -/// The theming API that we expose to JS visualizations -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct JsTheme { - styles: StyleWatch -} - -#[allow(non_snake_case)] -#[wasm_bindgen] -impl JsTheme { - /// Takes a qualified type name and returns the color that is used in the GUI for that type. - pub fn getColorForType(&self, tp_name: &str) -> String { - let tp = Type::from(tp_name.to_string()); - let lcha = type_coloring::compute(&tp,&self.styles); - format_lcha(lcha) - } - - /// Takes a qualified type name and returns the color that should be used for foreground - /// (e.g. text) that is shown on top of the background color returned by getColorForType. - pub fn getForegroundColorForType(&self, _tp_name: &str) -> String { - "white".to_string() - } - - /// Queries style sheet value for a value. - pub fn get(&self, path: &str) -> Option { - if let style::Data::Color(lcha) = self.styles.get(path)? { - Some(format_lcha(lcha)) - } else { - None - } - } -} - -fn format_lcha(lcha: color::Lcha) -> String { - let rgba = color::Rgba::from(lcha); - format!("rgba({:.0}, {:.0}, {:.0}, {})", - (rgba.red * 255.0).round(), - (rgba.green * 255.0).round(), - (rgba.blue * 255.0).round(), - rgba.alpha) -} diff --git a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs index ec920b20bb..370afcbceb 100644 --- a/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs +++ b/src/rust/ide/view/graph-editor/src/component/visualization/foreign/java_script/instance.rs @@ -159,9 +159,8 @@ impl InstanceModel { let logger = Logger::new("Instance"); let root_node = Self::create_root(scene,&logger)?; let (preprocessor_change,closure) = Self::preprocessor_change_callback(); - let init_data = JsConsArgs::new(root_node.clone_ref(), - StyleWatch::new(&scene.style_sheet), - closure); + let styles = StyleWatch::new(&scene.style_sheet); + let init_data = JsConsArgs::new(root_node.clone_ref(), styles, closure); let object = Self::instantiate_class_with_args(class,init_data)?; let on_data_received = get_method(object.as_ref(),method::ON_DATA_RECEIVED).ok(); let on_data_received = Rc::new(on_data_received); From a8e21b0ecc580cb5bcde5e08fab7d9ceaf70d313 Mon Sep 17 00:00:00 2001 From: Felix Rech Date: Tue, 23 Mar 2021 13:50:37 +0100 Subject: [PATCH 4/5] Fix formatting of changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5782e3754b..8e18ed0fea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,9 +46,9 @@ - [Users can opt out of anonymous data gathering.][1328] This can be done with the `--no-data-gathering` command-line flag during the startup of the IDE. - [Provide a theming API for JavaScript visualizations][1358]. It is now - possible to use the Enso theming engine while developing custom - visualizations in JavaScript. You can query it for all IDE colors, including - the colors used to represent types. + possible to use the Enso theming engine while developing custom visualizations + in JavaScript. You can query it for all IDE colors, including the colors used + to represent types. - [You can now start the IDE service without window again.][1353] The command line arguyment `--no-window` now starts all the required backend services again, and prints the port on the command line, allowing you to open the IDE From 459549c1c9fa50f300f50800de3e230ced61cea7 Mon Sep 17 00:00:00 2001 From: Felix Rech Date: Wed, 24 Mar 2021 16:34:01 +0100 Subject: [PATCH 5/5] Add documentation --- docs/product/visualizations.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/product/visualizations.md b/docs/product/visualizations.md index 32c36907a7..0a10b2f25f 100644 --- a/docs/product/visualizations.md +++ b/docs/product/visualizations.md @@ -223,6 +223,19 @@ In particular: It is initialized in the constructor to the DOM symbol used to host the visualization content. Users are free to modify the DOM element, including adding other elements as its children. + - #### Field `theme` + The IDE's current color theme. Exposes the following methods. + - ##### Method `getColorForType` + Takes a qualified type name and returns the color that is used in the GUI to represent that + type. + - ##### Method `getForegroundColorForType` + Takes a qualified type name and returns the color that should be used for foreground elements + (e.g. text) that are shown on top of the background color returned by `getColorForType`. + - ##### Method `get` + Takes a style sheet path as string and returns the corresponding value from the theme. For + example, `get("graph_editor.node.error.panic")` returns the orange color that is used to mark + nodes in an error state. + - ### [Optional] Field `label`