mirror of
https://github.com/enso-org/enso.git
synced 2024-08-16 16:30:33 +03:00
EnsoGL context abstraction (#3293)
This commit is contained in:
parent
d3846578cc
commit
f4d236fcd4
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1461,6 +1461,7 @@ dependencies = [
|
|||||||
name = "ensogl-example-sprite-system"
|
name = "ensogl-example-sprite-system"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"enso-web",
|
||||||
"ensogl-core",
|
"ensogl-core",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
@ -8,13 +8,9 @@ members = [
|
|||||||
"build/rust-scripts",
|
"build/rust-scripts",
|
||||||
"lib/rust/*",
|
"lib/rust/*",
|
||||||
"lib/rust/profiler/data",
|
"lib/rust/profiler/data",
|
||||||
"lib/rust/not-used/*",
|
|
||||||
"integration-test"
|
"integration-test"
|
||||||
]
|
]
|
||||||
|
|
||||||
# This directory is not a crate
|
|
||||||
exclude = ["lib/rust/not-used"]
|
|
||||||
|
|
||||||
# The default memebers are those we want to check and test by default.
|
# The default memebers are those we want to check and test by default.
|
||||||
default-members = ["app/gui", "lib/rust/*"]
|
default-members = ["app/gui", "lib/rust/*"]
|
||||||
|
|
||||||
|
@ -6,4 +6,4 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
js-sys = { version = "0.3.28" }
|
js-sys = { version = "0.3.28" }
|
||||||
wasm-bindgen = { version = "0.2.58", features = ["nightly", "serde-serialize"] }
|
wasm-bindgen = { version = "0.2.78", features = ["nightly", "serde-serialize"] }
|
||||||
|
@ -21,7 +21,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
serde_json = { version = "1.0", features = ["unbounded_depth"] }
|
serde_json = { version = "1.0", features = ["unbounded_depth"] }
|
||||||
shrinkwraprs = { version = "0.2.1" }
|
shrinkwraprs = { version = "0.2.1" }
|
||||||
uuid = { version = "0.8", features = ["serde", "v5", "wasm-bindgen"] }
|
uuid = { version = "0.8", features = ["serde", "v5", "wasm-bindgen"] }
|
||||||
wasm-bindgen = { version = "0.2.58" }
|
wasm-bindgen = { version = "0.2.78" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test = { version = "0.3.8" }
|
wasm-bindgen-test = { version = "0.3.8" }
|
||||||
|
@ -14,7 +14,7 @@ span-tree = { path = "../../span-tree" }
|
|||||||
enso-web = { path = "../../../../../lib/rust/web" }
|
enso-web = { path = "../../../../../lib/rust/web" }
|
||||||
enso-prelude = { path = "../../../../../lib/rust/prelude"}
|
enso-prelude = { path = "../../../../../lib/rust/prelude"}
|
||||||
enso-logger = { path = "../../../../../lib/rust/logger"}
|
enso-logger = { path = "../../../../../lib/rust/logger"}
|
||||||
wasm-bindgen = { version = "0.2.58", features = [
|
wasm-bindgen = { version = "0.2.78", features = [
|
||||||
"nightly",
|
"nightly",
|
||||||
"serde-serialize"
|
"serde-serialize"
|
||||||
] }
|
] }
|
||||||
|
@ -13,6 +13,9 @@ use futures::task::LocalFutureObj;
|
|||||||
use futures::task::LocalSpawn;
|
use futures::task::LocalSpawn;
|
||||||
use futures::task::SpawnError;
|
use futures::task::SpawnError;
|
||||||
|
|
||||||
|
/// An alias for a main animation loop.
|
||||||
|
pub type MainLoop = animation::Loop<Box<dyn FnMut(animation::TimeInfo)>>;
|
||||||
|
|
||||||
/// Executor. Uses a single-threaded `LocalPool` underneath, relying on ensogl's
|
/// Executor. Uses a single-threaded `LocalPool` underneath, relying on ensogl's
|
||||||
/// `animation::DynamicLoop` to do as much progress as possible on every animation frame.
|
/// `animation::DynamicLoop` to do as much progress as possible on every animation frame.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -22,7 +25,7 @@ pub struct EventLoopExecutor {
|
|||||||
/// Executor's spawner handle.
|
/// Executor's spawner handle.
|
||||||
pub spawner: LocalSpawner,
|
pub spawner: LocalSpawner,
|
||||||
/// Event loop that calls us on each frame.
|
/// Event loop that calls us on each frame.
|
||||||
event_loop: Option<animation::DynamicLoop>,
|
event_loop: Option<MainLoop>,
|
||||||
/// Handle to the callback - if dropped, loop would have stopped calling us.
|
/// Handle to the callback - if dropped, loop would have stopped calling us.
|
||||||
/// Also owns a shared handle to the `executor`.
|
/// Also owns a shared handle to the `executor`.
|
||||||
cb_handle: Option<callback::Handle>,
|
cb_handle: Option<callback::Handle>,
|
||||||
@ -44,14 +47,14 @@ impl EventLoopExecutor {
|
|||||||
///loop will live as long as this executor.
|
///loop will live as long as this executor.
|
||||||
pub fn new_running() -> EventLoopExecutor {
|
pub fn new_running() -> EventLoopExecutor {
|
||||||
let mut executor = EventLoopExecutor::new();
|
let mut executor = EventLoopExecutor::new();
|
||||||
executor.start_running(animation::DynamicLoop::new());
|
executor.start_running();
|
||||||
executor
|
executor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a callback compatible with `animation::DynamicLoop` that once called shall
|
/// Returns a callback compatible with `animation::DynamicLoop` that once called shall
|
||||||
/// attempt achieving as much progress on this executor's tasks as possible
|
/// attempt achieving as much progress on this executor's tasks as possible
|
||||||
/// without stalling.
|
/// without stalling.
|
||||||
pub fn runner(&self) -> impl animation::DynamicLoopCallback {
|
pub fn runner(&self) -> impl FnMut(animation::TimeInfo) {
|
||||||
let executor = self.executor.clone();
|
let executor = self.executor.clone();
|
||||||
move |_| {
|
move |_| {
|
||||||
// Safe, because this is the only place borrowing executor and loop
|
// Safe, because this is the only place borrowing executor and loop
|
||||||
@ -67,11 +70,8 @@ impl EventLoopExecutor {
|
|||||||
///
|
///
|
||||||
/// The executor will keep copy of this loop handle, so caller is not
|
/// The executor will keep copy of this loop handle, so caller is not
|
||||||
/// required to keep it alive.
|
/// required to keep it alive.
|
||||||
pub fn start_running(&mut self, event_loop: animation::DynamicLoop) {
|
fn start_running(&mut self) {
|
||||||
let cb = self.runner();
|
self.event_loop = Some(MainLoop::new(Box::new(self.runner())));
|
||||||
|
|
||||||
self.cb_handle = Some(event_loop.on_frame(cb));
|
|
||||||
self.event_loop = Some(event_loop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops event loop (previously assigned by `run` method) from calling this
|
/// Stops event loop (previously assigned by `run` method) from calling this
|
||||||
|
@ -62,7 +62,7 @@ impl Ide {
|
|||||||
|
|
||||||
fn alive_log_sending_loop(&self) -> impl Future<Output = ()> + 'static {
|
fn alive_log_sending_loop(&self) -> impl Future<Output = ()> + 'static {
|
||||||
let network = &self.network;
|
let network = &self.network;
|
||||||
let scene = self.ensogl_app.display.scene();
|
let scene = &self.ensogl_app.display.default_scene;
|
||||||
let mouse = &scene.mouse.frp;
|
let mouse = &scene.mouse.frp;
|
||||||
let keyboard = &scene.keyboard.frp;
|
let keyboard = &scene.keyboard.frp;
|
||||||
|
|
||||||
|
@ -63,9 +63,9 @@ impl Initializer {
|
|||||||
let executor = setup_global_executor();
|
let executor = setup_global_executor();
|
||||||
executor::global::spawn(async move {
|
executor::global::spawn(async move {
|
||||||
let ide = self.start().await;
|
let ide = self.start().await;
|
||||||
web::get_element_by_id("loader")
|
web::document
|
||||||
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()))
|
.get_element_by_id("loader")
|
||||||
.ok();
|
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()));
|
||||||
std::mem::forget(ide);
|
std::mem::forget(ide);
|
||||||
});
|
});
|
||||||
std::mem::forget(executor);
|
std::mem::forget(executor);
|
||||||
@ -75,8 +75,7 @@ impl Initializer {
|
|||||||
pub async fn start(self) -> Result<Ide, FailedIde> {
|
pub async fn start(self) -> Result<Ide, FailedIde> {
|
||||||
info!(self.logger, "Starting IDE with the following config: {self.config:?}");
|
info!(self.logger, "Starting IDE with the following config: {self.config:?}");
|
||||||
|
|
||||||
let root_element = web::get_html_element_by_id("root").unwrap();
|
let ensogl_app = ensogl::application::Application::new("root");
|
||||||
let ensogl_app = ensogl::application::Application::new(&root_element);
|
|
||||||
Initializer::register_views(&ensogl_app);
|
Initializer::register_views(&ensogl_app);
|
||||||
let view = ensogl_app.new_view::<ide_view::root::View>();
|
let view = ensogl_app.new_view::<ide_view::root::View>();
|
||||||
|
|
||||||
|
@ -67,7 +67,6 @@ pub mod transport;
|
|||||||
pub use crate::ide::*;
|
pub use crate::ide::*;
|
||||||
pub use ide_view as view;
|
pub use ide_view as view;
|
||||||
|
|
||||||
use ensogl::system::web;
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
// Those imports are required to have all EnsoGL examples entry points visible in IDE.
|
// Those imports are required to have all EnsoGL examples entry points visible in IDE.
|
||||||
@ -84,7 +83,6 @@ pub mod prelude {
|
|||||||
pub use ast::prelude::*;
|
pub use ast::prelude::*;
|
||||||
pub use enso_prelude::*;
|
pub use enso_prelude::*;
|
||||||
pub use ensogl::prelude::*;
|
pub use ensogl::prelude::*;
|
||||||
pub use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
pub use crate::constants;
|
pub use crate::constants;
|
||||||
pub use crate::controller;
|
pub use crate::controller;
|
||||||
@ -116,8 +114,6 @@ pub mod prelude {
|
|||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn entry_point_ide() {
|
pub fn entry_point_ide() {
|
||||||
web::forward_panic_hook_to_error();
|
|
||||||
|
|
||||||
ensogl_text_msdf_sys::run_once_initialized(|| {
|
ensogl_text_msdf_sys::run_once_initialized(|| {
|
||||||
// Logging of build information.
|
// Logging of build information.
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
@ -15,6 +15,7 @@ use crate::executor::global::spawn_stream_handler;
|
|||||||
use crate::presenter::graph::state::State;
|
use crate::presenter::graph::state::State;
|
||||||
|
|
||||||
use enso_frp as frp;
|
use enso_frp as frp;
|
||||||
|
use enso_web::traits::*;
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use ide_view as view;
|
use ide_view as view;
|
||||||
use ide_view::graph_editor::component::node as node_view;
|
use ide_view::graph_editor::component::node as node_view;
|
||||||
@ -22,6 +23,7 @@ use ide_view::graph_editor::component::visualization as visualization_view;
|
|||||||
use ide_view::graph_editor::EdgeEndpoint;
|
use ide_view::graph_editor::EdgeEndpoint;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===============
|
// ===============
|
||||||
// === Aliases ===
|
// === Aliases ===
|
||||||
// ===============
|
// ===============
|
||||||
@ -609,6 +611,30 @@ impl Graph {
|
|||||||
|
|
||||||
impl controller::upload::DataProvider for ensogl_drop_manager::File {
|
impl controller::upload::DataProvider for ensogl_drop_manager::File {
|
||||||
fn next_chunk(&mut self) -> LocalBoxFuture<FallibleResult<Option<Vec<u8>>>> {
|
fn next_chunk(&mut self) -> LocalBoxFuture<FallibleResult<Option<Vec<u8>>>> {
|
||||||
self.read_chunk().map(|f| f.map_err(|e| e.into())).boxed_local()
|
self.read_chunk()
|
||||||
|
.map(|f| f.map_err(|e| StringError::new(e.print_to_string()).into()))
|
||||||
|
.boxed_local()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// === Error ===
|
||||||
|
// =============
|
||||||
|
|
||||||
|
/// Generic error representation. This is used only once in the lines above. Probably should be
|
||||||
|
/// removed in the future.
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
#[fail(display = "{}", message)]
|
||||||
|
pub struct StringError {
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringError {
|
||||||
|
/// Constructor.
|
||||||
|
pub fn new(message: impl Into<String>) -> StringError {
|
||||||
|
let message = message.into();
|
||||||
|
StringError { message }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use enso_web::event::listener::Slot;
|
use enso_web::event::listener::Slot;
|
||||||
use enso_web::js_to_string;
|
use enso_web::traits::*;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use json_rpc::Transport;
|
use json_rpc::Transport;
|
||||||
@ -34,8 +34,8 @@ pub enum ConnectingError {
|
|||||||
|
|
||||||
impl ConnectingError {
|
impl ConnectingError {
|
||||||
/// Create a `ConstructionError` value from a JS value describing an error.
|
/// Create a `ConstructionError` value from a JS value describing an error.
|
||||||
pub fn construction_error(js_val: impl AsRef<JsValue>) -> Self {
|
pub fn construction_error(js_val: impl AsRef<enso_web::JsValue>) -> Self {
|
||||||
let text = js_to_string(js_val);
|
let text = js_val.as_ref().print_to_string();
|
||||||
ConnectingError::ConstructionError(text)
|
ConnectingError::ConstructionError(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,8 +54,8 @@ pub enum SendingError {
|
|||||||
|
|
||||||
impl SendingError {
|
impl SendingError {
|
||||||
/// Constructs from the error yielded by one of the JS's WebSocket sending functions.
|
/// Constructs from the error yielded by one of the JS's WebSocket sending functions.
|
||||||
pub fn from_send_error(error: JsValue) -> SendingError {
|
pub fn from_send_error(error: wasm_bindgen::JsValue) -> SendingError {
|
||||||
SendingError::FailedToSend(js_to_string(&error))
|
SendingError::FailedToSend(error.print_to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Close the socket.
|
/// Close the socket.
|
||||||
pub fn close(&mut self, reason: &str) -> Result<(), JsValue> {
|
pub fn close(&mut self, reason: &str) -> Result<(), wasm_bindgen::JsValue> {
|
||||||
// If socket was manually requested to close, it should not try to reconnect then.
|
// If socket was manually requested to close, it should not try to reconnect then.
|
||||||
self.auto_reconnect = false;
|
self.auto_reconnect = false;
|
||||||
let normal_closure = 1000;
|
let normal_closure = 1000;
|
||||||
@ -229,7 +229,7 @@ impl Model {
|
|||||||
|
|
||||||
/// Establish a new WS connection, using the same URL as the previous one.
|
/// Establish a new WS connection, using the same URL as the previous one.
|
||||||
/// All callbacks will be transferred to the new connection.
|
/// All callbacks will be transferred to the new connection.
|
||||||
pub fn reconnect(&mut self) -> Result<(), JsValue> {
|
pub fn reconnect(&mut self) -> Result<(), wasm_bindgen::JsValue> {
|
||||||
if !self.auto_reconnect {
|
if !self.auto_reconnect {
|
||||||
return Err(js_sys::Error::new("Reconnecting has been disabled").into());
|
return Err(js_sys::Error::new("Reconnecting has been disabled").into());
|
||||||
}
|
}
|
||||||
@ -256,7 +256,7 @@ impl Drop for Model {
|
|||||||
if let Err(e) = self.close("Rust Value has been dropped.") {
|
if let Err(e) = self.close("Rust Value has been dropped.") {
|
||||||
error!(
|
error!(
|
||||||
self.logger,
|
self.logger,
|
||||||
"Error when closing socket due to being dropped: {js_to_string(&e)}"
|
"Error when closing socket due to being dropped: {e.print_to_string()}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,7 +303,7 @@ impl WebSocket {
|
|||||||
move |_| {
|
move |_| {
|
||||||
if let Some(model) = model.upgrade() {
|
if let Some(model) = model.upgrade() {
|
||||||
if let Err(e) = model.borrow_mut().reconnect() {
|
if let Err(e) = model.borrow_mut().reconnect() {
|
||||||
error!(logger, "Failed to reconnect: {js_to_string(&e)}");
|
error!(logger, "Failed to reconnect: {e.print_to_string()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,7 +379,7 @@ impl WebSocket {
|
|||||||
///
|
///
|
||||||
/// WARNING: `f` works under borrow_mut and must not give away control.
|
/// WARNING: `f` works under borrow_mut and must not give away control.
|
||||||
fn send_with_open_socket<F, R>(&mut self, f: F) -> Result<R, Error>
|
fn send_with_open_socket<F, R>(&mut self, f: F) -> Result<R, Error>
|
||||||
where F: FnOnce(&mut web_sys::WebSocket) -> Result<R, JsValue> {
|
where F: FnOnce(&mut web_sys::WebSocket) -> Result<R, wasm_bindgen::JsValue> {
|
||||||
// Sending through the closed WebSocket can return Ok() with error only
|
// Sending through the closed WebSocket can return Ok() with error only
|
||||||
// appearing in the log. We explicitly check for this to get failure as
|
// appearing in the log. We explicitly check for this to get failure as
|
||||||
// early as possible.
|
// early as possible.
|
||||||
@ -433,7 +433,7 @@ impl Transport for WebSocket {
|
|||||||
let event = TransportEvent::BinaryMessage(binary_data);
|
let event = TransportEvent::BinaryMessage(binary_data);
|
||||||
channel::emit(&transmitter_copy, event);
|
channel::emit(&transmitter_copy, event);
|
||||||
} else {
|
} else {
|
||||||
info!(logger_copy, "Received other kind of message: {js_to_string(e.data())}.");
|
info!(logger_copy, "Received other kind of message: {e.data().print_to_string()}.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ ordered-float = { version = "2.7.0" }
|
|||||||
serde_json = { version = "1.0" }
|
serde_json = { version = "1.0" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
uuid = { version = "0.8", features = ["serde", "v4", "wasm-bindgen"] }
|
uuid = { version = "0.8", features = ["serde", "v4", "wasm-bindgen"] }
|
||||||
wasm-bindgen = { version = "0.2.58", features = [
|
wasm-bindgen = { version = "0.2.78", features = [
|
||||||
"nightly",
|
"nightly",
|
||||||
"serde-serialize"
|
"serde-serialize"
|
||||||
] }
|
] }
|
||||||
|
@ -17,4 +17,4 @@ ide-view = { path = "../.." }
|
|||||||
parser = { path = "../../../language/parser" }
|
parser = { path = "../../../language/parser" }
|
||||||
span-tree = { path = "../../../language/span-tree" }
|
span-tree = { path = "../../../language/span-tree" }
|
||||||
uuid = { version = "0.8", features = ["v4", "wasm-bindgen"] }
|
uuid = { version = "0.8", features = ["v4", "wasm-bindgen"] }
|
||||||
wasm-bindgen = { version = "0.2.58", features = ["nightly"] }
|
wasm-bindgen = { version = "0.2.78", features = ["nightly"] }
|
||||||
|
@ -45,10 +45,8 @@ const STUB_MODULE: &str = "from Base import all\n\nmain = IO.println \"Hello\"\n
|
|||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn entry_point_interface() {
|
pub fn entry_point_interface() {
|
||||||
web::forward_panic_hook_to_console();
|
|
||||||
web::set_stack_trace_limit();
|
|
||||||
run_once_initialized(|| {
|
run_once_initialized(|| {
|
||||||
let app = Application::new(&web::get_html_element_by_id("root").unwrap());
|
let app = Application::new("root");
|
||||||
init(&app);
|
init(&app);
|
||||||
mem::forget(app);
|
mem::forget(app);
|
||||||
});
|
});
|
||||||
@ -100,10 +98,10 @@ impl DummyTypeGenerator {
|
|||||||
// ========================
|
// ========================
|
||||||
|
|
||||||
fn init(app: &Application) {
|
fn init(app: &Application) {
|
||||||
let _bg = app.display.scene().style_sheet.var(theme::application::background);
|
let _bg = app.display.default_scene.style_sheet.var(theme::application::background);
|
||||||
|
|
||||||
let world = &app.display;
|
let world = &app.display;
|
||||||
let scene = world.scene();
|
let scene = &world.default_scene;
|
||||||
let camera = scene.camera();
|
let camera = scene.camera();
|
||||||
let navigator = Navigator::new(scene, &camera);
|
let navigator = Navigator::new(scene, &camera);
|
||||||
|
|
||||||
@ -252,7 +250,9 @@ fn init(app: &Application) {
|
|||||||
let mut to_theme_switch = 100;
|
let mut to_theme_switch = 100;
|
||||||
|
|
||||||
world
|
world
|
||||||
.on_frame(move |_| {
|
.on
|
||||||
|
.before_frame
|
||||||
|
.add(move |_| {
|
||||||
let _keep_alive = &navigator;
|
let _keep_alive = &navigator;
|
||||||
let _keep_alive = &root_view;
|
let _keep_alive = &root_view;
|
||||||
|
|
||||||
@ -292,9 +292,9 @@ fn init(app: &Application) {
|
|||||||
// Temporary code removing the web-loader instance.
|
// Temporary code removing the web-loader instance.
|
||||||
// To be changed in the future.
|
// To be changed in the future.
|
||||||
if was_rendered && !loader_hidden {
|
if was_rendered && !loader_hidden {
|
||||||
web::get_element_by_id("loader")
|
web::document
|
||||||
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()))
|
.get_element_by_id("loader")
|
||||||
.ok();
|
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()));
|
||||||
loader_hidden = true;
|
loader_hidden = true;
|
||||||
}
|
}
|
||||||
was_rendered = true;
|
was_rendered = true;
|
||||||
|
@ -16,4 +16,4 @@ ide-view = { path = "../.." }
|
|||||||
js-sys = { version = "0.3.28" }
|
js-sys = { version = "0.3.28" }
|
||||||
nalgebra = { version = "0.26.1" }
|
nalgebra = { version = "0.26.1" }
|
||||||
serde_json = { version = "1.0" }
|
serde_json = { version = "1.0" }
|
||||||
wasm-bindgen = { version = "0.2.58", features = ["nightly"] }
|
wasm-bindgen = { version = "0.2.78", features = ["nightly"] }
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
use ensogl::prelude::*;
|
use ensogl::prelude::*;
|
||||||
|
|
||||||
|
use ensogl::animation;
|
||||||
use ensogl::application::Application;
|
use ensogl::application::Application;
|
||||||
use ensogl::display::navigation::navigator::Navigator;
|
use ensogl::display::navigation::navigator::Navigator;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
@ -22,6 +23,8 @@ use js_sys::Math::sin;
|
|||||||
use nalgebra::Vector2;
|
use nalgebra::Vector2;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn generate_data(seconds: f64) -> Vec<Vector2<f32>> {
|
fn generate_data(seconds: f64) -> Vec<Vector2<f32>> {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
for x in 0..100 {
|
for x in 0..100 {
|
||||||
@ -89,10 +92,8 @@ fn constructor_graph() -> visualization::java_script::Definition {
|
|||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[allow(dead_code, missing_docs)]
|
#[allow(dead_code, missing_docs)]
|
||||||
pub fn entry_point_visualization() {
|
pub fn entry_point_visualization() {
|
||||||
web::forward_panic_hook_to_console();
|
|
||||||
web::set_stack_trace_limit();
|
|
||||||
run_once_initialized(|| {
|
run_once_initialized(|| {
|
||||||
let app = Application::new(&web::get_html_element_by_id("root").unwrap());
|
let app = Application::new("root");
|
||||||
init(&app);
|
init(&app);
|
||||||
std::mem::forget(app);
|
std::mem::forget(app);
|
||||||
});
|
});
|
||||||
@ -100,7 +101,7 @@ pub fn entry_point_visualization() {
|
|||||||
|
|
||||||
fn init(app: &Application) {
|
fn init(app: &Application) {
|
||||||
let world = &app.display;
|
let world = &app.display;
|
||||||
let scene = world.scene();
|
let scene = &world.default_scene;
|
||||||
let camera = scene.camera();
|
let camera = scene.camera();
|
||||||
let navigator = Navigator::new(scene, &camera);
|
let navigator = Navigator::new(scene, &camera);
|
||||||
let registry = Registry::new();
|
let registry = Registry::new();
|
||||||
@ -124,7 +125,9 @@ fn init(app: &Application) {
|
|||||||
let mut was_rendered = false;
|
let mut was_rendered = false;
|
||||||
let mut loader_hidden = false;
|
let mut loader_hidden = false;
|
||||||
world
|
world
|
||||||
.on_frame(move |time_info| {
|
.on
|
||||||
|
.before_frame
|
||||||
|
.add(move |time_info: animation::TimeInfo| {
|
||||||
let _keep_alive = &navigator;
|
let _keep_alive = &navigator;
|
||||||
|
|
||||||
let data = generate_data((time_info.local / 1000.0).into());
|
let data = generate_data((time_info.local / 1000.0).into());
|
||||||
@ -137,9 +140,9 @@ fn init(app: &Application) {
|
|||||||
// Temporary code removing the web-loader instance.
|
// Temporary code removing the web-loader instance.
|
||||||
// To be changed in the future.
|
// To be changed in the future.
|
||||||
if was_rendered && !loader_hidden {
|
if was_rendered && !loader_hidden {
|
||||||
web::get_element_by_id("loader")
|
web::document
|
||||||
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()))
|
.get_element_by_id("loader")
|
||||||
.ok();
|
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()));
|
||||||
loader_hidden = true;
|
loader_hidden = true;
|
||||||
}
|
}
|
||||||
was_rendered = true;
|
was_rendered = true;
|
||||||
|
@ -33,7 +33,7 @@ serde_json = { version = "1.0" }
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
sourcemap = "6.0"
|
sourcemap = "6.0"
|
||||||
uuid = { version = "0.8", features = ["serde", "v4", "wasm-bindgen"] }
|
uuid = { version = "0.8", features = ["serde", "v4", "wasm-bindgen"] }
|
||||||
wasm-bindgen = { version = "0.2.58", features = ["nightly", "serde-serialize"] }
|
wasm-bindgen = { version = "0.2.78", features = ["nightly", "serde-serialize"] }
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -14,8 +14,7 @@ use ensogl::display::scene::Scene;
|
|||||||
use ensogl::display::shape::primitive::StyleWatch;
|
use ensogl::display::shape::primitive::StyleWatch;
|
||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::AttributeSetter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl::system::web::StyleSetter;
|
|
||||||
use ensogl_hardcoded_theme;
|
use ensogl_hardcoded_theme;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@ -160,7 +159,7 @@ impl Model {
|
|||||||
/// Constructor.
|
/// Constructor.
|
||||||
fn new(scene: Scene) -> Self {
|
fn new(scene: Scene) -> Self {
|
||||||
let logger = Logger::new("RawText");
|
let logger = Logger::new("RawText");
|
||||||
let div = web::create_div();
|
let div = web::document.create_div_or_panic();
|
||||||
let dom = DomSymbol::new(&div);
|
let dom = DomSymbol::new(&div);
|
||||||
let size = Rc::new(Cell::new(Vector2(200.0, 200.0)));
|
let size = Rc::new(Cell::new(Vector2(200.0, 200.0)));
|
||||||
let displayed = Rc::new(CloneCell::new(Kind::Panic));
|
let displayed = Rc::new(CloneCell::new(Kind::Panic));
|
||||||
@ -169,15 +168,15 @@ impl Model {
|
|||||||
let styles = StyleWatch::new(&scene.style_sheet);
|
let styles = StyleWatch::new(&scene.style_sheet);
|
||||||
let padding_text = format!("{}px", PADDING_TEXT);
|
let padding_text = format!("{}px", PADDING_TEXT);
|
||||||
|
|
||||||
dom.dom().set_attribute_or_warn("class", "visualization scrollable", &logger);
|
dom.dom().set_attribute_or_warn("class", "visualization scrollable");
|
||||||
dom.dom().set_style_or_warn("overflow-x", "hidden", &logger);
|
dom.dom().set_style_or_warn("overflow-x", "hidden");
|
||||||
dom.dom().set_style_or_warn("overflow-y", "auto", &logger);
|
dom.dom().set_style_or_warn("overflow-y", "auto");
|
||||||
dom.dom().set_style_or_warn("font-family", "DejaVuSansMonoBook", &logger);
|
dom.dom().set_style_or_warn("font-family", "DejaVuSansMonoBook");
|
||||||
dom.dom().set_style_or_warn("font-size", "12px", &logger);
|
dom.dom().set_style_or_warn("font-size", "12px");
|
||||||
dom.dom().set_style_or_warn("border-radius", "14px", &logger);
|
dom.dom().set_style_or_warn("border-radius", "14px");
|
||||||
dom.dom().set_style_or_warn("padding-left", &padding_text, &logger);
|
dom.dom().set_style_or_warn("padding-left", &padding_text);
|
||||||
dom.dom().set_style_or_warn("padding-top", &padding_text, &logger);
|
dom.dom().set_style_or_warn("padding-top", &padding_text);
|
||||||
dom.dom().set_style_or_warn("pointer-events", "auto", &logger);
|
dom.dom().set_style_or_warn("pointer-events", "auto");
|
||||||
|
|
||||||
scene.dom.layers.back.manage(&dom);
|
scene.dom.layers.back.manage(&dom);
|
||||||
Model { logger, dom, size, styles, displayed, messages, scene }.init()
|
Model { logger, dom, size, styles, displayed, messages, scene }.init()
|
||||||
@ -243,7 +242,7 @@ impl Model {
|
|||||||
let green = text_color.green * 255.0;
|
let green = text_color.green * 255.0;
|
||||||
let blue = text_color.blue * 255.0;
|
let blue = text_color.blue * 255.0;
|
||||||
let text_color = format!("rgba({},{},{},{})", red, green, blue, text_color.alpha);
|
let text_color = format!("rgba({},{},{},{})", red, green, blue, text_color.alpha);
|
||||||
self.dom.dom().set_style_or_warn("color", text_color, &self.logger);
|
self.dom.dom().set_style_or_warn("color", text_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_layer(&self, layer: Layer) {
|
fn set_layer(&self, layer: Layer) {
|
||||||
|
@ -11,8 +11,7 @@ use ensogl::display::scene::Scene;
|
|||||||
use ensogl::display::shape::primitive::StyleWatch;
|
use ensogl::display::shape::primitive::StyleWatch;
|
||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::AttributeSetter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl::system::web::StyleSetter;
|
|
||||||
use ensogl_hardcoded_theme;
|
use ensogl_hardcoded_theme;
|
||||||
|
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ impl RawTextModel {
|
|||||||
/// Constructor.
|
/// Constructor.
|
||||||
fn new(scene: Scene) -> Self {
|
fn new(scene: Scene) -> Self {
|
||||||
let logger = Logger::new("RawText");
|
let logger = Logger::new("RawText");
|
||||||
let div = web::create_div();
|
let div = web::document.create_div_or_panic();
|
||||||
let dom = DomSymbol::new(&div);
|
let dom = DomSymbol::new(&div);
|
||||||
let size = Rc::new(Cell::new(Vector2(200.0, 200.0)));
|
let size = Rc::new(Cell::new(Vector2(200.0, 200.0)));
|
||||||
|
|
||||||
@ -100,16 +99,16 @@ impl RawTextModel {
|
|||||||
let text_color = format!("rgba({},{},{},{})", _red, _green, _blue, text_color.alpha);
|
let text_color = format!("rgba({},{},{},{})", _red, _green, _blue, text_color.alpha);
|
||||||
let padding_text = format!("{}px", PADDING_TEXT);
|
let padding_text = format!("{}px", PADDING_TEXT);
|
||||||
|
|
||||||
dom.dom().set_attribute_or_warn("class", "visualization scrollable", &logger);
|
dom.dom().set_attribute_or_warn("class", "visualization scrollable");
|
||||||
dom.dom().set_style_or_warn("white-space", "pre", &logger);
|
dom.dom().set_style_or_warn("white-space", "pre");
|
||||||
dom.dom().set_style_or_warn("overflow-y", "auto", &logger);
|
dom.dom().set_style_or_warn("overflow-y", "auto");
|
||||||
dom.dom().set_style_or_warn("overflow-x", "auto", &logger);
|
dom.dom().set_style_or_warn("overflow-x", "auto");
|
||||||
dom.dom().set_style_or_warn("font-family", "DejaVuSansMonoBook", &logger);
|
dom.dom().set_style_or_warn("font-family", "DejaVuSansMonoBook");
|
||||||
dom.dom().set_style_or_warn("font-size", "12px", &logger);
|
dom.dom().set_style_or_warn("font-size", "12px");
|
||||||
dom.dom().set_style_or_warn("padding-left", &padding_text, &logger);
|
dom.dom().set_style_or_warn("padding-left", &padding_text);
|
||||||
dom.dom().set_style_or_warn("padding-top", &padding_text, &logger);
|
dom.dom().set_style_or_warn("padding-top", &padding_text);
|
||||||
dom.dom().set_style_or_warn("color", text_color, &logger);
|
dom.dom().set_style_or_warn("color", text_color);
|
||||||
dom.dom().set_style_or_warn("pointer-events", "auto", &logger);
|
dom.dom().set_style_or_warn("pointer-events", "auto");
|
||||||
|
|
||||||
scene.dom.layers.back.manage(&dom);
|
scene.dom.layers.back.manage(&dom);
|
||||||
RawTextModel { logger, dom, size, scene }.init()
|
RawTextModel { logger, dom, size, scene }.init()
|
||||||
|
@ -97,7 +97,7 @@ impl AddNodeButton {
|
|||||||
pub fn new(app: &Application) -> Self {
|
pub fn new(app: &Application) -> Self {
|
||||||
let view = ensogl_component::button::View::new(app);
|
let view = ensogl_component::button::View::new(app);
|
||||||
let network = frp::Network::new("AddNodeButton");
|
let network = frp::Network::new("AddNodeButton");
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let camera = scene.camera();
|
let camera = scene.camera();
|
||||||
let style_watch = StyleWatchFrp::new(&scene.style_sheet);
|
let style_watch = StyleWatchFrp::new(&scene.style_sheet);
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ impl BreadcrumbsModel {
|
|||||||
/// The `gap_width` describes an empty space on the left of all the content. This space will be
|
/// The `gap_width` describes an empty space on the left of all the content. This space will be
|
||||||
/// covered by the background and is intended to make room for windows control buttons.
|
/// covered by the background and is intended to make room for windows control buttons.
|
||||||
pub fn new(app: Application, frp: &Frp) -> Self {
|
pub fn new(app: Application, frp: &Frp) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let project_name = app.new_view();
|
let project_name = app.new_view();
|
||||||
let logger = Logger::new("Breadcrumbs");
|
let logger = Logger::new("Breadcrumbs");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
@ -450,7 +450,7 @@ pub struct Breadcrumbs {
|
|||||||
impl Breadcrumbs {
|
impl Breadcrumbs {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(app: Application) -> Self {
|
pub fn new(app: Application) -> Self {
|
||||||
let scene = app.display.scene().clone_ref();
|
let scene = app.display.default_scene.clone_ref();
|
||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let model = Rc::new(BreadcrumbsModel::new(app, &frp));
|
let model = Rc::new(BreadcrumbsModel::new(app, &frp));
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
|
@ -279,7 +279,7 @@ impl BreadcrumbModel {
|
|||||||
method_pointer: &MethodPointer,
|
method_pointer: &MethodPointer,
|
||||||
expression_id: &ast::Id,
|
expression_id: &ast::Id,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new("Breadcrumbs");
|
let logger = Logger::new("Breadcrumbs");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let view_logger = Logger::new_sub(&logger, "view_logger");
|
let view_logger = Logger::new_sub(&logger, "view_logger");
|
||||||
@ -492,7 +492,7 @@ impl Breadcrumb {
|
|||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let model = Rc::new(BreadcrumbModel::new(app, &frp, method_pointer, expression_id));
|
let model = Rc::new(BreadcrumbModel::new(app, &frp, method_pointer, expression_id));
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
|
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||||
// system (#795)
|
// system (#795)
|
||||||
|
@ -134,7 +134,7 @@ impl ProjectNameModel {
|
|||||||
/// Constructor.
|
/// Constructor.
|
||||||
fn new(app: &Application) -> Self {
|
fn new(app: &Application) -> Self {
|
||||||
let app = app.clone_ref();
|
let app = app.clone_ref();
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new("ProjectName");
|
let logger = Logger::new("ProjectName");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||||
@ -251,7 +251,7 @@ impl ProjectName {
|
|||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let model = Rc::new(ProjectNameModel::new(app));
|
let model = Rc::new(ProjectNameModel::new(app));
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let text = &model.text_field.frp;
|
let text = &model.text_field.frp;
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||||
// system (#795)
|
// system (#795)
|
||||||
|
@ -1161,7 +1161,7 @@ impl Edge {
|
|||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(app: &Application) -> Self {
|
pub fn new(app: &Application) -> Self {
|
||||||
let network = frp::Network::new("node_edge");
|
let network = frp::Network::new("node_edge");
|
||||||
let data = Rc::new(EdgeModelData::new(app.display.scene(), &network));
|
let data = Rc::new(EdgeModelData::new(&app.display.default_scene, &network));
|
||||||
let model = Rc::new(EdgeModel { data });
|
let model = Rc::new(EdgeModel { data });
|
||||||
Self { model, network }.init(app)
|
Self { model, network }.init(app)
|
||||||
}
|
}
|
||||||
@ -1182,7 +1182,7 @@ impl Edge {
|
|||||||
let shape_events = &self.frp.shape_events;
|
let shape_events = &self.frp.shape_events;
|
||||||
let edge_color = color::Animation::new(network);
|
let edge_color = color::Animation::new(network);
|
||||||
let edge_focus_color = color::Animation::new(network);
|
let edge_focus_color = color::Animation::new(network);
|
||||||
let _style = StyleWatch::new(&app.display.scene().style_sheet);
|
let _style = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
|
|
||||||
model.data.front.register_proxy_frp(network, &input.shape_events);
|
model.data.front.register_proxy_frp(network, &input.shape_events);
|
||||||
model.data.back.register_proxy_frp(network, &input.shape_events);
|
model.data.back.register_proxy_frp(network, &input.shape_events);
|
||||||
|
@ -426,7 +426,7 @@ impl NodeModel {
|
|||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(app: &Application, registry: visualization::Registry) -> Self {
|
pub fn new(app: &Application, registry: visualization::Registry) -> Self {
|
||||||
ensogl::shapes_order_dependencies! {
|
ensogl::shapes_order_dependencies! {
|
||||||
app.display.scene() => {
|
app.display.default_scene => {
|
||||||
//TODO[ao] The two lines below should not be needed - the ordering should be
|
//TODO[ao] The two lines below should not be needed - the ordering should be
|
||||||
// transitive. But removing them causes a visual glitches described in
|
// transitive. But removing them causes a visual glitches described in
|
||||||
// https://github.com/enso-org/ide/issues/1624
|
// https://github.com/enso-org/ide/issues/1624
|
||||||
@ -449,7 +449,7 @@ impl NodeModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new("node");
|
let logger = Logger::new("node");
|
||||||
|
|
||||||
let main_logger = Logger::new_sub(&logger, "main_area");
|
let main_logger = Logger::new_sub(&logger, "main_area");
|
||||||
@ -495,7 +495,7 @@ impl NodeModel {
|
|||||||
let output = output::Area::new(&logger, app);
|
let output = output::Area::new(&logger, app);
|
||||||
display_object.add_child(&output);
|
display_object.add_child(&output);
|
||||||
|
|
||||||
let style = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
|
|
||||||
let comment = text::Area::new(app);
|
let comment = text::Area::new(app);
|
||||||
display_object.add_child(&comment);
|
display_object.add_child(&comment);
|
||||||
@ -624,7 +624,7 @@ impl Node {
|
|||||||
// in https://github.com/enso-org/ide/issues/1031
|
// in https://github.com/enso-org/ide/issues/1031
|
||||||
// let comment_color = color::Animation::new(network);
|
// let comment_color = color::Animation::new(network);
|
||||||
let error_color_anim = color::Animation::new(network);
|
let error_color_anim = color::Animation::new(network);
|
||||||
let style = StyleWatch::new(&app.display.scene().style_sheet);
|
let style = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
let style_frp = &model.style;
|
let style_frp = &model.style;
|
||||||
let action_bar = &model.action_bar.frp;
|
let action_bar = &model.action_bar.frp;
|
||||||
// Hook up the display object position updates to the node's FRP. Required to calculate the
|
// Hook up the display object position updates to the node's FRP. Required to calculate the
|
||||||
|
@ -134,7 +134,7 @@ struct Model {
|
|||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
fn new(logger: impl AnyLogger, app: &Application) -> Self {
|
fn new(logger: impl AnyLogger, app: &Application) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new_sub(logger, "ActionBar");
|
let logger = Logger::new_sub(logger, "ActionBar");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let hover_area = hover_area::View::new(&logger);
|
let hover_area = hover_area::View::new(&logger);
|
||||||
|
@ -9,7 +9,7 @@ use ensogl::display::shape::StyleWatch;
|
|||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
use ensogl::display::Scene;
|
use ensogl::display::Scene;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::StyleSetter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl_component::shadow;
|
use ensogl_component::shadow;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@ -93,7 +93,7 @@ impl Container {
|
|||||||
let scene = scene.clone_ref();
|
let scene = scene.clone_ref();
|
||||||
let logger = Logger::new("error::Container");
|
let logger = Logger::new("error::Container");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let background_dom = Self::create_background_dom(&logger, &scene);
|
let background_dom = Self::create_background_dom(&scene);
|
||||||
let visualization = error_visualization::Error::new(&scene);
|
let visualization = error_visualization::Error::new(&scene);
|
||||||
|
|
||||||
display_object.add_child(&background_dom);
|
display_object.add_child(&background_dom);
|
||||||
@ -102,7 +102,7 @@ impl Container {
|
|||||||
Self { logger, visualization, scene, background_dom, display_object }
|
Self { logger, visualization, scene, background_dom, display_object }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_background_dom(logger: &Logger, scene: &Scene) -> DomSymbol {
|
fn create_background_dom(scene: &Scene) -> DomSymbol {
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||||
// system (#795)
|
// system (#795)
|
||||||
let styles = StyleWatch::new(&scene.style_sheet);
|
let styles = StyleWatch::new(&scene.style_sheet);
|
||||||
@ -113,21 +113,21 @@ impl Container {
|
|||||||
let bg_blue = bg_color.blue * 255.0;
|
let bg_blue = bg_color.blue * 255.0;
|
||||||
let bg_hex = format!("rgba({},{},{},{})", bg_red, bg_green, bg_blue, bg_color.alpha);
|
let bg_hex = format!("rgba({},{},{},{})", bg_red, bg_green, bg_blue, bg_color.alpha);
|
||||||
|
|
||||||
let div = web::create_div();
|
let div = web::document.create_div_or_panic();
|
||||||
let background_dom = DomSymbol::new(&div);
|
let background_dom = DomSymbol::new(&div);
|
||||||
let (width, height) = SIZE;
|
let (width, height) = SIZE;
|
||||||
let width = format!("{}.px", width);
|
let width = format!("{}.px", width);
|
||||||
let height = format!("{}.px", height);
|
let height = format!("{}.px", height);
|
||||||
let z_index = Z_INDEX.to_string();
|
let z_index = Z_INDEX.to_string();
|
||||||
let border_radius = format!("{}.px", BORDER_RADIUS);
|
let border_radius = format!("{}.px", BORDER_RADIUS);
|
||||||
background_dom.dom().set_style_or_warn("width", width, logger);
|
background_dom.dom().set_style_or_warn("width", width);
|
||||||
background_dom.dom().set_style_or_warn("height", height, logger);
|
background_dom.dom().set_style_or_warn("height", height);
|
||||||
background_dom.dom().set_style_or_warn("z-index", z_index, logger);
|
background_dom.dom().set_style_or_warn("z-index", z_index);
|
||||||
background_dom.dom().set_style_or_warn("overflow-y", "auto", logger);
|
background_dom.dom().set_style_or_warn("overflow-y", "auto");
|
||||||
background_dom.dom().set_style_or_warn("overflow-x", "auto", logger);
|
background_dom.dom().set_style_or_warn("overflow-x", "auto");
|
||||||
background_dom.dom().set_style_or_warn("background", bg_hex, logger);
|
background_dom.dom().set_style_or_warn("background", bg_hex);
|
||||||
background_dom.dom().set_style_or_warn("border-radius", border_radius, logger);
|
background_dom.dom().set_style_or_warn("border-radius", border_radius);
|
||||||
shadow::add_to_dom_element(&background_dom, &styles, logger);
|
shadow::add_to_dom_element(&background_dom, &styles);
|
||||||
background_dom
|
background_dom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,8 +245,8 @@ impl Model {
|
|||||||
let label = app.new_view::<text::Area>();
|
let label = app.new_view::<text::Area>();
|
||||||
let id_crumbs_map = default();
|
let id_crumbs_map = default();
|
||||||
let expression = default();
|
let expression = default();
|
||||||
let styles = StyleWatch::new(&app.display.scene().style_sheet);
|
let styles = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
let styles_frp = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let styles_frp = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
display_object.add_child(&label);
|
display_object.add_child(&label);
|
||||||
display_object.add_child(&ports);
|
display_object.add_child(&ports);
|
||||||
ports.add_child(&header);
|
ports.add_child(&header);
|
||||||
@ -268,7 +268,7 @@ impl Model {
|
|||||||
fn init(self) -> Self {
|
fn init(self) -> Self {
|
||||||
// FIXME[WD]: Depth sorting of labels to in front of the mouse pointer. Temporary solution.
|
// FIXME[WD]: Depth sorting of labels to in front of the mouse pointer. Temporary solution.
|
||||||
// It needs to be more flexible once we have proper depth management.
|
// It needs to be more flexible once we have proper depth management.
|
||||||
let scene = self.app.display.scene();
|
let scene = &self.app.display.default_scene;
|
||||||
self.label.remove_from_scene_layer(&scene.layers.main);
|
self.label.remove_from_scene_layer(&scene.layers.main);
|
||||||
self.label.add_to_scene_layer(&scene.layers.label);
|
self.label.add_to_scene_layer(&scene.layers.label);
|
||||||
|
|
||||||
@ -289,7 +289,7 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scene(&self) -> &Scene {
|
fn scene(&self) -> &Scene {
|
||||||
self.app.display.scene()
|
&self.app.display.default_scene
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the provided function on the target port if exists.
|
/// Run the provided function on the target port if exists.
|
||||||
@ -614,7 +614,7 @@ impl Area {
|
|||||||
|
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for
|
||||||
// shape system (#795)
|
// shape system (#795)
|
||||||
let style_sheet = &self.model.app.display.scene().style_sheet;
|
let style_sheet = &self.model.app.display.default_scene.style_sheet;
|
||||||
let styles = StyleWatch::new(style_sheet);
|
let styles = StyleWatch::new(style_sheet);
|
||||||
let styles_frp = &self.model.styles_frp;
|
let styles_frp = &self.model.styles_frp;
|
||||||
let any_type_sel_color = styles_frp.get_color(theme::code::types::any::selection);
|
let any_type_sel_color = styles_frp.get_color(theme::code::types::any::selection);
|
||||||
|
@ -175,8 +175,8 @@ impl Model {
|
|||||||
let id_crumbs_map = default();
|
let id_crumbs_map = default();
|
||||||
let expression = default();
|
let expression = default();
|
||||||
let port_count = default();
|
let port_count = default();
|
||||||
let styles = StyleWatch::new(&app.display.scene().style_sheet);
|
let styles = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
let styles_frp = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let styles_frp = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
let frp = frp.output.clone_ref();
|
let frp = frp.output.clone_ref();
|
||||||
display_object.add_child(&label);
|
display_object.add_child(&label);
|
||||||
display_object.add_child(&ports);
|
display_object.add_child(&ports);
|
||||||
@ -199,7 +199,7 @@ impl Model {
|
|||||||
fn init(self) -> Self {
|
fn init(self) -> Self {
|
||||||
// FIXME[WD]: Depth sorting of labels to in front of the mouse pointer. Temporary solution.
|
// FIXME[WD]: Depth sorting of labels to in front of the mouse pointer. Temporary solution.
|
||||||
// It needs to be more flexible once we have proper depth management.
|
// It needs to be more flexible once we have proper depth management.
|
||||||
let scene = self.app.display.scene();
|
let scene = &self.app.display.default_scene;
|
||||||
self.label.remove_from_scene_layer(&scene.layers.main);
|
self.label.remove_from_scene_layer(&scene.layers.main);
|
||||||
self.label.add_to_scene_layer(&scene.layers.label);
|
self.label.add_to_scene_layer(&scene.layers.label);
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ impl Deref for ProfilingLabel {
|
|||||||
impl ProfilingLabel {
|
impl ProfilingLabel {
|
||||||
/// Constructs a `ProfilingLabel` for the given application.
|
/// Constructs a `ProfilingLabel` for the given application.
|
||||||
pub fn new(app: &Application) -> Self {
|
pub fn new(app: &Application) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let root = display::object::Instance::new(Logger::new("ProfilingIndicator"));
|
let root = display::object::Instance::new(Logger::new("ProfilingIndicator"));
|
||||||
|
|
||||||
let label = text::Area::new(app);
|
let label = text::Area::new(app);
|
||||||
|
@ -158,7 +158,7 @@ impl StatusIndicator {
|
|||||||
|
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||||
// system (#795)
|
// system (#795)
|
||||||
let styles = StyleWatch::new(&app.display.scene().style_sheet);
|
let styles = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
|
|
||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
frp.source.status <+ frp.input.set_status;
|
frp.source.status <+ frp.input.set_status;
|
||||||
|
@ -143,7 +143,7 @@ impl Deref for Button {
|
|||||||
impl Button {
|
impl Button {
|
||||||
/// Constructs a new button for toggling the editor's view mode.
|
/// Constructs a new button for toggling the editor's view mode.
|
||||||
pub fn new(app: &Application) -> Button {
|
pub fn new(app: &Application) -> Button {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let styles = StyleWatchFrp::new(&scene.style_sheet);
|
let styles = StyleWatchFrp::new(&scene.style_sheet);
|
||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
|
@ -188,7 +188,7 @@ impl Tooltip {
|
|||||||
|
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||||
// system (#795)
|
// system (#795)
|
||||||
let styles = StyleWatch::new(&app.display.scene().style_sheet);
|
let styles = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
let hide_delay_duration_ms = styles.get_number_or(
|
let hide_delay_duration_ms = styles.get_number_or(
|
||||||
ensogl_hardcoded_theme::application::tooltip::hide_delay_duration_ms,
|
ensogl_hardcoded_theme::application::tooltip::hide_delay_duration_ms,
|
||||||
0.0,
|
0.0,
|
||||||
|
@ -31,7 +31,7 @@ use ensogl::display::DomSymbol;
|
|||||||
use ensogl::Animation;
|
use ensogl::Animation;
|
||||||
|
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::StyleSetter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl_component::shadow;
|
use ensogl_component::shadow;
|
||||||
|
|
||||||
|
|
||||||
@ -192,19 +192,19 @@ impl View {
|
|||||||
bg_color.alpha
|
bg_color.alpha
|
||||||
);
|
);
|
||||||
|
|
||||||
let div = web::create_div();
|
let div = web::document.create_div_or_panic();
|
||||||
let background_dom = DomSymbol::new(&div);
|
let background_dom = DomSymbol::new(&div);
|
||||||
// TODO : We added a HTML background to the `View`, because "shape" background was
|
// TODO : We added a HTML background to the `View`, because "shape" background was
|
||||||
// overlapping the JS visualization. This should be further investigated
|
// overlapping the JS visualization. This should be further investigated
|
||||||
// while fixing rust visualization displaying. (#796)
|
// while fixing rust visualization displaying. (#796)
|
||||||
background_dom.dom().set_style_or_warn("width", "0", &logger);
|
background_dom.dom().set_style_or_warn("width", "0");
|
||||||
background_dom.dom().set_style_or_warn("height", "0", &logger);
|
background_dom.dom().set_style_or_warn("height", "0");
|
||||||
background_dom.dom().set_style_or_warn("z-index", "1", &logger);
|
background_dom.dom().set_style_or_warn("z-index", "1");
|
||||||
background_dom.dom().set_style_or_warn("overflow-y", "auto", &logger);
|
background_dom.dom().set_style_or_warn("overflow-y", "auto");
|
||||||
background_dom.dom().set_style_or_warn("overflow-x", "auto", &logger);
|
background_dom.dom().set_style_or_warn("overflow-x", "auto");
|
||||||
background_dom.dom().set_style_or_warn("background", bg_hex, &logger);
|
background_dom.dom().set_style_or_warn("background", bg_hex);
|
||||||
background_dom.dom().set_style_or_warn("border-radius", "14px", &logger);
|
background_dom.dom().set_style_or_warn("border-radius", "14px");
|
||||||
shadow::add_to_dom_element(&background_dom, &styles, &logger);
|
shadow::add_to_dom_element(&background_dom, &styles);
|
||||||
display_object.add_child(&background_dom);
|
display_object.add_child(&background_dom);
|
||||||
|
|
||||||
Self { display_object, background, overlay, background_dom, scene }.init()
|
Self { display_object, background, overlay, background_dom, scene }.init()
|
||||||
@ -259,7 +259,7 @@ pub struct ContainerModel {
|
|||||||
impl ContainerModel {
|
impl ContainerModel {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(logger: &Logger, app: &Application, registry: visualization::Registry) -> Self {
|
pub fn new(logger: &Logger, app: &Application, registry: visualization::Registry) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new_sub(logger, "visualization_container");
|
let logger = Logger::new_sub(logger, "visualization_container");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let drag_root = display::object::Instance::new(&logger);
|
let drag_root = display::object::Instance::new(&logger);
|
||||||
@ -391,10 +391,10 @@ impl ContainerModel {
|
|||||||
// self.fullscreen_view.background.shape.sprite.size.set(size);
|
// self.fullscreen_view.background.shape.sprite.size.set(size);
|
||||||
// self.view.background.shape.sprite.size.set(zero());
|
// self.view.background.shape.sprite.size.set(zero());
|
||||||
self.view.overlay.size.set(zero());
|
self.view.overlay.size.set(zero());
|
||||||
dom.set_style_or_warn("width", "0", &self.logger);
|
dom.set_style_or_warn("width", "0");
|
||||||
dom.set_style_or_warn("height", "0", &self.logger);
|
dom.set_style_or_warn("height", "0");
|
||||||
bg_dom.set_style_or_warn("width", format!("{}px", size[0]), &self.logger);
|
bg_dom.set_style_or_warn("width", format!("{}px", size[0]));
|
||||||
bg_dom.set_style_or_warn("height", format!("{}px", size[1]), &self.logger);
|
bg_dom.set_style_or_warn("height", format!("{}px", size[1]));
|
||||||
self.action_bar.frp.set_size.emit(Vector2::zero());
|
self.action_bar.frp.set_size.emit(Vector2::zero());
|
||||||
} else {
|
} else {
|
||||||
// self.view.background.shape.radius.set(CORNER_RADIUS);
|
// self.view.background.shape.radius.set(CORNER_RADIUS);
|
||||||
@ -402,10 +402,10 @@ impl ContainerModel {
|
|||||||
self.view.background.radius.set(CORNER_RADIUS);
|
self.view.background.radius.set(CORNER_RADIUS);
|
||||||
self.view.overlay.size.set(size);
|
self.view.overlay.size.set(size);
|
||||||
self.view.background.size.set(size + 2.0 * Vector2(PADDING, PADDING));
|
self.view.background.size.set(size + 2.0 * Vector2(PADDING, PADDING));
|
||||||
dom.set_style_or_warn("width", format!("{}px", size[0]), &self.logger);
|
dom.set_style_or_warn("width", format!("{}px", size[0]));
|
||||||
dom.set_style_or_warn("height", format!("{}px", size[1]), &self.logger);
|
dom.set_style_or_warn("height", format!("{}px", size[1]));
|
||||||
bg_dom.set_style_or_warn("width", "0", &self.logger);
|
bg_dom.set_style_or_warn("width", "0");
|
||||||
bg_dom.set_style_or_warn("height", "0", &self.logger);
|
bg_dom.set_style_or_warn("height", "0");
|
||||||
// self.fullscreen_view.background.shape.sprite.size.set(zero());
|
// self.fullscreen_view.background.shape.sprite.size.set(zero());
|
||||||
|
|
||||||
let action_bar_size = Vector2::new(size.x, ACTION_BAR_HEIGHT);
|
let action_bar_size = Vector2::new(size.x, ACTION_BAR_HEIGHT);
|
||||||
|
@ -271,9 +271,9 @@ impl Model {
|
|||||||
let icons = Icons::new(logger);
|
let icons = Icons::new(logger);
|
||||||
let shapes = compound::events::MouseEvents::default();
|
let shapes = compound::events::MouseEvents::default();
|
||||||
|
|
||||||
app.display.scene().layers.below_main.add_exclusive(&hover_area);
|
app.display.default_scene.layers.below_main.add_exclusive(&hover_area);
|
||||||
app.display.scene().layers.below_main.add_exclusive(&background);
|
app.display.default_scene.layers.below_main.add_exclusive(&background);
|
||||||
app.display.scene().layers.above_nodes.add_exclusive(&icons);
|
app.display.default_scene.layers.above_nodes.add_exclusive(&icons);
|
||||||
|
|
||||||
shapes.add_sub_shape(&hover_area);
|
shapes.add_sub_shape(&hover_area);
|
||||||
shapes.add_sub_shape(&background);
|
shapes.add_sub_shape(&background);
|
||||||
@ -359,7 +359,7 @@ impl ActionBar {
|
|||||||
let network = &self.frp.network;
|
let network = &self.frp.network;
|
||||||
let frp = &self.frp;
|
let frp = &self.frp;
|
||||||
let model = &self.model;
|
let model = &self.model;
|
||||||
let mouse = &app.display.scene().mouse.frp;
|
let mouse = &app.display.default_scene.mouse.frp;
|
||||||
let visualization_chooser = &model.visualization_chooser.frp;
|
let visualization_chooser = &model.visualization_chooser.frp;
|
||||||
|
|
||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
|
@ -8,7 +8,7 @@ use ensogl::display::shape::*;
|
|||||||
use ensogl::display::traits::*;
|
use ensogl::display::traits::*;
|
||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::StyleSetter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl_hardcoded_theme as theme;
|
use ensogl_hardcoded_theme as theme;
|
||||||
|
|
||||||
|
|
||||||
@ -74,18 +74,18 @@ impl Panel {
|
|||||||
let blue = bg_color.blue * 255.0;
|
let blue = bg_color.blue * 255.0;
|
||||||
let bg_hex = format!("rgba({},{},{},{})", red, green, blue, bg_color.alpha);
|
let bg_hex = format!("rgba({},{},{},{})", red, green, blue, bg_color.alpha);
|
||||||
|
|
||||||
let div = web::create_div();
|
let div = web::document.create_div_or_panic();
|
||||||
let background_dom = DomSymbol::new(&div);
|
let background_dom = DomSymbol::new(&div);
|
||||||
// TODO : We added a HTML background to the `View`, because "shape" background was
|
// TODO : We added a HTML background to the `View`, because "shape" background was
|
||||||
// overlapping the JS visualization. This should be further investigated
|
// overlapping the JS visualization. This should be further investigated
|
||||||
// while fixing rust visualization displaying. (#796)
|
// while fixing rust visualization displaying. (#796)
|
||||||
background_dom.dom().set_style_or_warn("width", "0", &logger);
|
background_dom.dom().set_style_or_warn("width", "0");
|
||||||
background_dom.dom().set_style_or_warn("height", "0", &logger);
|
background_dom.dom().set_style_or_warn("height", "0");
|
||||||
background_dom.dom().set_style_or_warn("z-index", "1", &logger);
|
background_dom.dom().set_style_or_warn("z-index", "1");
|
||||||
background_dom.dom().set_style_or_warn("overflow-y", "auto", &logger);
|
background_dom.dom().set_style_or_warn("overflow-y", "auto");
|
||||||
background_dom.dom().set_style_or_warn("overflow-x", "auto", &logger);
|
background_dom.dom().set_style_or_warn("overflow-x", "auto");
|
||||||
background_dom.dom().set_style_or_warn("background", bg_hex, &logger);
|
background_dom.dom().set_style_or_warn("background", bg_hex);
|
||||||
background_dom.dom().set_style_or_warn("border-radius", "0", &logger);
|
background_dom.dom().set_style_or_warn("border-radius", "0");
|
||||||
display_object.add_child(&background_dom);
|
display_object.add_child(&background_dom);
|
||||||
scene.dom.layers.fullscreen_vis.manage(&background_dom);
|
scene.dom.layers.fullscreen_vis.manage(&background_dom);
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ struct Model {
|
|||||||
impl Model {
|
impl Model {
|
||||||
pub fn new(app: &Application, registry: visualization::Registry) -> Self {
|
pub fn new(app: &Application, registry: visualization::Registry) -> Self {
|
||||||
let selection_menu = drop_down_menu::DropDownMenu::new(app);
|
let selection_menu = drop_down_menu::DropDownMenu::new(app);
|
||||||
app.display.scene().layers.below_main.add_exclusive(&selection_menu);
|
app.display.default_scene.layers.below_main.add_exclusive(&selection_menu);
|
||||||
Self { selection_menu, registry }
|
Self { selection_menu, registry }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,17 +4,21 @@ use crate::prelude::*;
|
|||||||
|
|
||||||
use crate::component::type_coloring;
|
use crate::component::type_coloring;
|
||||||
use crate::component::visualization::foreign::java_script::PreprocessorCallback;
|
use crate::component::visualization::foreign::java_script::PreprocessorCallback;
|
||||||
use crate::component::visualization::instance::PreprocessorConfiguration;
|
|
||||||
use crate::Type;
|
use crate::Type;
|
||||||
|
|
||||||
use ensogl::data::color;
|
use ensogl::data::color;
|
||||||
use ensogl::display::shape::StyleWatch;
|
use ensogl::display::shape::StyleWatch;
|
||||||
use ensogl::display::style::data::DataMatch;
|
use ensogl::display::style::data::DataMatch;
|
||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
|
use ensogl::system::web::HtmlDivElement;
|
||||||
|
use ensogl::system::web::JsValue;
|
||||||
use ensogl_hardcoded_theme;
|
use ensogl_hardcoded_theme;
|
||||||
use fmt::Formatter;
|
use fmt::Formatter;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
use web_sys::HtmlDivElement;
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use crate::component::visualization::instance::PreprocessorConfiguration;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
// =================
|
||||||
@ -30,6 +34,10 @@ pub const JS_CLASS_NAME: &str = "Visualization";
|
|||||||
// === JavaScript Bindings ===
|
// === JavaScript Bindings ===
|
||||||
// ===========================
|
// ===========================
|
||||||
|
|
||||||
|
// TODO: Add docs to the WASM32 bindings below - esp. what is the difference between
|
||||||
|
// __Visualization__ and Visualization.
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[wasm_bindgen(module = "/src/component/visualization/foreign/java_script/visualization.js")]
|
#[wasm_bindgen(module = "/src/component/visualization/foreign/java_script/visualization.js")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
@ -48,8 +56,32 @@ extern "C" {
|
|||||||
pub fn emitPreprocessorChange(this: &Visualization) -> Result<(), JsValue>;
|
pub fn emitPreprocessorChange(this: &Visualization) -> Result<(), JsValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides reference to the visualizations JavaScript base class.
|
#[allow(non_snake_case)]
|
||||||
pub fn js_class() -> JsValue {
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn __Visualization__() -> JsValue {
|
||||||
|
default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub struct Visualization {}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
impl Visualization {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn emitPreprocessorChange(&self) -> Result<(), JsValue> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides reference to the [`Visualization`] JavaScript base class.
|
||||||
|
pub fn js_visualization_class() -> JsValue {
|
||||||
__Visualization__()
|
__Visualization__()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +91,7 @@ pub fn js_class() -> JsValue {
|
|||||||
// === Theme ===
|
// === Theme ===
|
||||||
// =============
|
// =============
|
||||||
|
|
||||||
/// The theming API that we expose to JS visualizations
|
/// The theming API that we expose to JS visualizations.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct JsTheme {
|
pub struct JsTheme {
|
||||||
@ -123,6 +155,7 @@ impl JsTheme {
|
|||||||
/// Data that is passed into the javascript Visualization baseclass.
|
/// Data that is passed into the javascript Visualization baseclass.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
#[allow(dead_code)] // Some fields are used by wasm32 target only.
|
||||||
pub struct JsConsArgs {
|
pub struct JsConsArgs {
|
||||||
#[wasm_bindgen(skip)]
|
#[wasm_bindgen(skip)]
|
||||||
pub root: HtmlDivElement,
|
pub root: HtmlDivElement,
|
||||||
@ -151,6 +184,7 @@ impl JsConsArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl JsConsArgs {
|
impl JsConsArgs {
|
||||||
/// Getter for the root element for the visualization.
|
/// Getter for the root element for the visualization.
|
||||||
|
@ -2,11 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! For details of the API please see:
|
//! For details of the API please see:
|
||||||
//! * docstrings in the `visualization.js` file;
|
//! * docstrings in the `visualization.js` file;
|
||||||
//! * [visualization documentation](https://dev.enso.org/docs/ide/product/visualizations.html).
|
//! * [visualization documentation](https://enso.org/docs/developer/ide/product/visualizations.html).
|
||||||
// FIXME: Can we simplify the above definition so its more minimal, yet functional?
|
|
||||||
|
|
||||||
mod function;
|
|
||||||
use function::Function;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@ -18,12 +14,13 @@ use crate::component::visualization::InstantiationResult;
|
|||||||
use crate::visualization::foreign::java_script::Sources;
|
use crate::visualization::foreign::java_script::Sources;
|
||||||
|
|
||||||
use ensogl::display::Scene;
|
use ensogl::display::Scene;
|
||||||
|
use ensogl::system::web;
|
||||||
|
use ensogl::system::web::traits::*;
|
||||||
|
use ensogl::system::web::Function;
|
||||||
|
use ensogl::system::web::JsString;
|
||||||
use ensogl::system::web::JsValue;
|
use ensogl::system::web::JsValue;
|
||||||
use fmt::Formatter;
|
use fmt::Formatter;
|
||||||
use js_sys;
|
|
||||||
use js_sys::JsString;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -63,16 +60,13 @@ impl Definition {
|
|||||||
pub fn new(project: visualization::path::Project, sources: Sources) -> Result<Self, Error> {
|
pub fn new(project: visualization::path::Project, sources: Sources) -> Result<Self, Error> {
|
||||||
let source = sources.to_string(&project);
|
let source = sources.to_string(&project);
|
||||||
let context = JsValue::NULL;
|
let context = JsValue::NULL;
|
||||||
let function = Function::new_with_args(binding::JS_CLASS_NAME, &source)
|
let function = Function::new_with_args_fixed(binding::JS_CLASS_NAME, &source)
|
||||||
.map_err(Error::InvalidFunction)?;
|
.map_err(Error::InvalidFunction)?;
|
||||||
let js_class = binding::js_class();
|
let js_class = binding::js_visualization_class();
|
||||||
let class = function.call1(&context, &js_class).map_err(Error::InvalidFunction)?;
|
let class = function.call1(&context, &js_class).map_err(Error::InvalidFunction)?;
|
||||||
|
|
||||||
let input_type = try_str_field(&class, field::INPUT_TYPE).unwrap_or_default();
|
let input_type = try_str_field(&class, field::INPUT_TYPE).unwrap_or_default();
|
||||||
|
|
||||||
let input_format = try_str_field(&class, field::INPUT_FORMAT).unwrap_or_default();
|
let input_format = try_str_field(&class, field::INPUT_FORMAT).unwrap_or_default();
|
||||||
let input_format = visualization::data::Format::from_str(&input_format).unwrap_or_default();
|
let input_format = visualization::data::Format::from_str(&input_format).unwrap_or_default();
|
||||||
|
|
||||||
let label = label(&class)?;
|
let label = label(&class)?;
|
||||||
let path = visualization::Path::new(project, label);
|
let path = visualization::Path::new(project, label);
|
||||||
let signature = visualization::Signature::new(path, input_type, input_format);
|
let signature = visualization::Signature::new(path, input_type, input_format);
|
||||||
@ -102,7 +96,7 @@ impl From<Definition> for visualization::Definition {
|
|||||||
// === Utils ===
|
// === Utils ===
|
||||||
|
|
||||||
fn try_str_field(obj: &JsValue, field: &str) -> Option<String> {
|
fn try_str_field(obj: &JsValue, field: &str) -> Option<String> {
|
||||||
let field = js_sys::Reflect::get(obj, &field.into()).ok()?;
|
let field = web::Reflect::get(obj, &field.into()).ok()?;
|
||||||
let js_string = field.dyn_ref::<JsString>()?;
|
let js_string = field.dyn_ref::<JsString>()?;
|
||||||
Some(js_string.into())
|
Some(js_string.into())
|
||||||
}
|
}
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
//! The whole content of this file consists of parts that were copied from `js_sys` just to modify
|
|
||||||
//! `new_with_args` such that it catches syntax errors. This is supposed to be temporary solution.
|
|
||||||
//! I opened a GitHub issue suggesting that this will be included in wasm-bindgen
|
|
||||||
//! (https://github.com/rustwasm/wasm-bindgen/issues/2496).
|
|
||||||
//! A PR with a suggested change to wasm-bindgen can be found here:
|
|
||||||
//! https://github.com/rustwasm/wasm-bindgen/pull/2497
|
|
||||||
|
|
||||||
use js_sys::*;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
#[wasm_bindgen(extends = Object, is_type_of = JsValue::is_function)]
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub type Function;
|
|
||||||
|
|
||||||
/// The `Function` constructor creates a new `Function` object. Calling the
|
|
||||||
/// constructor directly can create functions dynamically, but suffers from
|
|
||||||
/// security and similar (but far less significant) performance issues
|
|
||||||
/// similar to `eval`. However, unlike `eval`, the `Function` constructor
|
|
||||||
/// allows executing code in the global scope, prompting better programming
|
|
||||||
/// habits and allowing for more efficient code minification.
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
#[wasm_bindgen(constructor, catch)]
|
|
||||||
pub fn new_with_args(args: &str, body: &str) -> Result<Function, JsValue>;
|
|
||||||
|
|
||||||
/// The `call()` method calls a function with a given this value and
|
|
||||||
/// arguments provided individually.
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
#[wasm_bindgen(method, catch, js_name = call)]
|
|
||||||
pub fn call1(this: &Function, context: &JsValue, arg1: &JsValue) -> Result<JsValue, JsValue>;
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ use crate::prelude::*;
|
|||||||
|
|
||||||
use crate::component::visualization;
|
use crate::component::visualization;
|
||||||
use crate::component::visualization::instance::PreprocessorConfiguration;
|
use crate::component::visualization::instance::PreprocessorConfiguration;
|
||||||
|
use crate::component::visualization::java_script;
|
||||||
use crate::component::visualization::java_script::binding::JsConsArgs;
|
use crate::component::visualization::java_script::binding::JsConsArgs;
|
||||||
use crate::component::visualization::java_script::method;
|
use crate::component::visualization::java_script::method;
|
||||||
use crate::component::visualization::*;
|
use crate::component::visualization::*;
|
||||||
@ -24,9 +25,8 @@ use ensogl::display::DomScene;
|
|||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
use ensogl::display::Scene;
|
use ensogl::display::Scene;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl::system::web::JsValue;
|
use ensogl::system::web::JsValue;
|
||||||
use ensogl::system::web::StyleSetter;
|
|
||||||
use js_sys;
|
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
|
||||||
|
|
||||||
@ -89,8 +89,8 @@ type PreprocessorCallbackCell = Rc<RefCell<Option<Box<dyn PreprocessorCallback>>
|
|||||||
pub struct InstanceModel {
|
pub struct InstanceModel {
|
||||||
pub root_node: DomSymbol,
|
pub root_node: DomSymbol,
|
||||||
pub logger: Logger,
|
pub logger: Logger,
|
||||||
on_data_received: Rc<Option<js_sys::Function>>,
|
on_data_received: Rc<Option<web::Function>>,
|
||||||
set_size: Rc<Option<js_sys::Function>>,
|
set_size: Rc<Option<web::Function>>,
|
||||||
#[derivative(Debug = "ignore")]
|
#[derivative(Debug = "ignore")]
|
||||||
object: Rc<java_script::binding::Visualization>,
|
object: Rc<java_script::binding::Visualization>,
|
||||||
#[derivative(Debug = "ignore")]
|
#[derivative(Debug = "ignore")]
|
||||||
@ -104,8 +104,8 @@ impl InstanceModel {
|
|||||||
styles.get_color(ensogl_hardcoded_theme::graph_editor::visualization::background)
|
styles.get_color(ensogl_hardcoded_theme::graph_editor::visualization::background)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_root(scene: &Scene, logger: &Logger) -> result::Result<DomSymbol, Error> {
|
fn create_root(scene: &Scene) -> result::Result<DomSymbol, Error> {
|
||||||
let div = web::create_div();
|
let div = web::document.create_div_or_panic();
|
||||||
let root_node = DomSymbol::new(&div);
|
let root_node = DomSymbol::new(&div);
|
||||||
root_node
|
root_node
|
||||||
.dom()
|
.dom()
|
||||||
@ -117,7 +117,7 @@ impl InstanceModel {
|
|||||||
let bg_green = bg_color.green * 255.0;
|
let bg_green = bg_color.green * 255.0;
|
||||||
let bg_blue = bg_color.blue * 255.0;
|
let bg_blue = bg_color.blue * 255.0;
|
||||||
let bg_hex = format!("rgba({},{},{},{})", bg_red, bg_green, bg_blue, bg_color.alpha);
|
let bg_hex = format!("rgba({},{},{},{})", bg_red, bg_green, bg_blue, bg_color.alpha);
|
||||||
root_node.dom().set_style_or_warn("background", bg_hex, logger);
|
root_node.dom().set_style_or_warn("background", bg_hex);
|
||||||
|
|
||||||
Ok(root_node)
|
Ok(root_node)
|
||||||
}
|
}
|
||||||
@ -140,11 +140,12 @@ impl InstanceModel {
|
|||||||
(closure_cell, closure)
|
(closure_cell, closure)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
fn instantiate_class_with_args(
|
fn instantiate_class_with_args(
|
||||||
class: &JsValue,
|
class: &JsValue,
|
||||||
args: JsConsArgs,
|
args: JsConsArgs,
|
||||||
) -> result::Result<java_script::binding::Visualization, Error> {
|
) -> result::Result<java_script::binding::Visualization, Error> {
|
||||||
let js_new = js_sys::Function::new_with_args("cls,arg", "return new cls(arg)");
|
let js_new = web::Function::new_with_args_fixed("cls,arg", "return new cls(arg)").unwrap();
|
||||||
let context = JsValue::NULL;
|
let context = JsValue::NULL;
|
||||||
let object = js_new
|
let object = js_new
|
||||||
.call2(&context, class, &args.into())
|
.call2(&context, class, &args.into())
|
||||||
@ -156,17 +157,25 @@ impl InstanceModel {
|
|||||||
Ok(object)
|
Ok(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn instantiate_class_with_args(
|
||||||
|
_class: &JsValue,
|
||||||
|
_args: JsConsArgs,
|
||||||
|
) -> result::Result<java_script::binding::Visualization, Error> {
|
||||||
|
Ok(java_script::binding::Visualization::new())
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to create a InstanceModel from the given visualisation class.
|
/// Tries to create a InstanceModel from the given visualisation class.
|
||||||
pub fn from_class(class: &JsValue, scene: &Scene) -> result::Result<Self, Error> {
|
pub fn from_class(class: &JsValue, scene: &Scene) -> result::Result<Self, Error> {
|
||||||
let logger = Logger::new("Instance");
|
let logger = Logger::new("Instance");
|
||||||
let root_node = Self::create_root(scene, &logger)?;
|
let root_node = Self::create_root(scene)?;
|
||||||
let (preprocessor_change, closure) = Self::preprocessor_change_callback();
|
let (preprocessor_change, closure) = Self::preprocessor_change_callback();
|
||||||
let styles = StyleWatch::new(&scene.style_sheet);
|
let styles = StyleWatch::new(&scene.style_sheet);
|
||||||
let init_data = JsConsArgs::new(root_node.clone_ref(), styles, closure);
|
let init_data = JsConsArgs::new(root_node.clone_ref(), styles, closure);
|
||||||
let object = Self::instantiate_class_with_args(class, init_data)?;
|
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 = get_method(&object, method::ON_DATA_RECEIVED).ok();
|
||||||
let on_data_received = Rc::new(on_data_received);
|
let on_data_received = Rc::new(on_data_received);
|
||||||
let set_size = get_method(object.as_ref(), method::SET_SIZE).ok();
|
let set_size = get_method(&object, method::SET_SIZE).ok();
|
||||||
let set_size = Rc::new(set_size);
|
let set_size = Rc::new(set_size);
|
||||||
let object = Rc::new(object);
|
let object = Rc::new(object);
|
||||||
let scene = scene.clone_ref();
|
let scene = scene.clone_ref();
|
||||||
@ -188,12 +197,17 @@ impl InstanceModel {
|
|||||||
scene.manage(&self.root_node);
|
scene.manage(&self.root_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
fn set_size(&self, size: Vector2) {
|
fn set_size(&self, size: Vector2) {
|
||||||
let data_json = JsValue::from_serde(&size).unwrap();
|
let data_json = JsValue::from_serde(&size).unwrap();
|
||||||
let _ = self.try_call1(&self.set_size, &data_json);
|
let _ = self.try_call1(&self.set_size, &data_json);
|
||||||
self.root_node.set_size(size);
|
self.root_node.set_size(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn set_size(&self, _size: Vector2) {}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
fn receive_data(&self, data: &Data) -> result::Result<(), DataError> {
|
fn receive_data(&self, data: &Data) -> result::Result<(), DataError> {
|
||||||
let data_json = match data {
|
let data_json = match data {
|
||||||
Data::Json { content } => content,
|
Data::Json { content } => content,
|
||||||
@ -209,15 +223,21 @@ impl InstanceModel {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn receive_data(&self, _data: &Data) -> result::Result<(), DataError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Prompt visualization JS object to emit preprocessor change with its currently desired state.
|
/// Prompt visualization JS object to emit preprocessor change with its currently desired state.
|
||||||
pub fn update_preprocessor(&self) -> result::Result<(), JsValue> {
|
pub fn update_preprocessor(&self) -> result::Result<(), JsValue> {
|
||||||
self.object.emitPreprocessorChange()
|
self.object.emitPreprocessorChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
/// Helper method to call methods on the wrapped javascript object.
|
/// Helper method to call methods on the wrapped javascript object.
|
||||||
fn try_call1(
|
fn try_call1(
|
||||||
&self,
|
&self,
|
||||||
method: &Option<js_sys::Function>,
|
method: &Option<web::Function>,
|
||||||
arg: &JsValue,
|
arg: &JsValue,
|
||||||
) -> result::Result<(), JsValue> {
|
) -> result::Result<(), JsValue> {
|
||||||
if let Some(method) = method {
|
if let Some(method) = method {
|
||||||
@ -283,13 +303,11 @@ impl Instance {
|
|||||||
let callback = move |preprocessor_config| change.emit(preprocessor_config);
|
let callback = move |preprocessor_config| change.emit(preprocessor_config);
|
||||||
let callback = Box::new(callback);
|
let callback = Box::new(callback);
|
||||||
self.model.preprocessor_change.borrow_mut().replace(callback);
|
self.model.preprocessor_change.borrow_mut().replace(callback);
|
||||||
if let Err(js_error) = self.model.update_preprocessor() {
|
if let Err(err) = self.model.update_preprocessor() {
|
||||||
use enso_frp::web::js_to_string;
|
|
||||||
let logger = self.model.logger.clone();
|
let logger = self.model.logger.clone();
|
||||||
error!(
|
error!(
|
||||||
logger,
|
logger,
|
||||||
"Failed to trigger initial preprocessor update from JS: \
|
"Failed to trigger initial preprocessor update from JS: {err.print_to_string()}"
|
||||||
{js_to_string(&js_error)}"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
@ -311,10 +329,11 @@ impl display::Object for Instance {
|
|||||||
|
|
||||||
// === Utils ===
|
// === Utils ===
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
/// Try to return the method specified by the given name on the given object as a
|
/// Try to return the method specified by the given name on the given object as a
|
||||||
/// `js_sys::Function`.
|
/// `web::Function`.
|
||||||
fn get_method(object: &js_sys::Object, property: &str) -> Result<js_sys::Function> {
|
fn get_method(object: &web::Object, property: &str) -> Result<web::Function> {
|
||||||
let method_value = js_sys::Reflect::get(object, &property.into());
|
let method_value = web::Reflect::get(object, &property.into());
|
||||||
let method_value = method_value.map_err(|object| Error::PropertyNotFoundOnObject {
|
let method_value = method_value.map_err(|object| Error::PropertyNotFoundOnObject {
|
||||||
object,
|
object,
|
||||||
property: property.to_string(),
|
property: property.to_string(),
|
||||||
@ -323,6 +342,14 @@ fn get_method(object: &js_sys::Object, property: &str) -> Result<js_sys::Functio
|
|||||||
let object: JsValue = object.into();
|
let object: JsValue = object.into();
|
||||||
return Err(Error::PropertyNotFoundOnObject { object, property: property.to_string() });
|
return Err(Error::PropertyNotFoundOnObject { object, property: property.to_string() });
|
||||||
}
|
}
|
||||||
let method_function: js_sys::Function = method_value.into();
|
let method_function: web::Function = method_value.into();
|
||||||
Ok(method_function)
|
Ok(method_function)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn get_method(
|
||||||
|
_object: &java_script::binding::Visualization,
|
||||||
|
_property: &str,
|
||||||
|
) -> Result<web::Function> {
|
||||||
|
Ok(default())
|
||||||
|
}
|
||||||
|
@ -63,6 +63,7 @@ use ensogl::display::Scene;
|
|||||||
use ensogl::gui::cursor;
|
use ensogl::gui::cursor;
|
||||||
use ensogl::prelude::*;
|
use ensogl::prelude::*;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl::Animation;
|
use ensogl::Animation;
|
||||||
use ensogl::DEPRECATED_Animation;
|
use ensogl::DEPRECATED_Animation;
|
||||||
use ensogl::DEPRECATED_Tween;
|
use ensogl::DEPRECATED_Tween;
|
||||||
@ -1623,7 +1624,7 @@ impl GraphEditorModel {
|
|||||||
#[allow(missing_docs)] // FIXME[everyone] All pub functions should have docs, always.
|
#[allow(missing_docs)] // FIXME[everyone] All pub functions should have docs, always.
|
||||||
pub fn new(app: &Application, cursor: cursor::Cursor, frp: &Frp) -> Self {
|
pub fn new(app: &Application, cursor: cursor::Cursor, frp: &Frp) -> Self {
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new("GraphEditor");
|
let logger = Logger::new("GraphEditor");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let nodes = Nodes::new(&logger);
|
let nodes = Nodes::new(&logger);
|
||||||
@ -1687,7 +1688,7 @@ impl GraphEditorModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scene(&self) -> &Scene {
|
fn scene(&self) -> &Scene {
|
||||||
self.app.display.scene()
|
&self.app.display.default_scene
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2479,7 +2480,7 @@ pub fn enable_disable_toggle(
|
|||||||
#[allow(unused_parens)]
|
#[allow(unused_parens)]
|
||||||
fn new_graph_editor(app: &Application) -> GraphEditor {
|
fn new_graph_editor(app: &Application) -> GraphEditor {
|
||||||
let world = &app.display;
|
let world = &app.display;
|
||||||
let scene = world.scene();
|
let scene = &world.default_scene;
|
||||||
let cursor = &app.cursor;
|
let cursor = &app.cursor;
|
||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let model = GraphEditorModelWithNetwork::new(app, cursor.clone_ref(), &frp);
|
let model = GraphEditorModelWithNetwork::new(app, cursor.clone_ref(), &frp);
|
||||||
@ -3263,8 +3264,8 @@ fn new_graph_editor(app: &Application) -> GraphEditor {
|
|||||||
viz_was_pressed <- viz_pressed.previous();
|
viz_was_pressed <- viz_pressed.previous();
|
||||||
viz_press <- viz_press_ev.gate_not(&viz_was_pressed);
|
viz_press <- viz_press_ev.gate_not(&viz_was_pressed);
|
||||||
viz_release <- viz_release_ev.gate(&viz_was_pressed);
|
viz_release <- viz_release_ev.gate(&viz_was_pressed);
|
||||||
viz_press_time <- viz_press . map(|_| web::performance().now() as f32);
|
viz_press_time <- viz_press . map(|_| web::window.performance_or_panic().now() as f32);
|
||||||
viz_release_time <- viz_release . map(|_| web::performance().now() as f32);
|
viz_release_time <- viz_release . map(|_| web::window.performance_or_panic().now() as f32);
|
||||||
viz_press_time_diff <- viz_release_time.map2(&viz_press_time,|t1,t0| t1-t0);
|
viz_press_time_diff <- viz_release_time.map2(&viz_press_time,|t1,t0| t1-t0);
|
||||||
viz_preview_mode <- viz_press_time_diff.map(|t| *t > VIZ_PREVIEW_MODE_TOGGLE_TIME_MS);
|
viz_preview_mode <- viz_press_time_diff.map(|t| *t > VIZ_PREVIEW_MODE_TOGGLE_TIME_MS);
|
||||||
viz_preview_mode_end <- viz_release.gate(&viz_preview_mode).gate_not(&out.is_fs_visualization_displayed);
|
viz_preview_mode_end <- viz_release.gate(&viz_preview_mode).gate_not(&out.is_fs_visualization_displayed);
|
||||||
|
@ -67,8 +67,8 @@ impl Deref for View {
|
|||||||
impl View {
|
impl View {
|
||||||
/// Create Code Editor component.
|
/// Create Code Editor component.
|
||||||
pub fn new(app: &Application) -> Self {
|
pub fn new(app: &Application) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let styles = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let styles = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let model = app.new_view::<text::Area>();
|
let model = app.new_view::<text::Area>();
|
||||||
@ -101,7 +101,7 @@ impl View {
|
|||||||
frp.source.is_visible <+ frp.toggle.map2(&is_visible, |(),b| !b);
|
frp.source.is_visible <+ frp.toggle.map2(&is_visible, |(),b| !b);
|
||||||
|
|
||||||
def init = source_();
|
def init = source_();
|
||||||
let shape = app.display.scene().shape();
|
let shape = app.display.default_scene.shape();
|
||||||
position <- all_with3(&height_fraction.value,shape,&init, |height_f,scene_size,_init| {
|
position <- all_with3(&height_fraction.value,shape,&init, |height_f,scene_size,_init| {
|
||||||
let height = height_f * scene_size.height;
|
let height = height_f * scene_size.height;
|
||||||
let x = -scene_size.width / 2.0 + PADDING_LEFT;
|
let x = -scene_size.width / 2.0 + PADDING_LEFT;
|
||||||
|
@ -61,8 +61,8 @@ impl PopupLabel {
|
|||||||
let network = frp::Network::new("PopupLabel");
|
let network = frp::Network::new("PopupLabel");
|
||||||
let label = Label::new(app);
|
let label = Label::new(app);
|
||||||
label.set_opacity(0.0);
|
label.set_opacity(0.0);
|
||||||
let background_layer = &app.display.scene().layers.panel;
|
let background_layer = &app.display.default_scene.layers.panel;
|
||||||
let text_layer = &app.display.scene().layers.panel_text;
|
let text_layer = &app.display.default_scene.layers.panel_text;
|
||||||
label.set_layers(background_layer, text_layer);
|
label.set_layers(background_layer, text_layer);
|
||||||
|
|
||||||
let opacity_animation = Animation::new(&network);
|
let opacity_animation = Animation::new(&network);
|
||||||
@ -175,7 +175,7 @@ impl View {
|
|||||||
|
|
||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
init <- source_();
|
init <- source_();
|
||||||
let shape = app.display.scene().shape();
|
let shape = app.display.default_scene.shape();
|
||||||
_eval <- all_with(shape, &init, f!([model](scene_size, _init) {
|
_eval <- all_with(shape, &init, f!([model](scene_size, _init) {
|
||||||
let half_height = scene_size.height / 2.0;
|
let half_height = scene_size.height / 2.0;
|
||||||
let label_height = model.label_height();
|
let label_height = model.label_height();
|
||||||
|
@ -15,13 +15,12 @@ use ensogl::display::shape::primitive::StyleWatch;
|
|||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::clipboard;
|
use ensogl::system::web::clipboard;
|
||||||
use ensogl::system::web::AttributeSetter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl::system::web::StyleSetter;
|
|
||||||
use ensogl_component::shadow;
|
use ensogl_component::shadow;
|
||||||
use wasm_bindgen::closure::Closure;
|
use web::Closure;
|
||||||
use wasm_bindgen::JsCast;
|
use web::HtmlElement;
|
||||||
use web_sys::HtmlElement;
|
use web::JsCast;
|
||||||
use web_sys::MouseEvent;
|
use web::MouseEvent;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -70,9 +69,9 @@ impl Model {
|
|||||||
fn new(scene: &Scene) -> Self {
|
fn new(scene: &Scene) -> Self {
|
||||||
let logger = Logger::new("DocumentationView");
|
let logger = Logger::new("DocumentationView");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let outer_div = web::create_div();
|
let outer_div = web::document.create_div_or_panic();
|
||||||
let outer_dom = DomSymbol::new(&outer_div);
|
let outer_dom = DomSymbol::new(&outer_div);
|
||||||
let inner_div = web::create_div();
|
let inner_div = web::document.create_div_or_panic();
|
||||||
let inner_dom = DomSymbol::new(&inner_div);
|
let inner_dom = DomSymbol::new(&inner_div);
|
||||||
let size =
|
let size =
|
||||||
Rc::new(Cell::new(Vector2(VIEW_WIDTH - PADDING, VIEW_HEIGHT - PADDING - PADDING_TOP)));
|
Rc::new(Cell::new(Vector2(VIEW_WIDTH - PADDING, VIEW_HEIGHT - PADDING - PADDING_TOP)));
|
||||||
@ -85,22 +84,22 @@ impl Model {
|
|||||||
let bg_color = styles.get_color(style_path);
|
let bg_color = styles.get_color(style_path);
|
||||||
let bg_color = bg_color.to_javascript_string();
|
let bg_color = bg_color.to_javascript_string();
|
||||||
|
|
||||||
outer_dom.dom().set_style_or_warn("white-space", "normal", &logger);
|
outer_dom.dom().set_style_or_warn("white-space", "normal");
|
||||||
outer_dom.dom().set_style_or_warn("overflow-y", "auto", &logger);
|
outer_dom.dom().set_style_or_warn("overflow-y", "auto");
|
||||||
outer_dom.dom().set_style_or_warn("overflow-x", "auto", &logger);
|
outer_dom.dom().set_style_or_warn("overflow-x", "auto");
|
||||||
outer_dom.dom().set_style_or_warn("background-color", bg_color, &logger);
|
outer_dom.dom().set_style_or_warn("background-color", bg_color);
|
||||||
outer_dom.dom().set_style_or_warn("pointer-events", "auto", &logger);
|
outer_dom.dom().set_style_or_warn("pointer-events", "auto");
|
||||||
outer_dom.dom().set_style_or_warn("border-radius", format!("{}px", CORNER_RADIUS), &logger);
|
outer_dom.dom().set_style_or_warn("border-radius", format!("{}px", CORNER_RADIUS));
|
||||||
shadow::add_to_dom_element(&outer_dom, &styles, &logger);
|
shadow::add_to_dom_element(&outer_dom, &styles);
|
||||||
|
|
||||||
inner_dom.dom().set_attribute_or_warn("class", "scrollable", &logger);
|
inner_dom.dom().set_attribute_or_warn("class", "scrollable");
|
||||||
inner_dom.dom().set_style_or_warn("white-space", "normal", &logger);
|
inner_dom.dom().set_style_or_warn("white-space", "normal");
|
||||||
inner_dom.dom().set_style_or_warn("overflow-y", "auto", &logger);
|
inner_dom.dom().set_style_or_warn("overflow-y", "auto");
|
||||||
inner_dom.dom().set_style_or_warn("overflow-x", "auto", &logger);
|
inner_dom.dom().set_style_or_warn("overflow-x", "auto");
|
||||||
inner_dom.dom().set_style_or_warn("padding", format!("{}px", PADDING), &logger);
|
inner_dom.dom().set_style_or_warn("padding", format!("{}px", PADDING));
|
||||||
inner_dom.dom().set_style_or_warn("padding-top", "5px", &logger);
|
inner_dom.dom().set_style_or_warn("padding-top", "5px");
|
||||||
inner_dom.dom().set_style_or_warn("pointer-events", "auto", &logger);
|
inner_dom.dom().set_style_or_warn("pointer-events", "auto");
|
||||||
inner_dom.dom().set_style_or_warn("border-radius", format!("{}px", CORNER_RADIUS), &logger);
|
inner_dom.dom().set_style_or_warn("border-radius", format!("{}px", CORNER_RADIUS));
|
||||||
|
|
||||||
overlay.roundness.set(1.0);
|
overlay.roundness.set(1.0);
|
||||||
overlay.radius.set(CORNER_RADIUS);
|
overlay.radius.set(CORNER_RADIUS);
|
||||||
@ -151,12 +150,11 @@ impl Model {
|
|||||||
let create_closures = || -> Option<CodeCopyClosure> {
|
let create_closures = || -> Option<CodeCopyClosure> {
|
||||||
let copy_button = copy_buttons.get_with_index(i)?.dyn_into::<HtmlElement>().ok()?;
|
let copy_button = copy_buttons.get_with_index(i)?.dyn_into::<HtmlElement>().ok()?;
|
||||||
let code_block = code_blocks.get_with_index(i)?.dyn_into::<HtmlElement>().ok()?;
|
let code_block = code_blocks.get_with_index(i)?.dyn_into::<HtmlElement>().ok()?;
|
||||||
let closure = Box::new(move |_event: MouseEvent| {
|
let closure: Closure<dyn FnMut(MouseEvent)> = Closure::new(move |_: MouseEvent| {
|
||||||
let inner_code = code_block.inner_text();
|
let inner_code = code_block.inner_text();
|
||||||
clipboard::write_text(inner_code);
|
clipboard::write_text(inner_code);
|
||||||
});
|
});
|
||||||
let closure: Closure<dyn FnMut(MouseEvent)> = Closure::wrap(closure);
|
let callback = closure.as_js_function();
|
||||||
let callback = closure.as_ref().unchecked_ref();
|
|
||||||
match copy_button.add_event_listener_with_callback("click", callback) {
|
match copy_button.add_event_listener_with_callback("click", callback) {
|
||||||
Ok(_) => Some(closure),
|
Ok(_) => Some(closure),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -219,12 +217,8 @@ impl Model {
|
|||||||
let padding = (size.x.min(size.y) / 2.0).min(PADDING);
|
let padding = (size.x.min(size.y) / 2.0).min(PADDING);
|
||||||
self.outer_dom.set_size(Vector2(size.x, size.y));
|
self.outer_dom.set_size(Vector2(size.x, size.y));
|
||||||
self.inner_dom.set_size(Vector2(size.x - padding, size.y - padding - PADDING_TOP));
|
self.inner_dom.set_size(Vector2(size.x - padding, size.y - padding - PADDING_TOP));
|
||||||
self.inner_dom.dom().set_style_or_warn("padding", format!("{}px", padding), &self.logger);
|
self.inner_dom.dom().set_style_or_warn("padding", format!("{}px", padding));
|
||||||
self.inner_dom.dom().set_style_or_warn(
|
self.inner_dom.dom().set_style_or_warn("padding-top", format!("{}px", PADDING_TOP));
|
||||||
"padding-top",
|
|
||||||
format!("{}px", PADDING_TOP),
|
|
||||||
&self.logger,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ impl OpenDialog {
|
|||||||
pub fn new(app: &Application) -> Self {
|
pub fn new(app: &Application) -> Self {
|
||||||
let logger = Logger::new("OpenDialog");
|
let logger = Logger::new("OpenDialog");
|
||||||
let network = frp::Network::new("OpenDialog");
|
let network = frp::Network::new("OpenDialog");
|
||||||
let style_watch = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style_watch = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
let project_list = project_list::ProjectList::new(app);
|
let project_list = project_list::ProjectList::new(app);
|
||||||
let file_browser = FileBrowser::new();
|
let file_browser = FileBrowser::new();
|
||||||
// Once FileBrowser will be implemented as component, it should be instantiated this way:
|
// Once FileBrowser will be implemented as component, it should be instantiated this way:
|
||||||
@ -47,7 +47,7 @@ impl OpenDialog {
|
|||||||
|
|
||||||
display_object.add_child(&project_list);
|
display_object.add_child(&project_list);
|
||||||
display_object.add_child(&file_browser);
|
display_object.add_child(&file_browser);
|
||||||
app.display.scene().layers.panel.add_exclusive(&display_object);
|
app.display.default_scene.layers.panel.add_exclusive(&display_object);
|
||||||
|
|
||||||
use theme::application as theme_app;
|
use theme::application as theme_app;
|
||||||
let project_list_width = style_watch.get_number(theme_app::project_list::width);
|
let project_list_width = style_watch.get_number(theme_app::project_list::width);
|
||||||
|
@ -94,19 +94,19 @@ impl ProjectList {
|
|||||||
display_object.add_child(&background);
|
display_object.add_child(&background);
|
||||||
display_object.add_child(&caption);
|
display_object.add_child(&caption);
|
||||||
display_object.add_child(&list);
|
display_object.add_child(&list);
|
||||||
app.display.scene().layers.panel.add_exclusive(&display_object);
|
app.display.default_scene.layers.panel.add_exclusive(&display_object);
|
||||||
caption.set_content("Open Project");
|
caption.set_content("Open Project");
|
||||||
caption.add_to_scene_layer(&app.display.scene().layers.panel_text);
|
caption.add_to_scene_layer(&app.display.default_scene.layers.panel_text);
|
||||||
list.set_label_layer(app.display.scene().layers.panel_text.id());
|
list.set_label_layer(app.display.default_scene.layers.panel_text.id());
|
||||||
|
|
||||||
ensogl::shapes_order_dependencies! {
|
ensogl::shapes_order_dependencies! {
|
||||||
app.display.scene() => {
|
app.display.default_scene => {
|
||||||
background -> list_view::selection;
|
background -> list_view::selection;
|
||||||
list_view::background -> background;
|
list_view::background -> background;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let style_watch = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style_watch = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
let width = style_watch.get_number(theme::width);
|
let width = style_watch.get_number(theme::width);
|
||||||
let height = style_watch.get_number(theme::height);
|
let height = style_watch.get_number(theme::height);
|
||||||
let bar_height = style_watch.get_number(theme::bar::height);
|
let bar_height = style_watch.get_number(theme::bar::height);
|
||||||
|
@ -22,6 +22,7 @@ use ensogl::display;
|
|||||||
use ensogl::display::shape::*;
|
use ensogl::display::shape::*;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::dom;
|
use ensogl::system::web::dom;
|
||||||
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl::Animation;
|
use ensogl::Animation;
|
||||||
use ensogl::DEPRECATED_Animation;
|
use ensogl::DEPRECATED_Animation;
|
||||||
use ensogl_hardcoded_theme::Theme;
|
use ensogl_hardcoded_theme::Theme;
|
||||||
@ -145,7 +146,7 @@ struct Model {
|
|||||||
impl Model {
|
impl Model {
|
||||||
fn new(app: &Application) -> Self {
|
fn new(app: &Application) -> Self {
|
||||||
let logger = Logger::new("project::View");
|
let logger = Logger::new("project::View");
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let searcher = app.new_view::<searcher::View>();
|
let searcher = app.new_view::<searcher::View>();
|
||||||
let graph_editor = app.new_view::<GraphEditor>();
|
let graph_editor = app.new_view::<GraphEditor>();
|
||||||
@ -213,7 +214,7 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_html_style(&self, style: &'static str) {
|
fn set_html_style(&self, style: &'static str) {
|
||||||
web::with_element_by_id_or_warn(&self.logger, "root", |root| root.set_class_name(style));
|
web::document.with_element_by_id_or_warn("root", |root| root.set_class_name(style));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn searcher_left_top_position_when_under_node_at(position: Vector2<f32>) -> Vector2<f32> {
|
fn searcher_left_top_position_when_under_node_at(position: Vector2<f32>) -> Vector2<f32> {
|
||||||
@ -369,7 +370,7 @@ impl View {
|
|||||||
|
|
||||||
display::style::javascript::expose_to_window(&app.themes);
|
display::style::javascript::expose_to_window(&app.themes);
|
||||||
|
|
||||||
let scene = app.display.scene().clone_ref();
|
let scene = app.display.default_scene.clone_ref();
|
||||||
let model = Model::new(app);
|
let model = Model::new(app);
|
||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let searcher = &model.searcher.frp;
|
let searcher = &model.searcher.frp;
|
||||||
|
@ -113,7 +113,7 @@ struct Model {
|
|||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
fn new(app: &Application) -> Self {
|
fn new(app: &Application) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let app = app.clone_ref();
|
let app = app.clone_ref();
|
||||||
let logger = Logger::new("SearcherView");
|
let logger = Logger::new("SearcherView");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
@ -126,7 +126,7 @@ impl Model {
|
|||||||
|
|
||||||
// FIXME: StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
// FIXME: StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||||
// system (#795)
|
// system (#795)
|
||||||
let style = StyleWatch::new(&app.display.scene().style_sheet);
|
let style = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
let action_list_gap_path = ensogl_hardcoded_theme::application::searcher::action_list_gap;
|
let action_list_gap_path = ensogl_hardcoded_theme::application::searcher::action_list_gap;
|
||||||
let action_list_gap = style.get_number_or(action_list_gap_path, 0.0);
|
let action_list_gap = style.get_number_or(action_list_gap_path, 0.0);
|
||||||
list.set_label_layer(scene.layers.above_nodes_text.id());
|
list.set_label_layer(scene.layers.above_nodes_text.id());
|
||||||
|
@ -10,7 +10,7 @@ use ensogl::display::shape::compound::path::path;
|
|||||||
use ensogl::display::shape::*;
|
use ensogl::display::shape::*;
|
||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::StyleSetter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl_hardcoded_theme::application::searcher::icons as theme;
|
use ensogl_hardcoded_theme::application::searcher::icons as theme;
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
@ -998,27 +998,24 @@ mod frame {
|
|||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn entry_point_searcher_icons() {
|
pub fn entry_point_searcher_icons() {
|
||||||
web::forward_panic_hook_to_console();
|
|
||||||
web::set_stack_trace_limit();
|
|
||||||
|
|
||||||
let logger = Logger::new("Icons example");
|
let logger = Logger::new("Icons example");
|
||||||
let app = Application::new(&web::get_html_element_by_id("root").unwrap());
|
let app = Application::new("root");
|
||||||
ensogl_hardcoded_theme::builtin::dark::register(&app);
|
ensogl_hardcoded_theme::builtin::dark::register(&app);
|
||||||
ensogl_hardcoded_theme::builtin::light::register(&app);
|
ensogl_hardcoded_theme::builtin::light::register(&app);
|
||||||
ensogl_hardcoded_theme::builtin::light::enable(&app);
|
ensogl_hardcoded_theme::builtin::light::enable(&app);
|
||||||
let world = app.display.clone();
|
let world = app.display.clone();
|
||||||
mem::forget(app);
|
mem::forget(app);
|
||||||
let scene = world.scene();
|
let scene = &world.default_scene;
|
||||||
mem::forget(Navigator::new(scene, &scene.camera()));
|
mem::forget(Navigator::new(scene, &scene.camera()));
|
||||||
|
|
||||||
|
|
||||||
// === Grid ===
|
// === Grid ===
|
||||||
|
|
||||||
let grid_div = web::create_div();
|
let grid_div = web::document.create_div_or_panic();
|
||||||
grid_div.set_style_or_panic("width", "1000px");
|
grid_div.set_style_or_warn("width", "1000px");
|
||||||
grid_div.set_style_or_panic("height", "16px");
|
grid_div.set_style_or_warn("height", "16px");
|
||||||
grid_div.set_style_or_panic("background-size", "1.0px 1.0px");
|
grid_div.set_style_or_warn("background-size", "1.0px 1.0px");
|
||||||
grid_div.set_style_or_panic(
|
grid_div.set_style_or_warn(
|
||||||
"background-image",
|
"background-image",
|
||||||
"linear-gradient(to right, grey 0.05px, transparent 0.05px),
|
"linear-gradient(to right, grey 0.05px, transparent 0.05px),
|
||||||
linear-gradient(to bottom, grey 0.05px, transparent 0.05px)",
|
linear-gradient(to bottom, grey 0.05px, transparent 0.05px)",
|
||||||
|
@ -158,7 +158,7 @@ struct Model {
|
|||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
fn new(app: &Application) -> Self {
|
fn new(app: &Application) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new("StatusBar");
|
let logger = Logger::new("StatusBar");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let root = display::object::Instance::new(&logger);
|
let root = display::object::Instance::new(&logger);
|
||||||
@ -174,7 +174,7 @@ impl Model {
|
|||||||
label.add_to_scene_layer(&scene.layers.panel_text);
|
label.add_to_scene_layer(&scene.layers.panel_text);
|
||||||
|
|
||||||
let text_color_path = theme::application::status_bar::text;
|
let text_color_path = theme::application::status_bar::text;
|
||||||
let style = StyleWatch::new(&app.display.scene().style_sheet);
|
let style = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
let text_color = style.get_color(text_color_path);
|
let text_color = style.get_color(text_color_path);
|
||||||
label.frp.set_color_all.emit(text_color);
|
label.frp.set_color_all.emit(text_color);
|
||||||
label.frp.set_default_color.emit(text_color);
|
label.frp.set_default_color.emit(text_color);
|
||||||
@ -279,7 +279,7 @@ impl View {
|
|||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let model = Model::new(app);
|
let model = Model::new(app);
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
|
|
||||||
enso_frp::extend! { network
|
enso_frp::extend! { network
|
||||||
event_added <- frp.add_event.map(f!((label) model.add_event(label)));
|
event_added <- frp.add_event.map(f!((label) model.add_event(label)));
|
||||||
|
@ -150,7 +150,7 @@ impl Model {
|
|||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
|
|
||||||
ensogl::shapes_order_dependencies! {
|
ensogl::shapes_order_dependencies! {
|
||||||
app.display.scene() => {
|
app.display.default_scene => {
|
||||||
shape -> close::shape;
|
shape -> close::shape;
|
||||||
shape -> fullscreen::shape;
|
shape -> fullscreen::shape;
|
||||||
}
|
}
|
||||||
@ -233,7 +233,7 @@ impl View {
|
|||||||
let model = Model::new(app);
|
let model = Model::new(app);
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
|
|
||||||
let style = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
let style_frp = LayoutParams::from_theme(&style);
|
let style_frp = LayoutParams::from_theme(&style);
|
||||||
let layout_style = style_frp.flatten(network);
|
let layout_style = style_frp.flatten(network);
|
||||||
let radius = style.get_number(theme::radius);
|
let radius = style.get_number(theme::radius);
|
||||||
|
@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
ensogl = { path = "../../../../lib/rust/ensogl" }
|
ensogl = { path = "../../../../lib/rust/ensogl" }
|
||||||
enso-frp = { path = "../../../../lib/rust/frp" }
|
enso-frp = { path = "../../../../lib/rust/frp" }
|
||||||
wasm-bindgen = { version = "0.2.58", features = [
|
wasm-bindgen = { version = "0.2.78", features = [
|
||||||
"nightly",
|
"nightly",
|
||||||
"serde-serialize"
|
"serde-serialize"
|
||||||
] }
|
] }
|
||||||
|
@ -19,14 +19,12 @@ use ensogl::application::Application;
|
|||||||
use ensogl::display;
|
use ensogl::display;
|
||||||
use ensogl::display::DomSymbol;
|
use ensogl::display::DomSymbol;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::NodeInserter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl::system::web::StyleSetter;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wasm_bindgen::closure::Closure;
|
use web::Closure;
|
||||||
use wasm_bindgen::JsCast;
|
use web::Element;
|
||||||
use web_sys::Element;
|
use web::HtmlDivElement;
|
||||||
use web_sys::HtmlDivElement;
|
use web::MouseEvent;
|
||||||
use web_sys::MouseEvent;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -83,26 +81,23 @@ impl Deref for ClickableElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClickableElement {
|
impl ClickableElement {
|
||||||
pub fn new(element: Element, logger: &Logger) -> Self {
|
pub fn new(element: Element) -> Self {
|
||||||
frp::new_network! { network
|
frp::new_network! { network
|
||||||
click <- source_();
|
click <- source_();
|
||||||
}
|
}
|
||||||
let closure: ClickClosure = Closure::wrap(Box::new(f_!(click.emit(()))));
|
let closure: ClickClosure = Closure::new(f_!(click.emit(())));
|
||||||
let callback = closure.as_ref().unchecked_ref();
|
let handle = web::add_event_listener(&element, "click", closure);
|
||||||
if element.add_event_listener_with_callback("click", callback).is_err() {
|
network.store(&handle);
|
||||||
error!(logger, "Could not add event listener for ClickableElement.");
|
|
||||||
}
|
|
||||||
network.store(&Rc::new(closure));
|
|
||||||
Self { element, network, click }
|
Self { element, network, click }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =============
|
// =============
|
||||||
// === Model ===
|
// === Model ===
|
||||||
// =============
|
// =============
|
||||||
|
|
||||||
|
|
||||||
// === CSS Styles ===
|
// === CSS Styles ===
|
||||||
|
|
||||||
static STYLESHEET: &str = include_str!("../style.css");
|
static STYLESHEET: &str = include_str!("../style.css");
|
||||||
@ -130,42 +125,37 @@ impl Model {
|
|||||||
|
|
||||||
let side_menu = SideMenu::new(&logger);
|
let side_menu = SideMenu::new(&logger);
|
||||||
let template_cards = TemplateCards::new(&logger);
|
let template_cards = TemplateCards::new(&logger);
|
||||||
let dom = Self::create_dom(&logger, &side_menu, &template_cards);
|
let dom = Self::create_dom(&side_menu, &template_cards);
|
||||||
display_object.add_child(&dom);
|
display_object.add_child(&dom);
|
||||||
|
|
||||||
// Use `welcome_screen` layer to lock position when panning.
|
// Use `welcome_screen` layer to lock position when panning.
|
||||||
app.display.scene().dom.layers.welcome_screen.manage(&dom);
|
app.display.default_scene.dom.layers.welcome_screen.manage(&dom);
|
||||||
|
|
||||||
let style = web::create_element("style");
|
let style = web::document.create_element_or_panic("style");
|
||||||
style.set_inner_html(STYLESHEET);
|
style.set_inner_html(STYLESHEET);
|
||||||
dom.append_or_warn(&style, &logger);
|
dom.append_or_warn(&style);
|
||||||
|
|
||||||
Self { application, logger, dom, display_object, side_menu, template_cards }
|
Self { application, logger, dom, display_object, side_menu, template_cards }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_dom(
|
fn create_dom(side_menu: &SideMenu, template_cards: &TemplateCards) -> DomSymbol {
|
||||||
logger: &Logger,
|
let root = web::document.create_div_or_panic();
|
||||||
side_menu: &SideMenu,
|
|
||||||
template_cards: &TemplateCards,
|
|
||||||
) -> DomSymbol {
|
|
||||||
let root = web::create_div();
|
|
||||||
root.set_class_name(css_class::TEMPLATES_VIEW_ROOT);
|
root.set_class_name(css_class::TEMPLATES_VIEW_ROOT);
|
||||||
// We explicitly enable pointer events for Welcome Screen elements. Pointer events are
|
// We explicitly enable pointer events for Welcome Screen elements. Pointer events are
|
||||||
// disabled for all DOM layers by default. See [`DomLayers`] documentation.
|
// disabled for all DOM layers by default. See [`DomLayers`] documentation.
|
||||||
root.set_style_or_warn("pointer-events", "auto", logger);
|
root.set_style_or_warn("pointer-events", "auto");
|
||||||
|
|
||||||
let container = Self::create_content_container();
|
let container = Self::create_content_container();
|
||||||
container.append_or_warn(&side_menu.model.root_dom, logger);
|
container.append_or_warn(&side_menu.model.root_dom);
|
||||||
container.append_or_warn(&template_cards.model.root_dom, logger);
|
container.append_or_warn(&template_cards.model.root_dom);
|
||||||
root.append_or_warn(&container, logger);
|
root.append_or_warn(&container);
|
||||||
|
|
||||||
DomSymbol::new(&root)
|
DomSymbol::new(&root)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_content_container() -> HtmlDivElement {
|
fn create_content_container() -> HtmlDivElement {
|
||||||
let container = web::create_div();
|
let container = web::document.create_div_or_panic();
|
||||||
container.set_class_name(css_class::CONTAINER);
|
container.set_class_name(css_class::CONTAINER);
|
||||||
|
|
||||||
container
|
container
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,7 +210,7 @@ impl View {
|
|||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
// === Update DOM's size so CSS styles work correctly. ===
|
// === Update DOM's size so CSS styles work correctly. ===
|
||||||
|
|
||||||
let scene_size = app.display.scene().shape().clone_ref();
|
let scene_size = app.display.default_scene.shape().clone_ref();
|
||||||
eval scene_size ((size) model.dom.set_size(Vector2::from(*size)));
|
eval scene_size ((size) model.dom.set_size(Vector2::from(*size)));
|
||||||
}
|
}
|
||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
|
@ -8,34 +8,33 @@ use crate::ClickableElement;
|
|||||||
|
|
||||||
use enso_frp as frp;
|
use enso_frp as frp;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::NodeInserter;
|
use ensogl::system::web::traits::*;
|
||||||
use web_sys::Element;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ================
|
// =============
|
||||||
// === Model ===
|
// === Model ===
|
||||||
// ================
|
// =============
|
||||||
|
|
||||||
#[derive(Clone, CloneRef, Debug)]
|
#[derive(Clone, CloneRef, Debug)]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
pub root_dom: Element,
|
pub root_dom: web::Element,
|
||||||
new_project_button: ClickableElement,
|
new_project_button: ClickableElement,
|
||||||
projects_list_dom: Element,
|
projects_list_dom: web::Element,
|
||||||
projects: Rc<RefCell<Vec<ClickableElement>>>,
|
projects: Rc<RefCell<Vec<ClickableElement>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(logger: Logger) -> Self {
|
pub fn new(logger: Logger) -> Self {
|
||||||
let root_dom = web::create_element("aside");
|
let root_dom = web::document.create_element_or_panic("aside");
|
||||||
root_dom.set_class_name(crate::css_class::SIDE_MENU);
|
root_dom.set_class_name(crate::css_class::SIDE_MENU);
|
||||||
let header = Self::create_header("Your projects");
|
let header = Self::create_header("Your projects");
|
||||||
root_dom.append_or_warn(&header, &logger);
|
root_dom.append_or_warn(&header);
|
||||||
let projects_list_dom = Self::create_projects_list();
|
let projects_list_dom = Self::create_projects_list();
|
||||||
root_dom.append_or_warn(&projects_list_dom, &logger);
|
root_dom.append_or_warn(&projects_list_dom);
|
||||||
let new_project_button = Self::create_new_project_button(&logger, &projects_list_dom);
|
let new_project_button = Self::create_new_project_button(&projects_list_dom);
|
||||||
let projects = default();
|
let projects = default();
|
||||||
|
|
||||||
Self { logger, root_dom, projects_list_dom, projects, new_project_button }
|
Self { logger, root_dom, projects_list_dom, projects, new_project_button }
|
||||||
@ -56,38 +55,38 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_projects_list_entry(&self, name: &str, open_project: &frp::Any<String>) {
|
fn add_projects_list_entry(&self, name: &str, open_project: &frp::Any<String>) {
|
||||||
let entry = Self::create_project_list_entry(name, &self.logger);
|
let entry = Self::create_project_list_entry(name);
|
||||||
let network = &entry.network;
|
let network = &entry.network;
|
||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
open_project <+ entry.click.constant(name.to_owned());
|
open_project <+ entry.click.constant(name.to_owned());
|
||||||
}
|
}
|
||||||
let new_project_button = &self.new_project_button;
|
let new_project_button = &self.new_project_button;
|
||||||
self.projects_list_dom.insert_before_or_warn(&entry, new_project_button, &self.logger);
|
self.projects_list_dom.insert_before_or_warn(&entry, new_project_button);
|
||||||
self.projects.borrow_mut().push(entry);
|
self.projects.borrow_mut().push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_new_project_button(logger: &Logger, projects_list: &Element) -> ClickableElement {
|
fn create_new_project_button(projects_list: &web::Element) -> ClickableElement {
|
||||||
let element = web::create_element("li");
|
let element = web::document.create_element_or_panic("li");
|
||||||
element.set_id(crate::css_id::NEW_PROJECT);
|
element.set_id(crate::css_id::NEW_PROJECT);
|
||||||
element.set_inner_html(r#"<img src="/assets/new-project.svg" />Create a new project"#);
|
element.set_inner_html(r#"<img src="/assets/new-project.svg" />Create a new project"#);
|
||||||
projects_list.append_or_warn(&element, logger);
|
projects_list.append_or_warn(&element);
|
||||||
ClickableElement::new(element, logger)
|
ClickableElement::new(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_header(text: &str) -> Element {
|
fn create_header(text: &str) -> web::Element {
|
||||||
let header = web::create_element("h2");
|
let header = web::document.create_element_or_panic("h2");
|
||||||
header.set_text_content(Some(text));
|
header.set_text_content(Some(text));
|
||||||
header
|
header
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_projects_list() -> Element {
|
fn create_projects_list() -> web::Element {
|
||||||
web::create_element("ul")
|
web::document.create_element_or_panic("ul")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_project_list_entry(project_name: &str, logger: &Logger) -> ClickableElement {
|
fn create_project_list_entry(project_name: &str) -> ClickableElement {
|
||||||
let element = web::create_element("li");
|
let element = web::document.create_element_or_panic("li");
|
||||||
element.set_inner_html(&format!(r#"<img src="assets/project.svg"/> {}"#, project_name));
|
element.set_inner_html(&format!(r#"<img src="assets/project.svg"/> {}"#, project_name));
|
||||||
ClickableElement::new(element, logger)
|
ClickableElement::new(element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,11 +9,10 @@ use crate::ClickableElement;
|
|||||||
|
|
||||||
use enso_frp as frp;
|
use enso_frp as frp;
|
||||||
use ensogl::system::web;
|
use ensogl::system::web;
|
||||||
use ensogl::system::web::AttributeSetter;
|
use ensogl::system::web::traits::*;
|
||||||
use ensogl::system::web::NodeInserter;
|
use web::Element;
|
||||||
use wasm_bindgen::JsCast;
|
use web::HtmlDivElement;
|
||||||
use web_sys::Element;
|
use web::JsCast;
|
||||||
use web_sys::HtmlDivElement;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -88,16 +87,16 @@ pub struct Model {
|
|||||||
impl Model {
|
impl Model {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(logger: Logger, open_template: &frp::Any<String>) -> Self {
|
pub fn new(logger: Logger, open_template: &frp::Any<String>) -> Self {
|
||||||
let root_dom = web::create_element("main");
|
let root_dom = web::document.create_element_or_panic("main");
|
||||||
root_dom.set_class_name(crate::css_class::CONTENT);
|
root_dom.set_class_name(crate::css_class::CONTENT);
|
||||||
let templates = web::create_div();
|
let templates = web::document.create_div_or_panic();
|
||||||
|
|
||||||
let header = Self::create_header("Templates");
|
let header = Self::create_header("Templates");
|
||||||
templates.append_or_warn(&header, &logger);
|
templates.append_or_warn(&header);
|
||||||
|
|
||||||
let (cards_dom, cards) = Self::create_cards(&logger);
|
let (cards_dom, cards) = Self::create_cards();
|
||||||
templates.append_or_warn(&cards_dom, &logger);
|
templates.append_or_warn(&cards_dom);
|
||||||
root_dom.append_or_warn(&templates, &logger);
|
root_dom.append_or_warn(&templates);
|
||||||
|
|
||||||
let model = Self { logger, root_dom, cards: Rc::new(cards) };
|
let model = Self { logger, root_dom, cards: Rc::new(cards) };
|
||||||
model.setup_event_listeners(open_template);
|
model.setup_event_listeners(open_template);
|
||||||
@ -116,58 +115,54 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_header(content: &str) -> Element {
|
fn create_header(content: &str) -> Element {
|
||||||
let header = web::create_element("h2");
|
let header = web::document.create_element_or_panic("h2");
|
||||||
header.set_text_content(Some(content));
|
header.set_text_content(Some(content));
|
||||||
header
|
header
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create main content, a set of cards.
|
/// Create main content, a set of cards.
|
||||||
fn create_cards(logger: &Logger) -> (HtmlDivElement, Vec<Card>) {
|
fn create_cards() -> (HtmlDivElement, Vec<Card>) {
|
||||||
let mut cards = Vec::new();
|
let mut cards = Vec::new();
|
||||||
let dom = web::create_div();
|
let dom = web::document.create_div_or_panic();
|
||||||
dom.set_class_name(crate::css_class::CARDS);
|
dom.set_class_name(crate::css_class::CARDS);
|
||||||
|
|
||||||
let row1 = Self::create_row(&[CARD_SPREADSHEETS, CARD_GEO], &mut cards, logger);
|
let row1 = Self::create_row(&[CARD_SPREADSHEETS, CARD_GEO], &mut cards);
|
||||||
dom.append_or_warn(&row1, logger);
|
dom.append_or_warn(&row1);
|
||||||
|
|
||||||
let row2 = Self::create_row(&[CARD_VISUALIZE], &mut cards, logger);
|
let row2 = Self::create_row(&[CARD_VISUALIZE], &mut cards);
|
||||||
dom.append_or_warn(&row2, logger);
|
dom.append_or_warn(&row2);
|
||||||
|
|
||||||
(dom, cards)
|
(dom, cards)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_row(
|
fn create_row(definitions: &[CardDefinition], cards: &mut Vec<Card>) -> HtmlDivElement {
|
||||||
definitions: &[CardDefinition],
|
let row = web::document.create_div_or_panic();
|
||||||
cards: &mut Vec<Card>,
|
|
||||||
logger: &Logger,
|
|
||||||
) -> HtmlDivElement {
|
|
||||||
let row = web::create_div();
|
|
||||||
row.set_class_name(crate::css_class::ROW);
|
row.set_class_name(crate::css_class::ROW);
|
||||||
for definition in definitions {
|
for definition in definitions {
|
||||||
let card = Self::create_card(definition, logger);
|
let card = Self::create_card(definition);
|
||||||
row.append_or_warn(&card.element, logger);
|
row.append_or_warn(&card.element);
|
||||||
cards.push(card.clone());
|
cards.push(card.clone());
|
||||||
}
|
}
|
||||||
row
|
row
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to create a single card DOM from provided definition.
|
/// Helper to create a single card DOM from provided definition.
|
||||||
fn create_card(definition: &CardDefinition, logger: &Logger) -> Card {
|
fn create_card(definition: &CardDefinition) -> Card {
|
||||||
let card = web::create_div();
|
let card = web::document.create_div_or_panic();
|
||||||
card.set_class_name(&format!("{} {}", crate::css_class::CARD, definition.class));
|
card.set_class_name(&format!("{} {}", crate::css_class::CARD, definition.class));
|
||||||
if let Some(src) = definition.background_image_url {
|
if let Some(src) = definition.background_image_url {
|
||||||
let img = web::create_element("img");
|
let img = web::document.create_element_or_panic("img");
|
||||||
img.set_attribute_or_warn("src", src, logger);
|
img.set_attribute_or_warn("src", src);
|
||||||
card.append_or_warn(&img, logger);
|
card.append_or_warn(&img);
|
||||||
}
|
}
|
||||||
let card_header = web::create_element("h3");
|
let card_header = web::document.create_element_or_panic("h3");
|
||||||
card_header.set_text_content(Some(definition.header));
|
card_header.set_text_content(Some(definition.header));
|
||||||
card.append_or_warn(&card_header, logger);
|
card.append_or_warn(&card_header);
|
||||||
let text_content = web::create_element("p");
|
let text_content = web::document.create_element_or_panic("p");
|
||||||
text_content.set_text_content(Some(definition.content));
|
text_content.set_text_content(Some(definition.content));
|
||||||
card.append_or_warn(&text_content, logger);
|
card.append_or_warn(&text_content);
|
||||||
|
|
||||||
let clickable_element = ClickableElement::new(card.unchecked_into(), logger);
|
let clickable_element = ClickableElement::new(card.unchecked_into());
|
||||||
Card { clickable_element, template_name: definition.template }
|
Card { clickable_element, template_name: definition.template }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ function printScamWarning() {
|
|||||||
'copy-paste something here, it is a scam and will give them access to your ' +
|
'copy-paste something here, it is a scam and will give them access to your ' +
|
||||||
'account and data.'
|
'account and data.'
|
||||||
let msg2 =
|
let msg2 =
|
||||||
'See https://github.com/enso-org/enso/tree/develop/gui/docs/security/selfxss.md for more ' +
|
'See https://github.com/enso-org/enso/blob/develop/docs/security/selfxss.md for more ' +
|
||||||
'information.'
|
'information.'
|
||||||
console.log('%cStop!', headerCSS1)
|
console.log('%cStop!', headerCSS1)
|
||||||
console.log('%cYou may be victim of a scam!', headerCSS2)
|
console.log('%cYou may be victim of a scam!', headerCSS2)
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
A simple http server that takes POST requests for path "/" on localhost and
|
|
||||||
writes their body to a new file in the "log" subdirectory of the current working
|
|
||||||
directory
|
|
||||||
|
|
||||||
To start the server, make sure that all necessary packages are installed
|
|
||||||
(through Lerna) and call `npm run start`. To learn more about the options and
|
|
||||||
their default values, use the `--help` option.
|
|
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"author": {
|
|
||||||
"name": "Enso Team",
|
|
||||||
"email": "contact@luna-lang.org"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/luna/ide",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@github.com:luna/ide.git"
|
|
||||||
},
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/luna/ide/issues"
|
|
||||||
},
|
|
||||||
"name": "enso-studio-logging-service",
|
|
||||||
"description": "A logging service that can receive crash reports from the Enso IDE.",
|
|
||||||
"main": "server.js",
|
|
||||||
"dependencies": {
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"uuid": "^8.3.1",
|
|
||||||
"yargs": "^16.1.1"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "node server.js",
|
|
||||||
"test": "mocha"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"axios": "^0.21.0",
|
|
||||||
"mocha": "^8.2.1",
|
|
||||||
"mock-fs": "^4.13.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
// This module implements a simple http server that listens for crash reports as POST requests and
|
|
||||||
// writes them to disk. To start the server, call `startServer(port)` or execute this file through
|
|
||||||
// Node.js. More information can be foun in the README.md.
|
|
||||||
|
|
||||||
const express = require('express')
|
|
||||||
const fs = require('fs')
|
|
||||||
const uuid = require('uuid')
|
|
||||||
const yargs = require('yargs')
|
|
||||||
|
|
||||||
const defaultPort = require('../../config.js').defaultLogServerPort
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
startServer,
|
|
||||||
}
|
|
||||||
|
|
||||||
function main(argv) {
|
|
||||||
startServer(parse_args(argv).port)
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Constants ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
const httpStatusCodes = {
|
|
||||||
noContent: 204,
|
|
||||||
forbidden: 403,
|
|
||||||
badRequest: 400,
|
|
||||||
internalServerError: 500,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// === Argument Parsing ===
|
|
||||||
// ========================
|
|
||||||
|
|
||||||
function parse_args(argv) {
|
|
||||||
return yargs(argv)
|
|
||||||
.option('port', {
|
|
||||||
alias: 'p',
|
|
||||||
description:
|
|
||||||
'The number of the port that this server will listen on. ' +
|
|
||||||
'If the the number is 0 then an arbitrary free port will be chosen.',
|
|
||||||
type: 'number',
|
|
||||||
default: defaultPort,
|
|
||||||
})
|
|
||||||
.help()
|
|
||||||
.alias('help', 'h').argv
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==============
|
|
||||||
// === Server ===
|
|
||||||
// ==============
|
|
||||||
|
|
||||||
function startServer(port) {
|
|
||||||
const app = express()
|
|
||||||
app.use(express.text())
|
|
||||||
|
|
||||||
app.post('/', async (req, res) => {
|
|
||||||
if (
|
|
||||||
typeof req.headers.origin === 'undefined' ||
|
|
||||||
new URL(req.headers.origin).hostname !== 'localhost'
|
|
||||||
) {
|
|
||||||
res.sendStatus(httpStatusCodes.forbidden)
|
|
||||||
} else if (typeof req.body !== 'string') {
|
|
||||||
res.sendStatus(httpStatusCodes.badRequest)
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
await writeLog(req.body)
|
|
||||||
console.log(`Saved log from origin ${req.headers.origin}`)
|
|
||||||
res.sendStatus(httpStatusCodes.noContent)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Could not write log file:\n' + e.message)
|
|
||||||
res.sendStatus(httpStatusCodes.internalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const server = app.listen(port, 'localhost')
|
|
||||||
server.on('listening', function () {
|
|
||||||
console.log(`Logging service listening at port ${server.address().port}`)
|
|
||||||
})
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// === File Utils ===
|
|
||||||
// ==================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes message to a new file in the log sub directory.
|
|
||||||
* The file name is composed of the UTC time and date and a V4 UUID to guarantee uniqueness.
|
|
||||||
*/
|
|
||||||
async function writeLog(message) {
|
|
||||||
const dir = 'log'
|
|
||||||
const file = `${timestamp()}__${uuid.v4()}`
|
|
||||||
await fs.promises.mkdir(dir, { recursive: true })
|
|
||||||
await fs.promises.writeFile(`${dir}/${file}`, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current UTC date and time in the format "yyy-MM-dd_HH:mm:ss.".
|
|
||||||
*/
|
|
||||||
function timestamp() {
|
|
||||||
const d = new Date()
|
|
||||||
|
|
||||||
const year = d.getUTCFullYear().toString()
|
|
||||||
const month = d.getUTCMonth().toString().padStart(2, '0')
|
|
||||||
const day = d.getUTCDate().toString().padStart(2, '0')
|
|
||||||
|
|
||||||
const hour = d.getUTCHours().toString().padStart(2, '0')
|
|
||||||
const minute = d.getUTCMinutes().toString().padStart(2, '0')
|
|
||||||
const second = d.getUTCSeconds().toString().padStart(2, '0')
|
|
||||||
|
|
||||||
return `${year}-${month}-${day}_${hour}:${minute}:${second}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
const command_line_args = process.argv.slice(2)
|
|
||||||
main(command_line_args)
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
const assert = require('assert')
|
|
||||||
const axios = require('axios').default
|
|
||||||
const fs = require('fs')
|
|
||||||
const mockFs = require('mock-fs')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
const { startServer } = require('../server')
|
|
||||||
|
|
||||||
describe('Logging Server', function () {
|
|
||||||
let server
|
|
||||||
let serverUrl
|
|
||||||
|
|
||||||
const dummyMessage = '<Dummy message>'
|
|
||||||
const goodConfig = {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'text/plain',
|
|
||||||
Origin: 'http://localhost/',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const wrongOriginConfig = {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'text/plain',
|
|
||||||
Origin: 'http://attacker/',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const wrongContentTypeConfig = {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'image/jpeg',
|
|
||||||
Origin: 'http://localhost/',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function (done) {
|
|
||||||
// The server relies on some package that imports from `raw-body` at runtime.
|
|
||||||
// Therefore, we need to include this directory in our mocked fs.
|
|
||||||
const rawBodyDir = path.dirname(require.resolve('raw-body'))
|
|
||||||
|
|
||||||
// We mock the file system so the server does not actually write logs to disk.
|
|
||||||
mockFs({
|
|
||||||
[rawBodyDir]: mockFs.load(rawBodyDir),
|
|
||||||
})
|
|
||||||
|
|
||||||
const port = 0 // Choose an arbitrary available port
|
|
||||||
server = startServer(port)
|
|
||||||
server.on('listening', function () {
|
|
||||||
serverUrl = `http://localhost:${server.address().port}/`
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
server.close()
|
|
||||||
mockFs.restore()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should write the body of valid requests to a file', async function () {
|
|
||||||
await axios.post(serverUrl, dummyMessage, goodConfig)
|
|
||||||
const log_files = fs.readdirSync('log/')
|
|
||||||
assert.strictEqual(log_files.length, 1)
|
|
||||||
assert.strictEqual(fs.readFileSync(`log/${log_files[0]}`).toString(), dummyMessage)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should reject requests from origins other than localhost', async function () {
|
|
||||||
let req = axios.post(serverUrl, '', wrongOriginConfig)
|
|
||||||
await assert.rejects(req, 'Error: Request failed with status code 403')
|
|
||||||
assert(!fs.existsSync('log/'))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should keep running after requests', async function () {
|
|
||||||
await Promise.allSettled([
|
|
||||||
axios.post(serverUrl, '', goodConfig),
|
|
||||||
axios.post(serverUrl, '', wrongOriginConfig),
|
|
||||||
axios.post(serverUrl, '', wrongContentTypeConfig),
|
|
||||||
])
|
|
||||||
|
|
||||||
await axios.post(serverUrl, dummyMessage, goodConfig)
|
|
||||||
const log_files = fs.readdirSync('log/')
|
|
||||||
assert(log_files.some(file => fs.readFileSync(`log/${file}`).toString() === dummyMessage))
|
|
||||||
})
|
|
||||||
})
|
|
@ -21,9 +21,8 @@ use enso_frp::future::EventOutputExt;
|
|||||||
use enso_gui::executor::web::EventLoopExecutor;
|
use enso_gui::executor::web::EventLoopExecutor;
|
||||||
use enso_gui::initializer::setup_global_executor;
|
use enso_gui::initializer::setup_global_executor;
|
||||||
use enso_gui::Ide;
|
use enso_gui::Ide;
|
||||||
|
use enso_web::traits::*;
|
||||||
use enso_web::HtmlDivElement;
|
use enso_web::HtmlDivElement;
|
||||||
use enso_web::NodeInserter;
|
|
||||||
use enso_web::StyleSetter;
|
|
||||||
use ensogl::application::Application;
|
use ensogl::application::Application;
|
||||||
|
|
||||||
/// Reexports of commonly-used structures, methods and traits.
|
/// Reexports of commonly-used structures, methods and traits.
|
||||||
@ -57,12 +56,11 @@ impl IntegrationTest {
|
|||||||
|
|
||||||
/// Initializes the executor and `Ide` structure and returns new Fixture.
|
/// Initializes the executor and `Ide` structure and returns new Fixture.
|
||||||
pub async fn setup() -> Self {
|
pub async fn setup() -> Self {
|
||||||
enso_web::forward_panic_hook_to_error();
|
|
||||||
let executor = setup_global_executor();
|
let executor = setup_global_executor();
|
||||||
let root_div = enso_web::create_div();
|
let root_div = enso_web::document.create_div_or_panic();
|
||||||
root_div.set_id("root");
|
root_div.set_id("root");
|
||||||
root_div.set_style_or_panic("display", "none");
|
root_div.set_style_or_warn("display", "none");
|
||||||
enso_web::body().append_or_panic(&root_div);
|
enso_web::document.body_or_panic().append_or_warn(&root_div);
|
||||||
|
|
||||||
let initializer = enso_gui::ide::Initializer::new(default());
|
let initializer = enso_gui::ide::Initializer::new(default());
|
||||||
let ide = initializer.start().await.expect("Failed to initialize the application.");
|
let ide = initializer.start().await.expect("Failed to initialize the application.");
|
||||||
@ -72,7 +70,7 @@ impl IntegrationTest {
|
|||||||
|
|
||||||
fn set_screen_size(app: &Application) {
|
fn set_screen_size(app: &Application) {
|
||||||
let (screen_width, screen_height) = Self::SCREEN_SIZE;
|
let (screen_width, screen_height) = Self::SCREEN_SIZE;
|
||||||
app.display.scene().layers.iter_sublayers_and_masks_nested(|layer| {
|
app.display.default_scene.layers.iter_sublayers_and_masks_nested(|layer| {
|
||||||
layer.camera().set_screen(screen_width, screen_height)
|
layer.camera().set_screen(screen_width, screen_height)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ async fn zooming() {
|
|||||||
let test = IntegrationTestOnNewProject::setup().await;
|
let test = IntegrationTestOnNewProject::setup().await;
|
||||||
let project = test.project_view();
|
let project = test.project_view();
|
||||||
let graph_editor = test.graph_editor();
|
let graph_editor = test.graph_editor();
|
||||||
let camera = test.ide.ensogl_app.display.scene().layers.main.camera();
|
let camera = test.ide.ensogl_app.display.default_scene.layers.main.camera();
|
||||||
let navigator = &graph_editor.model.navigator;
|
let navigator = &graph_editor.model.navigator;
|
||||||
|
|
||||||
let zoom_on_center = |amount: f32| ZoomEvent { focus: Vector2(0.0, 0.0), amount };
|
let zoom_on_center = |amount: f32| ZoomEvent { focus: Vector2(0.0, 0.0), amount };
|
||||||
@ -136,7 +136,7 @@ async fn adding_node_with_add_node_button() {
|
|||||||
assert_eq!(graph_editor.model.nodes.all.len(), INITIAL_NODE_COUNT + 2);
|
assert_eq!(graph_editor.model.nodes.all.len(), INITIAL_NODE_COUNT + 2);
|
||||||
|
|
||||||
// If there is a free space, the new node is created in the center of screen.
|
// If there is a free space, the new node is created in the center of screen.
|
||||||
let camera = test.ide.ensogl_app.display.scene().layers.main.camera();
|
let camera = test.ide.ensogl_app.display.default_scene.layers.main.camera();
|
||||||
camera.mod_position_xy(|pos| pos + Vector2(1000.0, 1000.0));
|
camera.mod_position_xy(|pos| pos + Vector2(1000.0, 1000.0));
|
||||||
let wait_for_update = Duration::from_millis(500);
|
let wait_for_update = Duration::from_millis(500);
|
||||||
sleep(wait_for_update).await;
|
sleep(wait_for_update).await;
|
||||||
@ -145,8 +145,8 @@ async fn adding_node_with_add_node_button() {
|
|||||||
assert!(node_source.is_none());
|
assert!(node_source.is_none());
|
||||||
assert_eq!(graph_editor.model.nodes.all.len(), INITIAL_NODE_COUNT + 3);
|
assert_eq!(graph_editor.model.nodes.all.len(), INITIAL_NODE_COUNT + 3);
|
||||||
let node_position = graph_editor.model.get_node_position(node_id).expect("Node was not added");
|
let node_position = graph_editor.model.get_node_position(node_id).expect("Node was not added");
|
||||||
let center_of_screen =
|
let scene = &test.ide.ensogl_app.display.default_scene;
|
||||||
test.ide.ensogl_app.display.scene().screen_to_scene_coordinates(Vector3(0.0, 0.0, 0.0));
|
let center_of_screen = scene.screen_to_scene_coordinates(Vector3(0.0, 0.0, 0.0));
|
||||||
assert_abs_diff_eq!(node_position.x, center_of_screen.x, epsilon = 10.0);
|
assert_abs_diff_eq!(node_position.x, center_of_screen.x, epsilon = 10.0);
|
||||||
assert_abs_diff_eq!(node_position.y, center_of_screen.y, epsilon = 10.0);
|
assert_abs_diff_eq!(node_position.y, center_of_screen.y, epsilon = 10.0);
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,66 @@
|
|||||||
#![feature(trait_alias)]
|
//! Definitions of a callback registry – utility allowing attaching and running attached functions.
|
||||||
|
|
||||||
//! Definitions of callback handling utilities.
|
// === Linter configuration ===
|
||||||
|
#![warn(missing_copy_implementations)]
|
||||||
|
#![warn(missing_debug_implementations)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![warn(trivial_casts)]
|
||||||
|
#![warn(trivial_numeric_casts)]
|
||||||
|
#![warn(unsafe_code)]
|
||||||
|
#![warn(unused_import_braces)]
|
||||||
|
#![warn(unused_qualifications)]
|
||||||
|
// === Features ===
|
||||||
|
#![feature(trait_alias)]
|
||||||
|
#![feature(unboxed_closures)]
|
||||||
|
#![feature(fn_traits)]
|
||||||
|
#![feature(unsize)]
|
||||||
|
|
||||||
use enso_prelude::*;
|
use enso_prelude::*;
|
||||||
|
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
use std::marker::Unsize;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ================
|
// ==============================
|
||||||
// === Callback ===
|
// === Popular Callback Types ===
|
||||||
// ================
|
// ==============================
|
||||||
|
|
||||||
/// Immutable callback type.
|
pub use callback_types::*;
|
||||||
pub trait CallbackFn = Fn() + 'static;
|
|
||||||
|
|
||||||
/// Immutable callback object.
|
/// Popular callback types. These are aliases for static [`Fn`] and [`FnMut`] with different amount
|
||||||
pub type Callback = Box<dyn CallbackFn>;
|
/// of arguments. The names directly correspond to the [`::registry`] namespace. For example,
|
||||||
|
/// the [`::registry::CopyMut3`] is a callback registry for [`CopyMut3`] callbacks.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
mod callback_types {
|
||||||
|
pub trait NoArgs = 'static + Fn();
|
||||||
|
pub trait MutNoArgs = 'static + FnMut();
|
||||||
|
|
||||||
/// Callback object smart constructor.
|
pub trait Copy1<T1> = 'static + Fn(T1);
|
||||||
#[allow(non_snake_case)]
|
pub trait Copy2<T1, T2> = 'static + Fn(T1, T2);
|
||||||
pub fn Callback<F: CallbackFn>(f: F) -> Callback {
|
pub trait Copy3<T1, T2, T3> = 'static + Fn(T1, T2, T3);
|
||||||
Box::new(f)
|
pub trait Copy4<T1, T2, T3, T4> = 'static + Fn(T1, T2, T3, T4);
|
||||||
|
pub trait Copy5<T1, T2, T3, T4, T5> = 'static + Fn(T1, T2, T3, T4, T5);
|
||||||
|
|
||||||
|
pub trait Ref1<T1> = 'static + Fn(&T1);
|
||||||
|
pub trait Ref2<T1, T2> = 'static + Fn(&T1, &T2);
|
||||||
|
pub trait Ref3<T1, T2, T3> = 'static + Fn(&T1, &T2, &T3);
|
||||||
|
pub trait Ref4<T1, T2, T3, T4> = 'static + Fn(&T1, &T2, &T3, &T4);
|
||||||
|
pub trait Ref5<T1, T2, T3, T4, T5> = 'static + Fn(&T1, &T2, &T3, &T4, &T5);
|
||||||
|
|
||||||
|
pub trait CopyMut1<T1> = 'static + FnMut(T1);
|
||||||
|
pub trait CopyMut2<T1, T2> = 'static + FnMut(T1, T2);
|
||||||
|
pub trait CopyMut3<T1, T2, T3> = 'static + FnMut(T1, T2, T3);
|
||||||
|
pub trait CopyMut4<T1, T2, T3, T4> = 'static + FnMut(T1, T2, T3, T4);
|
||||||
|
pub trait CopyMut5<T1, T2, T3, T4, T5> = 'static + FnMut(T1, T2, T3, T4, T5);
|
||||||
|
|
||||||
|
pub trait RefMut1<T1> = 'static + FnMut(&T1);
|
||||||
|
pub trait RefMut2<T1, T2> = 'static + FnMut(&T1, &T2);
|
||||||
|
pub trait RefMut3<T1, T2, T3> = 'static + FnMut(&T1, &T2, &T3);
|
||||||
|
pub trait RefMut4<T1, T2, T3, T4> = 'static + FnMut(&T1, &T2, &T3, &T4);
|
||||||
|
pub trait RefMut5<T1, T2, T3, T4, T5> = 'static + FnMut(&T1, &T2, &T3, &T4, &T5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable callback type.
|
|
||||||
pub trait CallbackMutFn = FnMut() + 'static;
|
|
||||||
|
|
||||||
/// Mutable callback object.
|
|
||||||
pub type CallbackMut = Box<dyn CallbackMutFn>;
|
|
||||||
|
|
||||||
/// Mutable callback type with one parameter.
|
|
||||||
pub trait CallbackMut1Fn<T> = FnMut(&T) + 'static;
|
|
||||||
|
|
||||||
/// Mutable callback object with one parameter.
|
|
||||||
pub type CallbackMut1<T> = Box<dyn CallbackMut1Fn<T>>;
|
|
||||||
|
|
||||||
/// Mutable callback type with one parameter.
|
|
||||||
pub trait CopyCallbackMut1Fn<T> = FnMut(T) + 'static;
|
|
||||||
|
|
||||||
/// Mutable callback object with one parameter.
|
|
||||||
pub type CopyCallbackMut1<T> = Box<dyn CopyCallbackMut1Fn<T>>;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==============
|
// ==============
|
||||||
@ -49,27 +68,21 @@ pub type CopyCallbackMut1<T> = Box<dyn CopyCallbackMut1Fn<T>>;
|
|||||||
// ==============
|
// ==============
|
||||||
|
|
||||||
/// Handle to a callback. When the handle is dropped, the callback is removed.
|
/// Handle to a callback. When the handle is dropped, the callback is removed.
|
||||||
#[derive(Clone, CloneRef, Debug)]
|
#[derive(Clone, CloneRef, Debug, Default)]
|
||||||
pub struct Handle {
|
pub struct Handle {
|
||||||
rc: Rc<Cell<bool>>,
|
is_invalidated: Rc<Cell<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle {
|
impl Handle {
|
||||||
/// Constructor.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let rc = Rc::new(Cell::new(true));
|
|
||||||
Self { rc }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create guard for this handle.
|
/// Create guard for this handle.
|
||||||
pub fn guard(&self) -> Guard {
|
pub fn guard(&self) -> Guard {
|
||||||
Guard { weak: Rc::downgrade(&self.rc) }
|
Guard { weak: Rc::downgrade(&self.is_invalidated) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invalidates all handles. Even if there exist some active handles, the callback will not be
|
/// Invalidates all handles. Even if there exist some active handles, the callback will not be
|
||||||
/// run anymore after performing this operation.
|
/// run anymore after performing this operation.
|
||||||
pub fn invalidate_all_handles(&self) {
|
pub fn invalidate_all_handles(&self) {
|
||||||
self.rc.set(false)
|
self.is_invalidated.set(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Forget the handle. Warning! You would not be able to stop the callback after performing this
|
/// Forget the handle. Warning! You would not be able to stop the callback after performing this
|
||||||
@ -79,12 +92,6 @@ impl Handle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Handle {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =============
|
// =============
|
||||||
@ -100,10 +107,75 @@ pub struct Guard {
|
|||||||
impl Guard {
|
impl Guard {
|
||||||
/// Checks if the handle is still valid.
|
/// Checks if the handle is still valid.
|
||||||
pub fn exists(&self) -> bool {
|
pub fn exists(&self) -> bool {
|
||||||
match self.weak.upgrade() {
|
self.weak.upgrade().map_or(false, |t| !t.get())
|
||||||
None => false,
|
}
|
||||||
Some(t) => t.get(),
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// === RegistryFn ===
|
||||||
|
// ==================
|
||||||
|
|
||||||
|
/// An abstraction for a [`Fn`] functions kept in the [`Registry`]. It is used to unify the handling
|
||||||
|
/// of [`Fn`] and [`FnMut`] functions. They are kept either in this structure or in the
|
||||||
|
/// [`RegistryFnMut`] one, both exposing the same API.
|
||||||
|
#[derive(Derivative)]
|
||||||
|
#[derivative(Clone(bound = ""))]
|
||||||
|
#[derivative(Debug(bound = ""))]
|
||||||
|
pub struct RegistryFn<F: ?Sized> {
|
||||||
|
#[derivative(Debug = "ignore")]
|
||||||
|
function: Rc<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An abstraction for a [`FnMut`] functions kept in the [`Registry`]. See the documentation of
|
||||||
|
/// [`RegistryFn`] to learn more.
|
||||||
|
#[derive(Derivative)]
|
||||||
|
#[derivative(Clone(bound = ""))]
|
||||||
|
#[derivative(Debug(bound = ""))]
|
||||||
|
pub struct RegistryFnMut<F: ?Sized> {
|
||||||
|
#[derivative(Debug = "ignore")]
|
||||||
|
function: Rc<RefCell<F>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor abstraction for [`RegistryFn`] and [`RegistryFnMut`].
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub trait RegistryFnNew {
|
||||||
|
type InternalF: ?Sized;
|
||||||
|
fn new<C: Unsize<Self::InternalF> + 'static>(f: C) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call abstraction for [`RegistryFn`] and [`RegistryFnMut`].
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub trait RegistryFnCall<Args> {
|
||||||
|
fn call(&self, args: Args);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: ?Sized> RegistryFnNew for RegistryFn<F> {
|
||||||
|
type InternalF = F;
|
||||||
|
fn new<C: Unsize<F> + 'static>(f: C) -> Self {
|
||||||
|
let function = Rc::new(f);
|
||||||
|
Self { function }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: ?Sized> RegistryFnNew for RegistryFnMut<F> {
|
||||||
|
type InternalF = F;
|
||||||
|
fn new<C: Unsize<F> + 'static>(f: C) -> Self {
|
||||||
|
let function = Rc::new(RefCell::new(f));
|
||||||
|
Self { function }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Args, F: ?Sized + Fn<Args>> RegistryFnCall<Args> for RegistryFn<F> {
|
||||||
|
fn call(&self, args: Args) {
|
||||||
|
(&*self.function).call(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Args, F: ?Sized + FnMut<Args>> RegistryFnCall<Args> for RegistryFnMut<F> {
|
||||||
|
fn call(&self, args: Args) {
|
||||||
|
(&mut *self.function.borrow_mut()).call_mut(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,117 +185,32 @@ impl Guard {
|
|||||||
// === Registry ===
|
// === Registry ===
|
||||||
// ================
|
// ================
|
||||||
|
|
||||||
/// Registry gathering callbacks. Each registered callback is assigned with a handle. Callback and
|
/// The main callback registry structure. The [`F`] parameter is either instantiated to
|
||||||
/// handle lifetimes are strictly connected. As soon a handle is dropped, the callback is removed
|
/// [`RegistryFn<dyn Fn<Args>>`] or to [`RegistryFnMut<dyn FnMut<Args>>`]. See the generated aliases
|
||||||
/// as well.
|
/// for common types below, in the [`registry`] module.
|
||||||
#[derive(Default, Derivative)]
|
|
||||||
#[derivative(Debug)]
|
|
||||||
pub struct Registry {
|
|
||||||
#[derivative(Debug = "ignore")]
|
|
||||||
callback_list: Vec<(Guard, CallbackMut)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Registry {
|
|
||||||
/// Adds new callback and returns a new handle for it.
|
|
||||||
pub fn add<F: CallbackMutFn>(&mut self, callback: F) -> Handle {
|
|
||||||
let callback = Box::new(callback);
|
|
||||||
let handle = Handle::new();
|
|
||||||
let guard = handle.guard();
|
|
||||||
self.callback_list.push((guard, callback));
|
|
||||||
handle
|
|
||||||
}
|
|
||||||
|
|
||||||
///Checks whether there are any callbacks registered.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.callback_list.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fires all registered callbacks and removes the ones which got dropped.
|
|
||||||
pub fn run_all(&mut self) {
|
|
||||||
self.clear_unused_callbacks();
|
|
||||||
self.callback_list.iter_mut().for_each(move |(_, callback)| callback());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks all registered callbacks and removes the ones which got dropped.
|
|
||||||
fn clear_unused_callbacks(&mut self) {
|
|
||||||
self.callback_list.retain(|(guard, _)| guard.exists());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =========================
|
|
||||||
// === SharedRegistryMut ===
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
/// Registry gathering callbacks implemented with internal mutability pattern. Each registered
|
|
||||||
/// callback is assigned with a handle. Callback and handle lifetimes are strictly connected. As
|
|
||||||
/// soon a handle is dropped, the callback is removed as well.
|
|
||||||
#[derive(Clone, CloneRef, Default, Derivative)]
|
|
||||||
#[derivative(Debug)]
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub struct SharedRegistryMut {
|
|
||||||
#[derivative(Debug = "ignore")]
|
|
||||||
callback_list: Rc<RefCell<Vec<(Guard, Rc<RefCell<dyn CallbackMutFn>>)>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SharedRegistryMut {
|
|
||||||
/// Adds new callback and returns a new handle for it.
|
|
||||||
pub fn add<F: CallbackMutFn>(&self, callback: F) -> Handle {
|
|
||||||
let callback = Rc::new(RefCell::new(callback));
|
|
||||||
let handle = Handle::new();
|
|
||||||
let guard = handle.guard();
|
|
||||||
self.callback_list.borrow_mut().push((guard, callback));
|
|
||||||
handle
|
|
||||||
}
|
|
||||||
|
|
||||||
///Checks whether there are any callbacks registered.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.callback_list.borrow().is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fires all registered callbacks and removes the ones which got dropped. The implementation is
|
|
||||||
/// safe - you are allowed to change the registry while a callback is running.
|
|
||||||
pub fn run_all(&self) {
|
|
||||||
self.clear_unused_callbacks();
|
|
||||||
let callbacks = self.callback_list.borrow().clone();
|
|
||||||
callbacks.iter().for_each(|(_, callback)| (&mut *callback.borrow_mut())());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks all registered callbacks and removes the ones which got dropped.
|
|
||||||
fn clear_unused_callbacks(&self) {
|
|
||||||
self.callback_list.borrow_mut().retain(|(guard, _)| guard.exists());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ==========================
|
|
||||||
// === SharedRegistryMut1 ===
|
|
||||||
// ==========================
|
|
||||||
|
|
||||||
/// Registry gathering callbacks implemented with internal mutability pattern. Each registered
|
|
||||||
/// callback is assigned with a handle. Callback and handle lifetimes are strictly connected. As
|
|
||||||
/// soon a handle is dropped, the callback is removed as well.
|
|
||||||
#[derive(CloneRef, Derivative)]
|
#[derive(CloneRef, Derivative)]
|
||||||
#[derivative(Clone(bound = ""))]
|
#[derivative(Clone(bound = ""))]
|
||||||
#[derivative(Debug(bound = ""))]
|
#[derivative(Debug(bound = ""))]
|
||||||
#[derivative(Default(bound = ""))]
|
#[derivative(Default(bound = ""))]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(missing_docs)]
|
||||||
pub struct SharedRegistryMut1<T> {
|
pub struct Registry<F> {
|
||||||
#[derivative(Debug = "ignore")]
|
#[derivative(Debug = "ignore")]
|
||||||
callback_list: Rc<RefCell<Vec<(Guard, Rc<RefCell<dyn CallbackMut1Fn<T>>>)>>>,
|
callback_list: Rc<RefCell<Vec<(Guard, F)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SharedRegistryMut1<T> {
|
impl<F> Registry<F> {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds new callback and returns a new handle for it.
|
/// Add a new callback. Returns a new [`Handle`], which dropped, will unregister the callback.
|
||||||
pub fn add<F: CallbackMut1Fn<T>>(&self, callback: F) -> Handle {
|
pub fn add<C>(&self, callback: C) -> Handle
|
||||||
let callback = Rc::new(RefCell::new(callback));
|
where
|
||||||
let handle = Handle::new();
|
F: RegistryFnNew,
|
||||||
|
C: Unsize<<F as RegistryFnNew>::InternalF> + 'static, {
|
||||||
|
let callback = F::new(callback);
|
||||||
|
let handle = Handle::default();
|
||||||
let guard = handle.guard();
|
let guard = handle.guard();
|
||||||
self.callback_list.borrow_mut().push((guard, callback));
|
self.callback_list.borrow_mut().push((guard, callback));
|
||||||
handle
|
handle
|
||||||
@ -234,103 +221,164 @@ impl<T> SharedRegistryMut1<T> {
|
|||||||
self.callback_list.borrow().is_empty()
|
self.callback_list.borrow().is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fires all registered callbacks and removes the ones which got dropped. The implementation is
|
|
||||||
/// safe - you are allowed to change the registry while a callback is running.
|
|
||||||
pub fn run_all(&self, t: &T) {
|
|
||||||
self.clear_unused_callbacks();
|
|
||||||
let callbacks = self.callback_list.borrow().clone();
|
|
||||||
callbacks.iter().for_each(|(_, callback)| (&mut *callback.borrow_mut())(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks all registered callbacks and removes the ones which got dropped.
|
/// Checks all registered callbacks and removes the ones which got dropped.
|
||||||
fn clear_unused_callbacks(&self) {
|
fn clear_unused_callbacks(&self) {
|
||||||
self.callback_list.borrow_mut().retain(|(guard, _)| guard.exists());
|
self.callback_list.borrow_mut().retain(|(guard, _)| guard.exists());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Fires all registered callbacks and removes the ones which got dropped. The implementation
|
||||||
|
/// is safe - you are allowed to change the registry while a callback is running.
|
||||||
// =================
|
pub fn run_all_with_args<Args: Copy>(&self, args: Args)
|
||||||
// === Registry1 ===
|
where F: Clone + RegistryFnCall<Args> {
|
||||||
// =================
|
|
||||||
|
|
||||||
/// Registry gathering callbacks. Each registered callback is assigned with a handle. Callback and
|
|
||||||
/// handle lifetimes are strictly connected. As soon a handle is dropped, the callback is removed
|
|
||||||
/// as well.
|
|
||||||
#[derive(Derivative)]
|
|
||||||
#[derivative(Debug, Default(bound = ""))]
|
|
||||||
pub struct Registry1<T> {
|
|
||||||
#[derivative(Debug = "ignore")]
|
|
||||||
callback_list: Vec<(Guard, CallbackMut1<T>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Registry1<T> {
|
|
||||||
/// Adds new callback and returns a new handle for it.
|
|
||||||
pub fn add<F: CallbackMut1Fn<T>>(&mut self, callback: F) -> Handle {
|
|
||||||
let callback = Box::new(callback);
|
|
||||||
let handle = Handle::new();
|
|
||||||
let guard = handle.guard();
|
|
||||||
self.callback_list.push((guard, callback));
|
|
||||||
handle
|
|
||||||
}
|
|
||||||
|
|
||||||
///Checks whether there are any callbacks registered.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.callback_list.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fires all registered callbacks and removes the ones which got dropped.
|
|
||||||
pub fn run_all(&mut self, t: &T) {
|
|
||||||
self.clear_unused_callbacks();
|
self.clear_unused_callbacks();
|
||||||
self.callback_list.iter_mut().for_each(move |(_, callback)| callback(t));
|
// The clone is performed in order for the callbacks to be able to register new ones.
|
||||||
|
let callbacks = self.callback_list.borrow().clone();
|
||||||
|
callbacks.iter().for_each(move |(_, callback)| {
|
||||||
|
callback.call(args);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks all registered callbacks and removes the ones which got dropped.
|
/// Aliases for common [`Registry`] instantiations. The names directly correspond to the
|
||||||
fn clear_unused_callbacks(&mut self) {
|
/// [`::callback_types`] namespace. For example, the [`::registry::CopyMut3`] is a callback registry
|
||||||
self.callback_list.retain(|(guard, _)| guard.exists());
|
/// for [`CopyMut3`] callbacks.
|
||||||
}
|
///
|
||||||
|
/// The used naming convention is `("Copy" | "Ref") ("Mut" | "") ("NoArgs" | <arg number>)`:
|
||||||
|
/// - The "Copy" prefix means that arguments are passed as copies. You should use this registry type
|
||||||
|
/// when working with callbacks consuming primitive types, such as [`usize`].
|
||||||
|
/// - The "Ref" prefix means that the arguments are passed as references. You should use this
|
||||||
|
/// registry type when working with callbacks consuming complex types, not implementing [`Copy`].
|
||||||
|
/// - The registry types which contain the "Mut" part accept `FnMut<Args>` functions. The rest
|
||||||
|
/// accepts the `Fn<Args>` ones.
|
||||||
|
/// - The arg number is a number from 1 to 5 indicating the number of arguments callbacks accept.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
/// - The [`CopyMut2`] registry accepts callbacks in a form of [`FnMut(T1,T2)`], where both [`T1`]
|
||||||
|
/// and [`T2`] will be passed as copies.
|
||||||
|
/// - The [`Ref1`] registry accepts callbacks in a form of [`Fn(&T1)`].
|
||||||
|
/// - The [`NoArgs`] registry is a registry for [`Fn()`] functions.
|
||||||
|
/// - The [`MutNoArgs`] registry is a registry for [`FnMut()`] functions.
|
||||||
|
///
|
||||||
|
/// It is possible to define a registry which uses callbacks whose arguments are a mix of references
|
||||||
|
/// and copy-able values. However, such types need to be defined manually and are not provided by
|
||||||
|
/// the alias set below.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub mod registry {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub type NoArgs = Registry<RegistryFn<dyn Fn()>>;
|
||||||
|
pub type MutNoArgs = Registry<RegistryFnMut<dyn FnMut()>>;
|
||||||
|
|
||||||
|
pub type Copy1<T1> = Registry<RegistryFn<dyn Fn(T1)>>;
|
||||||
|
pub type Copy2<T1, T2> = Registry<RegistryFn<dyn Fn(T1, T2)>>;
|
||||||
|
pub type Copy3<T1, T2, T3> = Registry<RegistryFn<dyn Fn(T1, T2, T3)>>;
|
||||||
|
pub type Copy4<T1, T2, T3, T4> = Registry<RegistryFn<dyn Fn(T1, T2, T3, T4)>>;
|
||||||
|
pub type Copy5<T1, T2, T3, T4, T5> = Registry<RegistryFn<dyn Fn(T1, T2, T3, T4, T5)>>;
|
||||||
|
|
||||||
|
pub type Ref1<T1> = Registry<RegistryFn<dyn Fn(&T1)>>;
|
||||||
|
pub type Ref2<T1, T2> = Registry<RegistryFn<dyn Fn(&T1, &T2)>>;
|
||||||
|
pub type Ref3<T1, T2, T3> = Registry<RegistryFn<dyn Fn(&T1, &T2, &T3)>>;
|
||||||
|
pub type Ref4<T1, T2, T3, T4> = Registry<RegistryFn<dyn Fn(&T1, &T2, &T3, &T4)>>;
|
||||||
|
pub type Ref5<T1, T2, T3, T4, T5> = Registry<RegistryFn<dyn Fn(&T1, &T2, &T3, &T4, &T5)>>;
|
||||||
|
|
||||||
|
pub type CopyMut1<T1> = Registry<RegistryFnMut<dyn FnMut(T1)>>;
|
||||||
|
pub type CopyMut2<T1, T2> = Registry<RegistryFnMut<dyn FnMut(T1, T2)>>;
|
||||||
|
pub type CopyMut3<T1, T2, T3> = Registry<RegistryFnMut<dyn FnMut(T1, T2, T3)>>;
|
||||||
|
pub type CopyMut4<T1, T2, T3, T4> = Registry<RegistryFnMut<dyn FnMut(T1, T2, T3, T4)>>;
|
||||||
|
pub type CopyMut5<T1, T2, T3, T4, T5> = Registry<RegistryFnMut<dyn FnMut(T1, T2, T3, T4, T5)>>;
|
||||||
|
|
||||||
|
pub type RefMut1<T1> = Registry<RegistryFnMut<dyn FnMut(&T1)>>;
|
||||||
|
pub type RefMut2<T1, T2> = Registry<RegistryFnMut<dyn FnMut(&T1, &T2)>>;
|
||||||
|
pub type RefMut3<T1, T2, T3> = Registry<RegistryFnMut<dyn FnMut(&T1, &T2, &T3)>>;
|
||||||
|
pub type RefMut4<T1, T2, T3, T4> = Registry<RegistryFnMut<dyn FnMut(&T1, &T2, &T3, &T4)>>;
|
||||||
|
pub type RefMut5<T1, T2, T3, T4, T5> =
|
||||||
|
Registry<RegistryFnMut<dyn FnMut(&T1, &T2, &T3, &T4, &T5)>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ====================
|
// ======================
|
||||||
// === CopyRegistry ===
|
// === RegistryRunner ===
|
||||||
// ====================
|
// ======================
|
||||||
|
|
||||||
/// Specialized version of `Registry` for arguments implementing `Copy`. Passing copy-able elements
|
/// Generator of traits allowing the usage of a [`run_all`] function. It is an alias for the
|
||||||
/// as values is more performant than by reference.
|
/// [`Registry::run_all_with_args`] where arguments are passed in a convenient way, instead than in
|
||||||
#[derive(Derivative)]
|
/// a tuple.
|
||||||
#[derivative(Debug(bound = ""), Default(bound = ""))]
|
macro_rules! gen_runner_traits {
|
||||||
pub struct CopyRegistry1<T> {
|
($name:ident, $ref_name:ident, ($($arg:ident),*)) => {
|
||||||
#[derivative(Debug = "ignore")]
|
#[allow(non_snake_case)]
|
||||||
callback_list: Vec<(Guard, CopyCallbackMut1<T>)>,
|
pub trait $name {
|
||||||
|
$( type $arg; )*
|
||||||
|
fn run_all(&self, $($arg : Self::$arg),*);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub trait $ref_name {
|
||||||
|
$( type $arg; )*
|
||||||
|
fn run_all(&self, $($arg : &Self::$arg),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy> CopyRegistry1<T> {
|
macro_rules! gen_runner {
|
||||||
/// Adds new callback and returns a new handle for it.
|
($name:ident, $ref_name:ident, $data:ident, $data_ref:ident, <$($arg:ident),*>) => {
|
||||||
pub fn add<F: CopyCallbackMut1Fn<T>>(&mut self, callback: F) -> Handle {
|
#[allow(non_snake_case)]
|
||||||
let callback = Box::new(callback);
|
impl<$($arg: Copy),*> $name for registry::$data<$($arg),*> {
|
||||||
let handle = Handle::new();
|
$(type $arg = $arg;)*
|
||||||
let guard = handle.guard();
|
fn run_all(&self, $($arg : Self::$arg),*) {
|
||||||
self.callback_list.push((guard, callback));
|
self.run_all_with_args(($($arg),*,))
|
||||||
handle
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
impl<$($arg),*> $ref_name for registry::$data_ref<$($arg),*> {
|
||||||
|
$(type $arg = $arg;)*
|
||||||
|
fn run_all(&self, $($arg : &Self::$arg),*) {
|
||||||
|
self.run_all_with_args(($($arg),*,))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ==============
|
||||||
|
// === Traits ===
|
||||||
|
// ==============
|
||||||
|
|
||||||
|
/// All trait types that should be imported to the scope when using callback registry.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub mod traits {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait RegistryRunner0 {
|
||||||
|
fn run_all(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
///Checks whether there are any callbacks registered.
|
impl RegistryRunner0 for registry::MutNoArgs {
|
||||||
pub fn is_empty(&self) -> bool {
|
fn run_all(&self) {
|
||||||
self.callback_list.is_empty()
|
self.run_all_with_args(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fires all registered callbacks and removes the ones which got dropped.
|
impl RegistryRunner0 for registry::NoArgs {
|
||||||
pub fn run_all(&mut self, t: T) {
|
fn run_all(&self) {
|
||||||
self.clear_unused_callbacks();
|
self.run_all_with_args(())
|
||||||
self.callback_list.iter_mut().for_each(move |(_, callback)| callback(t));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks all registered callbacks and removes the ones which got dropped.
|
gen_runner_traits!(RegistryRunner1, RegistryRunnerRef1, (T1));
|
||||||
fn clear_unused_callbacks(&mut self) {
|
gen_runner_traits!(RegistryRunner2, RegistryRunnerRef2, (T1, T2));
|
||||||
self.callback_list.retain(|(guard, _)| guard.exists());
|
gen_runner_traits!(RegistryRunner3, RegistryRunnerRef3, (T1, T2, T3));
|
||||||
}
|
gen_runner_traits!(RegistryRunner4, RegistryRunnerRef4, (T1, T2, T3, T4));
|
||||||
|
gen_runner_traits!(RegistryRunner5, RegistryRunnerRef5, (T1, T2, T3, T4, T5));
|
||||||
|
|
||||||
|
gen_runner!(RegistryRunner1, RegistryRunnerRef1, CopyMut1, RefMut1, <T1>);
|
||||||
|
gen_runner!(RegistryRunner2, RegistryRunnerRef2, CopyMut2, RefMut2, <T1,T2>);
|
||||||
|
gen_runner!(RegistryRunner3, RegistryRunnerRef3, CopyMut3, RefMut3, <T1,T2,T3>);
|
||||||
|
gen_runner!(RegistryRunner4, RegistryRunnerRef4, CopyMut4, RefMut4, <T1,T2,T3,T4>);
|
||||||
|
gen_runner!(RegistryRunner5, RegistryRunnerRef5, CopyMut5, RefMut5, <T1,T2,T3,T4,T5>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -360,17 +408,18 @@ impl DynEvent {
|
|||||||
#[derivative(Debug)]
|
#[derivative(Debug)]
|
||||||
pub struct DynEventDispatcher {
|
pub struct DynEventDispatcher {
|
||||||
#[derivative(Debug = "ignore")]
|
#[derivative(Debug = "ignore")]
|
||||||
listener_map: HashMap<TypeId, Vec<(Guard, CallbackMut1<DynEvent>)>>,
|
#[allow(clippy::type_complexity)]
|
||||||
|
listener_map: HashMap<TypeId, Vec<(Guard, Box<dyn RefMut1<DynEvent>>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynEventDispatcher {
|
impl DynEventDispatcher {
|
||||||
/// Registers a new listener for a given type.
|
/// Registers a new listener for a given type.
|
||||||
pub fn add_listener<F: CallbackMut1Fn<T>, T: 'static>(&mut self, mut f: F) -> Handle {
|
pub fn add_listener<F: RefMut1<T>, T: 'static>(&mut self, mut f: F) -> Handle {
|
||||||
let callback = Box::new(move |event: &DynEvent| {
|
let callback = Box::new(move |event: &DynEvent| {
|
||||||
event.any.downcast_ref::<T>().iter().for_each(|t| f(t))
|
event.any.downcast_ref::<T>().iter().for_each(|t| f(t))
|
||||||
});
|
});
|
||||||
let type_id = (&PhantomData::<T>).type_id();
|
let type_id = (&PhantomData::<T>).type_id();
|
||||||
let handle = Handle::new();
|
let handle = Handle::default();
|
||||||
let guard = handle.guard();
|
let guard = handle.guard();
|
||||||
let listeners = self.listener_map.entry(type_id).or_insert_with(default);
|
let listeners = self.listener_map.entry(type_id).or_insert_with(default);
|
||||||
listeners.push((guard, callback));
|
listeners.push((guard, callback));
|
||||||
|
@ -274,7 +274,7 @@ impl<Shape: ButtonShape> View<Shape> {
|
|||||||
let frp = Frp::new();
|
let frp = Frp::new();
|
||||||
let model = Model::<Shape>::new(app);
|
let model = Model::<Shape>::new(app);
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let style = StyleWatchFrp::new(&scene.style_sheet);
|
let style = StyleWatchFrp::new(&scene.style_sheet);
|
||||||
let mouse = &scene.mouse.frp;
|
let mouse = &scene.mouse.frp;
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ impl DropDownMenu {
|
|||||||
let frp = &self.frp;
|
let frp = &self.frp;
|
||||||
let model = &self.model;
|
let model = &self.model;
|
||||||
|
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let mouse = &scene.mouse.frp;
|
let mouse = &scene.mouse.frp;
|
||||||
|
|
||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
@ -368,7 +368,7 @@ impl DropDownMenu {
|
|||||||
|
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for
|
||||||
// shape system (#795)
|
// shape system (#795)
|
||||||
let styles = StyleWatch::new(&app.display.scene().style_sheet);
|
let styles = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
let text_color = styles.get_color(theme::widget::list_view::text);
|
let text_color = styles.get_color(theme::widget::list_view::text);
|
||||||
model.label.set_default_color(text_color);
|
model.label.set_default_color(text_color);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ enso-frp = { path = "../../../frp" }
|
|||||||
enso-logger = { path = "../../../logger"}
|
enso-logger = { path = "../../../logger"}
|
||||||
enso-prelude = { path = "../../../prelude"}
|
enso-prelude = { path = "../../../prelude"}
|
||||||
js-sys = { version = "0.3.28" }
|
js-sys = { version = "0.3.28" }
|
||||||
wasm-bindgen = { version = "0.2.58", features = ["nightly"] }
|
wasm-bindgen = { version = "0.2.78", features = ["nightly"] }
|
||||||
wasm-bindgen-futures = { version = "0.4.8" }
|
wasm-bindgen-futures = { version = "0.4.8" }
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
|
@ -21,12 +21,16 @@ pub mod prelude {
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use enso_frp as frp;
|
use enso_frp as frp;
|
||||||
|
use enso_web as web;
|
||||||
use enso_web::stream::BlobExt;
|
use enso_web::stream::BlobExt;
|
||||||
use enso_web::stream::ReadableStreamDefaultReader;
|
use enso_web::stream::ReadableStreamDefaultReader;
|
||||||
use enso_web::Error;
|
use enso_web::Closure;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use enso_web::JsCast;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
use js_sys::Uint8Array;
|
use js_sys::Uint8Array;
|
||||||
use wasm_bindgen::prelude::*;
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
use wasm_bindgen_futures::JsFuture;
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
|
||||||
|
|
||||||
@ -54,7 +58,7 @@ pub struct File {
|
|||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
/// Constructor from the [`web_sys::File`].
|
/// Constructor from the [`web_sys::File`].
|
||||||
pub fn from_js_file(file: &web_sys::File) -> Result<Self, Error> {
|
pub fn from_js_file(file: &web_sys::File) -> Result<Self, web::JsValue> {
|
||||||
let name = ImString::new(file.name());
|
let name = ImString::new(file.name());
|
||||||
let size = file.size() as u64;
|
let size = file.size() as u64;
|
||||||
let mime_type = ImString::new(file.type_());
|
let mime_type = ImString::new(file.type_());
|
||||||
@ -64,6 +68,7 @@ impl File {
|
|||||||
Ok(File { name, mime_type, size, reader })
|
Ok(File { name, mime_type, size, reader })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
/// Read the next chunk of file content.
|
/// Read the next chunk of file content.
|
||||||
///
|
///
|
||||||
/// If there is no more data, it returns [`None`].
|
/// If there is no more data, it returns [`None`].
|
||||||
@ -71,7 +76,7 @@ impl File {
|
|||||||
/// The chunk size depend on the browser implementation, but it is assumed to be reasonable.
|
/// The chunk size depend on the browser implementation, but it is assumed to be reasonable.
|
||||||
/// See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/read and
|
/// See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/read and
|
||||||
/// https://github.com/w3c/FileAPI/issues/144#issuecomment-570982732.
|
/// https://github.com/w3c/FileAPI/issues/144#issuecomment-570982732.
|
||||||
pub async fn read_chunk(&self) -> Result<Option<Vec<u8>>, Error> {
|
pub async fn read_chunk(&self) -> Result<Option<Vec<u8>>, web::JsValue> {
|
||||||
if let Some(reader) = &*self.reader {
|
if let Some(reader) = &*self.reader {
|
||||||
let js_result = JsFuture::from(reader.read()).await?;
|
let js_result = JsFuture::from(reader.read()).await?;
|
||||||
let is_done = js_sys::Reflect::get(&js_result, &"done".into())?.as_bool().unwrap();
|
let is_done = js_sys::Reflect::get(&js_result, &"done".into())?.as_bool().unwrap();
|
||||||
@ -86,6 +91,12 @@ impl File {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
/// Read the next chunk of file content.
|
||||||
|
pub async fn read_chunk(&self) -> Result<Option<Vec<u8>>, web::JsValue> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -105,44 +116,40 @@ type DragOverClosure = Closure<dyn Fn(web_sys::DragEvent) -> bool>;
|
|||||||
#[derive(Clone, CloneRef, Debug)]
|
#[derive(Clone, CloneRef, Debug)]
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
network: frp::Network,
|
network: frp::Network,
|
||||||
files_received: frp::Source<Vec<File>>,
|
files_received: frp::Source<Vec<File>>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
drop_callback: Rc<DropClosure>,
|
drop_handle: web::EventListenerHandle,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
drag_over_callback: Rc<DragOverClosure>,
|
drag_over_handle: web::EventListenerHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
/// Constructor, adding listener to the given target.
|
/// Constructor, adding listener to the given target.
|
||||||
pub fn new(target: &web_sys::EventTarget) -> Self {
|
pub fn new(target: &enso_web::EventTarget) -> Self {
|
||||||
let logger = Logger::new("DropFileManager");
|
let logger = Logger::new("DropFileManager");
|
||||||
debug!(logger, "Creating DropFileManager");
|
debug!(logger, "Creating");
|
||||||
let network = frp::Network::new("DropFileManager");
|
let network = frp::Network::new("DropFileManager");
|
||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
files_received <- source();
|
files_received <- source();
|
||||||
}
|
}
|
||||||
|
|
||||||
let drop: DropClosure =
|
let drop: DropClosure =
|
||||||
Closure::wrap(Box::new(f!([logger,files_received](event:web_sys::DragEvent) {
|
Closure::new(f!([logger,files_received](event:web_sys::DragEvent) {
|
||||||
debug!(logger, "Dropped files.");
|
debug!(logger, "Dropped files.");
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
Self::handle_drop_event(&logger,event,&files_received)
|
Self::handle_drop_event(&logger,event,&files_received)
|
||||||
})));
|
}));
|
||||||
// To mark element as a valid drop target, the `dragover` event handler should return
|
// To mark element as a valid drop target, the `dragover` event handler should return
|
||||||
// `false`. See
|
// `false`. See
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop#define_the_drop_zone
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop#define_the_drop_zone
|
||||||
let drag_over: DragOverClosure = Closure::wrap(Box::new(|event: web_sys::DragEvent| {
|
let drag_over: DragOverClosure = Closure::new(|event: web_sys::DragEvent| {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
false
|
false
|
||||||
}));
|
});
|
||||||
let drop_js = drop.as_ref().unchecked_ref();
|
let drop_handle = web::add_event_listener(target, "drop", drop);
|
||||||
let drag_over_js = drag_over.as_ref().unchecked_ref();
|
let drag_over_handle = web::add_event_listener(target, "dragover", drag_over);
|
||||||
target.add_event_listener_with_callback("drop", drop_js).unwrap();
|
Self { network, files_received, drop_handle, drag_over_handle }
|
||||||
target.add_event_listener_with_callback("dragover", drag_over_js).unwrap();
|
|
||||||
let drop_callback = Rc::new(drop);
|
|
||||||
let drag_over_callback = Rc::new(drag_over);
|
|
||||||
Self { network, files_received, drop_callback, drag_over_callback }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The frp endpoint emitting signal when a file is dropped.
|
/// The frp endpoint emitting signal when a file is dropped.
|
||||||
@ -161,7 +168,7 @@ impl Manager {
|
|||||||
let files_iter = js_files_iter.filter_map(|f| match File::from_js_file(&f) {
|
let files_iter = js_files_iter.filter_map(|f| match File::from_js_file(&f) {
|
||||||
Ok(file) => Some(file),
|
Ok(file) => Some(file),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(logger, "Error when processing dropped file: {err:?}");
|
error!(logger, "Error when processing dropped file: {err:?}.");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -112,7 +112,7 @@ impl component::Model for Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new(app: &Application, logger: &Logger) -> Self {
|
fn new(app: &Application, logger: &Logger) -> Self {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let label = app.new_view::<text::Area>();
|
let label = app.new_view::<text::Area>();
|
||||||
let background = background::View::new(&logger);
|
let background = background::View::new(&logger);
|
||||||
|
@ -83,7 +83,7 @@ impl<M: Model, F: Frp<M>> Component<M, F> {
|
|||||||
let logger = Logger::new(M::label());
|
let logger = Logger::new(M::label());
|
||||||
let model = Rc::new(M::new(&app, &logger));
|
let model = Rc::new(M::new(&app, &logger));
|
||||||
let frp = F::default();
|
let frp = F::default();
|
||||||
let style = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
frp.init(&app, &model, &style);
|
frp.init(&app, &model, &style);
|
||||||
let frp = Rc::new(frp);
|
let frp = Rc::new(frp);
|
||||||
Self { frp, model, app, logger }
|
Self { frp, model, app, logger }
|
||||||
|
@ -86,7 +86,7 @@ struct Model {
|
|||||||
impl Model {
|
impl Model {
|
||||||
fn new(app: Application) -> Self {
|
fn new(app: Application) -> Self {
|
||||||
let app = app.clone_ref();
|
let app = app.clone_ref();
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new("TextLabel");
|
let logger = Logger::new("TextLabel");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let label = app.new_view::<text::Area>();
|
let label = app.new_view::<text::Area>();
|
||||||
@ -95,7 +95,7 @@ impl Model {
|
|||||||
display_object.add_child(&background);
|
display_object.add_child(&background);
|
||||||
display_object.add_child(&label);
|
display_object.add_child(&label);
|
||||||
|
|
||||||
let style = StyleWatch::new(&app.display.scene().style_sheet);
|
let style = StyleWatch::new(&app.display.default_scene.style_sheet);
|
||||||
|
|
||||||
let model = Model { background, label, display_object, style };
|
let model = Model { background, label, display_object, style };
|
||||||
model.set_layers(&scene.layers.tooltip, &scene.layers.tooltip_text);
|
model.set_layers(&scene.layers.tooltip, &scene.layers.tooltip_text);
|
||||||
|
@ -90,7 +90,7 @@ impl Entry for Label {
|
|||||||
let display_object = display::object::Instance::new(logger);
|
let display_object = display::object::Instance::new(logger);
|
||||||
let label = app.new_view::<ensogl_text::Area>();
|
let label = app.new_view::<ensogl_text::Area>();
|
||||||
let network = frp::Network::new("list_view::entry::Label");
|
let network = frp::Network::new("list_view::entry::Label");
|
||||||
let style_watch = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style_watch = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
let color = style_watch.get_color(theme::widget::list_view::text);
|
let color = style_watch.get_color(theme::widget::list_view::text);
|
||||||
let size = style_watch.get_number(theme::widget::list_view::text::size);
|
let size = style_watch.get_number(theme::widget::list_view::text::size);
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ where E::Model: Default
|
|||||||
let entries_range = Rc::new(CloneCell::new(default()..default()));
|
let entries_range = Rc::new(CloneCell::new(default()..default()));
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
let provider = default();
|
let provider = default();
|
||||||
let label_layer = Rc::new(Cell::new(app.display.scene().layers.label.id()));
|
let label_layer = Rc::new(Cell::new(app.display.default_scene.layers.label.id()));
|
||||||
List { logger, app, display_object, entries, entries_range, provider, label_layer }
|
List { logger, app, display_object, entries, entries_range, provider, label_layer }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +183,9 @@ where E::Model: Default
|
|||||||
|
|
||||||
/// Sets the scene layer where the labels will be placed.
|
/// Sets the scene layer where the labels will be placed.
|
||||||
pub fn set_label_layer(&self, label_layer: LayerId) {
|
pub fn set_label_layer(&self, label_layer: LayerId) {
|
||||||
if let Some(layer) = self.app.display.scene().layers.get_sublayer(self.label_layer.get()) {
|
if let Some(layer) =
|
||||||
|
self.app.display.default_scene.layers.get_sublayer(self.label_layer.get())
|
||||||
|
{
|
||||||
for entry in &*self.entries.borrow() {
|
for entry in &*self.entries.borrow() {
|
||||||
entry.entry.set_label_layer(&layer);
|
entry.entry.set_label_layer(&layer);
|
||||||
}
|
}
|
||||||
@ -198,7 +200,7 @@ where E::Model: Default
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_new_entry(&self) -> DisplayedEntry<E> {
|
fn create_new_entry(&self) -> DisplayedEntry<E> {
|
||||||
let layers = &self.app.display.scene().layers;
|
let layers = &self.app.display.default_scene.layers;
|
||||||
let layer = layers.get_sublayer(self.label_layer.get()).unwrap_or_else(|| {
|
let layer = layers.get_sublayer(self.label_layer.get()).unwrap_or_else(|| {
|
||||||
error!(
|
error!(
|
||||||
self.logger,
|
self.logger,
|
||||||
|
@ -146,7 +146,7 @@ impl<E: Entry> Model<E> {
|
|||||||
fn padding(&self) -> f32 {
|
fn padding(&self) -> f32 {
|
||||||
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
// FIXME : StyleWatch is unsuitable here, as it was designed as an internal tool for shape
|
||||||
// system (#795)
|
// system (#795)
|
||||||
let styles = StyleWatch::new(&self.app.display.scene().style_sheet);
|
let styles = StyleWatch::new(&self.app.display.default_scene.style_sheet);
|
||||||
styles.get_number(ensogl_hardcoded_theme::application::searcher::padding)
|
styles.get_number(ensogl_hardcoded_theme::application::searcher::padding)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ impl<E: Entry> Model<E> {
|
|||||||
/// Check if the `point` is inside component assuming that it have given `size`.
|
/// Check if the `point` is inside component assuming that it have given `size`.
|
||||||
fn is_inside(&self, point: Vector2<f32>, size: Vector2<f32>) -> bool {
|
fn is_inside(&self, point: Vector2<f32>, size: Vector2<f32>) -> bool {
|
||||||
let pos_obj_space =
|
let pos_obj_space =
|
||||||
self.app.display.scene().screen_to_object_space(&self.background, point);
|
self.app.display.default_scene.screen_to_object_space(&self.background, point);
|
||||||
let x_range = (-size.x / 2.0)..=(size.x / 2.0);
|
let x_range = (-size.x / 2.0)..=(size.x / 2.0);
|
||||||
let y_range = (-size.y / 2.0)..=(size.y / 2.0);
|
let y_range = (-size.y / 2.0)..=(size.y / 2.0);
|
||||||
x_range.contains(&pos_obj_space.x) && y_range.contains(&pos_obj_space.y)
|
x_range.contains(&pos_obj_space.x) && y_range.contains(&pos_obj_space.y)
|
||||||
@ -295,7 +295,7 @@ where E::Model: Default
|
|||||||
let frp = &self.frp;
|
let frp = &self.frp;
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let model = &self.model;
|
let model = &self.model;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let mouse = &scene.mouse.frp;
|
let mouse = &scene.mouse.frp;
|
||||||
let view_y = DEPRECATED_Animation::<f32>::new(network);
|
let view_y = DEPRECATED_Animation::<f32>::new(network);
|
||||||
let selection_y = DEPRECATED_Animation::<f32>::new(network);
|
let selection_y = DEPRECATED_Animation::<f32>::new(network);
|
||||||
|
@ -95,7 +95,7 @@ impl display::Object for ScrollArea {
|
|||||||
impl ScrollArea {
|
impl ScrollArea {
|
||||||
/// Create a new scroll area for use in the given application.
|
/// Create a new scroll area for use in the given application.
|
||||||
pub fn new(app: &Application) -> ScrollArea {
|
pub fn new(app: &Application) -> ScrollArea {
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new("ScrollArea");
|
let logger = Logger::new("ScrollArea");
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ impl Frp {
|
|||||||
pub fn init(&self, app: &Application, model: &Model, style: &StyleWatchFrp) {
|
pub fn init(&self, app: &Application, model: &Model, style: &StyleWatchFrp) {
|
||||||
let frp = &self;
|
let frp = &self;
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let mouse = &scene.mouse.frp;
|
let mouse = &scene.mouse.frp;
|
||||||
let thumb_position = Animation::new(network);
|
let thumb_position = Animation::new(network);
|
||||||
let thumb_color = color::Animation::new(network);
|
let thumb_color = color::Animation::new(network);
|
||||||
@ -304,7 +304,7 @@ impl Scrollbar {
|
|||||||
let app = app.clone_ref();
|
let app = app.clone_ref();
|
||||||
let model = Rc::new(Model::new(&app));
|
let model = Rc::new(Model::new(&app));
|
||||||
let frp = Frp::default();
|
let frp = Frp::default();
|
||||||
let style = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
frp.init(&app, &model, &style);
|
frp.init(&app, &model, &style);
|
||||||
let frp = Rc::new(frp);
|
let frp = Rc::new(frp);
|
||||||
Self { frp, model, app }
|
Self { frp, model, app }
|
||||||
|
@ -58,8 +58,8 @@ impl Model {
|
|||||||
let label_full = app.new_view::<text::Area>();
|
let label_full = app.new_view::<text::Area>();
|
||||||
let label_left = app.new_view::<text::Area>();
|
let label_left = app.new_view::<text::Area>();
|
||||||
|
|
||||||
label_full.remove_from_scene_layer(&app.display.scene().layers.main);
|
label_full.remove_from_scene_layer(&app.display.default_scene.layers.main);
|
||||||
label_full.add_to_scene_layer(&app.display.scene().layers.label);
|
label_full.add_to_scene_layer(&app.display.default_scene.layers.label);
|
||||||
|
|
||||||
root.add_child(&label_full);
|
root.add_child(&label_full);
|
||||||
root.add_child(&label_left);
|
root.add_child(&label_left);
|
||||||
|
@ -75,24 +75,20 @@ impl Frp {
|
|||||||
size: frp::Stream<Vector2>,
|
size: frp::Stream<Vector2>,
|
||||||
mouse: &Mouse,
|
mouse: &Mouse,
|
||||||
) -> Frp {
|
) -> Frp {
|
||||||
|
let net = &network;
|
||||||
|
let scene = &model.app.display.default_scene;
|
||||||
let shadow = shadow::frp_from_style(style, theme::shadow);
|
let shadow = shadow::frp_from_style(style, theme::shadow);
|
||||||
let text_size = style.get_number(theme::text::size);
|
let text_size = style.get_number(theme::text::size);
|
||||||
|
|
||||||
let is_dragging_left_overflow =
|
let is_dragging_left_overflow = shape_is_dragged(net, &model.left_overflow.events, mouse);
|
||||||
shape_is_dragged(network, &model.left_overflow.events, mouse);
|
let is_dragging_right_overflow = shape_is_dragged(net, &model.right_overflow.events, mouse);
|
||||||
let is_dragging_right_overflow =
|
let is_dragging_track = shape_is_dragged(net, &model.track.events, mouse);
|
||||||
shape_is_dragged(network, &model.right_overflow.events, mouse);
|
let is_dragging_background = shape_is_dragged(net, &model.background.events, mouse);
|
||||||
let is_dragging_track = shape_is_dragged(network, &model.track.events, mouse);
|
let is_dragging_left_handle = shape_is_dragged(net, &model.track_handle_left.events, mouse);
|
||||||
let is_dragging_background = shape_is_dragged(network, &model.background.events, mouse);
|
|
||||||
let is_dragging_left_handle =
|
|
||||||
shape_is_dragged(network, &model.track_handle_left.events, mouse);
|
|
||||||
let is_dragging_right_handle =
|
let is_dragging_right_handle =
|
||||||
shape_is_dragged(network, &model.track_handle_right.events, mouse);
|
shape_is_dragged(net, &model.track_handle_right.events, mouse);
|
||||||
|
let background_click = relative_shape_down_position(net, scene, &model.background);
|
||||||
let background_click =
|
let track_click = relative_shape_down_position(net, scene, &model.track);
|
||||||
relative_shape_down_position(network, model.app.display.scene(), &model.background);
|
|
||||||
let track_click =
|
|
||||||
relative_shape_down_position(network, model.app.display.scene(), &model.track);
|
|
||||||
|
|
||||||
// Initialisation of components. Required for correct layout on startup.
|
// Initialisation of components. Required for correct layout on startup.
|
||||||
model.label_right.set_position_y(text_size.value() / 2.0);
|
model.label_right.set_position_y(text_size.value() / 2.0);
|
||||||
|
@ -76,7 +76,7 @@ impl NumberPicker {
|
|||||||
let app = app.clone_ref();
|
let app = app.clone_ref();
|
||||||
let model = Rc::new(Model::new(&app));
|
let model = Rc::new(Model::new(&app));
|
||||||
let frp = number::Frp::default();
|
let frp = number::Frp::default();
|
||||||
let style = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
frp.init(&app, &model, &style);
|
frp.init(&app, &model, &style);
|
||||||
let frp = Rc::new(frp);
|
let frp = Rc::new(frp);
|
||||||
Self { frp, model, app }
|
Self { frp, model, app }
|
||||||
@ -146,7 +146,7 @@ impl NumberRangePicker {
|
|||||||
let app = app.clone_ref();
|
let app = app.clone_ref();
|
||||||
let model = Rc::new(Model::new(&app));
|
let model = Rc::new(Model::new(&app));
|
||||||
let frp = range::Frp::default();
|
let frp = range::Frp::default();
|
||||||
let style = StyleWatchFrp::new(&app.display.scene().style_sheet);
|
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||||
frp.init(&app, &model, &style);
|
frp.init(&app, &model, &style);
|
||||||
let frp = Rc::new(frp);
|
let frp = Rc::new(frp);
|
||||||
Self { frp, model, app }
|
Self { frp, model, app }
|
||||||
|
@ -97,7 +97,7 @@ impl Model {
|
|||||||
let padding = default();
|
let padding = default();
|
||||||
|
|
||||||
let app = app.clone_ref();
|
let app = app.clone_ref();
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
scene.layers.add_global_shapes_order_dependency::<background::View, track::View>();
|
scene.layers.add_global_shapes_order_dependency::<background::View, track::View>();
|
||||||
scene.layers.add_global_shapes_order_dependency::<track::View, left_overflow::View>();
|
scene.layers.add_global_shapes_order_dependency::<track::View, left_overflow::View>();
|
||||||
scene.layers.add_global_shapes_order_dependency::<track::View, right_overflow::View>();
|
scene.layers.add_global_shapes_order_dependency::<track::View, right_overflow::View>();
|
||||||
|
@ -45,7 +45,7 @@ impl Frp {
|
|||||||
pub fn init(&self, app: &Application, model: &Model, style: &StyleWatchFrp) {
|
pub fn init(&self, app: &Application, model: &Model, style: &StyleWatchFrp) {
|
||||||
let frp = &self;
|
let frp = &self;
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let mouse = &scene.mouse.frp;
|
let mouse = &scene.mouse.frp;
|
||||||
|
|
||||||
model.show_background(true);
|
model.show_background(true);
|
||||||
@ -55,11 +55,8 @@ impl Frp {
|
|||||||
let track_shape_system = scene.shapes.shape_system(PhantomData::<track::Shape>);
|
let track_shape_system = scene.shapes.shape_system(PhantomData::<track::Shape>);
|
||||||
track_shape_system.shape_system.set_pointer_events(false);
|
track_shape_system.shape_system.set_pointer_events(false);
|
||||||
|
|
||||||
let background_click =
|
let background_click = relative_shape_down_position(network, scene, &model.background);
|
||||||
relative_shape_down_position(network, model.app.display.scene(), &model.background);
|
let track_click = relative_shape_down_position(network, scene, &model.track);
|
||||||
let track_click =
|
|
||||||
relative_shape_down_position(network, model.app.display.scene(), &model.track);
|
|
||||||
|
|
||||||
let style_track_color = style.get_color(theme::component::slider::track::color);
|
let style_track_color = style.get_color(theme::component::slider::track::color);
|
||||||
|
|
||||||
frp::extend! { network
|
frp::extend! { network
|
||||||
|
@ -41,7 +41,7 @@ impl Frp {
|
|||||||
pub fn init(&self, app: &Application, model: &Model, style: &StyleWatchFrp) {
|
pub fn init(&self, app: &Application, model: &Model, style: &StyleWatchFrp) {
|
||||||
let frp = &self;
|
let frp = &self;
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let mouse = &scene.mouse.frp;
|
let mouse = &scene.mouse.frp;
|
||||||
|
|
||||||
let base_frp = super::Frp::new(model, style, network, frp.resize.clone().into(), mouse);
|
let base_frp = super::Frp::new(model, style, network, frp.resize.clone().into(), mouse);
|
||||||
|
@ -20,7 +20,7 @@ use ensogl_core::display::shape::*;
|
|||||||
use ensogl_core::display::style;
|
use ensogl_core::display::style;
|
||||||
use ensogl_core::display::DomSymbol;
|
use ensogl_core::display::DomSymbol;
|
||||||
use ensogl_core::frp;
|
use ensogl_core::frp;
|
||||||
use ensogl_core::system::web::StyleSetter;
|
use ensogl_core::system::web::traits::*;
|
||||||
use ensogl_hardcoded_theme as theme;
|
use ensogl_hardcoded_theme as theme;
|
||||||
|
|
||||||
|
|
||||||
@ -112,14 +112,14 @@ pub fn from_shape_with_parameters_and_alpha(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add a theme defined box shadow to the given `DomSymbol`.
|
/// Add a theme defined box shadow to the given `DomSymbol`.
|
||||||
pub fn add_to_dom_element(element: &DomSymbol, style: &StyleWatch, logger: &Logger) {
|
pub fn add_to_dom_element(element: &DomSymbol, style: &StyleWatch) {
|
||||||
let off_x = style.get_number(theme::shadow::offset_x);
|
let off_x = style.get_number(theme::shadow::offset_x);
|
||||||
let off_y = -style.get_number(theme::shadow::offset_y);
|
let off_y = -style.get_number(theme::shadow::offset_y);
|
||||||
let alpha = style.get_number(ensogl_hardcoded_theme::shadow::html::alpha);
|
let alpha = style.get_number(ensogl_hardcoded_theme::shadow::html::alpha);
|
||||||
let blur = style.get_number(ensogl_hardcoded_theme::shadow::html::blur);
|
let blur = style.get_number(ensogl_hardcoded_theme::shadow::html::blur);
|
||||||
let spread = style.get_number(ensogl_hardcoded_theme::shadow::html::spread);
|
let spread = style.get_number(ensogl_hardcoded_theme::shadow::html::spread);
|
||||||
let shadow = format!("{}px {}px {}px {}px rgba(0,0,0,{})", off_x, off_y, blur, spread, alpha);
|
let shadow = format!("{}px {}px {}px {}px rgba(0,0,0,{})", off_x, off_y, blur, spread, alpha);
|
||||||
element.dom().set_style_or_warn("box-shadow", shadow, logger);
|
element.dom().set_style_or_warn("box-shadow", shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
enso-prelude = { path = "../../../../prelude"}
|
enso-prelude = { path = "../../../../prelude"}
|
||||||
js-sys = { version = "0.3" }
|
js-sys = { version = "0.3" }
|
||||||
nalgebra = { version = "0.26.1" }
|
nalgebra = { version = "0.26.1" }
|
||||||
wasm-bindgen = { version = "0.2.58" }
|
wasm-bindgen = { version = "0.2.78" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test = { version = "0.3.8" }
|
wasm-bindgen-test = { version = "0.3.8" }
|
||||||
|
@ -304,7 +304,7 @@ impl Area {
|
|||||||
fn init(self) -> Self {
|
fn init(self) -> Self {
|
||||||
let network = &self.frp.network;
|
let network = &self.frp.network;
|
||||||
let model = &self.data;
|
let model = &self.data;
|
||||||
let scene = model.app.display.scene();
|
let scene = &model.app.display.default_scene;
|
||||||
let mouse = &scene.mouse.frp;
|
let mouse = &scene.mouse.frp;
|
||||||
let input = &self.frp.input;
|
let input = &self.frp.input;
|
||||||
let out = &self.frp.output;
|
let out = &self.frp.output;
|
||||||
@ -532,7 +532,7 @@ impl Area {
|
|||||||
|
|
||||||
fn symbols(&self) -> SmallVec<[display::Symbol; 1]> {
|
fn symbols(&self) -> SmallVec<[display::Symbol; 1]> {
|
||||||
let text_symbol = self.data.glyph_system.sprite_system().symbol.clone_ref();
|
let text_symbol = self.data.glyph_system.sprite_system().symbol.clone_ref();
|
||||||
let shapes = &self.data.app.display.scene().shapes;
|
let shapes = &self.data.app.display.default_scene.shapes;
|
||||||
let selection_system = shapes.shape_system(PhantomData::<selection::shape::Shape>);
|
let selection_system = shapes.shape_system(PhantomData::<selection::shape::Shape>);
|
||||||
let _selection_symbol = selection_system.shape_system.symbol.clone_ref();
|
let _selection_symbol = selection_system.shape_system.symbol.clone_ref();
|
||||||
//TODO[ao] we cannot move selection symbol, as it is global for all the text areas.
|
//TODO[ao] we cannot move selection symbol, as it is global for all the text areas.
|
||||||
@ -568,7 +568,7 @@ impl AreaModel {
|
|||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(app: &Application, frp_endpoints: &FrpEndpoints) -> Self {
|
pub fn new(app: &Application, frp_endpoints: &FrpEndpoints) -> Self {
|
||||||
let app = app.clone_ref();
|
let app = app.clone_ref();
|
||||||
let scene = app.display.scene();
|
let scene = &app.display.default_scene;
|
||||||
let logger = Logger::new("text_area");
|
let logger = Logger::new("text_area");
|
||||||
let selection_map = default();
|
let selection_map = default();
|
||||||
let fonts = scene.extension::<typeface::font::Registry>();
|
let fonts = scene.extension::<typeface::font::Registry>();
|
||||||
@ -703,7 +703,7 @@ impl AreaModel {
|
|||||||
let origin_clip_space = camera.view_projection_matrix() * origin_world_space;
|
let origin_clip_space = camera.view_projection_matrix() * origin_world_space;
|
||||||
let inv_object_matrix = self.transform_matrix().try_inverse().unwrap();
|
let inv_object_matrix = self.transform_matrix().try_inverse().unwrap();
|
||||||
|
|
||||||
let shape = self.app.display.scene().frp.shape.value();
|
let shape = self.app.display.default_scene.frp.shape.value();
|
||||||
let clip_space_z = origin_clip_space.z;
|
let clip_space_z = origin_clip_space.z;
|
||||||
let clip_space_x = origin_clip_space.w * 2.0 * screen_pos.x / shape.width;
|
let clip_space_x = origin_clip_space.w * 2.0 * screen_pos.x / shape.width;
|
||||||
let clip_space_y = origin_clip_space.w * 2.0 * screen_pos.y / shape.height;
|
let clip_space_y = origin_clip_space.w * 2.0 * screen_pos.y / shape.height;
|
||||||
|
@ -102,7 +102,9 @@ impl System {
|
|||||||
let logger = Logger::new("glyph_system");
|
let logger = Logger::new("glyph_system");
|
||||||
let size = font::msdf::Texture::size();
|
let size = font::msdf::Texture::size();
|
||||||
let scene = scene.as_ref();
|
let scene = scene.as_ref();
|
||||||
let context = scene.context.clone_ref();
|
// FIXME: The following line is unsafe. It can fail if the context was lost before calling
|
||||||
|
// this function. Also, the texture will not be restored after context restoration.
|
||||||
|
let context = scene.context.borrow().as_ref().unwrap().clone_ref();
|
||||||
let sprite_system = SpriteSystem::new(scene);
|
let sprite_system = SpriteSystem::new(scene);
|
||||||
let symbol = sprite_system.symbol();
|
let symbol = sprite_system.symbol();
|
||||||
let texture = Texture::new(&context, (0, 0));
|
let texture = Texture::new(&context, (0, 0));
|
||||||
|
@ -45,7 +45,7 @@ typenum = { version = "1.11.2" }
|
|||||||
|
|
||||||
# We require exact version of wasm-bindgen because we do patching final js in our build process,
|
# We require exact version of wasm-bindgen because we do patching final js in our build process,
|
||||||
# and this is vulnerable to any wasm-bindgen version change.
|
# and this is vulnerable to any wasm-bindgen version change.
|
||||||
wasm-bindgen = { version = "0.2.58", features = ["nightly"] }
|
wasm-bindgen = { version = "0.2.78", features = ["nightly"] }
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
//! This module contains implementation of `DynamicLoop`, a loop manager which runs a
|
//! This module contains implementation of loops mainly used for per-frame callbacks firing.
|
||||||
//! `DynamicLoopCallback` once per frame.
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::control::callback;
|
|
||||||
use crate::system::web;
|
use crate::system::web;
|
||||||
|
use crate::system::web::traits::*;
|
||||||
|
|
||||||
use wasm_bindgen::prelude::Closure;
|
use web::Closure;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +69,9 @@ where Callback: RawLoopCallback
|
|||||||
let weak_data = Rc::downgrade(&data);
|
let weak_data = Rc::downgrade(&data);
|
||||||
let on_frame = move |time| weak_data.upgrade().for_each(|t| t.borrow_mut().run(time));
|
let on_frame = move |time| weak_data.upgrade().for_each(|t| t.borrow_mut().run(time));
|
||||||
data.borrow_mut().on_frame = Some(Closure::new(on_frame));
|
data.borrow_mut().on_frame = Some(Closure::new(on_frame));
|
||||||
let handle_id = web::request_animation_frame(data.borrow_mut().on_frame.as_ref().unwrap());
|
let handle_id = web::window.request_animation_frame_with_closure_or_panic(
|
||||||
|
data.borrow_mut().on_frame.as_ref().unwrap(),
|
||||||
|
);
|
||||||
data.borrow_mut().handle_id = handle_id;
|
data.borrow_mut().handle_id = handle_id;
|
||||||
Self { data }
|
Self { data }
|
||||||
}
|
}
|
||||||
@ -100,14 +101,14 @@ impl<Callback> RawLoopData<Callback> {
|
|||||||
let callback = &mut self.callback;
|
let callback = &mut self.callback;
|
||||||
self.handle_id = self.on_frame.as_ref().map_or(default(), |on_frame| {
|
self.handle_id = self.on_frame.as_ref().map_or(default(), |on_frame| {
|
||||||
callback(current_time_ms as f32);
|
callback(current_time_ms as f32);
|
||||||
web::request_animation_frame(on_frame)
|
web::window.request_animation_frame_with_closure_or_panic(on_frame)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Callback> Drop for RawLoopData<Callback> {
|
impl<Callback> Drop for RawLoopData<Callback> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
web::cancel_animation_frame(self.handle_id);
|
web::window.cancel_animation_frame_or_panic(self.handle_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,71 +239,3 @@ where Callback: LoopCallback
|
|||||||
Self::new(FixedFrameRateSampler::new(frame_rate, callback))
|
Self::new(FixedFrameRateSampler::new(frame_rate, callback))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===================
|
|
||||||
// === DynamicLoop ===
|
|
||||||
// ===================
|
|
||||||
|
|
||||||
/// A callback to register in DynamicLoop, taking time_ms:f32 as its input.
|
|
||||||
pub trait DynamicLoopCallback = callback::CopyCallbackMut1Fn<TimeInfo>;
|
|
||||||
|
|
||||||
/// Animation loop which allows registering and unregistering callbacks dynamically. After a
|
|
||||||
/// callback is registered, a `callback::Handle` is returned. The callback is automatically removed
|
|
||||||
/// as soon as its handle is dropped. You can also use the `forget` method on the handle to make the
|
|
||||||
/// callback registered forever, but beware that it can easily lead to memory leaks.
|
|
||||||
///
|
|
||||||
/// Please refer to `Loop` if you don't need the ability to add / remove callbacks dynamically and
|
|
||||||
/// you want better performance.
|
|
||||||
#[derive(Clone, CloneRef, Debug)]
|
|
||||||
pub struct DynamicLoop {
|
|
||||||
raw_loop: Loop<Box<dyn FnMut(TimeInfo)>>,
|
|
||||||
data: Rc<RefCell<DynamicLoopData>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal representation for `DynamicLoop`.
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct DynamicLoopData {
|
|
||||||
on_frame: callback::CopyRegistry1<TimeInfo>,
|
|
||||||
on_before_frame: callback::CopyRegistry1<TimeInfo>,
|
|
||||||
on_after_frame: callback::CopyRegistry1<TimeInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DynamicLoop {
|
|
||||||
fn default() -> Self {
|
|
||||||
let data = Rc::new(RefCell::new(DynamicLoopData::default()));
|
|
||||||
let weak = Rc::downgrade(&data);
|
|
||||||
let raw_loop: Loop<Box<dyn FnMut(TimeInfo)>> = Loop::new(Box::new(move |time| {
|
|
||||||
weak.upgrade().for_each(|data| {
|
|
||||||
let mut data_mut = data.borrow_mut();
|
|
||||||
data_mut.on_before_frame.run_all(time);
|
|
||||||
data_mut.on_frame.run_all(time);
|
|
||||||
data_mut.on_after_frame.run_all(time);
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
Self { raw_loop, data }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DynamicLoop {
|
|
||||||
/// Constructor.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add new callback which will be run on every animation frame.
|
|
||||||
pub fn on_frame<F: DynamicLoopCallback>(&self, callback: F) -> callback::Handle {
|
|
||||||
self.data.borrow_mut().on_frame.add(Box::new(callback))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add new callback which will be run on before all callbacks registered with `on_frame`.
|
|
||||||
pub fn on_before_frame<F: DynamicLoopCallback>(&self, callback: F) -> callback::Handle {
|
|
||||||
self.data.borrow_mut().on_before_frame.add(Box::new(callback))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add new callback which will be run on after all callbacks registered with `on_frame`.
|
|
||||||
pub fn on_after_frame<F: DynamicLoopCallback>(&self, callback: F) -> callback::Handle {
|
|
||||||
self.data.borrow_mut().on_after_frame.add(Box::new(callback))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -12,11 +12,12 @@ use crate::prelude::*;
|
|||||||
|
|
||||||
use crate::control::callback;
|
use crate::control::callback;
|
||||||
use crate::display;
|
use crate::display;
|
||||||
|
use crate::display::scene::DomPath;
|
||||||
use crate::display::style::theme;
|
use crate::display::style::theme;
|
||||||
use crate::display::world::World;
|
use crate::display::world::World;
|
||||||
use crate::gui::cursor::Cursor;
|
use crate::gui::cursor::Cursor;
|
||||||
use crate::system::web;
|
use crate::system::web;
|
||||||
use enso_web::StyleSetter;
|
use enso_web::traits::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -41,19 +42,20 @@ pub struct Application {
|
|||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(dom: &web_sys::HtmlElement) -> Self {
|
pub fn new(dom: impl DomPath) -> Self {
|
||||||
let logger = Logger::new("Application");
|
let logger = Logger::new("Application");
|
||||||
let display = World::new(dom);
|
let display = World::new();
|
||||||
let scene = display.scene();
|
let scene = &display.default_scene;
|
||||||
|
scene.display_in(dom);
|
||||||
let commands = command::Registry::create(&logger);
|
let commands = command::Registry::create(&logger);
|
||||||
let shortcuts =
|
let shortcuts =
|
||||||
shortcut::Registry::new(&logger, &scene.mouse.frp, &scene.keyboard.frp, &commands);
|
shortcut::Registry::new(&logger, &scene.mouse.frp, &scene.keyboard.frp, &commands);
|
||||||
let views = view::Registry::create(&logger, &display, &commands, &shortcuts);
|
let views = view::Registry::create(&logger, &display, &commands, &shortcuts);
|
||||||
let themes = theme::Manager::from(&display.scene().style_sheet);
|
let themes = theme::Manager::from(&display.default_scene.style_sheet);
|
||||||
let cursor = Cursor::new(display.scene());
|
let cursor = Cursor::new(&display.default_scene);
|
||||||
display.add_child(&cursor);
|
display.add_child(&cursor);
|
||||||
web::body().set_style_or_panic("cursor", "none");
|
web::document.body_or_panic().set_style_or_warn("cursor", "none");
|
||||||
let update_themes_handle = display.on_before_frame(f_!(themes.update()));
|
let update_themes_handle = display.on.before_frame.add(f_!(themes.update()));
|
||||||
Self { logger, cursor, display, commands, shortcuts, views, themes, update_themes_handle }
|
Self { logger, cursor, display, commands, shortcuts, views, themes, update_themes_handle }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,3 +76,18 @@ impl AsRef<theme::Manager> for Application {
|
|||||||
&self.themes
|
&self.themes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// === Tests ===
|
||||||
|
// =============
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn native_compilation_in_test_mode() {
|
||||||
|
let _app = Application::new("root");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -116,73 +116,82 @@ impl ArgReader for bool {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! read_args {
|
macro_rules! read_args {
|
||||||
([$($($path:tt)*).*] { $($field:ident : $field_type:ty),* $(,)? }) => {
|
([$($($path:tt)*).*] { $($field:ident : $field_type:ty),* $(,)? }) => {
|
||||||
|
mod _READ_ARGS {
|
||||||
|
use super::*;
|
||||||
|
use $crate::prelude::*;
|
||||||
|
use $crate::system::web::traits::*;
|
||||||
|
|
||||||
/// Reflection mechanism containing string representation of option names.
|
/// Reflection mechanism containing string representation of option names.
|
||||||
#[derive(Clone,Copy,Debug,Default)]
|
#[derive(Clone,Copy,Debug,Default)]
|
||||||
pub struct ArgNames;
|
pub struct ArgNames;
|
||||||
impl ArgNames {
|
impl ArgNames {
|
||||||
$(
|
$(
|
||||||
/// Name of the field.
|
/// Name of the field.
|
||||||
pub fn $field(&self) -> &'static str {
|
pub fn $field(&self) -> &'static str {
|
||||||
stringify!{$field}
|
stringify!{$field}
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The structure containing application configs.
|
|
||||||
#[derive(Clone,Debug,Default)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub struct Args {
|
|
||||||
$(pub $field : Option<$field_type>),*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Args {
|
|
||||||
/// Constructor.
|
|
||||||
fn new() -> Self {
|
|
||||||
let logger = Logger::new(stringify!{Args});
|
|
||||||
let window = web::window();
|
|
||||||
let path = vec![$($($path)*),*];
|
|
||||||
match web::reflect_get_nested_object(&window,&path).ok() {
|
|
||||||
None => {
|
|
||||||
let path = path.join(".");
|
|
||||||
error!(&logger,"The config path '{path}' is invalid.");
|
|
||||||
default()
|
|
||||||
}
|
}
|
||||||
Some(cfg) => {
|
)*
|
||||||
let keys = web::object_keys(&cfg);
|
|
||||||
let mut keys = keys.into_iter().collect::<HashSet<String>>();
|
|
||||||
$(
|
|
||||||
let name = stringify!{$field};
|
|
||||||
let tp = stringify!{$field_type};
|
|
||||||
let $field = web::reflect_get_nested_string(&cfg,&[name]).ok();
|
|
||||||
let $field = $field.map($crate::application::args::ArgReader::read_arg);
|
|
||||||
if $field == Some(None) {
|
|
||||||
warning!(&logger,"Failed to convert the argument '{name}' value \
|
|
||||||
to the '{tp}' type.");
|
|
||||||
}
|
|
||||||
let $field = $field.flatten();
|
|
||||||
keys.remove(name);
|
|
||||||
)*
|
|
||||||
for key in keys {
|
|
||||||
warning!(&logger,"Unknown config option provided '{key}'.");
|
|
||||||
}
|
|
||||||
Self {$($field),*}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a dummy function which initializes the arg reading process. This function
|
/// The structure containing application configs.
|
||||||
/// does nothing, however, in order to call it, the user would need to access a field in
|
#[derive(Clone,Debug,Default)]
|
||||||
/// the lazy static variable `ARGS`, which would trigger argument parsing process.
|
#[allow(missing_docs)]
|
||||||
pub fn init(&self) {}
|
pub struct Args {
|
||||||
|
$(pub $field : Option<$field_type>),*
|
||||||
|
}
|
||||||
|
|
||||||
/// Reflection mechanism to get string representation of argument names.
|
impl Args {
|
||||||
pub fn names(&self) -> ArgNames { ArgNames }
|
/// Constructor.
|
||||||
}
|
fn new() -> Self {
|
||||||
|
let logger = Logger::new(stringify!{Args});
|
||||||
|
let path = vec![$($($path)*),*];
|
||||||
|
match ensogl::system::web::Reflect::get_nested_object
|
||||||
|
(&ensogl::system::web::window,&path).ok() {
|
||||||
|
None => {
|
||||||
|
let path = path.join(".");
|
||||||
|
error!(&logger,"The config path '{path}' is invalid.");
|
||||||
|
default()
|
||||||
|
}
|
||||||
|
Some(cfg) => {
|
||||||
|
let keys = ensogl::system::web::Object::keys_vec(&cfg);
|
||||||
|
let mut keys = keys.into_iter().collect::<HashSet<String>>();
|
||||||
|
$(
|
||||||
|
let name = stringify!{$field};
|
||||||
|
let tp = stringify!{$field_type};
|
||||||
|
let $field = ensogl::system::web::Reflect::
|
||||||
|
get_nested_object_printed_as_string(&cfg,&[name]).ok();
|
||||||
|
let $field = $field.map
|
||||||
|
($crate::application::args::ArgReader::read_arg);
|
||||||
|
if $field == Some(None) {
|
||||||
|
warning!(&logger,"Failed to convert the argument '{name}' \
|
||||||
|
value to the '{tp}' type.");
|
||||||
|
}
|
||||||
|
let $field = $field.flatten();
|
||||||
|
keys.remove(name);
|
||||||
|
)*
|
||||||
|
for key in keys {
|
||||||
|
warning!(&logger,"Unknown config option provided '{key}'.");
|
||||||
|
}
|
||||||
|
Self {$($field),*}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
/// This is a dummy function which initializes the arg reading process. This
|
||||||
/// Application arguments initialized in a lazy way (on first read).
|
/// function does nothing, however, in order to call it, the user would need to
|
||||||
pub static ref ARGS : Args = Args::new();
|
/// access a field in the lazy static variable `ARGS`, which would trigger argument
|
||||||
|
/// parsing process.
|
||||||
|
pub fn init(&self) {}
|
||||||
|
|
||||||
|
/// Reflection mechanism to get string representation of argument names.
|
||||||
|
pub fn names(&self) -> ArgNames { ArgNames }
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
/// Application arguments initialized in a lazy way (on first read).
|
||||||
|
pub static ref ARGS : Args = Args::new();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pub use _READ_ARGS::*;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,92 +5,50 @@ use crate::prelude::*;
|
|||||||
pub mod event;
|
pub mod event;
|
||||||
|
|
||||||
use crate::control::callback;
|
use crate::control::callback;
|
||||||
|
use crate::control::callback::traits::*;
|
||||||
use crate::system::web;
|
use crate::system::web;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wasm_bindgen::prelude::Closure;
|
use web::Closure;
|
||||||
use wasm_bindgen::JsCast;
|
use web::JsCast;
|
||||||
use wasm_bindgen::JsValue;
|
use web::JsValue;
|
||||||
|
|
||||||
pub use crate::frp::io::mouse::*;
|
pub use crate::frp::io::mouse::*;
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =======================
|
|
||||||
// === EventDispatcher ===
|
|
||||||
// =======================
|
|
||||||
|
|
||||||
// TODO: Consider merging this implementation with crate::control::callback::* ones.
|
|
||||||
|
|
||||||
/// Shared event dispatcher.
|
|
||||||
#[derive(Debug, CloneRef, Derivative)]
|
|
||||||
#[derivative(Clone(bound = ""))]
|
|
||||||
#[derivative(Default(bound = ""))]
|
|
||||||
pub struct EventDispatcher<T> {
|
|
||||||
rc: Rc<RefCell<callback::Registry1<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> EventDispatcher<T> {
|
|
||||||
/// Adds a new callback.
|
|
||||||
pub fn add<F: FnMut(&T) + 'static>(&self, f: F) -> callback::Handle {
|
|
||||||
self.rc.borrow_mut().add(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dispatches event to all callbacks.
|
|
||||||
pub fn dispatch(&self, t: &T) {
|
|
||||||
self.rc.borrow_mut().run_all(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ====================
|
// ====================
|
||||||
// === MouseManager ===
|
// === MouseManager ===
|
||||||
// ====================
|
// ====================
|
||||||
|
|
||||||
/// An utility which registers JavaScript handlers for mouse events and translates them to Rust
|
/// A utility which registers JavaScript handlers for mouse events and translates them to Rust
|
||||||
/// handlers. It is a top level mouse registry hub.
|
/// handlers. It is a top level mouse registry hub.
|
||||||
#[derive(Clone, CloneRef, Debug, Shrinkwrap)]
|
#[derive(Clone, CloneRef, Debug, Shrinkwrap)]
|
||||||
pub struct MouseManager {
|
pub struct MouseManager {
|
||||||
#[shrinkwrap(main_field)]
|
#[shrinkwrap(main_field)]
|
||||||
dispatchers: MouseManagerDispatchers,
|
dispatchers: MouseManagerDispatchers,
|
||||||
closures: Rc<MouseManagerClosures>,
|
handles: Rc<MouseManagerEventListenerHandles>,
|
||||||
dom: web::dom::WithKnownShape<web::EventTarget>,
|
dom: web::dom::WithKnownShape<web::EventTarget>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A JavaScript callback closure for any mouse event.
|
/// A JavaScript callback closure for any mouse event.
|
||||||
pub type MouseEventJsClosure = Closure<dyn Fn(JsValue)>;
|
pub type MouseEventJsClosure = Closure<dyn FnMut(JsValue)>;
|
||||||
|
|
||||||
macro_rules! define_bindings {
|
macro_rules! define_bindings {
|
||||||
( $( $js_event:ident :: $js_name:ident => $name:ident ($target:ident) ),* $(,)? ) => {
|
( $( $js_event:ident :: $js_name:ident => $name:ident ($target:ident) ),* $(,)? ) => {
|
||||||
|
|
||||||
/// Keeps references to JavaScript closures in order to keep them alive.
|
/// Keeps references to JavaScript closures in order to keep them alive.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MouseManagerClosures {
|
pub struct MouseManagerEventListenerHandles {
|
||||||
target : web::EventTarget,
|
$($name : web::EventListenerHandle),*
|
||||||
$($name : MouseEventJsClosure),*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for MouseManagerClosures {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
$(
|
|
||||||
let target = &self.target;
|
|
||||||
let js_closure = self.$name.as_ref().unchecked_ref();
|
|
||||||
let js_name = stringify!($js_name);
|
|
||||||
let result = target.remove_event_listener_with_callback(js_name,js_closure);
|
|
||||||
if let Err(e) = result { panic!("Cannot add event listener. {:?}",e) }
|
|
||||||
)*
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set of dispatchers for various mouse events.
|
/// Set of dispatchers for various mouse events.
|
||||||
#[derive(Clone,CloneRef,Debug,Default)]
|
#[derive(Clone,CloneRef,Debug,Default)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct MouseManagerDispatchers {
|
pub struct MouseManagerDispatchers {
|
||||||
$(pub $name : EventDispatcher<$target>),*
|
$(pub $name : callback::registry::RefMut1<$target>),*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MouseManager {
|
impl MouseManager {
|
||||||
@ -101,41 +59,36 @@ macro_rules! define_bindings {
|
|||||||
|
|
||||||
/// Constructor which takes the exact element to set listener as a separate argument.
|
/// Constructor which takes the exact element to set listener as a separate argument.
|
||||||
///
|
///
|
||||||
/// Sometimes we want to listen for mouse event for element without ResizeObserver. Thus
|
/// Sometimes we want to listen for mouse event for element without ResizeObserver.
|
||||||
/// some html element may be passed as a size provider, and another one where we attach
|
/// Thus, some html element may be passed as a size provider, and another one where we
|
||||||
/// listeners (for example `body` and `window` respectively).
|
/// attach listeners (for example `body` and `window` respectively).
|
||||||
pub fn new_separated
|
pub fn new_separated
|
||||||
(dom:&web::dom::WithKnownShape<web::EventTarget>,target:&web::EventTarget) -> Self {
|
(dom:&web::dom::WithKnownShape<web::EventTarget>,target:&web::EventTarget) -> Self {
|
||||||
let dispatchers = MouseManagerDispatchers::default();
|
let dispatchers = MouseManagerDispatchers::default();
|
||||||
let dom = dom.clone();
|
let dom = dom.clone();
|
||||||
let target = target.clone();
|
|
||||||
$(
|
$(
|
||||||
let shape = dom.shape.clone_ref();
|
let shape = dom.shape.clone_ref();
|
||||||
let dispatcher = dispatchers.$name.clone_ref();
|
let dispatcher = dispatchers.$name.clone_ref();
|
||||||
let $name : MouseEventJsClosure = Closure::wrap(Box::new(move |event:JsValue| {
|
let closure : MouseEventJsClosure = Closure::new(move |event:JsValue| {
|
||||||
let shape = shape.value();
|
let shape = shape.value();
|
||||||
let event = event.unchecked_into::<web_sys::$js_event>();
|
let event = event.unchecked_into::<web::$js_event>();
|
||||||
dispatcher.dispatch(&event::$target::new(event,shape))
|
dispatcher.run_all(&event::$target::new(event,shape))
|
||||||
}));
|
});
|
||||||
let js_closure = $name.as_ref().unchecked_ref();
|
let js_name = stringify!($js_name);
|
||||||
let js_name = stringify!($js_name);
|
let opt = event_listener_options();
|
||||||
let options = event_listener_options();
|
let $name = web::add_event_listener_with_options(&target,js_name,closure,&opt);
|
||||||
let result =
|
|
||||||
target.add_event_listener_with_callback_and_add_event_listener_options
|
|
||||||
(js_name,js_closure,&options);
|
|
||||||
if let Err(e) = result { panic!("Cannot add event listener. {:?}",e) }
|
|
||||||
)*
|
)*
|
||||||
let closures = Rc::new(MouseManagerClosures {target,$($name),*});
|
let handles = Rc::new(MouseManagerEventListenerHandles {$($name),*});
|
||||||
Self {dispatchers,closures,dom}
|
Self {dispatchers,handles,dom}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrun options for addEventListener function. See also
|
/// Return options for addEventListener function. See also
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
/// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
||||||
fn event_listener_options() -> web_sys::AddEventListenerOptions {
|
fn event_listener_options() -> web::AddEventListenerOptions {
|
||||||
let mut options = web_sys::AddEventListenerOptions::new();
|
let mut options = web::AddEventListenerOptions::new();
|
||||||
// We listen for events in capture phase, so we can decide ourself if it should be passed
|
// We listen for events in capture phase, so we can decide ourself if it should be passed
|
||||||
// further.
|
// further.
|
||||||
options.capture(true);
|
options.capture(true);
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::system::web::dom::Shape;
|
use crate::system::web;
|
||||||
use enso_frp::io::mouse;
|
use enso_frp::io::mouse;
|
||||||
|
|
||||||
use wasm_bindgen::JsCast;
|
use web::dom::Shape;
|
||||||
|
use web::traits::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -19,13 +20,13 @@ macro_rules! define_events {
|
|||||||
#[derive(Debug,Clone,From,Shrinkwrap)]
|
#[derive(Debug,Clone,From,Shrinkwrap)]
|
||||||
pub struct $name {
|
pub struct $name {
|
||||||
#[shrinkwrap(main_field)]
|
#[shrinkwrap(main_field)]
|
||||||
raw : web_sys::$js_event,
|
raw : web::$js_event,
|
||||||
shape : Shape,
|
shape : Shape,
|
||||||
}
|
}
|
||||||
impl $name {
|
impl $name {
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(raw:web_sys::$js_event,shape:Shape) -> Self {
|
pub fn new(raw:web::$js_event,shape:Shape) -> Self {
|
||||||
Self {raw,shape}
|
Self {raw,shape}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,15 +67,15 @@ macro_rules! define_events {
|
|||||||
|
|
||||||
/// Return the event handler that caught this event if it exists and if it is an
|
/// Return the event handler that caught this event if it exists and if it is an
|
||||||
/// html element. Returns `None` if the event was caught, for example, byt the window.
|
/// html element. Returns `None` if the event was caught, for example, byt the window.
|
||||||
fn try_get_current_target_element(&self) -> Option<web_sys::Element> {
|
fn try_get_current_target_element(&self) -> Option<web::Element> {
|
||||||
let target = self.current_target()?;
|
let target = self.current_target()?;
|
||||||
target.value_of().dyn_into::<web_sys::Element>().ok()
|
target.value_of().dyn_into::<web::Element>().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the position relative to the given element.
|
/// Return the position relative to the given element.
|
||||||
///
|
///
|
||||||
/// Note: causes reflow of the JS layout.
|
/// Note: causes reflow of the JS layout.
|
||||||
pub fn relative_position_with_reflow(&self, element:&web_sys::Element) -> Vector2<f32> {
|
pub fn relative_position_with_reflow(&self, element:&web::Element) -> Vector2<f32> {
|
||||||
let rect = element.get_bounding_client_rect();
|
let rect = element.get_bounding_client_rect();
|
||||||
let x = self.client_x() as f64 - rect.left();
|
let x = self.client_x() as f64 - rect.left();
|
||||||
let y = self.client_y() as f64 - rect.top();
|
let y = self.client_y() as f64 - rect.top();
|
||||||
@ -83,9 +84,9 @@ macro_rules! define_events {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<web_sys::Event> for $name {
|
impl AsRef<web::Event> for $name {
|
||||||
fn as_ref(&self) -> &web_sys::Event {
|
fn as_ref(&self) -> &web::Event {
|
||||||
let js_event = AsRef::<web_sys::$js_event>::as_ref(self);
|
let js_event = AsRef::<web::$js_event>::as_ref(self);
|
||||||
js_event.as_ref()
|
js_event.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,12 @@ use crate::prelude::*;
|
|||||||
|
|
||||||
use crate::debug::stats::StatsData;
|
use crate::debug::stats::StatsData;
|
||||||
use crate::system::web;
|
use crate::system::web;
|
||||||
use crate::system::web::StyleSetter;
|
use crate::system::web::traits::*;
|
||||||
|
use crate::system::web::JsValue;
|
||||||
|
|
||||||
use num_traits::cast::AsPrimitive;
|
use num_traits::cast::AsPrimitive;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::f64;
|
use std::f64;
|
||||||
use wasm_bindgen;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -105,7 +103,7 @@ impl Default for Config {
|
|||||||
impl Config {
|
impl Config {
|
||||||
/// Translates the configuration to JS values.
|
/// Translates the configuration to JS values.
|
||||||
pub fn to_js_config(&self) -> SamplerConfig {
|
pub fn to_js_config(&self) -> SamplerConfig {
|
||||||
let ratio = web::window().device_pixel_ratio();
|
let ratio = web::window.device_pixel_ratio();
|
||||||
SamplerConfig {
|
SamplerConfig {
|
||||||
background_color: (&self.background_color).into(),
|
background_color: (&self.background_color).into(),
|
||||||
label_color_ok: (&self.label_color_ok).into(),
|
label_color_ok: (&self.label_color_ok).into(),
|
||||||
@ -165,19 +163,19 @@ impl DomData {
|
|||||||
/// Constructor.
|
/// Constructor.
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let root = web::create_div();
|
let root = web::document.create_div_or_panic();
|
||||||
root.set_class_name("performance-monitor");
|
root.set_class_name("performance-monitor");
|
||||||
root.set_style_or_panic("position", "absolute");
|
root.set_style_or_warn("position", "absolute");
|
||||||
root.set_style_or_panic("z-index", "100");
|
root.set_style_or_warn("z-index", "100");
|
||||||
root.set_style_or_panic("left", "8px");
|
root.set_style_or_warn("left", "8px");
|
||||||
root.set_style_or_panic("top", "8px");
|
root.set_style_or_warn("top", "8px");
|
||||||
root.set_style_or_panic("overflow", "hidden");
|
root.set_style_or_warn("overflow", "hidden");
|
||||||
root.set_style_or_panic("border-radius", "6px");
|
root.set_style_or_warn("border-radius", "6px");
|
||||||
root.set_style_or_panic("box-shadow", "0px 0px 20px -4px rgba(0,0,0,0.44)");
|
root.set_style_or_warn("box-shadow", "0px 0px 20px -4px rgba(0,0,0,0.44)");
|
||||||
web::body().prepend_with_node_1(&root).unwrap();
|
web::document.body_or_panic().prepend_with_node_1(&root).unwrap();
|
||||||
|
|
||||||
let canvas = web::create_canvas();
|
let canvas = web::document.create_canvas_or_panic();
|
||||||
canvas.set_style_or_panic("display", "block");
|
canvas.set_style_or_warn("display", "block");
|
||||||
|
|
||||||
let context = canvas.get_context("2d").unwrap().unwrap();
|
let context = canvas.get_context("2d").unwrap().unwrap();
|
||||||
let context: web::CanvasRenderingContext2d = context.dyn_into().unwrap();
|
let context: web::CanvasRenderingContext2d = context.dyn_into().unwrap();
|
||||||
@ -337,7 +335,7 @@ impl Renderer {
|
|||||||
|
|
||||||
fn resize(&mut self) {
|
fn resize(&mut self) {
|
||||||
if let Some(dom) = &self.dom {
|
if let Some(dom) = &self.dom {
|
||||||
let ratio = web::window().device_pixel_ratio();
|
let ratio = web::window.device_pixel_ratio();
|
||||||
let width = self.config.labels_width
|
let width = self.config.labels_width
|
||||||
+ self.config.results_width
|
+ self.config.results_width
|
||||||
+ self.config.plots_width
|
+ self.config.plots_width
|
||||||
@ -355,8 +353,8 @@ impl Renderer {
|
|||||||
self.height = height;
|
self.height = height;
|
||||||
dom.canvas.set_width(u_width);
|
dom.canvas.set_width(u_width);
|
||||||
dom.canvas.set_height(u_height);
|
dom.canvas.set_height(u_height);
|
||||||
dom.canvas.set_style_or_panic("width", format!("{}px", width / ratio));
|
dom.canvas.set_style_or_warn("width", format!("{}px", width / ratio));
|
||||||
dom.canvas.set_style_or_panic("height", format!("{}px", height / ratio));
|
dom.canvas.set_style_or_warn("height", format!("{}px", height / ratio));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::control::callback;
|
use crate::control::callback;
|
||||||
|
use crate::control::callback::traits::*;
|
||||||
use crate::data::dirty;
|
use crate::data::dirty;
|
||||||
use crate::data::dirty::traits::*;
|
use crate::data::dirty::traits::*;
|
||||||
use crate::display;
|
use crate::display;
|
||||||
@ -158,10 +159,10 @@ impl Default for Matrix {
|
|||||||
// ====================
|
// ====================
|
||||||
|
|
||||||
/// Function used to return the updated screen dimensions.
|
/// Function used to return the updated screen dimensions.
|
||||||
pub trait ScreenUpdateFn = callback::CallbackMut1Fn<Vector2<f32>>;
|
pub trait ScreenUpdateFn = Fn(Vector2<f32>) + 'static;
|
||||||
|
|
||||||
/// Function used to return the updated `Camera2d`'s zoom.
|
/// Function used to return the updated `Camera2d`'s zoom.
|
||||||
pub trait ZoomUpdateFn = callback::CallbackMut1Fn<f32>;
|
pub trait ZoomUpdateFn = Fn(f32) + 'static;
|
||||||
|
|
||||||
/// Internal `Camera2d` representation. Please see `Camera2d` for full documentation.
|
/// Internal `Camera2d` representation. Please see `Camera2d` for full documentation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -174,8 +175,8 @@ struct Camera2dData {
|
|||||||
clipping: Clipping,
|
clipping: Clipping,
|
||||||
matrix: Matrix,
|
matrix: Matrix,
|
||||||
dirty: Dirty,
|
dirty: Dirty,
|
||||||
zoom_update_registry: callback::Registry1<f32>,
|
zoom_update_registry: callback::registry::CopyMut1<f32>,
|
||||||
screen_update_registry: callback::Registry1<Vector2<f32>>,
|
screen_update_registry: callback::registry::CopyMut1<Vector2<f32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProjectionDirty = dirty::SharedBool<()>;
|
type ProjectionDirty = dirty::SharedBool<()>;
|
||||||
@ -271,8 +272,7 @@ impl Camera2dData {
|
|||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
self.matrix.view_projection = self.matrix.projection * self.matrix.view;
|
self.matrix.view_projection = self.matrix.projection * self.matrix.view;
|
||||||
let zoom = self.zoom;
|
self.zoom_update_registry.run_all(self.zoom);
|
||||||
self.zoom_update_registry.run_all(&zoom);
|
|
||||||
}
|
}
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
@ -311,7 +311,7 @@ impl Camera2dData {
|
|||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
let dimensions = Vector2::new(width, height);
|
let dimensions = Vector2::new(width, height);
|
||||||
self.screen_update_registry.run_all(&dimensions);
|
self.screen_update_registry.run_all(dimensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_zoom(&mut self) {
|
fn reset_zoom(&mut self) {
|
||||||
@ -406,6 +406,8 @@ impl Camera2d {
|
|||||||
self.data.borrow_mut().update(scene)
|
self.data.borrow_mut().update(scene)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This can fail, for example, when during calling the callback another callback is
|
||||||
|
// being registered.
|
||||||
/// Adds a callback to notify when `zoom` is updated.
|
/// Adds a callback to notify when `zoom` is updated.
|
||||||
pub fn add_zoom_update_callback<F: ZoomUpdateFn>(&self, f: F) -> callback::Handle {
|
pub fn add_zoom_update_callback<F: ZoomUpdateFn>(&self, f: F) -> callback::Handle {
|
||||||
self.data.borrow_mut().add_zoom_update_callback(f)
|
self.data.borrow_mut().add_zoom_update_callback(f)
|
||||||
|
@ -91,18 +91,18 @@ impl NavigatorModel {
|
|||||||
let distance_to_zoom_factor_of_1 = distance_to_zoom_factor_of_1(&camera);
|
let distance_to_zoom_factor_of_1 = distance_to_zoom_factor_of_1(&camera);
|
||||||
let pan_speed = pan_speed.get().into_on().unwrap_or(0.0);
|
let pan_speed = pan_speed.get().into_on().unwrap_or(0.0);
|
||||||
let movement_scale_for_distance = distance / distance_to_zoom_factor_of_1;
|
let movement_scale_for_distance = distance / distance_to_zoom_factor_of_1;
|
||||||
let diff = pan_speed * Vector3::new(pan.movement.x, pan.movement.y, 0.0) * movement_scale_for_distance;
|
let movement = Vector3::new(pan.movement.x, pan.movement.y, 0.0);
|
||||||
|
let diff = pan_speed * movement * movement_scale_for_distance;
|
||||||
simulator.update_target_value(|p| p - diff);
|
simulator.update_target_value(|p| p - diff);
|
||||||
});
|
});
|
||||||
|
|
||||||
let resize_callback = camera.add_screen_update_callback(
|
let resize_callback =
|
||||||
enclose!((mut simulator,camera) move |_:&Vector2<f32>| {
|
camera.add_screen_update_callback(enclose!((mut simulator,camera) move |_| {
|
||||||
let position = camera.position();
|
let position = camera.position();
|
||||||
simulator.set_value(position);
|
simulator.set_value(position);
|
||||||
simulator.set_target_value(position);
|
simulator.set_target_value(position);
|
||||||
simulator.set_velocity(default());
|
simulator.set_velocity(default());
|
||||||
}),
|
}));
|
||||||
);
|
|
||||||
|
|
||||||
let zoom_callback = f!([camera,simulator,max_zoom] (zoom:ZoomEvent) {
|
let zoom_callback = f!([camera,simulator,max_zoom] (zoom:ZoomEvent) {
|
||||||
let point = zoom.focus;
|
let point = zoom.focus;
|
||||||
|
@ -183,21 +183,7 @@ fn on_dirty_callback(f: &Rc<RefCell<Box<dyn Fn()>>>) -> OnDirtyCallback {
|
|||||||
/// A hierarchical representation of object containing information about transformation in 3D space,
|
/// A hierarchical representation of object containing information about transformation in 3D space,
|
||||||
/// list of children, and set of utils for dirty flag propagation.
|
/// list of children, and set of utils for dirty flag propagation.
|
||||||
///
|
///
|
||||||
/// ## Host
|
/// See the documentation of [`Instance`] to learn more.
|
||||||
/// The model is parametrized with a `Host`. In real life use cases, host will be instantiated with
|
|
||||||
/// `Scene`. For the needs of tests, its often instantiated with empty tuple for simplicity. Host
|
|
||||||
/// has a very important role in decoupling the architecture. You need to provide the `update`
|
|
||||||
/// method with a reference to the host, which is then passed to `on_show` and `on_hide` callbacks
|
|
||||||
/// when a particular display objects gets shown or hidden respectively. This can be used for a
|
|
||||||
/// dynamic management of GPU-side sprites. For example, after adding a display object to a scene,
|
|
||||||
/// a new sprites can be created to display it visually. After removing the objects, and adding it
|
|
||||||
/// to a different scene (second GPU context), the sprites in the first context can be removed, and
|
|
||||||
/// new sprites in the new context can be created. Thus, abstracting over `Host` allows users of
|
|
||||||
/// this library to define a view model (like few sliders in a box) without the need to contain
|
|
||||||
/// reference to a particular renderer, and attach the renderer on-demand, when the objects will be
|
|
||||||
/// placed on the stage.
|
|
||||||
///
|
|
||||||
/// Please note, that this functionality is fairly new, and the library do not use it like this yet.
|
|
||||||
#[derive(Derivative)]
|
#[derive(Derivative)]
|
||||||
#[derivative(Debug(bound = ""))]
|
#[derivative(Debug(bound = ""))]
|
||||||
pub struct Model<Host = Scene> {
|
pub struct Model<Host = Scene> {
|
||||||
@ -610,18 +596,33 @@ pub struct Id(usize);
|
|||||||
/// list of children, and set of utils for dirty flag propagation.
|
/// list of children, and set of utils for dirty flag propagation.
|
||||||
///
|
///
|
||||||
/// ## Host
|
/// ## Host
|
||||||
/// The structure is parametrized with a `Host`. In real life use cases, host will be instantiated
|
/// The model is parametrized with a `Host`. In real life use cases, host is **ALWAYS** instantiated
|
||||||
/// with [`Scene`]. For simplicity, it is instantiated to empty tuple in tests. Host has a very
|
/// with `Scene`. For the needs of tests, its often instantiated with empty tuple for simplicity.
|
||||||
/// important role in decoupling the architecture. You need to provide the `update` method with a
|
/// Host has a very important role in decoupling the architecture. You need to provide the `update`
|
||||||
/// reference to the host, which is then passed to `on_show` and `on_hide` callbacks when a
|
/// method with a reference to the host, which is then passed to `on_show` and `on_hide` callbacks
|
||||||
/// particular display objects gets shown or hidden respectively. This can be used for a dynamic
|
/// when a particular display objects gets shown or hidden respectively.
|
||||||
/// management of GPU-side sprites. For example, after adding a display object to a scene, a new
|
///
|
||||||
/// sprites can be created to display it visually. After removing the objects, and adding it to a
|
/// This can be used for a dynamic management of GPU-side rendering. After adding a display object
|
||||||
/// different scene (second GPU context), the sprites in the first context can be removed, and new
|
/// to a scene, a new sprite can be created to display it visually. After removing the object and
|
||||||
/// sprites in the new context can be created. Thus, abstracting over `Host` allows users of this
|
/// adding it to a different scene (another GPU context), the sprite in the first context can be
|
||||||
/// library to define a view model (like few sliders in a box) without the need to contain reference
|
/// trashed, and a new sprite in the new context can be created instead. This mechanism is currently
|
||||||
/// to a particular renderer, and attach the renderer on-demand, when the objects will be placed on
|
/// also used for sprites layer management, but this functionality is inherently connected to the
|
||||||
/// the stage.
|
/// sprites creation in a given context (moving object to a different layer of the same [`Scene`] is
|
||||||
|
/// similar to moving it to a layer of a different [`Scene`].
|
||||||
|
///
|
||||||
|
/// Thus, abstracting over `Host` allows users of this library to define a view model (like few
|
||||||
|
/// sliders in a box) without the need to contain reference to a particular renderer, and attach the
|
||||||
|
/// renderer on-demand, when the objects will be placed on the stage.
|
||||||
|
///
|
||||||
|
/// Please note, that moving an object between two Scenes (two WebGL contexts) is not implemented
|
||||||
|
/// yet.
|
||||||
|
///
|
||||||
|
/// ## Possible changes to the Host parametrization design
|
||||||
|
/// Instead of parametrizing the Display Object, the [`Host`] could be implemented as dyn trait
|
||||||
|
/// object exposing a few options to registering sprites in Scene layers. This is a way less generic
|
||||||
|
/// solution than the one implemented currently, but it would allow [`Scene`] parametrization. For
|
||||||
|
/// example, if we would like to implement [`Scene<Context>`], where the [`Context`] is either a
|
||||||
|
/// WebGL or an OpenGL context (currently contexts are implemented as dyn traits instead).
|
||||||
///
|
///
|
||||||
/// ## Scene Layers
|
/// ## Scene Layers
|
||||||
/// Each display object instance contains an optional list of [`scene::LayerId`]. During object
|
/// Each display object instance contains an optional list of [`scene::LayerId`]. During object
|
||||||
@ -630,13 +631,6 @@ pub struct Id(usize);
|
|||||||
/// plays a very important role in decoupling the architecture. It allows objects and their children
|
/// plays a very important role in decoupling the architecture. It allows objects and their children
|
||||||
/// to be assigned to a particular [`scene::Layer`], and thus allows for easy to use depth
|
/// to be assigned to a particular [`scene::Layer`], and thus allows for easy to use depth
|
||||||
/// management.
|
/// management.
|
||||||
///
|
|
||||||
/// ## Future Development
|
|
||||||
/// Please note, that currently, the design is abstract over [`Host`], but it is not abstract over
|
|
||||||
/// scene layers. This may change in the future, but first, the [`Scene`] implementation has to be
|
|
||||||
/// refactored to allow the creation of [`Symbol`]s without requirement of a [`Scene`] instance
|
|
||||||
/// existence. See this ticket to learn more: https://github.com/enso-org/ide/issues/1129 .
|
|
||||||
|
|
||||||
#[derive(Derivative)]
|
#[derive(Derivative)]
|
||||||
#[derive(CloneRef)]
|
#[derive(CloneRef)]
|
||||||
#[derivative(Clone(bound = ""))]
|
#[derivative(Clone(bound = ""))]
|
||||||
|
@ -10,6 +10,7 @@ pub use layer::Layer;
|
|||||||
pub use crate::system::web::dom::Shape;
|
pub use crate::system::web::dom::Shape;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use web::traits::*;
|
||||||
|
|
||||||
use crate::animation;
|
use crate::animation;
|
||||||
use crate::control::callback;
|
use crate::control::callback;
|
||||||
@ -30,20 +31,20 @@ use crate::display::style::data::DataMatch;
|
|||||||
use crate::display::symbol::registry::SymbolRegistry;
|
use crate::display::symbol::registry::SymbolRegistry;
|
||||||
use crate::display::symbol::Symbol;
|
use crate::display::symbol::Symbol;
|
||||||
use crate::display::symbol::SymbolId;
|
use crate::display::symbol::SymbolId;
|
||||||
|
use crate::system;
|
||||||
use crate::system::gpu::data::attribute;
|
use crate::system::gpu::data::attribute;
|
||||||
use crate::system::gpu::data::uniform::Uniform;
|
use crate::system::gpu::data::uniform::Uniform;
|
||||||
use crate::system::gpu::data::uniform::UniformScope;
|
use crate::system::gpu::data::uniform::UniformScope;
|
||||||
use crate::system::gpu::shader::Context;
|
|
||||||
use crate::system::web;
|
use crate::system::web;
|
||||||
use crate::system::web::IgnoreContextMenuHandle;
|
use crate::system::web::EventListenerHandle;
|
||||||
use crate::system::web::NodeInserter;
|
use crate::system::Context;
|
||||||
use crate::system::web::StyleSetter;
|
use crate::system::ContextLostHandler;
|
||||||
|
|
||||||
use enso_frp as frp;
|
use enso_frp as frp;
|
||||||
use enso_frp::io::js::CurrentJsEvent;
|
use enso_frp::io::js::CurrentJsEvent;
|
||||||
use enso_shapely::shared;
|
use enso_shapely::shared;
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use web_sys::HtmlElement;
|
use web::HtmlElement;
|
||||||
|
|
||||||
|
|
||||||
pub trait MouseTarget: Debug + 'static {
|
pub trait MouseTarget: Debug + 'static {
|
||||||
@ -326,7 +327,7 @@ impl Mouse {
|
|||||||
let position = variables.add_or_panic("mouse_position", Vector2::new(0, 0));
|
let position = variables.add_or_panic("mouse_position", Vector2::new(0, 0));
|
||||||
let hover_ids = variables.add_or_panic("mouse_hover_ids", target.to_internal(&logger));
|
let hover_ids = variables.add_or_panic("mouse_hover_ids", target.to_internal(&logger));
|
||||||
let target = Rc::new(Cell::new(target));
|
let target = Rc::new(Cell::new(target));
|
||||||
let mouse_manager = MouseManager::new_separated(&root.clone_ref().into(), &web::window());
|
let mouse_manager = MouseManager::new_separated(&root.clone_ref().into(), &web::window);
|
||||||
let frp = frp::io::Mouse::new();
|
let frp = frp::io::Mouse::new();
|
||||||
let on_move = mouse_manager.on_move.add(current_js_event.make_event_handler(
|
let on_move = mouse_manager.on_move.add(current_js_event.make_event_handler(
|
||||||
f!([frp,scene_frp,position,last_position] (event:&mouse::OnMove) {
|
f!([frp,scene_frp,position,last_position] (event:&mouse::OnMove) {
|
||||||
@ -414,10 +415,8 @@ pub struct Keyboard {
|
|||||||
|
|
||||||
impl Keyboard {
|
impl Keyboard {
|
||||||
pub fn new(current_event: &CurrentJsEvent) -> Self {
|
pub fn new(current_event: &CurrentJsEvent) -> Self {
|
||||||
let logger = Logger::new("keyboard");
|
|
||||||
let frp = enso_frp::io::keyboard::Keyboard::default();
|
let frp = enso_frp::io::keyboard::Keyboard::default();
|
||||||
let bindings =
|
let bindings = Rc::new(enso_frp::io::keyboard::DomBindings::new(&frp, current_event));
|
||||||
Rc::new(enso_frp::io::keyboard::DomBindings::new(&logger, &frp, current_event));
|
|
||||||
Self { frp, bindings }
|
Self { frp, bindings }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -440,12 +439,12 @@ pub struct Dom {
|
|||||||
impl Dom {
|
impl Dom {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(logger: &Logger) -> Self {
|
pub fn new(logger: &Logger) -> Self {
|
||||||
let root = web::create_div();
|
let root = web::document.create_div_or_panic();
|
||||||
let layers = DomLayers::new(logger, &root);
|
let layers = DomLayers::new(logger, &root);
|
||||||
root.set_class_name("scene");
|
root.set_class_name("scene");
|
||||||
root.set_style_or_panic("height", "100vh");
|
root.set_style_or_warn("height", "100vh");
|
||||||
root.set_style_or_panic("width", "100vw");
|
root.set_style_or_warn("width", "100vw");
|
||||||
root.set_style_or_panic("display", "block");
|
root.set_style_or_warn("display", "block");
|
||||||
let root = web::dom::WithKnownShape::new(&root);
|
let root = web::dom::WithKnownShape::new(&root);
|
||||||
Self { root, layers }
|
Self { root, layers }
|
||||||
}
|
}
|
||||||
@ -488,42 +487,42 @@ pub struct DomLayers {
|
|||||||
/// Front DOM scene layer.
|
/// Front DOM scene layer.
|
||||||
pub front: DomScene,
|
pub front: DomScene,
|
||||||
/// The WebGL scene layer.
|
/// The WebGL scene layer.
|
||||||
pub canvas: web_sys::HtmlCanvasElement,
|
pub canvas: web::HtmlCanvasElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DomLayers {
|
impl DomLayers {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(logger: &Logger, dom: &web_sys::HtmlDivElement) -> Self {
|
pub fn new(logger: &Logger, dom: &web::HtmlDivElement) -> Self {
|
||||||
let welcome_screen = DomScene::new(logger);
|
let welcome_screen = DomScene::new(logger);
|
||||||
welcome_screen.dom.set_class_name("welcome_screen");
|
welcome_screen.dom.set_class_name("welcome_screen");
|
||||||
welcome_screen.dom.set_style_or_warn("z-index", "0", logger);
|
welcome_screen.dom.set_style_or_warn("z-index", "0");
|
||||||
dom.append_or_panic(&welcome_screen.dom);
|
dom.append_or_warn(&welcome_screen.dom);
|
||||||
|
|
||||||
let back = DomScene::new(logger);
|
let back = DomScene::new(logger);
|
||||||
back.dom.set_class_name("back");
|
back.dom.set_class_name("back");
|
||||||
back.dom.set_style_or_warn("z-index", "1", logger);
|
back.dom.set_style_or_warn("z-index", "1");
|
||||||
dom.append_or_panic(&back.dom);
|
dom.append_or_warn(&back.dom);
|
||||||
|
|
||||||
let fullscreen_vis = DomScene::new(logger);
|
let fullscreen_vis = DomScene::new(logger);
|
||||||
fullscreen_vis.dom.set_class_name("fullscreen_vis");
|
fullscreen_vis.dom.set_class_name("fullscreen_vis");
|
||||||
fullscreen_vis.dom.set_style_or_warn("z-index", "2", logger);
|
fullscreen_vis.dom.set_style_or_warn("z-index", "2");
|
||||||
dom.append_or_panic(&fullscreen_vis.dom);
|
dom.append_or_warn(&fullscreen_vis.dom);
|
||||||
|
|
||||||
let canvas = web::create_canvas();
|
let canvas = web::document.create_canvas_or_panic();
|
||||||
canvas.set_style_or_warn("display", "block", logger);
|
canvas.set_style_or_warn("display", "block");
|
||||||
canvas.set_style_or_warn("z-index", "3", logger);
|
canvas.set_style_or_warn("z-index", "3");
|
||||||
// These properties are set by `DomScene::new` constuctor for other layers.
|
// These properties are set by `DomScene::new` constuctor for other layers.
|
||||||
// See its documentation for more info.
|
// See its documentation for more info.
|
||||||
canvas.set_style_or_warn("position", "absolute", logger);
|
canvas.set_style_or_warn("position", "absolute");
|
||||||
canvas.set_style_or_warn("height", "100vh", logger);
|
canvas.set_style_or_warn("height", "100vh");
|
||||||
canvas.set_style_or_warn("width", "100vw", logger);
|
canvas.set_style_or_warn("width", "100vw");
|
||||||
canvas.set_style_or_warn("pointer-events", "none", logger);
|
canvas.set_style_or_warn("pointer-events", "none");
|
||||||
dom.append_or_panic(&canvas);
|
dom.append_or_warn(&canvas);
|
||||||
|
|
||||||
let front = DomScene::new(logger);
|
let front = DomScene::new(logger);
|
||||||
front.dom.set_class_name("front");
|
front.dom.set_class_name("front");
|
||||||
front.dom.set_style_or_warn("z-index", "4", logger);
|
front.dom.set_style_or_warn("z-index", "4");
|
||||||
dom.append_or_panic(&front.dom);
|
dom.append_or_warn(&front.dom);
|
||||||
|
|
||||||
Self { back, welcome_screen, fullscreen_vis, front, canvas }
|
Self { back, welcome_screen, fullscreen_vis, front, canvas }
|
||||||
}
|
}
|
||||||
@ -565,74 +564,107 @@ pub struct Dirty {
|
|||||||
shape: ShapeDirty,
|
shape: ShapeDirty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dirty {
|
||||||
|
pub fn new<OnMut: Fn() + Clone + 'static>(logger: &Logger, on_mut: OnMut) -> Self {
|
||||||
|
let sub_logger = Logger::new_sub(logger, "shape_dirty");
|
||||||
|
let shape = ShapeDirty::new(sub_logger, Box::new(on_mut.clone()));
|
||||||
|
let sub_logger = Logger::new_sub(logger, "symbols_dirty");
|
||||||
|
let symbols = SymbolRegistryDirty::new(sub_logger, Box::new(on_mut));
|
||||||
|
Self { symbols, shape }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ================
|
// ================
|
||||||
// === Renderer ===
|
// === Renderer ===
|
||||||
// ================
|
// ================
|
||||||
|
|
||||||
|
/// Scene renderer. Manages the initialization and lifetime of both [`render::Pipeline`] and the
|
||||||
|
/// [`render::Composer`].
|
||||||
|
///
|
||||||
|
/// Please note that the composer can be empty if the context was either not provided yet or it was
|
||||||
|
/// lost.
|
||||||
#[derive(Clone, CloneRef, Debug)]
|
#[derive(Clone, CloneRef, Debug)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
pub logger: Logger,
|
pub logger: Logger,
|
||||||
dom: Dom,
|
dom: Dom,
|
||||||
context: Context,
|
variables: UniformScope,
|
||||||
variables: UniformScope,
|
|
||||||
|
|
||||||
pub pipeline: Rc<CloneCell<render::Pipeline>>,
|
pub pipeline: Rc<CloneCell<render::Pipeline>>,
|
||||||
pub composer: Rc<RefCell<render::Composer>>,
|
pub composer: Rc<RefCell<Option<render::Composer>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
fn new(logger: impl AnyLogger, dom: &Dom, context: &Context, variables: &UniformScope) -> Self {
|
fn new(logger: impl AnyLogger, dom: &Dom, variables: &UniformScope) -> Self {
|
||||||
let logger = Logger::new_sub(logger, "renderer");
|
let logger = Logger::new_sub(logger, "renderer");
|
||||||
let dom = dom.clone_ref();
|
let dom = dom.clone_ref();
|
||||||
let context = context.clone_ref();
|
|
||||||
let variables = variables.clone_ref();
|
let variables = variables.clone_ref();
|
||||||
let pipeline = default();
|
let pipeline = default();
|
||||||
let shape = dom.shape().device_pixels();
|
let composer = default();
|
||||||
let width = shape.width as i32;
|
Self { logger, dom, variables, pipeline, composer }
|
||||||
let height = shape.height as i32;
|
}
|
||||||
let composer = render::Composer::new(&pipeline, &context, &variables, width, height);
|
|
||||||
let pipeline = Rc::new(CloneCell::new(pipeline));
|
|
||||||
let composer = Rc::new(RefCell::new(composer));
|
|
||||||
|
|
||||||
context.enable(Context::BLEND);
|
fn set_context(&self, context: Option<&Context>) {
|
||||||
// To learn more about the blending equations used here, please see the following articles:
|
let composer = context.map(|context| {
|
||||||
// - http://www.realtimerendering.com/blog/gpus-prefer-premultiplication
|
// To learn more about the blending equations used here, please see the following
|
||||||
// - https://www.khronos.org/opengl/wiki/Blending#Colors
|
// articles:
|
||||||
context.blend_equation_separate(Context::FUNC_ADD, Context::FUNC_ADD);
|
// - http://www.realtimerendering.com/blog/gpus-prefer-premultiplication
|
||||||
context.blend_func_separate(
|
// - https://www.khronos.org/opengl/wiki/Blending#Colors
|
||||||
Context::ONE,
|
context.enable(Context::BLEND);
|
||||||
Context::ONE_MINUS_SRC_ALPHA,
|
context.blend_equation_separate(Context::FUNC_ADD, Context::FUNC_ADD);
|
||||||
Context::ONE,
|
context.blend_func_separate(
|
||||||
Context::ONE_MINUS_SRC_ALPHA,
|
Context::ONE,
|
||||||
);
|
Context::ONE_MINUS_SRC_ALPHA,
|
||||||
|
Context::ONE,
|
||||||
|
Context::ONE_MINUS_SRC_ALPHA,
|
||||||
|
);
|
||||||
|
|
||||||
Self { logger, dom, context, variables, pipeline, composer }
|
let (width, height) = self.view_size();
|
||||||
|
let pipeline = self.pipeline.get();
|
||||||
|
render::Composer::new(&pipeline, context, &self.variables, width, height)
|
||||||
|
});
|
||||||
|
*self.composer.borrow_mut() = composer;
|
||||||
|
self.update_composer_pipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the pipeline of this renderer.
|
/// Set the pipeline of this renderer.
|
||||||
pub fn set_pipeline<P: Into<render::Pipeline>>(&self, pipeline: P) {
|
pub fn set_pipeline(&self, pipeline: render::Pipeline) {
|
||||||
let pipeline = pipeline.into();
|
|
||||||
self.composer.borrow_mut().set_pipeline(&pipeline);
|
|
||||||
self.pipeline.set(pipeline);
|
self.pipeline.set(pipeline);
|
||||||
|
self.update_composer_pipeline()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reload the composer pipeline.
|
||||||
|
fn update_composer_pipeline(&self) {
|
||||||
|
if let Some(composer) = &mut *self.composer.borrow_mut() {
|
||||||
|
composer.set_pipeline(&self.pipeline.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reload the composer after scene shape change.
|
/// Reload the composer after scene shape change.
|
||||||
fn resize_composer(&self) {
|
fn resize_composer(&self) {
|
||||||
|
if let Some(composer) = &mut *self.composer.borrow_mut() {
|
||||||
|
let (width, height) = self.view_size();
|
||||||
|
composer.resize(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The width and height in device pixels should be integers. If they are not then this is due to
|
||||||
|
// rounding errors. We round to the nearest integer to compensate for those errors.
|
||||||
|
fn view_size(&self) -> (i32, i32) {
|
||||||
let shape = self.dom.shape().device_pixels();
|
let shape = self.dom.shape().device_pixels();
|
||||||
// The width and height in device pixels should be integers. If they are not then this is
|
|
||||||
// due to rounding errors. We round to the nearest integer to compensate for those errors.
|
|
||||||
let width = shape.width.round() as i32;
|
let width = shape.width.round() as i32;
|
||||||
let height = shape.height.round() as i32;
|
let height = shape.height.round() as i32;
|
||||||
self.composer.borrow_mut().resize(width, height);
|
(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the renderer.
|
/// Run the renderer.
|
||||||
pub fn run(&self) {
|
pub fn run(&self) {
|
||||||
debug!(self.logger, "Running.", || {
|
if let Some(composer) = &mut *self.composer.borrow_mut() {
|
||||||
self.composer.borrow_mut().run();
|
debug!(self.logger, "Running.", || {
|
||||||
})
|
composer.run();
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,7 +675,7 @@ impl Renderer {
|
|||||||
// ==============
|
// ==============
|
||||||
|
|
||||||
/// Please note that currently the `Layers` structure is implemented in a hacky way. It assumes the
|
/// Please note that currently the `Layers` structure is implemented in a hacky way. It assumes the
|
||||||
/// existence of several layers, which are needed for the GUI to display shapes properly. This \
|
/// existence of several layers, which are needed for the GUI to display shapes properly. This
|
||||||
/// should be abstracted away in the future.
|
/// should be abstracted away in the future.
|
||||||
#[derive(Clone, CloneRef, Debug)]
|
#[derive(Clone, CloneRef, Debug)]
|
||||||
pub struct HardcodedLayers {
|
pub struct HardcodedLayers {
|
||||||
@ -801,33 +833,33 @@ impl Extensions {
|
|||||||
|
|
||||||
#[derive(Clone, CloneRef, Debug)]
|
#[derive(Clone, CloneRef, Debug)]
|
||||||
pub struct SceneData {
|
pub struct SceneData {
|
||||||
pub display_object: display::object::Instance,
|
pub display_object: display::object::Instance,
|
||||||
pub dom: Dom,
|
pub dom: Dom,
|
||||||
pub context: Context,
|
pub context: Rc<RefCell<Option<Context>>>,
|
||||||
pub symbols: SymbolRegistry,
|
pub context_lost_handler: Rc<RefCell<Option<ContextLostHandler>>>,
|
||||||
pub variables: UniformScope,
|
pub symbols: SymbolRegistry,
|
||||||
pub current_js_event: CurrentJsEvent,
|
pub variables: UniformScope,
|
||||||
pub mouse: Mouse,
|
pub current_js_event: CurrentJsEvent,
|
||||||
pub keyboard: Keyboard,
|
pub mouse: Mouse,
|
||||||
pub uniforms: Uniforms,
|
pub keyboard: Keyboard,
|
||||||
pub shapes: ShapeRegistry,
|
pub uniforms: Uniforms,
|
||||||
pub stats: Stats,
|
pub shapes: ShapeRegistry,
|
||||||
pub dirty: Dirty,
|
pub stats: Stats,
|
||||||
pub logger: Logger,
|
pub dirty: Dirty,
|
||||||
pub renderer: Renderer,
|
pub logger: Logger,
|
||||||
pub layers: HardcodedLayers,
|
pub renderer: Renderer,
|
||||||
pub style_sheet: style::Sheet,
|
pub layers: HardcodedLayers,
|
||||||
pub bg_color_var: style::Var,
|
pub style_sheet: style::Sheet,
|
||||||
pub bg_color_change: callback::Handle,
|
pub bg_color_var: style::Var,
|
||||||
pub frp: Frp,
|
pub bg_color_change: callback::Handle,
|
||||||
extensions: Extensions,
|
pub frp: Frp,
|
||||||
disable_context_menu: Rc<IgnoreContextMenuHandle>,
|
extensions: Extensions,
|
||||||
|
disable_context_menu: Rc<EventListenerHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SceneData {
|
impl SceneData {
|
||||||
/// Create new instance with the provided on-dirty callback.
|
/// Create new instance with the provided on-dirty callback.
|
||||||
pub fn new<OnMut: Fn() + Clone + 'static>(
|
pub fn new<OnMut: Fn() + Clone + 'static>(
|
||||||
parent_dom: &HtmlElement,
|
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
stats: &Stats,
|
stats: &Stats,
|
||||||
on_mut: OnMut,
|
on_mut: OnMut,
|
||||||
@ -835,37 +867,24 @@ impl SceneData {
|
|||||||
debug!(logger, "Initializing.");
|
debug!(logger, "Initializing.");
|
||||||
|
|
||||||
let dom = Dom::new(&logger);
|
let dom = Dom::new(&logger);
|
||||||
parent_dom.append_child(&dom.root).unwrap();
|
|
||||||
dom.recompute_shape_with_reflow();
|
|
||||||
|
|
||||||
let display_object = display::object::Instance::new(&logger);
|
let display_object = display::object::Instance::new(&logger);
|
||||||
display_object.force_set_visibility(true);
|
display_object.force_set_visibility(true);
|
||||||
let context = web::get_webgl2_context(&dom.layers.canvas);
|
|
||||||
let sub_logger = Logger::new_sub(&logger, "shape_dirty");
|
|
||||||
let shape_dirty = ShapeDirty::new(sub_logger, Box::new(on_mut.clone()));
|
|
||||||
let sub_logger = Logger::new_sub(&logger, "symbols_dirty");
|
|
||||||
let dirty_flag = SymbolRegistryDirty::new(sub_logger, Box::new(on_mut));
|
|
||||||
let on_change = enclose!((dirty_flag) move || dirty_flag.set());
|
|
||||||
let var_logger = Logger::new_sub(&logger, "global_variables");
|
let var_logger = Logger::new_sub(&logger, "global_variables");
|
||||||
let variables = UniformScope::new(var_logger);
|
let variables = UniformScope::new(var_logger);
|
||||||
let symbols = SymbolRegistry::mk(&variables, stats, &logger, on_change);
|
let dirty = Dirty::new(&logger, on_mut);
|
||||||
// FIXME: This should be abstracted away and should also handle context loss when Symbol
|
let symbols_dirty = &dirty.symbols;
|
||||||
// definition will be finally refactored in such way, that it would not require
|
let symbols = SymbolRegistry::mk(&variables, stats, &logger, f!(symbols_dirty.set()));
|
||||||
// Scene instance to be created.
|
|
||||||
symbols.set_context(Some(&context));
|
|
||||||
let symbols_dirty = dirty_flag;
|
|
||||||
let layers = HardcodedLayers::new(&logger);
|
let layers = HardcodedLayers::new(&logger);
|
||||||
let stats = stats.clone();
|
let stats = stats.clone();
|
||||||
let shapes = ShapeRegistry::default();
|
let shapes = ShapeRegistry::default();
|
||||||
let uniforms = Uniforms::new(&variables);
|
let uniforms = Uniforms::new(&variables);
|
||||||
let dirty = Dirty { symbols: symbols_dirty, shape: shape_dirty };
|
let renderer = Renderer::new(&logger, &dom, &variables);
|
||||||
let renderer = Renderer::new(&logger, &dom, &context, &variables);
|
|
||||||
let style_sheet = style::Sheet::new();
|
let style_sheet = style::Sheet::new();
|
||||||
let current_js_event = CurrentJsEvent::new();
|
let current_js_event = CurrentJsEvent::new();
|
||||||
let frp = Frp::new(&dom.root.shape);
|
let frp = Frp::new(&dom.root.shape);
|
||||||
let mouse_logger = Logger::new_sub(&logger, "mouse");
|
let mouse_logger = Logger::new_sub(&logger, "mouse");
|
||||||
let mouse = Mouse::new(&frp, &dom.root, &variables, ¤t_js_event, mouse_logger);
|
let mouse = Mouse::new(&frp, &dom.root, &variables, ¤t_js_event, mouse_logger);
|
||||||
let disable_context_menu = Rc::new(web::ignore_context_menu(&dom.root).unwrap());
|
let disable_context_menu = Rc::new(web::ignore_context_menu(&dom.root));
|
||||||
let keyboard = Keyboard::new(¤t_js_event);
|
let keyboard = Keyboard::new(¤t_js_event);
|
||||||
let network = &frp.network;
|
let network = &frp.network;
|
||||||
let extensions = Extensions::default();
|
let extensions = Extensions::default();
|
||||||
@ -873,7 +892,7 @@ impl SceneData {
|
|||||||
let bg_color_change = bg_color_var.on_change(f!([dom](change){
|
let bg_color_change = bg_color_var.on_change(f!([dom](change){
|
||||||
change.color().for_each(|color| {
|
change.color().for_each(|color| {
|
||||||
let color = color.to_javascript_string();
|
let color = color.to_javascript_string();
|
||||||
dom.root.set_style_or_panic("background-color",color);
|
dom.root.set_style_or_warn("background-color",color);
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -883,10 +902,13 @@ impl SceneData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uniforms.pixel_ratio.set(dom.shape().pixel_ratio);
|
uniforms.pixel_ratio.set(dom.shape().pixel_ratio);
|
||||||
|
let context = default();
|
||||||
|
let context_lost_handler = default();
|
||||||
Self {
|
Self {
|
||||||
display_object,
|
display_object,
|
||||||
dom,
|
dom,
|
||||||
context,
|
context,
|
||||||
|
context_lost_handler,
|
||||||
symbols,
|
symbols,
|
||||||
variables,
|
variables,
|
||||||
current_js_event,
|
current_js_event,
|
||||||
@ -908,6 +930,13 @@ impl SceneData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_context(&self, context: Option<&Context>) {
|
||||||
|
self.symbols.set_context(context);
|
||||||
|
*self.context.borrow_mut() = context.cloned();
|
||||||
|
self.dirty.shape.set();
|
||||||
|
self.renderer.set_context(context);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn shape(&self) -> &frp::Sampler<Shape> {
|
pub fn shape(&self) -> &frp::Sampler<Shape> {
|
||||||
&self.dom.root.shape
|
&self.dom.root.shape
|
||||||
}
|
}
|
||||||
@ -990,12 +1019,18 @@ impl SceneData {
|
|||||||
let width = canvas.width.round() as i32;
|
let width = canvas.width.round() as i32;
|
||||||
let height = canvas.height.round() as i32;
|
let height = canvas.height.round() as i32;
|
||||||
debug!(self.logger, "Resized to {screen.width}px x {screen.height}px.", || {
|
debug!(self.logger, "Resized to {screen.width}px x {screen.height}px.", || {
|
||||||
self.dom.layers.canvas.set_attribute("width", &width.to_string()).unwrap();
|
self.dom.layers.canvas.set_attribute_or_warn("width", &width.to_string());
|
||||||
self.dom.layers.canvas.set_attribute("height", &height.to_string()).unwrap();
|
self.dom.layers.canvas.set_attribute_or_warn("height", &height.to_string());
|
||||||
self.context.viewport(0, 0, width, height);
|
if let Some(context) = &*self.context.borrow() {
|
||||||
|
context.viewport(0, 0, width, height);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render(&self) {
|
||||||
|
self.renderer.run()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn screen_to_scene_coordinates(&self, position: Vector3<f32>) -> Vector3<f32> {
|
pub fn screen_to_scene_coordinates(&self, position: Vector3<f32>) -> Vector3<f32> {
|
||||||
let position = position / self.camera().zoom();
|
let position = position / self.camera().zoom();
|
||||||
let position = Vector4::new(position.x, position.y, position.z, 1.0);
|
let position = Vector4::new(position.x, position.y, position.z, 1.0);
|
||||||
@ -1043,26 +1078,54 @@ pub struct Scene {
|
|||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
pub fn new<OnMut: Fn() + Clone + 'static>(
|
pub fn new<OnMut: Fn() + Clone + 'static>(
|
||||||
parent_dom: &HtmlElement,
|
|
||||||
logger: impl AnyLogger,
|
logger: impl AnyLogger,
|
||||||
stats: &Stats,
|
stats: &Stats,
|
||||||
on_mut: OnMut,
|
on_mut: OnMut,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let logger = Logger::new_sub(logger, "scene");
|
let logger = Logger::new_sub(logger, "scene");
|
||||||
let no_mut_access = SceneData::new(parent_dom, logger, stats, on_mut);
|
let no_mut_access = SceneData::new(logger, stats, on_mut);
|
||||||
let this = Self { no_mut_access };
|
let this = Self { no_mut_access };
|
||||||
|
|
||||||
// FIXME MEMORY LEAK in all lines below:
|
// FIXME MEMORY LEAK:
|
||||||
this.no_mut_access.shapes.rc.borrow_mut().scene = Some(this.clone_ref());
|
this.no_mut_access.shapes.rc.borrow_mut().scene = Some(this.clone_ref());
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn display_in(&self, parent_dom: impl DomPath) {
|
||||||
|
match parent_dom.try_into_dom_element() {
|
||||||
|
None => error!(&self.logger, "The scene host element could not be found."),
|
||||||
|
Some(parent_dom) => {
|
||||||
|
parent_dom.append_or_warn(&self.dom.root);
|
||||||
|
self.dom.recompute_shape_with_reflow();
|
||||||
|
self.uniforms.pixel_ratio.set(self.dom.shape().pixel_ratio);
|
||||||
|
self.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&self) {
|
||||||
|
let context_loss_handler = crate::system::context::init_webgl_2_context(self);
|
||||||
|
match context_loss_handler {
|
||||||
|
Err(err) => error!(self.logger, "{err}"),
|
||||||
|
Ok(handler) => *self.context_lost_handler.borrow_mut() = Some(handler),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn extension<T: Extension>(&self) -> T {
|
pub fn extension<T: Extension>(&self) -> T {
|
||||||
self.extensions.get(self)
|
self.extensions.get(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl system::context::Display for Scene {
|
||||||
|
fn device_context_handler(&self) -> &system::context::DeviceContextHandler {
|
||||||
|
&self.dom.layers.canvas
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_context(&self, context: Option<&Context>) {
|
||||||
|
self.no_mut_access.set_context(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<SceneData> for Scene {
|
impl AsRef<SceneData> for Scene {
|
||||||
fn as_ref(&self) -> &SceneData {
|
fn as_ref(&self) -> &SceneData {
|
||||||
&self.no_mut_access
|
&self.no_mut_access
|
||||||
@ -1084,17 +1147,19 @@ impl Deref for Scene {
|
|||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
pub fn update(&self, t: animation::TimeInfo) {
|
pub fn update(&self, t: animation::TimeInfo) {
|
||||||
debug!(self.logger, "Updating.", || {
|
if self.context.borrow().is_some() {
|
||||||
self.frp.frame_time_source.emit(t.local);
|
debug!(self.logger, "Updating.", || {
|
||||||
// Please note that `update_camera` is called first as it may trigger FRP events which
|
self.frp.frame_time_source.emit(t.local);
|
||||||
// may change display objects layout.
|
// Please note that `update_camera` is called first as it may trigger FRP events
|
||||||
self.update_camera(self);
|
// which may change display objects layout.
|
||||||
self.display_object.update(self);
|
self.update_camera(self);
|
||||||
self.layers.update();
|
self.display_object.update(self);
|
||||||
self.update_shape();
|
self.layers.update();
|
||||||
self.update_symbols();
|
self.update_shape();
|
||||||
self.handle_mouse_events();
|
self.update_symbols();
|
||||||
})
|
self.handle_mouse_events();
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1109,3 +1174,46 @@ impl display::Object for Scene {
|
|||||||
&self.display_object
|
&self.display_object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ===============
|
||||||
|
// === DomPath ===
|
||||||
|
// ===============
|
||||||
|
|
||||||
|
/// Abstraction for DOM path. It can be either a specific HTML element or a DOM id, a string used
|
||||||
|
/// to query the DOM structure to find the corresponding HTML node.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub trait DomPath {
|
||||||
|
fn try_into_dom_element(self) -> Option<HtmlElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DomPath for HtmlElement {
|
||||||
|
fn try_into_dom_element(self) -> Option<HtmlElement> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t> DomPath for &'t HtmlElement {
|
||||||
|
fn try_into_dom_element(self) -> Option<HtmlElement> {
|
||||||
|
Some(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DomPath for String {
|
||||||
|
fn try_into_dom_element(self) -> Option<HtmlElement> {
|
||||||
|
web::document.get_html_element_by_id(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t> DomPath for &'t String {
|
||||||
|
fn try_into_dom_element(self) -> Option<HtmlElement> {
|
||||||
|
web::document.get_html_element_by_id(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t> DomPath for &'t str {
|
||||||
|
fn try_into_dom_element(self) -> Option<HtmlElement> {
|
||||||
|
web::document.get_html_element_by_id(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! This module defines a DOM management utilities.
|
//! This module defines a DOM management utilities.
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use web::traits::*;
|
||||||
|
|
||||||
use crate::display::camera::camera2d::Projection;
|
use crate::display::camera::camera2d::Projection;
|
||||||
use crate::display::camera::Camera2d;
|
use crate::display::camera::Camera2d;
|
||||||
@ -8,13 +9,13 @@ use crate::display::object::traits::*;
|
|||||||
use crate::display::symbol::dom::eps;
|
use crate::display::symbol::dom::eps;
|
||||||
use crate::display::symbol::dom::inverse_y_translation;
|
use crate::display::symbol::dom::inverse_y_translation;
|
||||||
use crate::display::symbol::DomSymbol;
|
use crate::display::symbol::DomSymbol;
|
||||||
use crate::system::gpu::data::JsBufferView;
|
|
||||||
use crate::system::web;
|
use crate::system::web;
|
||||||
use crate::system::web::NodeInserter;
|
use web::HtmlDivElement;
|
||||||
use crate::system::web::StyleSetter;
|
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use crate::system::gpu::data::JsBufferView;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
use web_sys::HtmlDivElement;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ use web_sys::HtmlDivElement;
|
|||||||
// === Js Bindings ===
|
// === Js Bindings ===
|
||||||
// ===================
|
// ===================
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
mod js {
|
mod js {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[wasm_bindgen(inline_js = "
|
#[wasm_bindgen(inline_js = "
|
||||||
@ -64,6 +66,8 @@ mod js {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn setup_camera_perspective(dom: &web::JsValue, near: f32, matrix: &Matrix4<f32>) {
|
fn setup_camera_perspective(dom: &web::JsValue, near: f32, matrix: &Matrix4<f32>) {
|
||||||
// Views to WASM memory are only valid as long the backing buffer isn't
|
// Views to WASM memory are only valid as long the backing buffer isn't
|
||||||
@ -75,6 +79,7 @@ fn setup_camera_perspective(dom: &web::JsValue, near: f32, matrix: &Matrix4<f32>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn setup_camera_orthographic(dom: &web::JsValue, matrix: &Matrix4<f32>) {
|
fn setup_camera_orthographic(dom: &web::JsValue, matrix: &Matrix4<f32>) {
|
||||||
// Views to WASM memory are only valid as long the backing buffer isn't
|
// Views to WASM memory are only valid as long the backing buffer isn't
|
||||||
@ -86,6 +91,18 @@ fn setup_camera_orthographic(dom: &web::JsValue, matrix: &Matrix4<f32>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod js {
|
||||||
|
use super::*;
|
||||||
|
pub fn setup_perspective(_dom: &web::JsValue, _znear: &web::JsValue) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn setup_camera_perspective(_dom: &web::JsValue, _near: f32, _matrix: &Matrix4<f32>) {}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn setup_camera_orthographic(_dom: &web::JsValue, _matrix: &Matrix4<f32>) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =============
|
// =============
|
||||||
@ -149,27 +166,27 @@ impl DomScene {
|
|||||||
/// Constructor.
|
/// Constructor.
|
||||||
pub fn new(logger: impl AnyLogger) -> Self {
|
pub fn new(logger: impl AnyLogger) -> Self {
|
||||||
let logger = Logger::new_sub(logger, "DomScene");
|
let logger = Logger::new_sub(logger, "DomScene");
|
||||||
let dom = web::create_div();
|
let dom = web::document.create_div_or_panic();
|
||||||
let view_projection_dom = web::create_div();
|
let view_projection_dom = web::document.create_div_or_panic();
|
||||||
|
|
||||||
dom.set_class_name("dom-scene-layer");
|
dom.set_class_name("dom-scene-layer");
|
||||||
// z-index works on positioned elements only.
|
// z-index works on positioned elements only.
|
||||||
dom.set_style_or_warn("position", "absolute", &logger);
|
dom.set_style_or_warn("position", "absolute");
|
||||||
dom.set_style_or_warn("top", "0px", &logger);
|
dom.set_style_or_warn("top", "0px");
|
||||||
dom.set_style_or_warn("overflow", "hidden", &logger);
|
dom.set_style_or_warn("overflow", "hidden");
|
||||||
dom.set_style_or_warn("overflow", "hidden", &logger);
|
dom.set_style_or_warn("overflow", "hidden");
|
||||||
dom.set_style_or_warn("width", "100%", &logger);
|
dom.set_style_or_warn("width", "100%");
|
||||||
dom.set_style_or_warn("height", "100%", &logger);
|
dom.set_style_or_warn("height", "100%");
|
||||||
// We ignore pointer events to avoid stealing them from other DomScenes.
|
// We ignore pointer events to avoid stealing them from other DomScenes.
|
||||||
// See https://github.com/enso-org/enso/blob/develop/lib/rust/ensogl/doc/mouse-handling.md.
|
// See https://github.com/enso-org/enso/blob/develop/lib/rust/ensogl/doc/mouse-handling.md.
|
||||||
dom.set_style_or_warn("pointer-events", "none", &logger);
|
dom.set_style_or_warn("pointer-events", "none");
|
||||||
|
|
||||||
view_projection_dom.set_class_name("view_projection");
|
view_projection_dom.set_class_name("view_projection");
|
||||||
view_projection_dom.set_style_or_warn("width", "100%", &logger);
|
view_projection_dom.set_style_or_warn("width", "100%");
|
||||||
view_projection_dom.set_style_or_warn("height", "100%", &logger);
|
view_projection_dom.set_style_or_warn("height", "100%");
|
||||||
view_projection_dom.set_style_or_warn("transform-style", "preserve-3d", &logger);
|
view_projection_dom.set_style_or_warn("transform-style", "preserve-3d");
|
||||||
|
|
||||||
dom.append_or_warn(&view_projection_dom, &logger);
|
dom.append_or_warn(&view_projection_dom);
|
||||||
|
|
||||||
let data = DomSceneData::new(dom, view_projection_dom, logger);
|
let data = DomSceneData::new(dom, view_projection_dom, logger);
|
||||||
let data = Rc::new(data);
|
let data = Rc::new(data);
|
||||||
@ -183,13 +200,13 @@ impl DomScene {
|
|||||||
|
|
||||||
/// Sets the z-index of this DOM element.
|
/// Sets the z-index of this DOM element.
|
||||||
pub fn set_z_index(&self, z: i32) {
|
pub fn set_z_index(&self, z: i32) {
|
||||||
self.data.dom.set_style_or_warn("z-index", z.to_string(), &self.logger);
|
self.data.dom.set_style_or_warn("z-index", z.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the CSS property `filter: grayscale({value})` on this element. A value of 0.0 displays
|
/// Sets the CSS property `filter: grayscale({value})` on this element. A value of 0.0 displays
|
||||||
/// the element normally. A value of 1.0 will make the element completely gray.
|
/// the element normally. A value of 1.0 will make the element completely gray.
|
||||||
pub fn filter_grayscale(&self, value: f32) {
|
pub fn filter_grayscale(&self, value: f32) {
|
||||||
self.data.dom.set_style_or_warn("filter", format!("grayscale({})", value), &self.logger);
|
self.data.dom.set_style_or_warn("filter", format!("grayscale({})", value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new instance of DomSymbol and adds it to parent.
|
/// Creates a new instance of DomSymbol and adds it to parent.
|
||||||
@ -197,11 +214,11 @@ impl DomScene {
|
|||||||
let dom = object.dom();
|
let dom = object.dom();
|
||||||
let data = &self.data;
|
let data = &self.data;
|
||||||
if object.is_visible() {
|
if object.is_visible() {
|
||||||
self.view_projection_dom.append_or_panic(dom);
|
self.view_projection_dom.append_or_warn(dom);
|
||||||
}
|
}
|
||||||
object.display_object().set_on_hide(f_!(dom.remove()));
|
object.display_object().set_on_hide(f_!(dom.remove()));
|
||||||
object.display_object().set_on_show(f__!([data,dom] {
|
object.display_object().set_on_show(f__!([data,dom] {
|
||||||
data.view_projection_dom.append_or_panic(&dom)
|
data.view_projection_dom.append_or_warn(&dom)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,33 @@
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[wasm_bindgen(module = "/src/display/shape/primitive/glsl/overload.js")]
|
mod js {
|
||||||
extern "C" {
|
use super::*;
|
||||||
/// Returns GLSL code which redirects mangled function names to their original primitive
|
#[wasm_bindgen(module = "/src/display/shape/primitive/glsl/overload.js")]
|
||||||
/// definitions.
|
extern "C" {
|
||||||
#[allow(unsafe_code)]
|
/// Returns GLSL code which redirects mangled function names to their original primitive
|
||||||
pub fn builtin_redirections() -> String;
|
/// definitions.
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn builtin_redirections() -> String;
|
||||||
|
|
||||||
/// Mangles the provided GLSL code to allow primitive definitions overloading.
|
/// Mangles the provided GLSL code to allow primitive definitions overloading.
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub fn allow_overloading(s: &str) -> String;
|
pub fn allow_overloading(s: &str) -> String;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
mod mock {
|
||||||
|
pub fn builtin_redirections() -> String {
|
||||||
|
"".into()
|
||||||
|
}
|
||||||
|
pub fn allow_overloading(_: &str) -> String {
|
||||||
|
"".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub use js::*;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub use mock::*;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user