Skip to content
This repository has been archived by the owner on Dec 28, 2021. It is now read-only.

Add a theming API for visualizations #1358

Merged
merged 8 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
ignored.
- [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.
- [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
Expand Down
13 changes: 13 additions & 0 deletions docs/product/visualizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ 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 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 ===
// =================
Expand Down Expand Up @@ -50,16 +55,78 @@ 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<color::Lcha> 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<JsColor> {
Some(self.styles.get(path).color()?.into())
}
}



// ========================
// === Constructor Args ===
// ========================

/// Data that is passed into the javascript Visualization baseclass.
#[allow(missing_docs)]
#[wasm_bindgen]
pub struct JsConsArgs {
#[wasm_bindgen(skip)]
pub root : HtmlDivElement,
theme : JsTheme,
#[wasm_bindgen(skip)]
pub set_preprocessor : Box<dyn PreprocessorCallback>,
}
Expand All @@ -72,10 +139,12 @@ impl Debug for JsConsArgs {

impl JsConsArgs {
/// Constructor.
pub fn new<F:'static+PreprocessorCallback>(root:DomSymbol, closure:F) -> Self {
pub fn new<F:'static+PreprocessorCallback>
(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}
}
}

Expand All @@ -86,6 +155,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()
}

/// Helper method to emit an preprocessor change event from the visualisation.
pub fn emit_preprocessor_change(&self, code:Option<String>, module:Option<String>){
let closure = &self.set_preprocessor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +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(), 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class Visualization {
this.__preprocessorModule__ = null

this.dom = api.root()
this.theme = api.theme()
this.__api__ = api
}

Expand Down