diff --git a/Cargo.lock b/Cargo.lock index 159e0b53123..0865c57d8fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1354,16 +1354,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ensogl-example-custom-shape-system" -version = "0.1.0" -dependencies = [ - "enso-frp", - "enso-profiler", - "ensogl-core", - "wasm-bindgen", -] - [[package]] name = "ensogl-example-dom-symbols" version = "0.1.0" @@ -1501,7 +1491,6 @@ dependencies = [ name = "ensogl-example-shape-system" version = "0.1.0" dependencies = [ - "enso-profiler", "ensogl-core", "wasm-bindgen", ] @@ -1552,7 +1541,6 @@ version = "0.1.0" dependencies = [ "ensogl-example-animation", "ensogl-example-complex-shape-system", - "ensogl-example-custom-shape-system", "ensogl-example-dom-symbols", "ensogl-example-drop-manager", "ensogl-example-easing-animator", diff --git a/app/gui/view/graph-editor/src/component/breadcrumbs/project_name.rs b/app/gui/view/graph-editor/src/component/breadcrumbs/project_name.rs index be68ba44348..16c520bcbd9 100644 --- a/app/gui/view/graph-editor/src/component/breadcrumbs/project_name.rs +++ b/app/gui/view/graph-editor/src/component/breadcrumbs/project_name.rs @@ -265,10 +265,9 @@ impl ProjectName { // === Mouse IO === - mouse_down <- model.view.events.mouse_down.constant(()); frp.source.is_hovered <+ bool(&model.view.events.mouse_out, &model.view.events.mouse_over); - frp.source.mouse_down <+ model.view.events.mouse_down.constant(()); + frp.source.mouse_down <+ model.view.events.mouse_down; not_selected <- frp.output.selected.map(|selected| !selected); mouse_over_if_not_selected <- model.view.events.mouse_over.gate(¬_selected); @@ -281,7 +280,7 @@ impl ProjectName { ); on_deselect <- not_selected.gate(¬_selected).constant(()); - edit_click <- mouse_down.gate(&frp.ide_text_edit_mode); + edit_click <- model.view.events.mouse_down.gate(&frp.ide_text_edit_mode); start_editing <- any(edit_click,frp.input.start_editing); eval_ start_editing ({ text.set_focus(true); diff --git a/app/gui/view/graph-editor/src/component/edge.rs b/app/gui/view/graph-editor/src/component/edge.rs index 9b0d15e8e05..562cf18b286 100644 --- a/app/gui/view/graph-editor/src/component/edge.rs +++ b/app/gui/view/graph-editor/src/component/edge.rs @@ -12,7 +12,7 @@ use ensogl::application::Application; use ensogl::data::color; use ensogl::display; use ensogl::display::scene::Scene; -use ensogl::gui::component::PointerTarget; +use ensogl::gui::component::ShapeViewEvents; use ensogl_hardcoded_theme as theme; use nalgebra::Rotation2; @@ -74,7 +74,7 @@ trait EdgeShape: display::Object { fn id(&self) -> display::object::Id { self.display_object().id() } - fn events(&self) -> &PointerTarget; + fn events(&self) -> &ShapeViewEvents; fn set_color(&self, color: color::Rgba); fn set_color_focus(&self, color: color::Rgba); @@ -172,8 +172,8 @@ trait AnyEdgeShape { /// Return references to all `EdgeShape`s in this `AnyEdgeShape`. fn shapes(&self) -> Vec<&dyn EdgeShape>; - /// Connect the given `PointerTargetProxy` to the mouse events of all sub-shapes. - fn register_proxy_frp(&self, network: &frp::Network, frp: &PointerTargetProxy) { + /// Connect the given `ShapeViewEventsProxy` to the mouse events of all sub-shapes. + fn register_proxy_frp(&self, network: &frp::Network, frp: &ShapeViewEventsProxy) { for shape in &self.shapes() { let event = shape.events(); let id = shape.id(); @@ -374,7 +374,7 @@ macro_rules! define_corner_start { self.focus_split_angle.set(angle); } - fn events(&self) -> &PointerTarget { + fn events(&self) -> &ShapeViewEvents { &self.events } @@ -469,7 +469,7 @@ macro_rules! define_corner_end { self.focus_split_angle.set(angle); } - fn events(&self) -> &PointerTarget { + fn events(&self) -> &ShapeViewEvents { &self.events } @@ -549,7 +549,7 @@ macro_rules! define_line { self.focus_split_angle.set(angle); } - fn events(&self) -> &PointerTarget { + fn events(&self) -> &ShapeViewEvents { &self.events } @@ -621,7 +621,7 @@ macro_rules! define_arrow { () => { self.focus_split_angle.set(angle); } - fn events(&self) -> &PointerTarget { + fn events(&self) -> &ShapeViewEvents { &self.events } @@ -752,7 +752,7 @@ macro_rules! define_components { pub struct $name { pub logger : Logger, pub display_object : display::object::Instance, - pub shape_view_events : Rc>, + pub shape_view_events : Rc>, shape_type_map : Rc>, $(pub $field : $field_type),* } @@ -764,7 +764,7 @@ macro_rules! define_components { let display_object = display::object::Instance::new(&logger); $(let $field = <$field_type>::new(Logger::new_sub(&logger,stringify!($field)));)* $(display_object.add_child(&$field);)* - let mut shape_view_events:Vec = Vec::default(); + let mut shape_view_events:Vec = Vec::default(); $(shape_view_events.push($field.events.clone_ref());)* let shape_view_events = Rc::new(shape_view_events); @@ -1021,7 +1021,7 @@ impl SemanticSplit { /// emit events via th internal `on_mouse_down`/`on_mouse_over`/`on_mouse_out` sources. #[derive(Clone, CloneRef, Debug)] #[allow(missing_docs)] -pub struct PointerTargetProxy { +pub struct ShapeViewEventsProxy { pub mouse_down: frp::Stream, pub mouse_over: frp::Stream, pub mouse_out: frp::Stream, @@ -1032,7 +1032,7 @@ pub struct PointerTargetProxy { } #[allow(missing_docs)] -impl PointerTargetProxy { +impl ShapeViewEventsProxy { pub fn new(network: &frp::Network) -> Self { frp::extend! { network on_mouse_over <- source(); @@ -1063,7 +1063,7 @@ pub struct Frp { pub set_color: frp::Source, pub hover_position: frp::Source>>, - pub shape_events: PointerTargetProxy, + pub shape_events: ShapeViewEventsProxy, } impl Frp { @@ -1080,7 +1080,7 @@ impl Frp { def set_disabled = source(); def set_color = source(); } - let shape_events = PointerTargetProxy::new(network); + let shape_events = ShapeViewEventsProxy::new(network); Self { source_width, source_height, diff --git a/app/gui/view/graph-editor/src/component/node.rs b/app/gui/view/graph-editor/src/component/node.rs index ad5338eec41..c46fbc55ab4 100644 --- a/app/gui/view/graph-editor/src/component/node.rs +++ b/app/gui/view/graph-editor/src/component/node.rs @@ -696,7 +696,7 @@ impl Node { // === Background Press === - out.background_press <+ model.drag_area.events.mouse_down.constant(()); + out.background_press <+ model.drag_area.events.mouse_down; out.background_press <+ model.input.on_background_press; diff --git a/app/gui/view/graph-editor/src/component/node/growth_animation.rs b/app/gui/view/graph-editor/src/component/node/growth_animation.rs index ad849597d2d..d804558b715 100644 --- a/app/gui/view/graph-editor/src/component/node/growth_animation.rs +++ b/app/gui/view/graph-editor/src/component/node/growth_animation.rs @@ -9,15 +9,12 @@ use ensogl::prelude::*; use crate::GraphEditorModelWithNetwork; use crate::NodeId; - use enso_frp as frp; use ensogl::animation::easing::EndStatus::Normal; use ensogl::display::Scene; use ensogl::Animation; use ensogl::Easing; - - /// Describes the "speed" of growth/shrink animation. /// /// To determine the duration of the blending animation, we divide the length of the camera path by diff --git a/app/gui/view/graph-editor/src/component/node/input/area.rs b/app/gui/view/graph-editor/src/component/node/input/area.rs index 10a98cb12ad..37e4c103b54 100644 --- a/app/gui/view/graph-editor/src/component/node/input/area.rs +++ b/app/gui/view/graph-editor/src/component/node/input/area.rs @@ -635,7 +635,7 @@ impl Area { let mouse_over_raw = port_shape.hover.events.mouse_over.clone_ref(); let mouse_out = port_shape.hover.events.mouse_out.clone_ref(); - mouse_down_raw <- port_shape.hover.events.mouse_down.constant(()); + let mouse_down_raw = port_shape.hover.events.mouse_down.clone_ref(); // === Body Hover === diff --git a/app/gui/view/graph-editor/src/component/node/output/port.rs b/app/gui/view/graph-editor/src/component/node/output/port.rs index ab7d65418f5..fdc69520082 100644 --- a/app/gui/view/graph-editor/src/component/node/output/port.rs +++ b/app/gui/view/graph-editor/src/component/node/output/port.rs @@ -403,7 +403,7 @@ impl PortShapeView { set_padding_right (this,t:f32) { this.padding_right.set(t) } } - fn events(&self) -> &component::PointerTarget { + fn events(&self) -> &component::ShapeViewEvents { match self { Self::Single(t) => &t.events, Self::Multi(t) => &t.events, @@ -525,7 +525,7 @@ impl Model { // === Mouse Event Handling === frp.source.on_hover <+ bool(&events.mouse_out,&events.mouse_over); - frp.source.on_press <+ events.mouse_down.constant(()); + frp.source.on_press <+ events.mouse_down; // === Opacity === diff --git a/app/gui/view/graph-editor/src/component/visualization/container.rs b/app/gui/view/graph-editor/src/component/visualization/container.rs index 73b0424e69a..7331cd60ed7 100644 --- a/app/gui/view/graph-editor/src/component/visualization/container.rs +++ b/app/gui/view/graph-editor/src/component/visualization/container.rs @@ -443,7 +443,7 @@ impl ContainerModel { } /// Check if given mouse-event-target means this visualization. - fn is_this_target(&self, target: scene::PointerTargetId) -> bool { + fn is_this_target(&self, target: scene::PointerTarget) -> bool { self.view.overlay.is_this_target(target) } diff --git a/app/gui/view/graph-editor/src/component/visualization/container/action_bar.rs b/app/gui/view/graph-editor/src/component/visualization/container/action_bar.rs index 19c303cbd5e..27cf2bd2c01 100644 --- a/app/gui/view/graph-editor/src/component/visualization/container/action_bar.rs +++ b/app/gui/view/graph-editor/src/component/visualization/container/action_bar.rs @@ -394,16 +394,15 @@ impl ActionBar { frp.source.visualisation_selection <+ visualization_chooser.chosen_entry; let reset_position_icon = &model.icons.reset_position_icon.events; - reset_position_icon_down <- reset_position_icon.mouse_down.constant(()); - frp.source.on_container_reset_position <+ reset_position_icon_down; + frp.source.on_container_reset_position <+ reset_position_icon.mouse_down; let drag_icon = &model.icons.drag_icon.events; - start_dragging <- drag_icon.mouse_down.constant(()); + let start_dragging = drag_icon.mouse_down.clone_ref(); end_dragging <- mouse.up.gate(&frp.source.container_drag_state); should_drag <- bool(&end_dragging,&start_dragging); frp.source.container_drag_state <+ should_drag; - show_reset_icon <- bool(&reset_position_icon_down,&start_dragging); + show_reset_icon <- bool(&reset_position_icon.mouse_down,&start_dragging); eval show_reset_icon((visibility) model.icons.set_reset_icon_visibility(*visibility)); } self diff --git a/app/gui/view/graph-editor/src/lib.rs b/app/gui/view/graph-editor/src/lib.rs index eece12c27d0..8bdfeaee444 100644 --- a/app/gui/view/graph-editor/src/lib.rs +++ b/app/gui/view/graph-editor/src/lib.rs @@ -2623,14 +2623,33 @@ fn new_graph_editor(app: &Application) -> GraphEditor { // === Selection Target Redirection === frp::extend! { network - let scene = model.scene(); - - mouse_up_target <- mouse.up_primary.map(f_!(model.scene().mouse.target.get())); - background_up <= mouse_up_target.map( - |t| (t==&display::scene::PointerTargetId::Background).as_some(()) + mouse_down_target <- mouse.down_primary.map(f_!(model.scene().mouse.target.get())); + mouse_up_target <- mouse.up_primary.map(f_!(model.scene().mouse.target.get())); + background_up <= mouse_up_target.map( + |t| (t==&display::scene::PointerTarget::Background).as_some(()) ); - eval_ scene.background.mouse_down (touch.background.down.emit(())); + eval mouse_down_target([touch,model](target) { + match target { + display::scene::PointerTarget::Background => touch.background.down.emit(()), + display::scene::PointerTarget::Symbol {..} => { + if let Some(target) = model.scene().shapes.get_mouse_target(*target) { + target.mouse_down().emit(()); + } + } + } + }); + + eval mouse_up_target([model](target) { + match target { + display::scene::PointerTarget::Background => {} // touch.background.up.emit(()), + display::scene::PointerTarget::Symbol {..} => { + if let Some(target) = model.scene().shapes.get_mouse_target(*target) { + target.mouse_up().emit(()); + } + } + } + }); } diff --git a/lib/rust/ensogl/component/selector/src/shape.rs b/lib/rust/ensogl/component/selector/src/shape.rs index ae0fe249203..9ba60bad83d 100644 --- a/lib/rust/ensogl/component/selector/src/shape.rs +++ b/lib/rust/ensogl/component/selector/src/shape.rs @@ -195,8 +195,8 @@ pub mod right_overflow { use enso_frp::Network; use ensogl_core::frp::io::Mouse; -use ensogl_core::gui::component::PointerTarget; use ensogl_core::gui::component::ShapeView; +use ensogl_core::gui::component::ShapeViewEvents; pub use super::frp::*; pub use super::model::*; @@ -208,7 +208,7 @@ use ensogl_core::display::Scene; /// Dragging is ended by a mouse up. pub fn shape_is_dragged( network: &Network, - shape: &PointerTarget, + shape: &ShapeViewEvents, mouse: &Mouse, ) -> enso_frp::Stream { enso_frp::extend! { network @@ -259,7 +259,7 @@ mod tests { fn test_shape_is_dragged() { let network = enso_frp::Network::new("TestNetwork"); let mouse = enso_frp::io::Mouse::default(); - let shape = PointerTarget::default(); + let shape = ShapeViewEvents::default(); let is_dragged = shape_is_dragged(&network, &shape, &mouse); let _watch = is_dragged.register_watch(); diff --git a/lib/rust/ensogl/component/toggle-button/src/lib.rs b/lib/rust/ensogl/component/toggle-button/src/lib.rs index 9f84f944a77..6eeaeaef6a3 100644 --- a/lib/rust/ensogl/component/toggle-button/src/lib.rs +++ b/lib/rust/ensogl/component/toggle-button/src/lib.rs @@ -230,7 +230,7 @@ impl ToggleButton { // === State === - toggle <- any_(frp.toggle,icon.mouse_down); + toggle <- any(frp.toggle,icon.mouse_down); frp.source.state <+ frp.state.not().sample(&toggle); frp.source.state <+ frp.set_state; diff --git a/lib/rust/ensogl/core/src/debug/stats.rs b/lib/rust/ensogl/core/src/debug/stats.rs index 1e95aa8809d..4e5e43acc4d 100644 --- a/lib/rust/ensogl/core/src/debug/stats.rs +++ b/lib/rust/ensogl/core/src/debug/stats.rs @@ -185,16 +185,15 @@ macro_rules! gen_stats { self.[](value); } - // FIXME: saturating_add is proper solution, but even without it it should not crash, but it does. To be investigated. emit_if_integer!($field_type, /// Increments field's value. pub fn [](&self) { - self.[](|t| t.saturating_add(1)); + self.[](|t| t + 1); } /// Decrements field's value. pub fn [](&self) { - self.[](|t| t.saturating_sub(1)); + self.[](|t| t - 1); } ); diff --git a/lib/rust/ensogl/core/src/display/object/class.rs b/lib/rust/ensogl/core/src/display/object/class.rs index 289d1629458..718517631f4 100644 --- a/lib/rust/ensogl/core/src/display/object/class.rs +++ b/lib/rust/ensogl/core/src/display/object/class.rs @@ -1188,33 +1188,6 @@ impl Object for Any { -// ========================= -// === UnsetParentOnDrop === -// ========================= - -/// Wrapper that unsets parent of a display object when dropped. Please note that [`Instance`] -/// implements [`CloneRef`], so it can still be alive even if this struct is dropped. -#[derive(Debug, NoCloneBecauseOfCustomDrop)] -pub struct UnsetParentOnDrop { - instance: Instance, -} - -impl UnsetParentOnDrop { - /// Constructor. - pub fn new(instance: impl Into) -> Self { - let instance = instance.into(); - Self { instance } - } -} - -impl Drop for UnsetParentOnDrop { - fn drop(&mut self) { - self.instance.unset_parent() - } -} - - - // ============= // === Tests === // ============= diff --git a/lib/rust/ensogl/core/src/display/scene.rs b/lib/rust/ensogl/core/src/display/scene.rs index b79300099b3..0e099d03c07 100644 --- a/lib/rust/ensogl/core/src/display/scene.rs +++ b/lib/rust/ensogl/core/src/display/scene.rs @@ -22,7 +22,9 @@ use crate::display::style; use crate::display::style::data::DataMatch; use crate::display::symbol::registry::SymbolRegistry; use crate::display::symbol::Symbol; +use crate::display::symbol::SymbolId; use crate::system; +use crate::system::gpu::data::attribute; use crate::system::gpu::data::uniform::Uniform; use crate::system::gpu::data::uniform::UniformScope; use crate::system::web; @@ -37,7 +39,6 @@ use std::any::TypeId; use web::HtmlElement; - // ============== // === Export === // ============== @@ -46,13 +47,18 @@ use web::HtmlElement; pub mod dom; #[warn(missing_docs)] pub mod layer; -#[warn(missing_docs)] -pub mod pointer_target; pub use crate::system::web::dom::Shape; pub use layer::Layer; -pub use pointer_target::PointerTarget; -pub use pointer_target::PointerTargetId; + + + +pub trait MouseTarget: Debug + 'static { + fn mouse_down(&self) -> &frp::Source; + fn mouse_up(&self) -> &frp::Source; + fn mouse_over(&self) -> &frp::Source; + fn mouse_out(&self) -> &frp::Source; +} @@ -61,24 +67,17 @@ pub use pointer_target::PointerTargetId; // ===================== shared! { ShapeRegistry -#[derive(Debug)] +#[derive(Debug,Default)] pub struct ShapeRegistryData { // FIXME[WD]: The only valid field here is the `mouse_target_map`. The rest should be removed // after proper implementation of text depth sorting, which is the only component // using the obsolete fields now. scene : Option, shape_system_map : HashMap>, - mouse_target_map : HashMap, + mouse_target_map : HashMap<(SymbolId,attribute::InstanceIndex),Rc>, } impl { - fn new(background: &PointerTarget) -> Self { - let scene = default(); - let shape_system_map = default(); - let mouse_target_map = default(); - Self {scene, shape_system_map, mouse_target_map} . init(background) - } - fn get(&self) -> Option { let id = TypeId::of::(); self.shape_system_map.get(&id).and_then(|t| t.downcast_ref::()).map(|t| t.clone_ref()) @@ -106,30 +105,198 @@ impl { system.new_instance() } - pub fn insert_mouse_target - (&mut self, id:impl Into, target:impl Into) { - self.mouse_target_map.insert(id.into(),target.into()); + pub fn insert_mouse_target + (&mut self, symbol_id:SymbolId, instance_id:attribute::InstanceIndex, target:T) { + let target = Rc::new(target); + self.mouse_target_map.insert((symbol_id,instance_id),target); } pub fn remove_mouse_target - (&mut self, id:impl Into) { - self.mouse_target_map.remove(&id.into()); + (&mut self, symbol_id:SymbolId, instance_id:attribute::InstanceIndex) { + self.mouse_target_map.remove(&(symbol_id,instance_id)); } - pub fn get_mouse_target(&self, target:PointerTargetId) -> Option { - self.mouse_target_map.get(&target).cloned() - } - - pub fn with_mouse_target - (&self, target:PointerTargetId, f: impl FnOnce(&PointerTarget) -> T) -> Option { - self.mouse_target_map.get(&target).as_ref().map(|t| f(t)) + pub fn get_mouse_target(&mut self, target:PointerTarget) -> Option> { + match target { + PointerTarget::Background => None, + PointerTarget::Symbol {symbol_id,instance_id} => { + self.mouse_target_map.get(&(symbol_id,instance_id)).cloned() + } + } } }} -impl ShapeRegistryData { - fn init(mut self, background: &PointerTarget) -> Self { - self.mouse_target_map.insert(PointerTargetId::Background, background.clone_ref()); - self + + +// ============== +// === Target === +// ============== + +/// Result of a Decoding operation in the Target. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +enum DecodingResult { + /// Values had to be truncated. + Truncated(u8, u8, u8), + /// Values have been encoded successfully. + Ok(u8, u8, u8), +} + +/// Mouse target. Contains a path to an object pointed by mouse. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum PointerTarget { + Background, + Symbol { symbol_id: SymbolId, instance_id: attribute::InstanceIndex }, +} + +impl PointerTarget { + /// Encode two u32 values into three u8 values. + /// + /// This is the same encoding that is used in the `fragment_runner`. This encoding is lossy and + /// can only encode values up to 4096 (2^12) each. + /// + /// We use 12 bits from each value and pack them into the 3 output bytes like described in the + /// following diagram. + /// + /// ```text + /// Input + /// + /// value1 (v1) as bytes value2 (v2) as bytes + /// +-----+-----+-----+-----+ +-----+-----+-----+-----+ + /// | | | | | | | | | | + /// +-----+-----+-----+-----+ +-----+-----+-----+-----+ + /// 32 24 16 8 0 32 24 16 8 0 <- Bit index + /// + /// + /// Output + /// + /// byte1 byte2 byte3 + /// +-----------+ +----------------------+ +------------+ + /// | v ]12..4] | | v1 ]4..0] v2 ]4..0] | | v2 ]12..4] | + /// +-----------+ +----------------------+ +------------+ + /// + /// Ranges use mathematical notation for inclusion/exclusion. + /// ``` + fn encode(value1: u32, value2: u32) -> DecodingResult { + let chunk1 = (value1 >> 4u32) & 0x00FFu32; + let chunk2 = (value1 & 0x000Fu32) << 4u32; + let chunk2 = chunk2 | ((value2 & 0x0F00u32) >> 8u32); + let chunk3 = value2 & 0x00FFu32; + + if value1 > 2u32.pow(12) || value2 > 2u32.pow(12) { + DecodingResult::Truncated(chunk1 as u8, chunk2 as u8, chunk3 as u8) + } else { + DecodingResult::Ok(chunk1 as u8, chunk2 as u8, chunk3 as u8) + } + } + + /// Decode the symbol_id and instance_id that was encoded in the `fragment_runner`. + /// + /// See the `encode` method for more information on the encoding. + fn decode(chunk1: u32, chunk2: u32, chunk3: u32) -> (u32, u32) { + let value1 = (chunk1 << 4) + (chunk2 >> 4); + let value2 = chunk3 + ((chunk2 & 0x000F) << 8); + (value1, value2) + } + + fn to_internal(self, logger: &Logger) -> Vector4 { + match self { + Self::Background => Vector4::new(0, 0, 0, 0), + Self::Symbol { symbol_id, instance_id } => { + match Self::encode(*symbol_id, (*instance_id) as u32) { + DecodingResult::Truncated(pack0, pack1, pack2) => { + warning!( + logger, + "Target values too big to encode: \ + ({symbol_id},{instance_id})." + ); + Vector4::new(pack0.into(), pack1.into(), pack2.into(), 1) + } + DecodingResult::Ok(pack0, pack1, pack2) => + Vector4::new(pack0.into(), pack1.into(), pack2.into(), 1), + } + } + } + } + + fn from_internal(v: Vector4) -> Self { + if v.w == 0 { + Self::Background + } else if v.w == 255 { + let decoded = Self::decode(v.x, v.y, v.z); + let symbol_id = SymbolId::new(decoded.0); + let instance_id = attribute::InstanceIndex::new(decoded.1 as usize); + Self::Symbol { symbol_id, instance_id } + } else { + panic!("Wrong internal format alpha for mouse target.") + } + } + + pub fn is_background(self) -> bool { + self == Self::Background + } + + pub fn is_symbol(self) -> bool { + !self.is_background() + } +} + +impl Default for PointerTarget { + fn default() -> Self { + Self::Background + } +} + + +// === Target Tests === + +#[cfg(test)] +mod target_tests { + use super::*; + + /// Asserts that decoding encoded the given values returns the correct initial values again. + /// That means that `decode(encode(value1,value2)) == (value1,value2)`. + fn assert_valid_roundtrip(value1: u32, value2: u32) { + let pack = PointerTarget::encode(value1, value2); + match pack { + DecodingResult::Truncated { .. } => { + panic!("Values got truncated. This is an invalid test case: {}, {}", value1, value1) + } + DecodingResult::Ok(pack0, pack1, pack2) => { + let unpack = PointerTarget::decode(pack0.into(), pack1.into(), pack2.into()); + assert_eq!(unpack.0, value1); + assert_eq!(unpack.1, value2); + } + } + } + + #[test] + fn test_roundtrip_coding() { + assert_valid_roundtrip(0, 0); + assert_valid_roundtrip(0, 5); + assert_valid_roundtrip(512, 0); + assert_valid_roundtrip(1024, 64); + assert_valid_roundtrip(1024, 999); + } + + #[test] + fn test_encoding() { + let pack = PointerTarget::encode(0, 0); + assert_eq!(pack, DecodingResult::Ok(0, 0, 0)); + + let pack = PointerTarget::encode(3, 7); + assert_eq!(pack, DecodingResult::Ok(0, 48, 7)); + + let pack = PointerTarget::encode(3, 256); + assert_eq!(pack, DecodingResult::Ok(0, 49, 0)); + + let pack = PointerTarget::encode(255, 356); + assert_eq!(pack, DecodingResult::Ok(15, 241, 100)); + + let pack = PointerTarget::encode(256, 356); + assert_eq!(pack, DecodingResult::Ok(16, 1, 100)); + + let pack = PointerTarget::encode(31256, 0); + assert_eq!(pack, DecodingResult::Truncated(161, 128, 0)); } } @@ -144,8 +311,8 @@ pub struct Mouse { pub mouse_manager: MouseManager, pub last_position: Rc>>, pub position: Uniform>, - pub hover_rgba: Uniform>, - pub target: Rc>, + pub hover_ids: Uniform>, + pub target: Rc>, pub handles: Rc<[callback::Handle; 4]>, pub frp: enso_frp::io::Mouse, pub scene_frp: Frp, @@ -161,10 +328,10 @@ impl Mouse { logger: Logger, ) -> Self { let scene_frp = scene_frp.clone_ref(); - let target = PointerTargetId::default(); + let target = PointerTarget::default(); let last_position = Rc::new(Cell::new(Vector2::new(0, 0))); - let position = variables.add_or_panic("mouse_position", Vector2(0, 0)); - let hover_rgba = variables.add_or_panic("mouse_hover_ids", Vector4(0, 0, 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 target = Rc::new(Cell::new(target)); let mouse_manager = MouseManager::new_separated(&root.clone_ref().into(), &web::window); let frp = frp::io::Mouse::new(); @@ -203,7 +370,7 @@ impl Mouse { mouse_manager, last_position, position, - hover_rgba, + hover_ids, target, handles, frp, @@ -215,12 +382,12 @@ impl Mouse { /// Re-emits FRP mouse changed position event with the last mouse position value. /// /// The immediate question that appears is why it is even needed. The reason is tightly coupled - /// with how the rendering engine works, and it is important to understand it properly. When + /// with how the rendering engine works and it is important to understand it properly. When /// moving a mouse the following events happen: /// - `MouseManager` gets notification and fires callbacks. /// - Callback above is run. The value of `screen_position` uniform changes and FRP events are /// emitted. - /// - FRP events propagate through the whole system. + /// - FRP events propagate trough the whole system. /// - The rendering engine renders a frame and waits for the pixel read pass to report symbol ID /// under the cursor. This is normally done the next frame but sometimes could take even few /// frames. @@ -705,7 +872,6 @@ pub struct SceneData { pub mouse: Mouse, pub keyboard: Keyboard, pub uniforms: Uniforms, - pub background: PointerTarget, pub shapes: ShapeRegistry, pub stats: Stats, pub dirty: Dirty, @@ -739,8 +905,7 @@ impl SceneData { let symbols = SymbolRegistry::mk(&variables, stats, &logger, f!(symbols_dirty.set())); let layers = HardcodedLayers::new(&logger); let stats = stats.clone(); - let background = PointerTarget::new(); - let shapes = ShapeRegistry::new(&background); + let shapes = ShapeRegistry::default(); let uniforms = Uniforms::new(&variables); let renderer = Renderer::new(&logger, &dom, &variables); let style_sheet = style::Sheet::new(); @@ -780,7 +945,6 @@ impl SceneData { keyboard, uniforms, shapes, - background, stats, dirty, logger, @@ -793,12 +957,6 @@ impl SceneData { extensions, disable_context_menu, } - .init() - } - - fn init(self) -> Self { - self.init_mouse_down_and_up_events(); - self } pub fn set_context(&self, context: Option<&Context>) { @@ -825,6 +983,17 @@ impl SceneData { &self.symbols } + fn handle_mouse_events(&self) { + let new_target = PointerTarget::from_internal(self.mouse.hover_ids.get()); + let current_target = self.mouse.target.get(); + if new_target != current_target { + self.mouse.target.set(new_target); + self.shapes.get_mouse_target(current_target).for_each(|t| t.mouse_out().emit(())); + self.shapes.get_mouse_target(new_target).for_each(|t| t.mouse_over().emit(())); + self.mouse.re_emit_position_event(); // See docs to learn why. + } + } + fn update_shape(&self) { if self.dirty.shape.check_all() { let screen = self.dom.shape(); @@ -925,52 +1094,6 @@ impl SceneData { } } - -// === Mouse === - -impl SceneData { - /// Init handling of mouse up and down events. It is also responsible for discovering of the - /// mouse release events. To learn more see the documentation of [`PointerTarget`]. - fn init_mouse_down_and_up_events(&self) { - let network = &self.frp.network; - let shapes = &self.shapes; - let target = &self.mouse.target; - let pressed: Rc>> = default(); - - frp::extend! { network - eval self.mouse.frp.down ([shapes,target,pressed](button) { - let current_target = target.get(); - pressed.borrow_mut().insert(*button,current_target); - shapes.with_mouse_target(current_target, |t| t.mouse_down.emit(button)); - }); - eval self.mouse.frp.up ([shapes,target,pressed](button) { - let current_target = target.get(); - if let Some(last_target) = pressed.borrow_mut().remove(button) { - shapes.with_mouse_target(last_target, |t| t.mouse_release.emit(button)); - } - shapes.with_mouse_target(current_target, |t| t.mouse_up.emit(button)); - }); - } - } - - /// Discover what object the mouse pointer is on. - fn handle_mouse_over_and_out_events(&self) { - let opt_new_target = PointerTargetId::decode_from_rgba(self.mouse.hover_rgba.get()); - let new_target = opt_new_target.unwrap_or_else(|err| { - error!(self.logger, "{err}"); - default() - }); - let current_target = self.mouse.target.get(); - if new_target != current_target { - self.mouse.target.set(new_target); - self.shapes.get_mouse_target(current_target).for_each(|t| t.mouse_out.emit(())); - self.shapes.get_mouse_target(new_target).for_each(|t| t.mouse_over.emit(())); - self.mouse.re_emit_position_event(); // See docs to learn why. - } - } -} - - impl display::Object for SceneData { fn display_object(&self) -> &display::object::Instance { &self.display_object @@ -1070,7 +1193,7 @@ impl Scene { self.layers.update(); self.update_shape(); self.update_symbols(); - self.handle_mouse_over_and_out_events(); + self.handle_mouse_events(); }) } } diff --git a/lib/rust/ensogl/core/src/display/scene/layer.rs b/lib/rust/ensogl/core/src/display/scene/layer.rs index 6b2aab6c8be..e20e4d48cd6 100644 --- a/lib/rust/ensogl/core/src/display/scene/layer.rs +++ b/lib/rust/ensogl/core/src/display/scene/layer.rs @@ -13,8 +13,8 @@ use crate::display::shape::system::DynShapeSystemOf; use crate::display::shape::system::KnownShapeSystemId; use crate::display::shape::system::ShapeSystemId; use crate::display::shape::ShapeSystemInstance; -use crate::display::symbol; use crate::display::symbol::SymbolId; +use crate::system::gpu::data::attribute; use enso_data_structures::dependency_graph::DependencyGraph; use enso_shapely::shared; @@ -199,10 +199,10 @@ impl Layer { /// Instantiate the provided [`DynamicShape`]. pub fn instantiate(&self, scene: &Scene, shape: &T) -> LayerDynamicShapeInstance where T: display::shape::system::DynamicShape { - let (shape_system_info, symbol_id, global_instance_id) = + let (shape_system_info, symbol_id, instance_id) = self.shape_system_registry.instantiate(scene, shape); self.add_shape(shape_system_info, symbol_id); - LayerDynamicShapeInstance::new(self, global_instance_id) + LayerDynamicShapeInstance::new(self, symbol_id, instance_id) } /// Iterate over all layers and sublayers of this layer hierarchically. Parent layers will be @@ -790,15 +790,16 @@ impl std::borrow::Borrow for Layer { #[derive(Debug)] #[allow(missing_docs)] pub struct LayerDynamicShapeInstance { - pub layer: WeakLayer, - pub global_instance_id: symbol::GlobalInstanceId, + pub layer: WeakLayer, + pub symbol_id: SymbolId, + pub instance_id: attribute::InstanceIndex, } impl LayerDynamicShapeInstance { /// Constructor. - pub fn new(layer: &Layer, global_instance_id: symbol::GlobalInstanceId) -> Self { + pub fn new(layer: &Layer, symbol_id: SymbolId, instance_id: attribute::InstanceIndex) -> Self { let layer = layer.downgrade(); - Self { layer, global_instance_id } + Self { layer, symbol_id, instance_id } } } @@ -961,19 +962,19 @@ impl { /// Instantiate the provided [`DynamicShape`]. pub fn instantiate - (&mut self, scene:&Scene, shape:&T) -> (ShapeSystemInfo, SymbolId, symbol::GlobalInstanceId) + (&mut self, scene:&Scene, shape:&T) -> (ShapeSystemInfo,SymbolId,attribute::InstanceIndex) where T : display::shape::system::DynamicShape { self.with_get_or_register_mut::,_,_>(scene,|entry| { - let system = entry.shape_system; - let system_id = DynShapeSystemOf::::id(); - let global_instance_id = system.instantiate(shape); - let symbol_id = system.shape_system().sprite_system.symbol.id; - let above = DynShapeSystemOf::::above(); - let below = DynShapeSystemOf::::below(); - let ordering = ShapeSystemStaticDepthOrdering {above,below}; + let system = entry.shape_system; + let system_id = DynShapeSystemOf::::id(); + let instance_id = system.instantiate(shape); + let symbol_id = system.shape_system().sprite_system.symbol.id; + let above = DynShapeSystemOf::::above(); + let below = DynShapeSystemOf::::below(); + let ordering = ShapeSystemStaticDepthOrdering {above,below}; let shape_system_info = ShapeSystemInfo::new(system_id,ordering); *entry.instance_count += 1; - (shape_system_info, symbol_id, global_instance_id) + (shape_system_info,symbol_id,instance_id) }) } diff --git a/lib/rust/ensogl/core/src/display/scene/pointer_target.rs b/lib/rust/ensogl/core/src/display/scene/pointer_target.rs deleted file mode 100644 index b4aa10a02e7..00000000000 --- a/lib/rust/ensogl/core/src/display/scene/pointer_target.rs +++ /dev/null @@ -1,168 +0,0 @@ -//! Abstractions for objects that can interact with the pointer (in most cases this is a mouse). - -use crate::prelude::*; - -use crate::control::io::mouse; -use crate::display::symbol; - -use enso_frp as frp; - - -// ================= -// === Constants === -// ================= - -const ID_ENCODING_OVERFLOW_ERR: u32 = - include!("../shape/primitive/glsl/error_codes/id_encoding_overflow.txt"); - - - -// ===================== -// === PointerTarget === -// ===================== - -/// Abstraction for objects that can interact with a mouse. -#[derive(Clone, CloneRef, Debug)] -#[allow(missing_docs)] -pub struct PointerTarget { - network: frp::Network, - /// Mouse button was pressed while the pointer was hovering this object. - pub mouse_down: frp::Source, - /// Mouse button was released while the pointer was hovering this object. - pub mouse_up: frp::Source, - /// Mouse button that was earlier pressed on this object was just released. The mouse pointer - /// does not have to hover this object anymore. - pub mouse_release: frp::Source, - /// Mouse pointer entered the object shape. - pub mouse_over: frp::Source, - /// Mouse pointer exited the object shape. - pub mouse_out: frp::Source, - /// The mouse target was dropped. - pub on_drop: frp::Source, -} - -impl PointerTarget { - /// Constructor. - pub fn new() -> Self { - frp::new_network! { network - on_drop <- source_(); - mouse_down <- source(); - mouse_up <- source(); - mouse_release <- source(); - mouse_over <- source_(); - mouse_out <- source_(); - - is_mouse_over <- bool(&mouse_out,&mouse_over); - out_on_drop <- on_drop.gate(&is_mouse_over); - eval_ out_on_drop (mouse_out.emit(())); - } - Self { network, mouse_down, mouse_up, mouse_release, mouse_over, mouse_out, on_drop } - } -} - -impl Default for PointerTarget { - fn default() -> Self { - Self::new() - } -} - - - -// ======================= -// === PointerTargetId === -// ======================= - -/// Pointer target ID, a unique ID for an object pointed by the mouse. -#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] -#[allow(missing_docs)] -pub enum PointerTargetId { - Background, - Symbol { id: symbol::GlobalInstanceId }, -} - -impl PointerTargetId { - /// Decode the [`PointerTargetId`] from an RGBA value. If alpha is set to 0, the result will be - /// background. In case alpha is 255, the result will be decoded based on the first 3 bytes, - /// which allows for storing up to 16 581 375 unique IDs. - /// - /// Please see the [`fragment_runner.glsl`] file to see the encoding implementation and learn - /// more about the possible overflow behavior. - pub fn decode_from_rgba(v: Vector4) -> Result { - let alpha = v.w; - match alpha { - 0 => Ok(Self::Background), - 255 => { - let raw_id = Self::decode_raw(v.x, v.y, v.z); - let id = symbol::GlobalInstanceId::new(raw_id); - Ok(Self::Symbol { id }) - } - _ => { - let err = if alpha == ID_ENCODING_OVERFLOW_ERR { - DecodeError::Overflow - } else { - DecodeError::WrongAlpha(alpha) - }; - Err(err) - } - } - } - - fn decode_raw(r: u32, g: u32, b: u32) -> u32 { - (b << 16) + (g << 8) + r - } - - /// Check whether this id points to the background. - pub fn is_background(self) -> bool { - self == Self::Background - } - - /// Check whether this id points to a symbol. - pub fn is_symbol(self) -> bool { - !self.is_background() - } -} - -impl Default for PointerTargetId { - fn default() -> Self { - Self::Background - } -} - -impl From for PointerTargetId { - fn from(id: symbol::GlobalInstanceId) -> Self { - Self::Symbol { id } - } -} - -impl From<&symbol::GlobalInstanceId> for PointerTargetId { - fn from(id: &symbol::GlobalInstanceId) -> Self { - Self::from(*id) - } -} - - -// === Errors === - -/// [`PointerTargetId`] decoding error. See the docs of [`PointerTargetId::decode_from_rgba`] to -/// learn more. -#[derive(Copy, Clone, Debug, Fail)] -#[allow(missing_docs)] -pub enum DecodeError { - WrongAlpha(u32), - Overflow, -} - -impl Display for DecodeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::WrongAlpha(alpha) => { - let err1 = "Failed to decode mouse target."; - let err2 = "The alpha channel should be either 0 or 255, got"; - write!(f, "{} {} {}.", err1, err2, alpha) - } - Self::Overflow => { - write!(f, "ID overflow error, too many objects on the scene.") - } - } - } -} diff --git a/lib/rust/ensogl/core/src/display/shape/compound/events.rs b/lib/rust/ensogl/core/src/display/shape/compound/events.rs index bd98e32ea88..aa0a0934d26 100644 --- a/lib/rust/ensogl/core/src/display/shape/compound/events.rs +++ b/lib/rust/ensogl/core/src/display/shape/compound/events.rs @@ -49,7 +49,7 @@ impl MouseEvents { default() } - /// Connect the given [`PointerTarget`] to the [`Events`] output. + /// Connect the given [`ShapeViewEvents`] to the [`Events`] output. pub fn add_sub_shape(&self, sub_shape: &ShapeView) { frp::extend! { network self.frp.source.mouse_over <+ sub_shape.events.mouse_over; diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/glsl/error_codes/id_encoding_overflow.txt b/lib/rust/ensogl/core/src/display/shape/primitive/glsl/error_codes/id_encoding_overflow.txt deleted file mode 100644 index 29d6383b52c..00000000000 --- a/lib/rust/ensogl/core/src/display/shape/primitive/glsl/error_codes/id_encoding_overflow.txt +++ /dev/null @@ -1 +0,0 @@ -100 diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/glsl/fragment_runner.glsl b/lib/rust/ensogl/core/src/display/shape/primitive/glsl/fragment_runner.glsl index 528451e7a4b..c7f8379a516 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/glsl/fragment_runner.glsl +++ b/lib/rust/ensogl/core/src/display/shape/primitive/glsl/fragment_runner.glsl @@ -1,9 +1,17 @@ /// This code is the body of the fragment shader main function of a GLSL shape. + + // ================= // === Constants === // ================= +/// The threshold used to decide whether a value should be included in the generated ID map. The +/// threshold is defined as 0.0 because it is failry common to use almost completely transparent +/// colors (like `Rgba(0.0, 0.0, 0.0, 0.000001)`) for shapes which should just catch mouse events +/// without providing any visual feedback. +const float ID_ALPHA_THRESHOLD = 0.0; + const int DISPLAY_MODE_NORMAL = 0; const int DISPLAY_MODE_DEBUG_SDF = 1; const int DISPLAY_MODE_DEBUG_ID = 2; @@ -25,10 +33,12 @@ float alpha = shape.color.color.raw.a; // === Object ID Rendering === // =========================== +uvec3 chunks = encode(input_symbol_id,input_instance_id); float alpha_no_aa = alpha > ID_ALPHA_THRESHOLD ? 1.0 : 0.0; if (pointer_events_enabled) { - output_id = encode(input_global_instance_id,alpha_no_aa); + output_id = vec4(as_float_u8(chunks),alpha_no_aa); + output_id.rgb *= alpha_no_aa; } @@ -50,7 +60,7 @@ if (input_display_mode == DISPLAY_MODE_NORMAL) { output_color.rgb *= alpha_no_aa; } else if (input_display_mode == DISPLAY_MODE_DEBUG_ID) { - float object_hue = float((input_global_instance_id * 7) % 100) / 100.0; + float object_hue = float((input_instance_id * 7) % 100) / 100.0; Srgb object_color = srgb(hsv(object_hue, 1.0, 0.5)); output_color.rgb = object_color.raw.rgb; output_color.a = alpha_no_aa; diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/glsl/math.glsl b/lib/rust/ensogl/core/src/display/shape/primitive/glsl/math.glsl index 4b8c4524c83..93b8cfc646e 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/glsl/math.glsl +++ b/lib/rust/ensogl/core/src/display/shape/primitive/glsl/math.glsl @@ -186,11 +186,18 @@ float neg(float a) { // === Encode === -/// Enables check for ID encoding. -#define ID_ENCODING_OVERFLOW_CHECK +// This encoding must correspond to the decoding in the `Target` struct in +// src\rust\ensogl\src\display\scene.rs See there for more explanation. +uvec3 encode(int value1, int value2) { + uint chunk1 = (uint(value1) >> 4u) & 0x00FFu; + uint chunk2 = (uint(value1) & 0x000Fu) << 4u; + chunk2 = chunk2 + ((uint(value2) & 0x0F00u) >> 8u); + uint chunk3 = uint(value2) & 0x00FFu; + return uvec3(chunk1,chunk2,chunk3); +} -/// Encodes na [`uint`] values so it can be stored as a u8 encoded [`float`]. Will clamp values that -/// are out of range. +// Encodes a uint values so it can be stored in a u8 encoded float. Will clamp values that are +// out of range. float as_float_u8(uint value) { return clamp(float(value) / 255.0); } @@ -198,41 +205,3 @@ float as_float_u8(uint value) { vec3 as_float_u8(uvec3 v) { return vec3(as_float_u8(v.x),as_float_u8(v.y),as_float_u8(v.z)); } - -/// The threshold used to decide whether a value should be included in the generated ID map. The -/// threshold is defined as 0.0 because it is quite common to use almost completely transparent -/// colors (like `Rgba(0.0, 0.0, 0.0, 0.000001)`) for shapes which should just catch mouse events -/// without providing any visual feedback. -const float ID_ALPHA_THRESHOLD = 0.0; - -/// The maximum ID that can be encoded. We are encoding IDs using rgb values (3 bytes). -const int MAX_ENCODE_ID = 256 * 256 * 256 - 1; - -/// Converts provided [`int`] value to three [`u8`] chunks, skipping overflow bits. -uvec3 int_to_rgb_drop_overflow(int value) { - int r_mask = 0xFF; - int g_mask = 0xFF00; - int b_mask = 0xFF0000; - int r = (value & r_mask); - int g = (value & g_mask) >> 8; - int b = (value & b_mask) >> 16; - return uvec3(r,g,b); -} - -/// This encoding must correspond to the decoding in the [`PointerTarget`] struct in the -/// `ensogl/core/src/display/scene/pointer_target.rs` file. -/// -/// *Overflow Behavior* -/// If [`ID_ENCODING_OVERFLOW_CHECK`] is defined, the overflow will be reported to the CPU code as -/// part of the alpha channel. In case it is not defined, the overflow bits will be skipped and the -/// ID may alias with existing ones. -vec4 encode(int value, float alpha) { - uvec3 chunks = int_to_rgb_drop_overflow(value); - vec3 rgb = as_float_u8(chunks); - rgb *= alpha; -#ifdef ID_ENCODING_OVERFLOW_CHECK - bool is_overflow = value > MAX_ENCODE_ID; - alpha = is_overflow ? (ID_ENCODING_OVERFLOW_ERROR_CODE/255.0) : alpha; -#endif - return vec4(as_float_u8(chunks),alpha); -} diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/shader/builder.rs b/lib/rust/ensogl/core/src/display/shape/primitive/shader/builder.rs index 690e3fa73d6..2b06d7b8e4b 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/shader/builder.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/shader/builder.rs @@ -23,10 +23,6 @@ const COLOR: &str = include_str!("../glsl/color.glsl"); const DEBUG: &str = include_str!("../glsl/debug.glsl"); const SHAPE: &str = include_str!("../glsl/shape.glsl"); const FRAGMENT_RUNNER: &str = include_str!("../glsl/fragment_runner.glsl"); -const ERROR_CODES: &[(&str, &str)] = &[( - "ID_ENCODING_OVERFLOW_ERROR_CODE", - include_str!("../glsl/error_codes/id_encoding_overflow.txt"), -)]; // === Definition === @@ -75,19 +71,13 @@ lazy_static! { static ref GLSL_PRELUDE: String = make_glsl_prelude(); } -fn make_error_codes() -> String { - let codes = ERROR_CODES.iter(); - codes.map(|(name, code)| format!("const float {} = {}.0;", name, code.trim())).join("\n") -} - fn make_glsl_prelude() -> String { let redirections = overload::builtin_redirections(); let math = overload::allow_overloading(MATH); let color = overload::allow_overloading(COLOR); let debug = overload::allow_overloading(DEBUG); let shape = overload::allow_overloading(SHAPE); - let err_codes = make_error_codes(); let defs_header = header("SDF Primitives"); let sdf_defs = overload::allow_overloading(&primitive::all_shapes_glsl_definitions()); - [redirections, err_codes, math, color, debug, shape, defs_header, sdf_defs].join("\n\n") + [redirections, math, color, debug, shape, defs_header, sdf_defs].join("\n\n") } diff --git a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs index 89fead187e0..4a574e3f2b1 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs @@ -7,12 +7,12 @@ use crate::system::gpu::types::*; use crate::display; use crate::display::scene::Scene; use crate::display::shape::primitive::shader; -use crate::display::symbol; use crate::display::symbol::geometry::compound::sprite; use crate::display::symbol::geometry::Sprite; use crate::display::symbol::geometry::SpriteSystem; use crate::display::symbol::material; use crate::display::symbol::material::Material; +use crate::system::gpu::data::attribute; use crate::system::gpu::data::buffer::item::Storable; use super::def; @@ -166,7 +166,7 @@ pub trait DynShapeSystemInstance: ShapeSystemInstance { /// The dynamic shape type of this shape system definition. type DynamicShape: DynamicShape; /// New shape instantiation. Used to bind a shape to a specific scene implementation. - fn instantiate(&self, shape: &Self::DynamicShape) -> symbol::GlobalInstanceId; + fn instantiate(&self, shape: &Self::DynamicShape) -> attribute::InstanceIndex; } /// Abstraction for every entity which is associated with a shape system (user generated one). For @@ -191,7 +191,7 @@ pub trait Shape: display::Object + CloneRef + Debug + Sized { /// Accessor for the underlying sprite. fn sprite(&self) -> &Sprite; /// Check if given mouse-event-target means this shape. - fn is_this_target(&self, target: display::scene::PointerTargetId) -> bool { + fn is_this_target(&self, target: display::scene::PointerTarget) -> bool { self.sprite().is_this_target(target) } } @@ -217,7 +217,7 @@ pub trait DynamicShape: display::Object + CloneRef + Debug + Sized { /// The "canvas" size of the shape. It defines the bounding-box for the shape drawing area. fn size(&self) -> &DynamicParam; /// Check if given pointer-event-target means this object. - fn is_this_target(&self, target: display::scene::PointerTargetId) -> bool { + fn is_this_target(&self, target: display::scene::PointerTarget) -> bool { self.sprites().into_iter().any(|sprite| sprite.is_this_target(target)) } } @@ -372,7 +372,6 @@ macro_rules! _define_shape_system { mod shape_system_definition { use super::*; use $crate::display; - use $crate::display::symbol; use $crate::display::symbol::geometry::compound::sprite; use $crate::display::symbol::geometry::Sprite; use $crate::system::gpu; @@ -576,14 +575,14 @@ macro_rules! _define_shape_system { impl display::shape::DynShapeSystemInstance for ShapeSystem { type DynamicShape = DynamicShape; - fn instantiate(&self, dyn_shape:&Self::DynamicShape) -> symbol::GlobalInstanceId { + fn instantiate(&self, dyn_shape:&Self::DynamicShape) + -> gpu::data::attribute::InstanceIndex { let sprite = self.shape_system.new_instance(); - let instance_id = sprite.instance_id; - let global_id = sprite.global_instance_id; - $(let $gpu_param = self.$gpu_param.at(instance_id);)* + let id = sprite.instance_id; + $(let $gpu_param = self.$gpu_param.at(id);)* let shape = Shape {sprite, $($gpu_param),*}; dyn_shape.add_instance(shape); - global_id + id } } diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu.rs b/lib/rust/ensogl/core/src/display/symbol/gpu.rs index b748d59f9db..0eb7c2330fe 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu.rs @@ -16,7 +16,6 @@ use crate::system::gpu::data::uniform::AnyTextureUniform; use crate::system::gpu::data::uniform::AnyUniform; use enso_shapely::newtype_prim; -use enso_shapely::shared2; use shader::Shader; use wasm_bindgen::JsValue; use web_sys::WebGlProgram; @@ -230,47 +229,6 @@ impl Drop for SymbolStatsData { -// ================================ -// === GlobalInstanceIdProvider === -// ================================ - -newtype_prim! { - /// Global [`Symbol`] instance id. Allows encoding symbol IDs in a texture and then decode on - /// mouse interaction. - /// - /// Please see the [`fragment_runner.glsl`] file to see the encoding implementation and learn - /// more about the possible overflow behavior. - GlobalInstanceId(u32); -} - -shared2! { GlobalInstanceIdProvider - /// [`GlobalInstanceId`] provider. - #[derive(Debug,Default)] - pub struct GlobalInstanceIdProviderData { - next: GlobalInstanceId, - free: Vec, - } - - impl { - /// Get a new [`GlobalInstanceId`] either by reusing previously disposed one or reserving a - /// new one. - pub fn reserve(&mut self) -> GlobalInstanceId { - self.free.pop().unwrap_or_else(|| { - let out = self.next; - self.next = GlobalInstanceId::new((*out) + 1); - out - }) - } - - /// Dispose previously used [`GlobalInstanceId`]. It will be reused for new instances. - pub fn dispose(&mut self, id: GlobalInstanceId) { - self.free.push(id); - } - } -} - - - // ============== // === Symbol === // ============== @@ -302,44 +260,36 @@ pub struct Bindings { newtype_prim! { /// The ID of a [`Symbol`] instance. The ID is also the index of the symbol inside of symbol - /// registry. + /// registry. In case the symbol was not yet registered, the ID will be `0`. SymbolId(u32); } /// Symbol is a surface with attached `Shader`. #[derive(Debug, Clone, CloneRef)] pub struct Symbol { - pub id: SymbolId, - global_id_provider: GlobalInstanceIdProvider, - display_object: display::object::Instance, - surface: Mesh, - shader: Shader, - surface_dirty: GeometryDirty, - shader_dirty: ShaderDirty, - variables: UniformScope, + pub id: SymbolId, + display_object: display::object::Instance, + surface: Mesh, + shader: Shader, + surface_dirty: GeometryDirty, + shader_dirty: ShaderDirty, + variables: UniformScope, /// Please note that changing the uniform type to `u32` breaks node ID encoding in GLSL, as the /// functions are declared to work on `int`s, not `uint`s. This might be improved one day. - symbol_id_uniform: Uniform, - context: Rc>>, - logger: Logger, - bindings: Rc>, - stats: SymbolStats, - is_hidden: Rc>, - global_instance_id: Buffer, + symbol_id_uniform: Uniform, + context: Rc>>, + logger: Logger, + bindings: Rc>, + stats: SymbolStats, + is_hidden: Rc>, } impl Symbol { /// Create new instance with the provided on-dirty callback. - pub fn new( - stats: &Stats, - id: SymbolId, - global_id_provider: &GlobalInstanceIdProvider, - on_mut: OnMut, - ) -> Self { + pub fn new(stats: &Stats, id: SymbolId, on_mut: OnMut) -> Self { let logger = Logger::new(format!("symbol_{}", id)); let init_logger = logger.clone(); debug!(init_logger, "Initializing.", || { - let global_id_provider = global_id_provider.clone_ref(); let on_mut2 = on_mut.clone(); let surface_logger = Logger::new_sub(&logger, "surface"); let shader_logger = Logger::new_sub(&logger, "shader"); @@ -358,13 +308,8 @@ impl Symbol { let symbol_id_uniform = variables.add_or_panic("symbol_id", (*id) as i32); let display_object = display::object::Instance::new(logger.clone()); let is_hidden = Rc::new(Cell::new(false)); - - let instance_scope = surface.instance_scope(); - let global_instance_id = instance_scope.add_buffer("global_instance_id"); - Self { id, - global_id_provider, display_object, surface, shader, @@ -377,7 +322,6 @@ impl Symbol { bindings, stats, is_hidden, - global_instance_id, } .init() }) @@ -399,10 +343,6 @@ impl Symbol { self } - pub fn new_instance(&self) -> SymbolInstance { - SymbolInstance::new(self) - } - pub(crate) fn set_context(&self, context: Option<&Context>) { *self.context.borrow_mut() = context.cloned(); self.surface.set_context(context); @@ -717,45 +657,3 @@ impl display::Object for Symbol { &self.display_object } } - - - -// ====================== -// === SymbolInstance === -// ====================== - -/// Instance of a [`Symbol`]. It does not define any custom parameters, however, it manages the -/// [`InstanceIndex`] and [`GlobalInstanceId`] ones. -#[derive(Debug, Clone, CloneRef, Deref)] -pub struct SymbolInstance { - rc: Rc, -} - -#[derive(Debug, NoCloneBecauseOfCustomDrop)] -pub struct SymbolInstanceData { - pub symbol: Symbol, - pub instance_id: attribute::InstanceIndex, - pub global_instance_id: GlobalInstanceId, -} - -impl SymbolInstance { - fn new(symbol: &Symbol) -> Self { - let symbol = symbol.clone_ref(); - let global_instance_id = symbol.global_id_provider.reserve(); - let instance_id = symbol.surface().instance_scope().add_instance(); - - let global_instance_id_attr = symbol.global_instance_id.at(instance_id); - global_instance_id_attr.set(*global_instance_id as i32); - - let data = SymbolInstanceData { symbol, instance_id, global_instance_id }; - let rc = Rc::new(data); - Self { rc } - } -} - -impl Drop for SymbolInstanceData { - fn drop(&mut self) { - self.symbol.surface().instance_scope().dispose(self.instance_id); - self.symbol.global_id_provider.dispose(self.global_instance_id); - } -} diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu/geometry/compound/sprite.rs b/lib/rust/ensogl/core/src/display/symbol/gpu/geometry/compound/sprite.rs index 8cd9f4fbad5..f176e9069b8 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu/geometry/compound/sprite.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu/geometry/compound/sprite.rs @@ -8,14 +8,12 @@ use crate::system::gpu::types::*; use crate::debug::Stats; use crate::display; -use crate::display::attribute::EraseOnDrop; use crate::display::layout::alignment; use crate::display::layout::Alignment; use crate::display::scene::Scene; use crate::display::symbol::material::Material; use crate::display::symbol::Symbol; use crate::display::symbol::SymbolId; -use crate::display::symbol::SymbolInstance; @@ -54,6 +52,45 @@ impl Drop for SpriteStats { +// =================== +// === SpriteGuard === +// =================== + +/// Lifetime guard for `Sprite`. After sprite is dropped, it is removed from the sprite system. +/// Note that the removal does not involve many changes to buffers. What really happens is setting +/// the sprite dimensions to zero and marking it index as a free for future reuse. +#[derive(Debug)] +pub struct SpriteGuard { + instance_id: attribute::InstanceIndex, + symbol: Symbol, + size: Attribute>, + display_object: display::object::Instance, +} + +impl SpriteGuard { + fn new( + instance_id: attribute::InstanceIndex, + symbol: &Symbol, + size: &Attribute>, + display_object: &display::object::Instance, + ) -> Self { + let symbol = symbol.clone_ref(); + let size = size.clone_ref(); + let display_object = display_object.clone_ref(); + Self { instance_id, symbol, size, display_object } + } +} + +impl Drop for SpriteGuard { + fn drop(&mut self) { + self.size.set(zero()); + self.symbol.surface().instance_scope().dispose(self.instance_id); + self.display_object.unset_parent(); + } +} + + + // ============ // === Size === // ============ @@ -121,46 +158,33 @@ impl Size { #[derive(Debug, Clone, CloneRef)] #[allow(missing_docs)] pub struct Sprite { - pub symbol: Symbol, - pub instance: SymbolInstance, - pub size: Size, - display_object: display::object::Instance, - transform: Attribute>, - stats: Rc, - erase_on_drop: Rc>>>, - unset_parent_on_drop: Rc, + pub symbol: Symbol, + pub instance_id: attribute::InstanceIndex, + pub size: Size, + display_object: display::object::Instance, + transform: Attribute>, + stats: Rc, + guard: Rc, } impl Sprite { /// Constructor. pub fn new( symbol: &Symbol, - instance: SymbolInstance, + instance_id: attribute::InstanceIndex, transform: Attribute>, size: Attribute>, stats: &Stats, ) -> Self { let symbol = symbol.clone_ref(); - let logger = Logger::new(iformat!("Sprite{instance.instance_id}")); + let logger = Logger::new(iformat!("Sprite{instance_id}")); let display_object = display::object::Instance::new(logger); let stats = Rc::new(SpriteStats::new(stats)); - let erase_on_drop = Rc::new(EraseOnDrop::new(size.clone_ref())); + let guard = Rc::new(SpriteGuard::new(instance_id, &symbol, &size, &display_object)); let size = Size::new(size); - let unset_parent_on_drop = - Rc::new(display::object::UnsetParentOnDrop::new(&display_object)); let default_size = Vector2(DEFAULT_SPRITE_SIZE.0, DEFAULT_SPRITE_SIZE.1); size.set(default_size); - Self { - symbol, - instance, - size, - display_object, - transform, - stats, - erase_on_drop, - unset_parent_on_drop, - } - .init() + Self { symbol, instance_id, size, display_object, transform, stats, guard }.init() } /// Init display object bindings. In particular defines the behavior of the show and hide @@ -180,10 +204,11 @@ impl Sprite { } /// Check if given pointer-event-target means this object. - pub fn is_this_target(&self, target: display::scene::PointerTargetId) -> bool { + pub fn is_this_target(&self, target: display::scene::PointerTarget) -> bool { match target { - display::scene::PointerTargetId::Background => false, - display::scene::PointerTargetId::Symbol { id } => self.global_instance_id == id, + display::scene::PointerTarget::Background => false, + display::scene::PointerTarget::Symbol { symbol_id, instance_id } => + self.symbol_id() == symbol_id && self.instance_id == instance_id, } } } @@ -194,13 +219,6 @@ impl display::Object for Sprite { } } -impl Deref for Sprite { - type Target = SymbolInstance; - fn deref(&self) -> &Self::Target { - &self.instance - } -} - // ==================== @@ -247,10 +265,10 @@ impl SpriteSystem { /// Creates a new sprite instance. pub fn new_instance(&self) -> Sprite { - let instance = self.symbol.new_instance(); - let transform = self.transform.at(instance.instance_id); - let size = self.size.at(instance.instance_id); - let sprite = Sprite::new(&self.symbol, instance, transform, size, &self.stats); + let instance_id = self.symbol.surface().instance_scope().add_instance(); + let transform = self.transform.at(instance_id); + let size = self.size.at(instance_id); + let sprite = Sprite::new(&self.symbol, instance_id, transform, size, &self.stats); self.add_child(&sprite); sprite } @@ -323,18 +341,16 @@ impl SpriteSystem { material.add_input_def::>("transform"); material.add_input_def::>("view_projection"); material.add_input_def::>("alignment"); - material.add_input_def::("global_instance_id"); material.add_output_def::>("local"); + material.add_output_def::("instance_id"); material.set_main( " mat4 model_view_projection = input_view_projection * input_transform; input_local = vec3((input_uv - input_alignment) * input_size, 0.0); gl_Position = model_view_projection * vec4(input_local,1.0); input_local.z = gl_Position.z; + input_instance_id = gl_InstanceID; ", - // This is left here in case it will be needed. The `instance_id` is the same as the - // built-in `gl_InstanceID` and can be implemented very efficiently: - // input_instance_id = gl_InstanceID; ); material } diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu/geometry/primitive/mesh.rs b/lib/rust/ensogl/core/src/display/symbol/gpu/geometry/primitive/mesh.rs index f7c68e4d1ca..811a5d19542 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu/geometry/primitive/mesh.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu/geometry/primitive/mesh.rs @@ -8,7 +8,7 @@ use crate::data::dirty; use crate::debug::stats::Stats; use crate::system::Context; -use enso_shapely::shared2; +use enso_shapely::shared; use num_enum::IntoPrimitive; @@ -90,9 +90,9 @@ macro_rules! update_scopes { // === Definition === -shared2! { Mesh +shared! { Mesh /// A polygon mesh is a collection of vertices, edges and faces that defines the shape of a -/// polyhedral object. Mesh describes the shape of the display element. It consists of several +/// polyhedral object. Mesh describes the shape of the display element. It consist of several /// scopes containing sets of variables. See the documentation of `Scopes` to learn more. /// /// Please note, that there are other, higher-level scopes defined by other structures, including: @@ -101,7 +101,7 @@ shared2! { Mesh /// Object refers to the whole geometry with all of its instances. /// /// - Global Scope -/// Global scope is shared by all objects, and it contains some universal global variables, like +/// Global scope is shared by all objects and it contains some universal global variables, like /// the current 'time' counter. /// /// Each scope can contain named attributes which can be accessed from within materials. If the same diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu/registry.rs b/lib/rust/ensogl/core/src/display/symbol/gpu/registry.rs index 4b46855cf0f..e6e89afe43e 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu/registry.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu/registry.rs @@ -7,7 +7,6 @@ use crate::prelude::*; use crate::data::dirty; use crate::debug::stats::Stats; use crate::display::camera::Camera2d; -use crate::display::symbol; use crate::display::symbol::Symbol; use crate::display::symbol::SymbolId; use crate::system::gpu::data::uniform::Uniform; @@ -36,15 +35,14 @@ pub type SymbolDirty = dirty::SharedSet>; /// which the `zoom` value is `1.0`. #[derive(Clone, CloneRef, Debug)] pub struct SymbolRegistry { - symbols: Rc>>, - global_id_provider: symbol::GlobalInstanceIdProvider, - symbol_dirty: SymbolDirty, - logger: Logger, - view_projection: Uniform>, - z_zoom_1: Uniform, - variables: UniformScope, - context: Rc>>, - stats: Stats, + symbols: Rc>>, + symbol_dirty: SymbolDirty, + logger: Logger, + view_projection: Uniform>, + z_zoom_1: Uniform, + variables: UniformScope, + context: Rc>>, + stats: Stats, } impl SymbolRegistry { @@ -65,18 +63,7 @@ impl SymbolRegistry { let z_zoom_1 = variables.add_or_panic("z_zoom_1", 1.0); let context = default(); let stats = stats.clone_ref(); - let global_id_provider = default(); - Self { - symbols, - global_id_provider, - symbol_dirty, - logger, - view_projection, - z_zoom_1, - variables, - context, - stats, - } + Self { symbols, symbol_dirty, logger, view_projection, z_zoom_1, variables, context, stats } } /// Creates a new `Symbol` instance and returns its id. @@ -86,7 +73,7 @@ impl SymbolRegistry { let index = self.symbols.borrow_mut().insert_with_ix_(|ix| { let id = SymbolId::new(ix as u32); let on_mut = move || symbol_dirty.set(id); - let symbol = Symbol::new(stats, id, &self.global_id_provider, on_mut); + let symbol = Symbol::new(stats, id, on_mut); symbol.set_context(self.context.borrow().as_ref()); symbol }); diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs b/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs index ce91c381d7d..87f0f3ec237 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu/shader.rs @@ -62,7 +62,6 @@ pub struct ShaderData { geometry_material : Material, surface_material : Material, program : Option, - shader : Option, dirty : Dirty, logger : Logger, stats : Stats, @@ -98,11 +97,10 @@ impl { let geometry_material = default(); let surface_material = default(); let program = default(); - let shader = default(); let dirty_logger = Logger::new_sub(&logger,"dirty"); let dirty = Dirty::new(dirty_logger,Box::new(on_mut)); let stats = stats.clone_ref(); - Self {context,geometry_material,surface_material,program,shader,dirty,logger,stats} + Self {context,geometry_material,surface_material,program,dirty,logger,stats} } /// Check dirty flags and update the state accordingly. @@ -148,10 +146,7 @@ impl { let shader = shader_builder.build(); let program = compile_program(context,&shader.vertex,&shader.fragment); match program { - Ok(program) => { - self.program = Some(program); - self.shader = Some(shader); - } + Ok(program) => { self.program = Some(program);} Err(ContextLossOrError::Error(err)) => error!(self.logger, "{err}"), Err(ContextLossOrError::ContextLoss) => {} } @@ -168,11 +163,6 @@ impl { let surface_material_inputs = self.surface_material.inputs().clone(); geometry_material_inputs.into_iter().chain(surface_material_inputs).collect() } - - /// Get the generated shader, if it was already generated. - pub fn shader(&self) -> Option { - self.shader.clone() - } }} impl Drop for ShaderData { diff --git a/lib/rust/ensogl/core/src/display/symbol/gpu/shader/builder.rs b/lib/rust/ensogl/core/src/display/symbol/gpu/shader/builder.rs index f08577663f5..505970e4a0c 100644 --- a/lib/rust/ensogl/core/src/display/symbol/gpu/shader/builder.rs +++ b/lib/rust/ensogl/core/src/display/symbol/gpu/shader/builder.rs @@ -108,19 +108,8 @@ pub struct AttributeQualifier { } impl AttributeQualifier { - /// Convert the qualifier to input variable definition. The [`use_qual`] parameter defines if - /// the qualified storage interpolator (e.g. `flat`) should be used. Interpolation modifiers are - /// illegal on vertex shader input attributes, however, they are required on fragment shader - /// ones. - pub fn to_input_var>( - &self, - name: Name, - use_qual: bool, - ) -> glsl::GlobalVar { - let mut storage = self.storage; - if !use_qual { - storage.interpolation = None; - } + pub fn to_input_var>(&self, name: Name) -> glsl::GlobalVar { + let storage = self.storage; glsl::GlobalVar { layout: None, storage: Some(glsl::GlobalVarStorage::InStorage(storage)), @@ -331,9 +320,9 @@ impl ShaderBuilder { let vert_name = mk_vertex_name(name); let frag_name = mk_fragment_name(name); let sharing = glsl::Assignment::new(&frag_name, &vert_name); - self.vertex.add(qual.to_input_var(&vert_name, false)); + self.vertex.add(qual.to_input_var(&vert_name)); self.vertex.add(qual.to_output_var(&frag_name)); - self.fragment.add(qual.to_input_var(&frag_name, true)); + self.fragment.add(qual.to_input_var(&frag_name)); self.vertex.main.add(sharing); } } @@ -344,7 +333,7 @@ impl ShaderBuilder { for (name, qual) in &cfg.shared { let frag_name = mk_fragment_name(name); self.vertex.add(qual.to_output_var(&frag_name)); - self.fragment.add(qual.to_input_var(&frag_name, true)); + self.fragment.add(qual.to_input_var(&frag_name)); } } } diff --git a/lib/rust/ensogl/core/src/display/world.rs b/lib/rust/ensogl/core/src/display/world.rs index d50d4faa5f9..14aa1bfbc0f 100644 --- a/lib/rust/ensogl/core/src/display/world.rs +++ b/lib/rust/ensogl/core/src/display/world.rs @@ -258,10 +258,10 @@ impl WorldData { } fn init_composer(&self) { - let mouse_hover_rgba = self.default_scene.mouse.hover_rgba.clone_ref(); + let mouse_hover_ids = self.default_scene.mouse.hover_ids.clone_ref(); let mut pixel_read_pass = PixelReadPass::::new(&self.default_scene.mouse.position); pixel_read_pass.set_callback(move |v| { - mouse_hover_rgba.set(Vector4::from_iterator(v.iter().map(|value| *value as u32))) + mouse_hover_ids.set(Vector4::from_iterator(v.iter().map(|value| *value as u32))) }); // TODO: We may want to enable it on weak hardware. // pixel_read_pass.set_threshold(1); diff --git a/lib/rust/ensogl/core/src/gui/component.rs b/lib/rust/ensogl/core/src/gui/component.rs index e3553b09e78..6c8ea69eaf2 100644 --- a/lib/rust/ensogl/core/src/gui/component.rs +++ b/lib/rust/ensogl/core/src/gui/component.rs @@ -1,4 +1,7 @@ //! Root module for GUI related components. +//! NOTE +//! This file is under a heavy development. It contains commented lines of code and some code may +//! be of poor quality. Expect drastic changes. use crate::display::object::traits::*; use crate::prelude::*; @@ -6,18 +9,72 @@ use crate::prelude::*; use crate::display; use crate::display::scene; use crate::display::scene::layer::WeakLayer; +use crate::display::scene::MouseTarget; use crate::display::scene::Scene; use crate::display::scene::ShapeRegistry; use crate::display::shape::primitive::system::DynamicShape; use crate::display::shape::primitive::system::DynamicShapeInternals; -use crate::display::symbol; +use crate::display::symbol::SymbolId; +use crate::system::gpu::data::attribute; + +use enso_frp as frp; -// ============== -// === Export === -// ============== -pub use crate::display::scene::PointerTarget; +// ======================= +// === ShapeViewEvents === +// ======================= + +/// FRP event endpoints exposed by each shape view. In particular these are all mouse events +/// which are triggered by mouse interactions after the shape view is placed on the scene. +#[derive(Clone, CloneRef, Debug)] +#[allow(missing_docs)] +pub struct ShapeViewEvents { + pub network: frp::Network, + pub mouse_up: frp::Source, + pub mouse_down: frp::Source, + pub mouse_over: frp::Source, + pub mouse_out: frp::Source, + pub on_drop: frp::Source, +} + +impl ShapeViewEvents { + fn new() -> Self { + frp::new_network! { network + on_drop <- source_(); + mouse_down <- source_(); + mouse_up <- source_(); + mouse_over <- source_(); + mouse_out <- source_(); + + is_mouse_over <- bool(&mouse_out,&mouse_over); + out_on_drop <- on_drop.gate(&is_mouse_over); + eval_ out_on_drop (mouse_out.emit(())); + } + Self { network, mouse_up, mouse_down, mouse_over, mouse_out, on_drop } + } +} + +impl MouseTarget for ShapeViewEvents { + fn mouse_down(&self) -> &frp::Source { + &self.mouse_down + } + fn mouse_up(&self) -> &frp::Source { + &self.mouse_up + } + fn mouse_over(&self) -> &frp::Source { + &self.mouse_over + } + fn mouse_out(&self) -> &frp::Source { + &self.mouse_out + } +} + +impl Default for ShapeViewEvents { + fn default() -> Self { + Self::new() + } +} @@ -79,9 +136,9 @@ impl HasContent for ShapeView { #[allow(missing_docs)] pub struct ShapeViewModel { shape: S, - pub events: PointerTarget, + pub events: ShapeViewEvents, pub registry: RefCell>, - pub pointer_targets: RefCell>, + pub pointer_targets: RefCell>, } impl Deref for ShapeViewModel { @@ -132,7 +189,7 @@ impl ShapeViewModel { /// Constructor. pub fn new(logger: impl AnyLogger) -> Self { let shape = S::new(logger); - let events = PointerTarget::new(); + let events = ShapeViewEvents::new(); let registry = default(); let pointer_targets = default(); ShapeViewModel { shape, events, registry, pointer_targets } @@ -140,8 +197,12 @@ impl ShapeViewModel { fn add_to_scene_layer(&self, scene: &Scene, layer: &scene::Layer) { let instance = layer.instantiate(scene, &self.shape); - scene.shapes.insert_mouse_target(instance.global_instance_id, self.events.clone_ref()); - self.pointer_targets.borrow_mut().push(instance.global_instance_id); + scene.shapes.insert_mouse_target( + instance.symbol_id, + instance.instance_id, + self.events.clone_ref(), + ); + self.pointer_targets.borrow_mut().push((instance.symbol_id, instance.instance_id)); *self.registry.borrow_mut() = Some(scene.shapes.clone_ref()); } } @@ -149,8 +210,8 @@ impl ShapeViewModel { impl ShapeViewModel { fn unregister_existing_mouse_targets(&self) { if let Some(registry) = &*self.registry.borrow() { - for global_instance_id in mem::take(&mut *self.pointer_targets.borrow_mut()) { - registry.remove_mouse_target(global_instance_id); + for (symbol_id, instance_id) in mem::take(&mut *self.pointer_targets.borrow_mut()) { + registry.remove_mouse_target(symbol_id, instance_id); } } } diff --git a/lib/rust/ensogl/core/src/lib.rs b/lib/rust/ensogl/core/src/lib.rs index 5bff133dc7e..a515b1f94e8 100644 --- a/lib/rust/ensogl/core/src/lib.rs +++ b/lib/rust/ensogl/core/src/lib.rs @@ -5,7 +5,6 @@ #![recursion_limit = "512"] // === Features === #![allow(incomplete_features)] -#![feature(negative_impls)] #![feature(associated_type_defaults)] #![feature(bool_to_option)] #![feature(cell_update)] diff --git a/lib/rust/ensogl/core/src/system/gpu/data/attribute.rs b/lib/rust/ensogl/core/src/system/gpu/data/attribute.rs index b905880fa83..c117691df80 100644 --- a/lib/rust/ensogl/core/src/system/gpu/data/attribute.rs +++ b/lib/rust/ensogl/core/src/system/gpu/data/attribute.rs @@ -252,45 +252,3 @@ impl CellSetter for Attribute { self.buffer.set(self.index.into(), value); } } - -impl Erase for Attribute { - fn erase(&self) { - self.set(default()) - } -} - - - -// ============= -// === Erase === -// ============= - -/// Generalization for internally mutable structures which can be erased. -/// -/// For now, it is placed here, as only [`Attribute`] uses it, but it might be refactored in the -/// future if it will be usable in more places. -#[allow(missing_docs)] -pub trait Erase { - fn erase(&self); -} - -/// The provided element will be erased whenever this structure is dropped. Please note that the -///provided element implements [`CloneRef`] it can still be referenced after this struct is -/// dropped. -#[derive(Debug, NoCloneBecauseOfCustomDrop)] -pub struct EraseOnDrop { - elem: T, -} - -impl EraseOnDrop { - /// Constructor. - pub fn new(elem: T) -> Self { - Self { elem } - } -} - -impl Drop for EraseOnDrop { - fn drop(&mut self) { - self.elem.erase() - } -} diff --git a/lib/rust/ensogl/core/src/system/gpu/data/buffer.rs b/lib/rust/ensogl/core/src/system/gpu/data/buffer.rs index 180692676e1..8b9f23ab77c 100644 --- a/lib/rust/ensogl/core/src/system/gpu/data/buffer.rs +++ b/lib/rust/ensogl/core/src/system/gpu/data/buffer.rs @@ -217,24 +217,18 @@ impl { /// https://stackoverflow.com/questions/38853096/webgl-how-to-bind-values-to-a-mat4-attribute pub fn vertex_attrib_pointer(&self, loc:u32, instanced:bool) { if let Some(gl) = &self.gl { - let is_integer = T::is_integer(); let item_byte_size = T::item_gpu_byte_size() as i32; let item_type = T::item_gl_enum().into(); let rows = T::rows() as i32; let cols = T::cols() as i32; let col_byte_size = item_byte_size * rows; let stride = col_byte_size * cols; + let normalize = false; for col in 0..cols { let lloc = loc + col as u32; let off = col * col_byte_size; gl.context.enable_vertex_attrib_array(lloc); - if is_integer { - gl.context.vertex_attrib_i_pointer_with_i32(lloc,rows,item_type,stride,off); - } else { - let normalize = false; - gl.context.vertex_attrib_pointer_with_i32 - (lloc,rows,item_type,normalize,stride,off); - } + gl.context.vertex_attrib_pointer_with_i32(lloc,rows,item_type,normalize,stride,off); if instanced { let instance_count = 1; gl.context.vertex_attrib_divisor(lloc,instance_count); diff --git a/lib/rust/ensogl/core/src/system/gpu/data/buffer/item.rs b/lib/rust/ensogl/core/src/system/gpu/data/buffer/item.rs index c7e6c0156a6..1e275788769 100644 --- a/lib/rust/ensogl/core/src/system/gpu/data/buffer/item.rs +++ b/lib/rust/ensogl/core/src/system/gpu/data/buffer/item.rs @@ -71,11 +71,6 @@ pub trait Storable: BufferItemBounds { /// The number of columns of the type encoded as 2d matrix. type Cols: DimName; - /// Checks if the type should be interpreted as integer. This is important when binding the - /// buffer to the shader. See WebGL's `vertexAttribPointer` vs `vertexAttribIPointer` to learn - /// more. - fn is_integer() -> bool; - // === Size === @@ -147,10 +142,6 @@ impl Storable for bool { type Rows = U1; type Cols = U1; - fn is_integer() -> bool { - false - } - fn slice_from_items(buffer: &[Self::Cell]) -> &[Self] { buffer } @@ -170,10 +161,6 @@ impl Storable for i32 { type Rows = U1; type Cols = U1; - fn is_integer() -> bool { - true - } - fn slice_from_items(buffer: &[Self::Cell]) -> &[Self] { buffer } @@ -193,10 +180,6 @@ impl Storable for u32 { type Rows = U1; type Cols = U1; - fn is_integer() -> bool { - true - } - fn slice_from_items(buffer: &[Self::Cell]) -> &[Self] { buffer } @@ -216,10 +199,6 @@ impl Storable for f32 { type Rows = U1; type Cols = U1; - fn is_integer() -> bool { - false - } - fn slice_from_items(buffer: &[Self::Cell]) -> &[Self] { buffer } @@ -244,10 +223,6 @@ where type Rows = R; type Cols = C; - fn is_integer() -> bool { - T::is_integer() - } - #[allow(unsafe_code)] fn slice_from_items(buffer: &[Self::Cell]) -> &[Self] { // This code casts slice to matrix. This is safe because `MatrixMN` diff --git a/lib/rust/ensogl/core/src/system/gpu/shader.rs b/lib/rust/ensogl/core/src/system/gpu/shader.rs index 2a548fc4f4b..8f1a736756c 100644 --- a/lib/rust/ensogl/core/src/system/gpu/shader.rs +++ b/lib/rust/ensogl/core/src/system/gpu/shader.rs @@ -2,11 +2,9 @@ #![allow(missing_docs)] use enso_prelude::*; -use enso_web::traits::*; use crate::system::Context; -use enso_web as web; use js_sys::Float32Array; use web_sys::WebGlBuffer; use web_sys::WebGlProgram; @@ -49,44 +47,12 @@ pub type Program = WebGlProgram; // === Error === // ============= -#[derive(Debug, Fail)] -pub enum SingleTargetError { - #[fail(display = "Unable to create {}.", target)] - Create { target: ErrorTarget }, - #[fail(display = "Unable to compile {}.\n{}\n\n{}", target, message, preview_code)] - Compile { target: ErrorTarget, message: String, preview_code: String }, -} - #[derive(Debug, Fail, From)] pub enum Error { - Create { - target: ErrorTarget, - }, - - Compile { - js_path: String, - vertex: Option, - fragment: Option, - }, -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Create { target } => write!(f, "Unable to create {}", target), - Self::Compile { js_path, vertex, fragment } => { - let vtx_msg = vertex.as_ref().map(|t| format!("\n\n{}", t)).unwrap_or_default(); - let frag_msg = fragment.as_ref().map(|t| format!("\n\n{}", t)).unwrap_or_default(); - let run_msg = |n| { - format!("Run `console.log({}.{})` to inspect the {} shader.", js_path, n, n) - }; - let vtx_run_msg = run_msg("vertex"); - let frag_run_msg = run_msg("fragment"); - let err_msg = "Unable to create shader."; - write!(f, "{}\n{}\n{}{}{}", err_msg, vtx_run_msg, frag_run_msg, vtx_msg, frag_msg) - } - } - } + #[fail(display = "Unable to create {}.", target)] + Create { target: ErrorTarget }, + #[fail(display = "Unable to compile {}.\n{}\n\n{}", target, message, code)] + Compile { target: ErrorTarget, message: String, code: String }, } #[derive(Copy, Clone, Debug, Fail)] @@ -154,17 +120,17 @@ fn unwrap_error(opt_err: Option) -> String { // === Compile / Link === // ====================== -pub fn compile_vertex_shader(ctx: &Context, src: &str) -> Result { +pub fn compile_vertex_shader(ctx: &Context, src: &str) -> Result { compile_shader(ctx, Context::VERTEX_SHADER, src) } -pub fn compile_fragment_shader(ctx: &Context, src: &str) -> Result { +pub fn compile_fragment_shader(ctx: &Context, src: &str) -> Result { compile_shader(ctx, Context::FRAGMENT_SHADER, src) } -pub fn compile_shader(ctx: &Context, tp: u32, src: &str) -> Result { +pub fn compile_shader(ctx: &Context, tp: u32, src: &str) -> Result { let target = ErrorTarget::Shader; - let shader = ctx.create_shader(tp).ok_or(SingleTargetError::Create { target })?; + let shader = ctx.create_shader(tp).ok_or(Error::Create { target })?; ctx.shader_source(&shader, src); ctx.compile_shader(&shader); match shader.check(ctx) { @@ -180,7 +146,7 @@ pub fn compile_shader(ctx: &Context, tp: u32, src: &str) -> Result>(); let code_with_num = lines_with_num.join("\n"); let error_loc_pfx = "ERROR: 0:"; - let preview_code = if let Some(msg) = message.strip_prefix(error_loc_pfx) { + let out = if let Some(msg) = message.strip_prefix(error_loc_pfx) { let line_num: String = msg.chars().take_while(|c| c.is_digit(10)).collect(); let line_num = line_num.parse::().unwrap() - 1; let preview_radius = 5; @@ -190,7 +156,7 @@ pub fn compile_shader(ctx: &Context, tp: u32, src: &str) -> Result Result { - let vert_shader = compile_vertex_shader(ctx, vert_src); - let frag_shader = compile_fragment_shader(ctx, frag_src); - match (vert_shader, frag_shader) { - (Ok(vert_shader), Ok(frag_shader)) => link_program(ctx, &vert_shader, &frag_shader), - (vert_shader, frag_shader) => { - let vertex = vert_shader.err(); - let fragment = frag_shader.err(); - - // FIXME: this should be taken from config and refactored to a function - let path = &["enso", "debug", "shader"]; - let shader_dbg = web::Reflect::get_nested_object_or_create(&web::window, path); - let shader_dbg = shader_dbg.unwrap(); - let len = web::Object::keys(&shader_dbg).length(); - let debug_var_name = format!("shader_{}", len); - let shader_tgt_dbg = web::Object::new(); - web::Reflect::set(&shader_dbg, &(&debug_var_name).into(), &shader_tgt_dbg).ok(); - web::Reflect::set(&shader_tgt_dbg, &"vertex".into(), &vert_src.into()).ok(); - web::Reflect::set(&shader_tgt_dbg, &"fragment".into(), &frag_src.into()).ok(); - - let js_path = format!("window.{}.{}", path.join("."), debug_var_name); - Err(ContextLossOrError::Error(Error::Compile { js_path, vertex, fragment })) - } - } + let vert_shader = compile_vertex_shader(ctx, vert_src).map_err(ContextLossOrError::Error)?; + let frag_shader = compile_fragment_shader(ctx, frag_src).map_err(ContextLossOrError::Error)?; + link_program(ctx, &vert_shader, &frag_shader) } diff --git a/lib/rust/ensogl/example/Cargo.toml b/lib/rust/ensogl/example/Cargo.toml index 3b84e805348..bbdef7116c9 100644 --- a/lib/rust/ensogl/example/Cargo.toml +++ b/lib/rust/ensogl/example/Cargo.toml @@ -10,7 +10,6 @@ crate-type = ["cdylib", "rlib"] [dependencies] ensogl-example-animation = { path = "animation" } ensogl-example-complex-shape-system = { path = "complex-shape-system" } -ensogl-example-custom-shape-system = { path = "custom-shape-system" } ensogl-example-dom-symbols = { path = "dom-symbols" } ensogl-example-drop-manager = { path = "drop-manager" } ensogl-example-easing-animator = { path = "easing-animator" } diff --git a/lib/rust/ensogl/example/animation/src/lib.rs b/lib/rust/ensogl/example/animation/src/lib.rs index be42a5dbb4e..f0ba6ab1157 100644 --- a/lib/rust/ensogl/example/animation/src/lib.rs +++ b/lib/rust/ensogl/example/animation/src/lib.rs @@ -35,9 +35,9 @@ use logger::TraceLogger as Logger; // =================== /// An entry point. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_animation() { run_once_initialized(|| { let app = Application::new("root"); let logger: Logger = Logger::new("AnimationTest"); diff --git a/lib/rust/ensogl/example/complex-shape-system/src/lib.rs b/lib/rust/ensogl/example/complex-shape-system/src/lib.rs index c23d9a24ae7..c5569d46cd5 100644 --- a/lib/rust/ensogl/example/complex-shape-system/src/lib.rs +++ b/lib/rust/ensogl/example/complex-shape-system/src/lib.rs @@ -55,9 +55,9 @@ mod mask { // =================== /// The example entry point. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_complex_shape_system() { let world = World::new().displayed_in("root"); let scene = &world.default_scene; let camera = scene.camera().clone_ref(); diff --git a/lib/rust/ensogl/example/custom-shape-system/Cargo.toml b/lib/rust/ensogl/example/custom-shape-system/Cargo.toml deleted file mode 100644 index 0c9bbe230bd..00000000000 --- a/lib/rust/ensogl/example/custom-shape-system/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "ensogl-example-custom-shape-system" -version = "0.1.0" -authors = ["Enso Team "] -edition = "2021" - -[lib] -crate-type = ["cdylib", "rlib"] - - -[dependencies] -ensogl-core = { path = "../../core" } -enso-frp = { path = "../../../frp" } -wasm-bindgen = { version = "0.2.78", features = [ "nightly" ] } -enso-profiler = { path = "../../../profiler"} diff --git a/lib/rust/ensogl/example/custom-shape-system/src/lib.rs b/lib/rust/ensogl/example/custom-shape-system/src/lib.rs deleted file mode 100644 index 88c486348ad..00000000000 --- a/lib/rust/ensogl/example/custom-shape-system/src/lib.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! Example scene showing simple usage of a shape system. - -// === Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] - -use ensogl_core::display::shape::*; -use ensogl_core::display::world::*; -use ensogl_core::prelude::*; -use wasm_bindgen::prelude::*; - -use enso_frp as frp; -use ensogl_core::data::color; -use ensogl_core::display::navigation::navigator::Navigator; -use ensogl_core::display::object::ObjectOps; - - - -// ============== -// === Shapes === -// ============== - -mod shape { - use super::*; - ensogl_core::define_shape_system! { - (style:Style) { - let circle1 = Circle(50.px()); - let circle_bg = circle1.translate_x(-(50.0.px())); - let circle_sub = circle1.translate_y(-(50.0.px())); - let rect = Rect((100.0.px(),100.0.px())); - let shape = circle_bg + rect - circle_sub; - let shape = shape.fill(color::Rgba::new(0.3, 0.3, 0.3, 1.0)); - shape.into() - } - } -} - - - -// =================== -// === Entry Point === -// =================== - -/// The example entry point. -#[entry_point] -#[allow(dead_code)] -pub fn main() { - let world = World::new().displayed_in("root"); - let scene = &world.default_scene; - let camera = scene.camera().clone_ref(); - let navigator = Navigator::new(scene, &camera); - let logger = Logger::new("ShapeView"); - - let view1 = shape::View::new(&logger); - view1.size.set(Vector2::new(300.0, 300.0)); - view1.mod_position(|t| *t = Vector3::new(50.0, 50.0, 0.0)); - - world.add_child(&view1); - world.keep_alive_forever(); - - frp::new_network! { network - trace view1.events.mouse_over; - trace view1.events.mouse_out; - trace view1.events.mouse_down; - } - - world - .on - .before_frame - .add(move |_time| { - let _keep_alive = &network; - let _keep_alive = &view1; - let _keep_alive = &navigator; - }) - .forget(); -} diff --git a/lib/rust/ensogl/example/dom-symbols/src/lib.rs b/lib/rust/ensogl/example/dom-symbols/src/lib.rs index 37ace4bc4b4..48e39e17c7c 100644 --- a/lib/rust/ensogl/example/dom-symbols/src/lib.rs +++ b/lib/rust/ensogl/example/dom-symbols/src/lib.rs @@ -32,10 +32,10 @@ use nalgebra::Vector3; -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] #[allow(clippy::many_single_char_names)] -pub fn main() { +pub fn entry_point_dom_symbols() { let world = World::new().displayed_in("root"); let scene = &world.default_scene; let camera = scene.camera(); diff --git a/lib/rust/ensogl/example/drop-manager/src/lib.rs b/lib/rust/ensogl/example/drop-manager/src/lib.rs index 9367ae7e9a3..3eff035c9a3 100644 --- a/lib/rust/ensogl/example/drop-manager/src/lib.rs +++ b/lib/rust/ensogl/example/drop-manager/src/lib.rs @@ -52,9 +52,9 @@ fn download_file(file: ensogl_drop_manager::File) { } /// The example entry point. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_drop_manager() { let world = World::new().displayed_in("root"); let drop_manager = ensogl_drop_manager::Manager::new(world.default_scene.dom.root.as_ref()); let network = enso_frp::Network::new("Debug Scene"); diff --git a/lib/rust/ensogl/example/easing-animator/src/lib.rs b/lib/rust/ensogl/example/easing-animator/src/lib.rs index 103aa2dc1fc..0c4a468e3bb 100644 --- a/lib/rust/ensogl/example/easing-animator/src/lib.rs +++ b/lib/rust/ensogl/example/easing-animator/src/lib.rs @@ -285,10 +285,10 @@ macro_rules! examples { )*}; } -/// Runs EasingAnimator example. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +/// Runs EasingAnimator example. +pub fn entry_point_easing_animator() { web::forward_panic_hook_to_console(); web::set_stack_trace_limit(); let container = web::document.create_div_or_panic(); diff --git a/lib/rust/ensogl/example/glyph-system/src/lib.rs b/lib/rust/ensogl/example/glyph-system/src/lib.rs index dd1b2aa8689..4127e6b6b17 100644 --- a/lib/rust/ensogl/example/glyph-system/src/lib.rs +++ b/lib/rust/ensogl/example/glyph-system/src/lib.rs @@ -31,9 +31,9 @@ use ensogl_text_msdf_sys::run_once_initialized; /// Main example runner. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_glyph_system() { run_once_initialized(|| init(&World::new().displayed_in("root"))); } diff --git a/lib/rust/ensogl/example/list-view/src/lib.rs b/lib/rust/ensogl/example/list-view/src/lib.rs index ab5e60f030b..90f2de7b79d 100644 --- a/lib/rust/ensogl/example/list-view/src/lib.rs +++ b/lib/rust/ensogl/example/list-view/src/lib.rs @@ -38,9 +38,9 @@ use logger::TraceLogger as Logger; // =================== /// An entry point. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_list_view() { run_once_initialized(|| { let app = Application::new("root"); init(&app); diff --git a/lib/rust/ensogl/example/mouse-events/src/lib.rs b/lib/rust/ensogl/example/mouse-events/src/lib.rs index 3ae591c3616..e95ca83c078 100644 --- a/lib/rust/ensogl/example/mouse-events/src/lib.rs +++ b/lib/rust/ensogl/example/mouse-events/src/lib.rs @@ -106,10 +106,12 @@ impl View { pub fn new(app: &Application) -> Self { let frp = Frp::new(); let model = Model::new(app); - let network = &frp.network; + let events = &model.shape.events; + let network = &events.network; frp::extend! { network + // FIXME [mwu] Currently only `mouse_over` and `mouse_out` events are delivered. + // See: https://github.com/enso-org/ide/issues/1477 trace model.shape.events.mouse_up; - trace model.shape.events.mouse_release; trace model.shape.events.mouse_down; trace model.shape.events.mouse_over; trace model.shape.events.mouse_out; @@ -157,9 +159,9 @@ impl application::View for View { // =================== /// The example entry point. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_mouse_events() { run_once_initialized(|| { let app = Application::new("root"); let shape: View = app.new_view(); diff --git a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs index 6767c148359..8fe72f571ef 100644 --- a/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs +++ b/lib/rust/ensogl/example/profiling-run-graph/src/lib.rs @@ -34,9 +34,9 @@ use ensogl_flame_graph as flame_graph; // =================== /// The example entry point. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_profiling_run_graph() { web::forward_panic_hook_to_console(); web::set_stack_trace_limit(); diff --git a/lib/rust/ensogl/example/render-profile/src/lib.rs b/lib/rust/ensogl/example/render-profile/src/lib.rs index aff57cd4b4a..14c42050477 100644 --- a/lib/rust/ensogl/example/render-profile/src/lib.rs +++ b/lib/rust/ensogl/example/render-profile/src/lib.rs @@ -31,9 +31,9 @@ use ensogl_flame_graph as flame_graph; // =================== /// Render a graph of a profile file. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub async fn main() { +pub async fn entry_point_render_profile() { use ensogl_core::display::object::ObjectOps; let app = application::Application::new("root"); let world = &app.display; diff --git a/lib/rust/ensogl/example/scroll-area/src/lib.rs b/lib/rust/ensogl/example/scroll-area/src/lib.rs index cfa49921c1d..a63ce837f80 100644 --- a/lib/rust/ensogl/example/scroll-area/src/lib.rs +++ b/lib/rust/ensogl/example/scroll-area/src/lib.rs @@ -43,8 +43,8 @@ use ensogl_text_msdf_sys::run_once_initialized; // =================== /// An entry point. -#[entry_point] -pub fn main() { +#[wasm_bindgen] +pub fn entry_point_scroll_area() { run_once_initialized(|| { let app = Application::new("root"); init(&app); diff --git a/lib/rust/ensogl/example/shape-system/Cargo.toml b/lib/rust/ensogl/example/shape-system/Cargo.toml index 3a00722c3ad..e2b595818a8 100644 --- a/lib/rust/ensogl/example/shape-system/Cargo.toml +++ b/lib/rust/ensogl/example/shape-system/Cargo.toml @@ -11,4 +11,3 @@ crate-type = ["cdylib", "rlib"] [dependencies] ensogl-core = { path = "../../core" } wasm-bindgen = { version = "0.2.78", features = [ "nightly" ] } -enso-profiler = { path = "../../../profiler"} diff --git a/lib/rust/ensogl/example/shape-system/src/lib.rs b/lib/rust/ensogl/example/shape-system/src/lib.rs index 7a37fc7005f..8f546fbf98d 100644 --- a/lib/rust/ensogl/example/shape-system/src/lib.rs +++ b/lib/rust/ensogl/example/shape-system/src/lib.rs @@ -54,9 +54,9 @@ pub fn shape() -> AnyShape { // =================== /// The example entry point. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_shape_system() { let world = World::new().displayed_in("root"); let scene = &world.default_scene; let camera = scene.camera().clone_ref(); @@ -70,19 +70,12 @@ pub fn main() { world.add_child(&sprite_system); world.keep_alive_forever(); - let mut i = 0; world .on .before_frame .add(move |_time| { let _keep_alive = &sprite; let _keep_alive = &navigator; - i += 1; - if i == 5 { - let shader = sprite.symbol.shader().shader().unwrap(); - DEBUG!("\n\nVERTEX:\n{shader.vertex}"); - DEBUG!("\n\nFRAGMENT:\n{shader.fragment}"); - } }) .forget(); } diff --git a/lib/rust/ensogl/example/slider/src/lib.rs b/lib/rust/ensogl/example/slider/src/lib.rs index 290179a9495..c6af28dcc04 100644 --- a/lib/rust/ensogl/example/slider/src/lib.rs +++ b/lib/rust/ensogl/example/slider/src/lib.rs @@ -38,9 +38,9 @@ use ensogl_text_msdf_sys::run_once_initialized; // =================== /// An entry point. -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_slider() { run_once_initialized(|| { let app = Application::new("root"); init(&app); diff --git a/lib/rust/ensogl/example/sprite-system-benchmark/src/lib.rs b/lib/rust/ensogl/example/sprite-system-benchmark/src/lib.rs index ff73b9d81a1..9709b3be12d 100644 --- a/lib/rust/ensogl/example/sprite-system-benchmark/src/lib.rs +++ b/lib/rust/ensogl/example/sprite-system-benchmark/src/lib.rs @@ -31,9 +31,9 @@ use nalgebra::Vector3; -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_sprite_system_benchmark() { let world = World::new().displayed_in("root"); let scene = &world.default_scene; let camera = scene.camera().clone_ref(); diff --git a/lib/rust/ensogl/example/sprite-system/src/lib.rs b/lib/rust/ensogl/example/sprite-system/src/lib.rs index 849075524a8..3a817f79994 100644 --- a/lib/rust/ensogl/example/sprite-system/src/lib.rs +++ b/lib/rust/ensogl/example/sprite-system/src/lib.rs @@ -26,9 +26,9 @@ use ensogl_core::display::symbol::geometry::SpriteSystem; -#[entry_point] +#[wasm_bindgen] #[allow(dead_code)] -pub fn main() { +pub fn entry_point_sprite_system() { let world = World::new().displayed_in("root"); let navigator = Navigator::new(&world.default_scene, &world.default_scene.camera()); let sprite_system = SpriteSystem::new(&world); diff --git a/lib/rust/frp/src/io/mouse.rs b/lib/rust/frp/src/io/mouse.rs index 41613df73dd..eb0007a2036 100644 --- a/lib/rust/frp/src/io/mouse.rs +++ b/lib/rust/frp/src/io/mouse.rs @@ -20,7 +20,7 @@ use nalgebra::Vector2; /// JS supports up to 5 mouse buttons currently: /// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button /// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[allow(missing_docs)] pub enum Button { Button0, @@ -57,12 +57,7 @@ impl Button { /// Construct a button from the provided code point. In case the code is unrecognized, the /// default button will be returned. pub fn from_code(code: i32) -> Self { - Self::try_from_code(code).unwrap_or_else(|| { - let invalid_msg = "The provided mouse button code is invalid"; - let revert_msg = "Reverting to the default button."; - WARNING!("{invalid_msg} ({code}). {revert_msg}"); - default() - }) + Self::try_from_code(code).unwrap_or_default() } /// The code point of the button. diff --git a/lib/rust/prelude/src/clone.rs b/lib/rust/prelude/src/clone.rs index 8cf0246d5f4..4b3daac3ab4 100644 --- a/lib/rust/prelude/src/clone.rs +++ b/lib/rust/prelude/src/clone.rs @@ -5,9 +5,7 @@ use crate::*; // === Export === // ============== -pub use enso_shapely::entry_point; pub use enso_shapely::CloneRef; -pub use enso_shapely::NoCloneBecauseOfCustomDrop; diff --git a/lib/rust/prelude/src/lib.rs b/lib/rust/prelude/src/lib.rs index bdd71fa5d4e..9d346e5e8d4 100644 --- a/lib/rust/prelude/src/lib.rs +++ b/lib/rust/prelude/src/lib.rs @@ -438,14 +438,3 @@ impl WeakRef for Weak { Weak::upgrade(self) } } - - - -// ====================== -// === ImplementsDrop === -// ====================== - -/// Check whether the structure implements custom drop behavior. Used mainly by the -/// [`NoCloneBecauseOfCustomDrop`] macro. -#[allow(drop_bounds)] -pub trait ImplementsDrop: Drop {} diff --git a/lib/rust/shapely/macros/src/derive_entry_point.rs b/lib/rust/shapely/macros/src/derive_entry_point.rs deleted file mode 100644 index eb9f86a6772..00000000000 --- a/lib/rust/shapely/macros/src/derive_entry_point.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::prelude::*; - - - -// =================== -// === Entry Point === -// =================== - -fn crate_name_to_fn_name(name: &str) -> String { - let name = name.replace("ensogl-example-", ""); - let name = name.replace("enso-example-", ""); - let name = name.replace("enso-", ""); - let name = name.replace("example-", ""); - let name = name.replace('-', "_"); - format!("entry_point_{}", name) -} - -pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); - let decl = syn::parse_macro_input!(input as syn::Item); - match decl { - syn::Item::Fn(f) => { - let name = f.sig.ident.to_string(); - if &name != "main" { - panic!("The function should be named 'main'."); - } - let fn_name = quote::format_ident!("{}", crate_name_to_fn_name(&crate_name)); - let mut fn_sig = f.sig.clone(); - fn_sig.ident = fn_name; - let attrs = &f.attrs; - let block = &f.block; - let output = quote! { - #(#attrs)* - #[wasm_bindgen] - pub #fn_sig #block - }; - output.into() - } - _ => panic!("This macro is intended to be used on functions only."), - } -} diff --git a/lib/rust/shapely/macros/src/derive_no_clone.rs b/lib/rust/shapely/macros/src/derive_no_clone.rs deleted file mode 100644 index 994f3e6724f..00000000000 --- a/lib/rust/shapely/macros/src/derive_no_clone.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::prelude::*; - -use syn::DeriveInput; - - - -// =================== -// === Entry Point === -// =================== - -/// Makes sure that the structure does not derive [`Clone`] and that it implements custom [`Drop`] -/// implementation. -pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let decl = syn::parse_macro_input!(input as DeriveInput); - let ident = &decl.ident; - let (impl_generics, ty_generics, _) = &decl.generics.split_for_impl(); - let output = quote! { - impl #impl_generics !Clone for #ident #ty_generics {} - impl #impl_generics ImplementsDrop for #ident #ty_generics {} - }; - output.into() -} diff --git a/lib/rust/shapely/macros/src/lib.rs b/lib/rust/shapely/macros/src/lib.rs index 82cbf3acac3..11492fc41bd 100644 --- a/lib/rust/shapely/macros/src/lib.rs +++ b/lib/rust/shapely/macros/src/lib.rs @@ -22,9 +22,7 @@ extern crate proc_macro; mod derive_clone_ref; -mod derive_entry_point; mod derive_iterator; -mod derive_no_clone; mod overlappable; mod prelude { @@ -93,36 +91,6 @@ pub fn derive_clone_ref(input: proc_macro::TokenStream) -> proc_macro::TokenStre derive_clone_ref::derive(input) } -/// Makes sure that the structure does not derive [`Clone`] and that it implements custom [`Drop`] -/// implementation. -/// -/// For the given input -/// ```ignore -/// #[derive(NoCloneBecauseOfCustomDrop)] -/// struct Test {} -/// ``` -/// -/// The following output will be generated: -/// ```ignore -/// struct Test {} -/// impl !Clone for Test {} -// impl ImplementsDrop for Test {} -/// ``` -#[proc_macro_derive(NoCloneBecauseOfCustomDrop)] -pub fn derive_no_clone(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - derive_no_clone::derive(input) -} - -/// Exposes the function as an application entry point. Entry points are alternative application -/// running modes that you can access by adding `?entry=` to the end of the application URL. -#[proc_macro_attribute] -pub fn entry_point( - _: proc_macro::TokenStream, - item: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - derive_entry_point::derive(item) -} - #[allow(missing_docs)] #[proc_macro_attribute] pub fn overlappable( diff --git a/lib/rust/shapely/src/shared.rs b/lib/rust/shapely/src/shared.rs index 18fb2f3f141..e6a073af1c6 100644 --- a/lib/rust/shapely/src/shared.rs +++ b/lib/rust/shapely/src/shared.rs @@ -378,109 +378,3 @@ macro_rules! _normalize_input { $crate::_normalize_input! { $f $f_args [$($out)* $in] $($rest)* } }; } - - - -/// New version of [`shared`] - faster, understood by IntelliJ, but not yet covering all cases yet -/// (like where contexts in functions). These cases can be added in the future. -#[macro_export] -macro_rules! shared2 { - ($name:ident - $(#$data_meta:tt)* - pub struct $data_name:ident $body:tt - - impl {$( - $(#$fn_meta:tt)* - $fn_vis:vis fn $fn_name:ident $(<$($fn_param:ident : $fn_param_ty:ty),*>)? - ($($fn_args:tt)*) $(-> $fn_out:ty)? { $($fn_body:tt)* } - )*} - ) => { - $(#$data_meta)* - pub struct $data_name $body - - #[derive(Clone, CloneRef)] - $(#$data_meta)* - pub struct $name { - rc: Rc> - } - - $( - $crate::shared_fn2!{ $name $data_name - [[$(#$fn_meta)*] $fn_vis [<$($($fn_param : ($fn_param_ty)),*)?>]] $fn_name - ($($fn_args)*) ($($fn_args)*) [-> ($($fn_out)?)] { $($fn_body)* } - } - )* - }; -} - -#[macro_export] -macro_rules! shared_fn2 { - ($name:ident $data_name:ident - $fn_sig:tt $fn_name:ident - (&self $(,$($fn_arg:ident : $fn_arg_ty:ty),*)?) $fn_all_args:tt $fn_out:tt $fn_body:tt - ) => { - impl $data_name { - $crate::shared_fn_flatten2! { $fn_sig $fn_name $fn_all_args $fn_out $fn_body } - } - - impl $name { - $crate::shared_fn_flatten2! { - $fn_sig $fn_name (&self $(,$($fn_arg : $fn_arg_ty),*)?) $fn_out { - self.rc.borrow().$fn_name($($($fn_arg),*)?) - } - } - } - }; - - ($name:ident $data_name:ident - $fn_sig:tt $fn_name:ident - (&mut self $(,$($fn_arg:ident : $fn_arg_ty:ty),*)?) $fn_all_args:tt $fn_out:tt $fn_body:tt - ) => { - impl $data_name { - $crate::shared_fn_flatten2! { $fn_sig $fn_name $fn_all_args $fn_out $fn_body } - } - - impl $name { - $crate::shared_fn_flatten2! { - $fn_sig $fn_name (&self $(,$($fn_arg : $fn_arg_ty),*)?) $fn_out { - self.rc.borrow_mut().$fn_name($($($fn_arg),*)?) - } - } - } - }; - - ($name:ident $data_name:ident - $fn_sig:tt new ($($fn_arg:ident : $fn_arg_ty:ty),*) $fn_all_args:tt $fn_out:tt $fn_body:tt - ) => { - impl $data_name { - $crate::shared_fn_flatten2! { $fn_sig new $fn_all_args $fn_out $fn_body } - } - - impl $name { - $crate::shared_fn_flatten2! { $fn_sig new $fn_all_args $fn_out { - Self { rc: Rc::new(RefCell::new($data_name::new ($($fn_arg),*))) } - } } - } - }; - - ($name:ident $data_name:ident - $fn_sig:tt $fn_name:ident ($($fn_args2:tt)*) $fn_all_args:tt $fn_out:tt $fn_body:tt - ) => { - impl $data_name { - $crate::shared_fn_flatten2! { $fn_sig $fn_name $fn_all_args $fn_out $fn_body } - } - }; -} - -#[macro_export] -macro_rules! shared_fn_flatten2 { - ( - [ [$($fn_meta:tt)*] $fn_vis:vis [$($fn_params:tt)*] ] - $fn_name:ident ($($fn_args:tt)*) [-> $fn_out:tt] { - $($fn_body:tt)* - } - ) => { - $($fn_meta)* - $fn_vis fn $fn_name $($fn_params)* ($($fn_args)*) -> $fn_out { $($fn_body)* } - }; -} diff --git a/lib/rust/web/src/binding/mock.rs b/lib/rust/web/src/binding/mock.rs index 181378880e4..93c991d0423 100644 --- a/lib/rust/web/src/binding/mock.rs +++ b/lib/rust/web/src/binding/mock.rs @@ -316,7 +316,6 @@ where Self: MockData + MockDefault + AsRef + Into { mock_data! { JsValue fn is_undefined(&self) -> bool; - fn is_null(&self) -> bool; } impl JsValue { @@ -393,9 +392,7 @@ impl From<&JsString> for String { // === Array === -mock_data! { Array => Object - fn length(&self) -> u32; -} +mock_data! { Array => Object } // === Error === diff --git a/lib/rust/web/src/lib.rs b/lib/rust/web/src/lib.rs index ebcd1c6fbfa..167061195be 100644 --- a/lib/rust/web/src/lib.rs +++ b/lib/rust/web/src/lib.rs @@ -359,14 +359,6 @@ ops! { ReflectOps for Reflect /// [`get_nested`] to learn more. fn get_nested_object(target: &JsValue, keys: &[&str]) -> Result; - /// Get the nested value of the provided object. In case the object does not exist, they - /// will be created. See docs of [`get_nested`] to learn more. - fn get_nested_or_create(target: &JsValue, keys: &[&str]) -> Result; - - /// Get the nested value of the provided object and cast it to [`Object`]. In case the - /// object does not exist, they will be created. See docs of [`get_nested`] to learn more. - fn get_nested_object_or_create(target: &JsValue, keys: &[&str]) -> Result; - /// Get the nested value of the provided object and cast it to [`String`]. See docs of /// [`get_nested`] to learn more. fn get_nested_object_printed_as_string(target: &JsValue, keys: &[&str]) @@ -389,35 +381,6 @@ ops! { ReflectOps for Reflect tgt.dyn_into() } - fn get_nested_or_create - (target: &JsValue, keys: &[&str]) -> Result { - let mut tgt = target.clone(); - for key in keys { - let obj = tgt.dyn_into::()?; - let key = (*key).into(); - match Reflect::get(&obj, &key) { - Ok(v) => { - if v.is_undefined() || v.is_null() { - tgt = Object::new().into(); - Reflect::set(&obj, &key, &tgt)?; - } else { - tgt = v; - } - } - Err(_) => { - tgt = Object::new().into(); - Reflect::set(&obj, &key, &tgt)?; - } - } - } - Ok(tgt) - } - - fn get_nested_object_or_create(target: &JsValue, keys: &[&str]) -> Result { - let tgt = Self::get_nested_or_create(target, keys)?; - tgt.dyn_into() - } - fn get_nested_object_printed_as_string (target: &JsValue, keys: &[&str]) -> Result { let tgt = Self::get_nested(target, keys)?; diff --git a/run b/run index 6aca628c31a..bcf4cbe29ee 100755 --- a/run +++ b/run @@ -7,7 +7,7 @@ const os = require('os') const fss = require('fs') const paths = require('./build/paths') -process.on('unhandledRejection', error => { throw(error.stack) }) +process.on('unhandledRejection', error => { throw(error) }) let args = process.argv.slice(2)