mirror of
https://github.com/enso-org/enso.git
synced 2025-01-02 02:02:08 +03:00
Documentation View (https://github.com/enso-org/ide/pull/702)
Original commit: d7fabd103b
This commit is contained in:
parent
7e6bc37bf3
commit
cd8be60b78
@ -54,12 +54,13 @@ tags: [product,ui]
|
||||
|
||||
| Shortcut | Action |
|
||||
| -------- | ------ |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>space<kbd> | Toggle visualization visibility of the selected node. |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>f<kbd> | Cycle visualizations of the selected node. |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>space</kbd> | Toggle visualization visibility of the selected node. |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>f</kbd> | Cycle visualizations of the selected node. |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>\\</kbd> | Toggle documentation view visibility |
|
||||
|
||||
### Debug
|
||||
| Shortcut | Action |
|
||||
| -------- | ------ |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>d<kbd> | Send test data to the selected node. |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>d</kbd> | Send test data to the selected node. |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>shift</kbd> + <kbd>enter</kbd> | Push a hardcoded breadcrumb without navigating. |
|
||||
| <kbd>cmd</kbd> / <kbd>ctrl</kbd> + <kbd>shift</kbd> + <kbd>arrow up</kbd> | Pop a breadcrumb without navigating. |
|
||||
|
1
gui/src/rust/Cargo.lock
generated
1
gui/src/rust/Cargo.lock
generated
@ -1197,6 +1197,7 @@ dependencies = [
|
||||
"js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"logger 0.1.0",
|
||||
"nalgebra 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parser 0.1.0",
|
||||
"serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"span-tree 0.1.0",
|
||||
|
@ -15,6 +15,7 @@ ensogl = { version = "0.1.0", path = "../../../ensogl"
|
||||
ensogl-text = { version = "0.1.0", path = "../../../ensogl/lib/text" }
|
||||
ensogl-text-msdf-sys = { version = "0.1.0", path = "../../../ensogl/lib/text/msdf-sys" }
|
||||
enso-shapely = { version = "0.1.0", path = "../../../lib/enso-shapely/impl" }
|
||||
parser = { version = "0.1.0", path = "../parser" }
|
||||
span-tree = { version = "0.1.0", path = "../span-tree" }
|
||||
logger = { version = "0.1.0", path = "../../../lib/logger" }
|
||||
enso-protocol = { version = "0.1.0", path = "../../../ide/lib/enso-protocol" }
|
||||
|
@ -18,10 +18,12 @@ pub mod component;
|
||||
pub mod builtin;
|
||||
pub mod data;
|
||||
|
||||
use crate::graph_editor::builtin::visualization::native::documentation;
|
||||
use crate::graph_editor::component::node;
|
||||
use crate::graph_editor::component::type_coloring::MISSING_TYPE_COLOR;
|
||||
use crate::graph_editor::component::visualization::MockDataGenerator3D;
|
||||
use crate::graph_editor::component::visualization;
|
||||
use crate::graph_editor::component::visualization::MockDataGenerator3D;
|
||||
use crate::graph_editor::component::visualization::MockDocGenerator;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl::application::Application;
|
||||
@ -314,6 +316,13 @@ ensogl::def_command_api! { Commands
|
||||
/// Disable mode in which the pressed node will be edited.
|
||||
edit_mode_off,
|
||||
|
||||
|
||||
/// Documentation open press event. In case the event will be shortly followed by `release_documentation_view_visibility`, the documentation will be shown permanently. In other case, it will be disabled as soon as the `release_documentation_view_visibility` is emitted.
|
||||
press_documentation_view_visibility,
|
||||
/// Documentation open release event. See `press_documentation_view_visibility` to learn more.
|
||||
release_documentation_view_visibility,
|
||||
|
||||
|
||||
/// Enable nodes multi selection mode. It works like inverse mode for single node selection and like merge mode for multi node selection mode.
|
||||
enable_node_multi_select,
|
||||
/// Disable nodes multi selection mode. It works like inverse mode for single node selection and like merge mode for multi node selection mode.
|
||||
@ -387,6 +396,7 @@ pub struct FrpInputs {
|
||||
pub set_visualization : frp::Source<(NodeId,Option<visualization::Path>)>,
|
||||
pub register_visualization : frp::Source<Option<visualization::Definition>>,
|
||||
pub set_visualization_data : frp::Source<(NodeId,visualization::Data)>,
|
||||
pub set_documentation_data : frp::Source<visualization::Data>,
|
||||
|
||||
hover_node_input : frp::Source<Option<EdgeTarget>>,
|
||||
hover_node_output : frp::Source<Option<EdgeTarget>>,
|
||||
@ -425,6 +435,7 @@ impl FrpInputs {
|
||||
cycle_visualization <- source();
|
||||
set_visualization <- source();
|
||||
register_visualization <- source();
|
||||
set_documentation_data <- source();
|
||||
|
||||
hover_node_input <- source();
|
||||
hover_node_output <- source();
|
||||
@ -441,9 +452,9 @@ impl FrpInputs {
|
||||
,unset_edge_source,unset_edge_target
|
||||
,set_node_position,set_expression_type,set_method_pointer,select_node,remove_node
|
||||
,set_node_expression,connect_nodes,deselect_all_nodes,cycle_visualization
|
||||
,set_visualization,register_visualization,some_edge_targets_detached
|
||||
,some_edge_sources_detached,all_edge_targets_attached,hover_node_input
|
||||
,all_edge_sources_attached,hover_node_output,press_node_output
|
||||
,set_visualization,register_visualization,set_documentation_data
|
||||
,some_edge_targets_detached,some_edge_sources_detached,all_edge_targets_attached
|
||||
,hover_node_input,all_edge_sources_attached,hover_node_output,press_node_output
|
||||
,set_detached_edge_sources,all_edges_attached
|
||||
}
|
||||
}
|
||||
@ -543,11 +554,12 @@ generate_frp_outputs! {
|
||||
connection_added : EdgeId,
|
||||
connection_removed : EdgeId,
|
||||
|
||||
visualization_enabled : NodeId,
|
||||
visualization_disabled : NodeId,
|
||||
visualization_enabled : NodeId,
|
||||
visualization_disabled : NodeId,
|
||||
visualization_enable_fullscreen : NodeId,
|
||||
visualization_set_preprocessor : (NodeId,data::EnsoCode),
|
||||
|
||||
documentation_visible : bool,
|
||||
}
|
||||
|
||||
|
||||
@ -1094,15 +1106,16 @@ impl GraphEditorModelWithNetwork {
|
||||
|
||||
#[derive(Debug,Clone,CloneRef)]
|
||||
pub struct GraphEditorModel {
|
||||
pub logger : Logger,
|
||||
pub display_object : display::object::Instance,
|
||||
pub app : Application,
|
||||
pub breadcrumbs : component::Breadcrumbs,
|
||||
pub cursor : cursor::Cursor,
|
||||
pub nodes : Nodes,
|
||||
pub edges : Edges,
|
||||
touch_state : TouchState,
|
||||
frp : FrpInputs,
|
||||
pub logger : Logger,
|
||||
pub display_object : display::object::Instance,
|
||||
pub app : Application,
|
||||
pub breadcrumbs : component::Breadcrumbs,
|
||||
pub cursor : cursor::Cursor,
|
||||
pub nodes : Nodes,
|
||||
pub edges : Edges,
|
||||
pub documentation_view : documentation::View,
|
||||
touch_state : TouchState,
|
||||
frp : FrpInputs,
|
||||
}
|
||||
|
||||
|
||||
@ -1115,17 +1128,20 @@ impl GraphEditorModel {
|
||||
, network : &frp::Network
|
||||
, focus_manager : &FocusManager
|
||||
) -> Self {
|
||||
let scene = app.display.scene();
|
||||
let logger = Logger::new("GraphEditor");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let nodes = Nodes::new(&logger);
|
||||
// let visualizations = Stage::new(scene.clone_ref(), Logger::new("VisualisationCollection"));
|
||||
let edges = default();
|
||||
let frp = FrpInputs::new(network);
|
||||
let touch_state = TouchState::new(network,&scene.mouse.frp);
|
||||
let breadcrumbs = component::Breadcrumbs::new(scene,focus_manager);
|
||||
let app = app.clone_ref();
|
||||
Self {logger,display_object,app,cursor,nodes,edges,touch_state,frp,breadcrumbs}.init()//visualizations }
|
||||
let scene = app.display.scene();
|
||||
let logger = Logger::new("GraphEditor");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let nodes = Nodes::new(&logger);
|
||||
let documentation_view = documentation::View::new(&scene);
|
||||
let edges = default();
|
||||
let frp = FrpInputs::new(network);
|
||||
let touch_state = TouchState::new(network,&scene.mouse.frp);
|
||||
let breadcrumbs = component::Breadcrumbs::new(scene,focus_manager);
|
||||
display_object.add_child(&documentation_view);
|
||||
display_object.remove_child(&documentation_view);
|
||||
let app = app.clone_ref();
|
||||
Self {logger,display_object,app,cursor,nodes,edges,touch_state,frp,breadcrumbs,
|
||||
documentation_view}.init()
|
||||
}
|
||||
|
||||
fn init(self) -> Self {
|
||||
@ -1215,6 +1231,15 @@ impl GraphEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_documentation_visibility(&self, is_visible:bool) {
|
||||
if is_visible { self.app.remove_child(&self.documentation_view) }
|
||||
else { self.app.add_child(&self.documentation_view) }
|
||||
}
|
||||
|
||||
fn is_documentation_visible(&self) -> bool {
|
||||
self.documentation_view.has_parent()
|
||||
}
|
||||
|
||||
/// Warning! This function does not remove connected edges. It needs to be handled by the
|
||||
/// implementation.
|
||||
fn remove_node(&self, node_id:impl Into<NodeId>) {
|
||||
@ -1571,6 +1596,8 @@ impl application::shortcut::DefaultShortcutProvider for GraphEditor {
|
||||
, Self::self_shortcut(shortcut::Action::press (&[Key::Control,Key::Character(" ".into())],&[]) , "press_visualization_visibility")
|
||||
, Self::self_shortcut(shortcut::Action::double_press (&[Key::Control,Key::Character(" ".into())],&[]) , "double_press_visualization_visibility")
|
||||
, Self::self_shortcut(shortcut::Action::release (&[Key::Control,Key::Character(" ".into())],&[]) , "release_visualization_visibility")
|
||||
, Self::self_shortcut(shortcut::Action::press (&[Key::Control,Key::Character("\\".into())],&[]) , "press_documentation_view_visibility")
|
||||
, Self::self_shortcut(shortcut::Action::release (&[Key::Control,Key::Character("\\".into())],&[]) , "release_documentation_view_visibility")
|
||||
, Self::self_shortcut(shortcut::Action::press (&[Key::Meta],&[]) , "toggle_node_multi_select")
|
||||
, Self::self_shortcut(shortcut::Action::release (&[Key::Meta],&[]) , "toggle_node_multi_select")
|
||||
, Self::self_shortcut(shortcut::Action::press (&[Key::Control],&[]) , "toggle_node_multi_select")
|
||||
@ -2284,6 +2311,12 @@ fn new_graph_editor(app:&Application) -> GraphEditor {
|
||||
let data = visualization::Data::from(content);
|
||||
inputs.set_visualization_data.emit((*node_id,data));
|
||||
}
|
||||
|
||||
let mock_documentaion = MockDocGenerator::default();
|
||||
let data = mock_documentaion.generate_data();
|
||||
let content = serde_json::to_value(data).unwrap_or_default();
|
||||
let data = visualization::Data::from(content);
|
||||
inputs.set_documentation_data.emit(data);
|
||||
}));
|
||||
|
||||
def _set_data = inputs.set_visualization_data.map(f!([nodes]((node_id,data)) {
|
||||
@ -2413,6 +2446,31 @@ fn new_graph_editor(app:&Application) -> GraphEditor {
|
||||
}
|
||||
|
||||
|
||||
// === Documentation Set ===
|
||||
|
||||
frp::extend! { network
|
||||
|
||||
eval inputs.set_documentation_data ((data) model.documentation_view.frp.send_data.emit(data));
|
||||
|
||||
|
||||
// === Documentation toggle ===
|
||||
|
||||
let documentation_press_ev = inputs.press_documentation_view_visibility.clone_ref();
|
||||
let documentation_release = inputs.release_documentation_view_visibility.clone_ref();
|
||||
documentation_pressed <- bool(&documentation_release,&documentation_press_ev);
|
||||
documentation_was_pressed <- documentation_pressed.previous();
|
||||
documentation_press <- documentation_press_ev.gate_not(&documentation_was_pressed);
|
||||
documentation_press_on_off <- documentation_press.map(f_!(model.is_documentation_visible()));
|
||||
outputs.documentation_visible <+ documentation_press_on_off;
|
||||
|
||||
|
||||
// === OUTPUTS REBIND ===
|
||||
|
||||
eval outputs.documentation_visible ((vis) model.set_documentation_visibility(*vis));
|
||||
|
||||
}
|
||||
|
||||
|
||||
// === Remove Edge ===
|
||||
frp::extend! { network
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
//! Examples of defining visualization in Rust using web_sys or ensogl.
|
||||
|
||||
pub mod bubble_chart;
|
||||
pub mod documentation;
|
||||
pub mod raw_text;
|
||||
|
||||
pub use bubble_chart::*;
|
||||
pub use documentation::*;
|
||||
pub use raw_text::*;
|
||||
|
@ -0,0 +1,256 @@
|
||||
//! Documentation view visualization generating and presenting Enso Documentation under
|
||||
//! the documented node.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::graph_editor::component::visualization;
|
||||
|
||||
use ast::prelude::FallibleResult;
|
||||
use enso_frp as frp;
|
||||
use ensogl::display;
|
||||
use ensogl::display::DomSymbol;
|
||||
use ensogl::display::scene::Scene;
|
||||
use ensogl::system::web;
|
||||
use ensogl::system::web::StyleSetter;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
pub const VIEW_WIDTH : f32 = 300.0;
|
||||
pub const VIEW_MARGIN : f32 = 15.0;
|
||||
|
||||
/// Content in the documentation view when there is no data available.
|
||||
const PLACEHOLDER_STR : &str = "<h3>Documentation Viewer</h3><p>No documentation available</p>";
|
||||
const CORNER_RADIUS : f32 = crate::graph_editor::component::node::CORNER_RADIUS;
|
||||
|
||||
/// Get documentation view stylesheet from a CSS file.
|
||||
///
|
||||
/// TODO [MM] : This file is generated currently from SASS file, and generated code should never
|
||||
/// be included in a codebase, so it will be moved to rust-based generator to achieve
|
||||
/// compatibility with IDE's theme manager.
|
||||
/// Expect them to land with https://github.com/enso-org/ide/issues/709
|
||||
fn documentation_style() -> String {
|
||||
format!("<style>{}</style>", include_str!("documentation/style.css"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === ViewModel ===
|
||||
// =================
|
||||
|
||||
/// Model of Native visualization that generates documentation for given Enso code and embeds
|
||||
/// it in a HTML container.
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct ViewModel {
|
||||
logger : Logger,
|
||||
dom : DomSymbol,
|
||||
size : Rc<Cell<Vector2>>,
|
||||
}
|
||||
|
||||
impl ViewModel {
|
||||
/// Constructor.
|
||||
fn new(scene:&Scene) -> Self {
|
||||
let logger = Logger::new("DocumentationView");
|
||||
let div = web::create_div();
|
||||
let dom = DomSymbol::new(&div);
|
||||
let screen = scene.camera().screen();
|
||||
let view_height = screen.height - (VIEW_MARGIN * 2.0);
|
||||
let size_vec = Vector2(VIEW_WIDTH, view_height);
|
||||
let size = Rc::new(Cell::new(size_vec));
|
||||
|
||||
dom.dom().set_style_or_warn("white-space" ,"normal" ,&logger);
|
||||
dom.dom().set_style_or_warn("overflow-y" ,"auto" ,&logger);
|
||||
dom.dom().set_style_or_warn("overflow-x" ,"auto" ,&logger);
|
||||
dom.dom().set_style_or_warn("background-color","rgba(255, 255, 255, 0.85)" ,&logger);
|
||||
dom.dom().set_style_or_warn("padding" ,"5px" ,&logger);
|
||||
dom.dom().set_style_or_warn("pointer-events" ,"auto" ,&logger);
|
||||
dom.dom().set_style_or_warn("border-radius" ,format!("{}px",CORNER_RADIUS),&logger);
|
||||
dom.dom().set_style_or_warn("width" ,format!("{}px",VIEW_WIDTH) ,&logger);
|
||||
dom.dom().set_style_or_warn("height" ,format!("{}px",view_height) ,&logger);
|
||||
|
||||
scene.dom.layers.main.manage(&dom);
|
||||
ViewModel {logger,dom,size}.init()
|
||||
}
|
||||
|
||||
fn init(self) -> Self {
|
||||
self.reload_style();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set size of the documentation view.
|
||||
fn set_size(&self, size:Vector2) {
|
||||
self.size.set(size);
|
||||
self.reload_style();
|
||||
}
|
||||
|
||||
/// Generate HTML documentation from documented Enso code.
|
||||
fn gen_html_from(program:String) -> FallibleResult<String> {
|
||||
let parser = parser::DocParser::new()?;
|
||||
let output = parser.generate_html_docs(program);
|
||||
Ok(output?)
|
||||
}
|
||||
|
||||
/// Prepare data string for Doc Parser to work with after deserialization.
|
||||
/// FIXME [MM]: Removes characters that are not supported by Doc Parser yet.
|
||||
/// https://github.com/enso-org/enso/issues/1063
|
||||
fn prepare_data_string(data_inner:&visualization::Json) -> String {
|
||||
let data_str = serde_json::to_string_pretty(&**data_inner);
|
||||
let data_str = data_str.unwrap_or_else(|e| format!("<Cannot render data: {}>",e));
|
||||
let data_str = data_str.replace("\\n", "\n");
|
||||
data_str.replace("\"", "")
|
||||
}
|
||||
|
||||
/// Create a container for generated content and embed it with stylesheet.
|
||||
fn push_to_dom(&self, content:String) {
|
||||
let data_str = format!(r#"<div class="docVis">{}{}</div>"#,documentation_style(),content);
|
||||
self.dom.dom().set_inner_html(&data_str)
|
||||
}
|
||||
|
||||
/// Receive data, process and present it in the documentation view.
|
||||
fn receive_data(&self, data:&visualization::Data) -> Result<(),visualization::DataError> {
|
||||
let data_inner = match data {
|
||||
visualization::Data::Json {content} => content,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
let data_str = ViewModel::prepare_data_string(data_inner);
|
||||
let output = ViewModel::gen_html_from(data_str);
|
||||
let mut output = output.unwrap_or_else(|_| String::from(PLACEHOLDER_STR));
|
||||
if output == "" { output = String::from(PLACEHOLDER_STR) }
|
||||
// FIXME [MM] : Because of how Doc Parser was implemented in Engine repo, there is need to
|
||||
// remove stylesheet link from generated code, that would otherwise point to
|
||||
// non-existing file, as now stylesheet is connected by include_str! macro, and
|
||||
// soon will be replaced by a style generator.
|
||||
// This hack will be removed when https://github.com/enso-org/enso/issues/1063
|
||||
// will land in Engine's repo, also fixing non-existent character bug.
|
||||
let import_css = r#"<link rel="stylesheet" href="style.css" />"#;
|
||||
let output = output.replace(import_css, "");
|
||||
|
||||
self.push_to_dom(output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load an HTML file into the documentation view when user is waiting for data to be received.
|
||||
/// TODO [MM] : This should be replaced with a EnsoGL spinner in the next PR.
|
||||
fn load_waiting_screen(&self) {
|
||||
let spinner = r#"
|
||||
<div>
|
||||
<style>
|
||||
.spinner {
|
||||
margin: 40vh auto;
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.spinner > div {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: rgb(50, 48, 47);
|
||||
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.spinner .bounce1 {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.spinner .bounce2 {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
@keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% {
|
||||
transform: scale(0);
|
||||
} 40% {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
</div>
|
||||
"#;
|
||||
self.push_to_dom(String::from(spinner))
|
||||
}
|
||||
|
||||
fn reload_style(&self) {
|
||||
self.dom.set_size(self.size.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === View ===
|
||||
// ============
|
||||
|
||||
/// View of the visualization that renders the given documentation as a HTML page.
|
||||
#[derive(Clone,CloneRef,Debug,Shrinkwrap)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct View {
|
||||
#[shrinkwrap(main_field)]
|
||||
pub model : ViewModel,
|
||||
pub frp : visualization::instance::Frp,
|
||||
network : frp::Network,
|
||||
}
|
||||
|
||||
impl View {
|
||||
/// Definition of this visualization.
|
||||
pub fn definition() -> visualization::Definition {
|
||||
let path = visualization::Path::builtin("Documentation View");
|
||||
visualization::Definition::new(
|
||||
visualization::Signature::new_for_any_type(path,visualization::Format::Json),
|
||||
|scene| { Ok(Self::new(scene).into()) }
|
||||
)
|
||||
}
|
||||
|
||||
/// Constructor.
|
||||
pub fn new(scene:&Scene) -> Self {
|
||||
let network = default();
|
||||
let frp = visualization::instance::Frp::new(&network);
|
||||
let model = ViewModel::new(scene);
|
||||
model.load_waiting_screen();
|
||||
Self {model,frp,network} . init(scene)
|
||||
}
|
||||
|
||||
fn init(self, scene:&Scene) -> Self {
|
||||
let network = &self.network;
|
||||
let model = &self.model;
|
||||
let frp = &self.frp;
|
||||
frp::extend! { network
|
||||
eval frp.set_size ((size) model.set_size(*size));
|
||||
eval frp.send_data ([frp, model](data) {
|
||||
if let Err(e) = model.receive_data(data) {
|
||||
frp.data_receive_error.emit(Some(e));
|
||||
}
|
||||
});
|
||||
eval scene.frp.shape([model,frp](shape) {
|
||||
model.dom.set_position_x((shape.width - VIEW_WIDTH) / 2.0 - VIEW_MARGIN);
|
||||
frp.set_size.emit(Vector2::new(VIEW_WIDTH,shape.height - (VIEW_MARGIN * 2.0)));
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<View> for visualization::Instance {
|
||||
fn from(t: View) -> Self {
|
||||
Self::new(&t,&t.frp,&t.network)
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for View {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.dom.display_object()
|
||||
}
|
||||
}
|
@ -0,0 +1,431 @@
|
||||
/*
|
||||
* TODO [MM] : This file is undergoing drastic changes.
|
||||
* Expect them to land with https://github.com/enso-org/ide/issues/709
|
||||
* This file is currently generated from SASS file, and it is very
|
||||
* temporary. It will be moved to rust - it will become css generator
|
||||
* connected with the Theme manager to enable dark mode for documentation.
|
||||
*/
|
||||
|
||||
.docVis {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-style: normal;
|
||||
word-wrap: break-word;
|
||||
font-size: 17px;
|
||||
line-height: 1.52947;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.021em;
|
||||
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||
color: #333333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.docVis p {
|
||||
display: block;
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.docVis a:hover {
|
||||
color: #0070c9 !important;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
.docVis a {
|
||||
color: #333333;
|
||||
background-color: transparent;
|
||||
text-decoration: inherit;
|
||||
display: inline-block;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.docVis img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.docVis code {
|
||||
color: #0070c9;
|
||||
background-color: transparent;
|
||||
font-size: inherit;
|
||||
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||
line-height: inherit;
|
||||
display: inline-block;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.docVis button {
|
||||
display: inline-block;
|
||||
padding: 8px 30px;
|
||||
margin: 10px 0;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
border: 1px solid #333333;
|
||||
color: #333333;
|
||||
border-radius: 5px;
|
||||
font-size: 13px;
|
||||
vertical-align: top;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.docVis button:hover {
|
||||
background-color: #333333;
|
||||
color: #e5e5e5;
|
||||
}
|
||||
|
||||
.docVis b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docVis h1 {
|
||||
font-size: 34px;
|
||||
line-height: 1.08824;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.docVis h2 {
|
||||
font-size: 28px;
|
||||
line-height: 1.1073;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.012em;
|
||||
}
|
||||
|
||||
.Body h2 {
|
||||
margin: 0.65rem 0 0;
|
||||
}
|
||||
|
||||
.docVis li {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.creator .Unclosed,
|
||||
.creator .invalidIndent,
|
||||
.creator .invalidLink {
|
||||
display: inline;
|
||||
color: orangered;
|
||||
}
|
||||
.creator .Tags .UNRECOGNIZED {
|
||||
border: 2px solid;
|
||||
color: orangered;
|
||||
}
|
||||
|
||||
.Unclosed,
|
||||
.invalidIndent,
|
||||
.invalidLink {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.Header {
|
||||
font-size: 19px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.Important .Header,
|
||||
.Info .Header,
|
||||
.Example .Header {
|
||||
margin-bottom: 0.7em;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.021em;
|
||||
line-height: 17px;
|
||||
font-synthesis: none;
|
||||
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||
}
|
||||
|
||||
.Tags {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 20px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
.Tags .DEPRECATED,
|
||||
.Tags .MODIFIED,
|
||||
.Tags .ADDED,
|
||||
.Tags .UPCOMING,
|
||||
.Tags .REMOVED,
|
||||
.Tags .UNRECOGNIZED {
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
letter-spacing: -0.021em;
|
||||
display: inline-flex;
|
||||
padding: 5px 15px;
|
||||
margin: 2px;
|
||||
white-space: nowrap;
|
||||
background: transparent;
|
||||
}
|
||||
.Tags .DEPRECATED {
|
||||
border: 1px solid #d20606;
|
||||
color: #d20606;
|
||||
}
|
||||
.Tags .MODIFIED {
|
||||
border: 1px solid #003ec3;
|
||||
color: #003ec3;
|
||||
}
|
||||
.Tags .ADDED {
|
||||
border: 1px solid #79A129;
|
||||
color: #79A129;
|
||||
}
|
||||
.Tags .UPCOMING,
|
||||
.Tags .REMOVED,
|
||||
.Tags .UNRECOGNIZED {
|
||||
border: 1px solid #666666;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.ExtForTagDetails {
|
||||
margin: 0 3px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.Raw,
|
||||
.Important,
|
||||
.Info,
|
||||
.CodeBlock,
|
||||
.Example {
|
||||
margin-top: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: relative;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
.Body .Raw {
|
||||
margin-bottom: 0.6rem;
|
||||
font-size: 17px;
|
||||
line-height: 1.52947;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.021em;
|
||||
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||
color: #333333;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.Important,
|
||||
.Info,
|
||||
.CodeBlock,
|
||||
.Example {
|
||||
font-size: 17px;
|
||||
padding: 15px 10px 15px 20px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
margin: 0.7em 0;
|
||||
}
|
||||
|
||||
.Important {
|
||||
background-color: #FBECC2;
|
||||
}
|
||||
|
||||
.Info {
|
||||
background-color: #D6E1CA;
|
||||
}
|
||||
|
||||
.Example {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.CodeBlock {
|
||||
background-color: #fefefe;
|
||||
margin: 10px 20px;
|
||||
display: none;
|
||||
}
|
||||
.CodeBlock code {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.Def {
|
||||
margin: 40px auto auto;
|
||||
padding: 0 15px;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
.Def .Synopsis,
|
||||
.Def .Body,
|
||||
.Def .Tags,
|
||||
.Def .ASTData {
|
||||
padding-left: 0;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
.Def .Synopsis {
|
||||
padding: 0;
|
||||
margin-bottom: 15px;
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
font-style: normal;
|
||||
}
|
||||
.Def .constr {
|
||||
padding: 25px 0;
|
||||
margin: 0;
|
||||
}
|
||||
.Def .DefDoc .Body {
|
||||
display: none;
|
||||
}
|
||||
.Def .DefDoc .documentation {
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.Def .DefDoc .documentation .ASTHead {
|
||||
width: 30% !important;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.Def .DefDoc .documentation .ASTHead .DefTitle,
|
||||
.Def .DefDoc .documentation .ASTHead .Infix {
|
||||
padding: 0;
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
.Def .DefDoc .documentation .ASTData {
|
||||
width: 70% !important;
|
||||
}
|
||||
.Def .DefDoc .documentation .Doc {
|
||||
text-decoration: inherit;
|
||||
}
|
||||
.Def .DefDoc .documentation .Doc .Synopsis {
|
||||
text-decoration: inherit;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.Def .DefDoc .documentation .Tags {
|
||||
margin: 2px 0 0 auto;
|
||||
padding: 0;
|
||||
}
|
||||
.Def .DefNoDoc {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.DefTitle {
|
||||
display: inline-flex;
|
||||
font-size: x-large;
|
||||
font-weight: 400;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.DefArgs {
|
||||
margin-left: 5px;
|
||||
font-weight: 400;
|
||||
color: #0070c9;
|
||||
}
|
||||
|
||||
.Synopsis,
|
||||
.Body {
|
||||
margin: 0 auto;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.Synopsis {
|
||||
margin-top: 35px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.Documentation .ASTData,
|
||||
.Documentation .ASTHead {
|
||||
text-align: left;
|
||||
line-height: 1.05;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.Documentation .ASTData {
|
||||
width: 100%;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.Documentation .ASTHead {
|
||||
margin: 20px auto 5px;
|
||||
/*background-color: #fafafa;*/
|
||||
}
|
||||
.Documentation .ASTHead .DefTitle {
|
||||
font-size: 42px;
|
||||
margin: 0;
|
||||
}
|
||||
.Documentation .ASTData .ASTHead {
|
||||
/*background-color: #fafafa;*/
|
||||
}
|
||||
.Documentation .ASTData .ASTHead .DefTitle {
|
||||
font-size: x-large;
|
||||
}
|
||||
.Documentation .Documented {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.Documentation .DefNoBody {
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.Synopsis,
|
||||
.Body,
|
||||
.Tags,
|
||||
.Documentation .ASTData .Def {
|
||||
max-width: 380px;
|
||||
}
|
||||
|
||||
.Documentation .ASTHead,
|
||||
.DefNoBody,
|
||||
.DefBody {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.Def {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 500px) {
|
||||
.Synopsis,
|
||||
.Body,
|
||||
.Tags,
|
||||
.Documentation .ASTData .Def {
|
||||
max-width: 440px;
|
||||
}
|
||||
|
||||
.Documentation .ASTHead,
|
||||
.DefNoBody,
|
||||
.DefBody {
|
||||
max-width: 470px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 600px) {
|
||||
.Synopsis,
|
||||
.Body,
|
||||
.Tags,
|
||||
.Documentation .ASTData .Def {
|
||||
max-width: 490px;
|
||||
}
|
||||
|
||||
.Documentation .ASTHead,
|
||||
.DefNoBody,
|
||||
.DefBody {
|
||||
max-width: 520px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
.Synopsis,
|
||||
.Body,
|
||||
.Tags,
|
||||
.Documentation .ASTData .Def {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.Documentation .ASTHead,
|
||||
.DefNoBody,
|
||||
.DefBody {
|
||||
max-width: 710px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1300px) {
|
||||
.Synopsis,
|
||||
.Body,
|
||||
.Tags,
|
||||
.Documentation .ASTData .Def {
|
||||
max-width: 790px;
|
||||
}
|
||||
|
||||
.Documentation .ASTHead,
|
||||
.DefNoBody,
|
||||
.DefBody {
|
||||
max-width: 820px;
|
||||
}
|
||||
}
|
@ -145,3 +145,32 @@ impl MockDataGenerator3D {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The `MockDocGenerator` creates sample documentation string in the format of `String`.
|
||||
#[derive(Clone,CloneRef,Copy,Debug,Default)]
|
||||
pub struct MockDocGenerator;
|
||||
|
||||
impl MockDocGenerator {
|
||||
/// Generate new data set.
|
||||
pub fn generate_data(self) -> String {
|
||||
let input = r#"
|
||||
##
|
||||
Optional values.
|
||||
|
||||
Type `Option` represents an optional value: every `Option` is either `Some`
|
||||
and contains a value, or `None`, and does not.
|
||||
|
||||
? Information
|
||||
`Option`s are commonly paired with pattern matching to query the presence of
|
||||
a value and take action, always accounting for the None case.
|
||||
type Option a
|
||||
## The `Some` type indicates a presence of a value.
|
||||
type Some a
|
||||
|
||||
## The `None` type indicates a lack of a value.
|
||||
type None
|
||||
"#;
|
||||
input.to_string()
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ impl Path {
|
||||
/// Constructor.
|
||||
pub fn new(library:impl Into<data::LibraryName>, name:impl Into<Name>) -> Self {
|
||||
let library = library.into();
|
||||
let name = name.into();
|
||||
let name = name.into();
|
||||
Self {library,name}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user