diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 8f9b1f7d11..6fe0cb4343 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,5 @@ # .git-blame-ignore-revs # Fix prettier config; run prettier 0aa7d7ee4d969ec8e8f9d376e72741dca324fdf6 +# Update code style to use rust fmt (#3131) +c822256e6c531e56e894f9f92654654f63cfd6bc diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f4a5bf5cdb..f3643ca15c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -24,7 +24,7 @@ Cargo.toml /app/gui/ @MichaelMauderer @wdanilo @farmaazon @mwu-tow @kazcw /app/gui/view/ @MichaelMauderer @wdanilo @farmaazon @kazcw /app/gui/view/graph-editor/src/builtin/visualization/java_script/ @MichaelMauderer @wdanilo @farmaazon @kazcw @jdunkerley -/app/ide-desktop/ @MichaelMauderer @wdanilo @kazcw +/app/ide-desktop/ @MichaelMauderer @wdanilo # Engine (old) # This section should be removed once the engine moves to /app/engine diff --git a/app/gui/view/execution-mode-selector/src/lib.rs b/app/gui/view/execution-mode-selector/src/lib.rs index 07c88143f5..9a179df485 100644 --- a/app/gui/view/execution-mode-selector/src/lib.rs +++ b/app/gui/view/execution-mode-selector/src/lib.rs @@ -203,8 +203,6 @@ impl component::Model for Model { scene.layers.panel.add(÷r); dropdown.set_label_layer(&scene.layers.panel_text); - dropdown.restore_shape_constraints(app); - Self { display_object, background, play_button, dropdown, inner_root, divider } } diff --git a/app/gui/view/graph-editor/src/component/breadcrumbs.rs b/app/gui/view/graph-editor/src/component/breadcrumbs.rs index db51543cf6..dacbd089e0 100644 --- a/app/gui/view/graph-editor/src/component/breadcrumbs.rs +++ b/app/gui/view/graph-editor/src/component/breadcrumbs.rs @@ -13,8 +13,6 @@ use ensogl::application::Application; use ensogl::display; use ensogl::display::camera::Camera2d; use ensogl::display::object::ObjectOps; -use ensogl::display::style; -use ensogl::display::Scene; use ensogl::gui::cursor; use std::cmp::Ordering; @@ -50,12 +48,11 @@ pub const HEIGHT: f32 = VERTICAL_MARGIN + breadcrumb::VERTICAL_MARGIN + VERTICAL_MARGIN; -// This should be as large as the shadow around the background. -const MAGIC_SHADOW_MARGIN: f32 = 25.0; /// Text offset to make the text appear more centered. const TEXT_Y_OFFSET: f32 = 2.0; + // ======================== // === RelativePosition === // ======================== @@ -68,38 +65,6 @@ enum RelativePosition { -// ================== -// === Background === -// ================== - -/// A background shape. -pub mod background { - use super::*; - - ensogl::shape! { - alignment = center; - (style:Style) { - let theme = ensogl_hardcoded_theme::graph_editor::breadcrumbs::background; - let theme = style::Path::from(&theme); - let width = Var::::from("input_size.x"); - let height = Var::::from("input_size.y"); - - let corner_radius = style.get_number(theme.sub("corner_radius")); - let shape_width = width - MAGIC_SHADOW_MARGIN.px() * 2.0; - let shape_height = height - MAGIC_SHADOW_MARGIN.px() * 2.0; - let shape = Rect((&shape_width,&shape_height)); - let shape = shape.corners_radius(corner_radius.px()); - - let bg_color = style.get_color(&theme); - let bg = shape.fill(bg_color); - - bg.into() - } - } -} - - - // =========== // === Frp === // =========== @@ -173,7 +138,7 @@ ensogl::define_endpoints! { pub struct BreadcrumbsModel { /// The breadcrumbs panel display object. display_object: display::object::Instance, - background: background::View, + background: Rectangle, project_name: ProjectName, root: display::object::Instance, /// A container for all the breadcrumbs after project name. This contained and all its @@ -205,9 +170,13 @@ impl BreadcrumbsModel { let frp_inputs = frp.input.clone_ref(); let current_index = default(); let camera = scene.camera().clone_ref(); - let background = background::View::new(); + let background: Rectangle = default(); let gap_width = default(); + let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); + use ensogl_hardcoded_theme::graph_editor::breadcrumbs; + background.set_style(breadcrumbs::background::HERE, &style); + scene.layers.panel_background.add(&background); Self { @@ -223,22 +192,15 @@ impl BreadcrumbsModel { camera, gap_width, } - .init(&scene) + .init() } - fn init(self, scene: &Scene) -> Self { + fn init(self) -> Self { self.add_child(&self.root); self.root.add_child(&self.project_name); self.root.add_child(&self.breadcrumbs_container); self.root.add_child(&self.background); - ensogl::shapes_order_dependencies! { - scene => { - background -> breadcrumb::background; - background -> project_name::background; - } - } - self.update_layout(); self @@ -275,11 +237,9 @@ impl BreadcrumbsModel { let background_width = width + 2.0 * BACKGROUND_PADDING; let background_height = crate::MACOS_TRAFFIC_LIGHTS_CONTENT_HEIGHT + BACKGROUND_PADDING * 2.0; - let width_with_shadow = background_width + MAGIC_SHADOW_MARGIN * 2.0; - let height_with_shadow = background_height + MAGIC_SHADOW_MARGIN * 2.0; - self.background.set_size(Vector2(width_with_shadow, height_with_shadow)); - self.background.set_x(width / 2.0); - self.background.set_y(-HEIGHT / 2.0); + self.background.set_size(Vector2(background_width, background_height)); + self.background.set_x(-BACKGROUND_PADDING); + self.background.set_y(-HEIGHT / 2.0 - background_height / 2.0); } fn get_breadcrumb(&self, index: usize) -> Option { diff --git a/app/gui/view/src/status_bar.rs b/app/gui/view/src/status_bar.rs index dfa2d524dc..fff1653762 100644 --- a/app/gui/view/src/status_bar.rs +++ b/app/gui/view/src/status_bar.rs @@ -15,8 +15,6 @@ use crate::graph_editor::component::node::input::area::TEXT_SIZE; use ensogl::application::Application; use ensogl::display; use ensogl::display::camera::Camera2d; -use ensogl::display::style; -use ensogl_component::shadow; use ensogl_hardcoded_theme as theme; use ensogl_text as text; use std::future::Future; @@ -33,8 +31,6 @@ const HEIGHT: f32 = 28.0; pub const PADDING: f32 = 12.0; /// Margin between status bar and edge of the screen const MARGIN: f32 = 12.0; -/// This should be as large as the shadow around the background. -const MAGIC_SHADOW_MARGIN: f32 = 40.0; @@ -85,40 +81,6 @@ pub mod process { -// ================== -// === Background === -// ================== - -mod background { - use super::*; - - ensogl::shape! { - alignment = center; - (style:Style) { - let theme = ensogl_hardcoded_theme::application::status_bar::background; - let theme = style::Path::from(theme); - let width = Var::::from("input_size.x"); - let height = Var::::from("input_size.y"); - - let corner_radius = style.get_number(theme.sub("corner_radius")); - let shape_width = width - MAGIC_SHADOW_MARGIN.px() * 2.0; - let shape_height = height - MAGIC_SHADOW_MARGIN.px() * 2.0; - let shape = Rect((&shape_width,&shape_height)); - let shape = shape.corners_radius(corner_radius.px()); - - let bg_color = style.get_color(&theme); - let bg = shape.fill(bg_color); - let shadow_parameters = shadow::parameters_from_style_path(style,theme.sub("shadow")); - let shadow = shadow::from_shape_with_parameters - (shape.into(),shadow_parameters); - - (shadow + bg).into() - } - } -} - - - // =========== // === FRP === // =========== @@ -149,7 +111,7 @@ ensogl::define_endpoints! { struct Model { display_object: display::object::Instance, root: display::object::Instance, - background: background::View, + background: Rectangle, label: text::Text, events: Rc>>, processes: Rc>>, @@ -162,7 +124,7 @@ impl Model { let scene = &app.display.default_scene; let display_object = display::object::Instance::new(); let root = display::object::Instance::new(); - let background = background::View::new(); + let background: Rectangle = default(); let label = text::Text::new(app); let events = default(); let processes = default(); @@ -173,11 +135,14 @@ impl Model { scene.layers.main.remove(&label); label.add_to_scene_layer(&scene.layers.panel_text); - let text_color_path = theme::application::status_bar::text; + use theme::application::status_bar; + let text_color_path = status_bar::text; let style = StyleWatch::new(&app.display.default_scene.style_sheet); let text_color = style.get_color(text_color_path); label.frp.set_property(.., text_color); label.frp.set_property_default(text_color); + let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet); + background.set_style(status_bar::background::HERE, &style); Self { display_object, root, background, label, events, processes, next_process_id, camera } .init() @@ -205,14 +170,11 @@ impl Model { self.label.set_x(-label_width / 2.0); self.label.set_y(-HEIGHT / 2.0 + TEXT_SIZE / 2.0); - let bg_width = if label_width > 0.0 { - label_width + 2.0 * PADDING + 2.0 * MAGIC_SHADOW_MARGIN - } else { - 0.0 - }; - let bg_height = HEIGHT + 2.0 * MAGIC_SHADOW_MARGIN; + let bg_width = if label_width > 0.0 { label_width + 2.0 * PADDING } else { 0.0 }; + let bg_height = HEIGHT; self.background.set_size(Vector2(bg_width, bg_height)); - self.background.set_y(-HEIGHT / 2.0); + self.background.set_x(-bg_width / 2.0); + self.background.set_y(-HEIGHT / 2.0 - bg_height / 2.0); } fn add_event(&self, label: &event::Label) -> event::Id { diff --git a/app/gui/view/src/window_control_buttons.rs b/app/gui/view/src/window_control_buttons.rs index 7d91653c47..a4f017b4df 100644 --- a/app/gui/view/src/window_control_buttons.rs +++ b/app/gui/view/src/window_control_buttons.rs @@ -149,8 +149,6 @@ impl Model { ensogl::shapes_order_dependencies! { app.display.default_scene => { - ide_view_graph_editor::component::breadcrumbs::background -> close::shape; - ide_view_graph_editor::component::breadcrumbs::background -> fullscreen::shape; shape -> close::shape; shape -> fullscreen::shape; } diff --git a/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs b/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs index 6c2db44d44..d06e694346 100644 --- a/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs +++ b/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs @@ -445,18 +445,9 @@ define_themes! { [light:0, dark:1] status_bar { offset_y = -30.0, -30.0; text = text, text; - background = graph_editor::node::background , graph_editor::node::background; background { + color = graph_editor::node::background , graph_editor::node::background; corner_radius = 14.0 , 14.0; - shadow = shadow , shadow; - shadow { - size = shadow::size , shadow::size; - spread = shadow::spread , shadow::spread; - fading = shadow::fading , shadow::fading; - exponent = shadow::exponent , shadow::exponent; - offset_x = shadow::offset_x , shadow::offset_x; - offset_y = shadow::offset_y , shadow::offset_y; - } } } } @@ -617,18 +608,9 @@ define_themes! { [light:0, dark:1] right = Lcha(0.0,0.0,0.0,0.6) , Lcha(1.0,0.0,0.0,0.6); } } - background = application::background , application::background; background { + color = application::background , application::background; corner_radius = 8.0 , 8.0; - shadow = shadow , shadow; - shadow { - size = shadow::size , shadow::size; - spread = shadow::spread , shadow::spread; - fading = shadow::fading , shadow::fading; - exponent = shadow::exponent , shadow::exponent; - offset_x = shadow::offset_x , shadow::offset_x; - offset_y = shadow::offset_y , shadow::offset_y; - } } } edge { diff --git a/lib/rust/ensogl/component/drop-down-menu/src/lib.rs b/lib/rust/ensogl/component/drop-down-menu/src/lib.rs index 75c42be933..d97bbb9fcd 100644 --- a/lib/rust/ensogl/component/drop-down-menu/src/lib.rs +++ b/lib/rust/ensogl/component/drop-down-menu/src/lib.rs @@ -431,17 +431,6 @@ impl DropDownMenu { self.model.selection_menu.set_label_layer(layer); self.model.label.add_to_scene_layer(layer); } - - /// Set the correct order for the shapes. To be sued after moving the component to a different - /// layer. Workaround for #6241. - pub fn restore_shape_constraints(&self, app: &Application) { - let scene = &app.display.default_scene; - shapes_order_dependencies! { - scene => { - arrow -> chooser_hover_area; - } - } - } } impl display::Object for DropDownMenu { diff --git a/lib/rust/ensogl/component/drop-down/src/model.rs b/lib/rust/ensogl/component/drop-down/src/model.rs index 51ade486f9..05eb0b3e0b 100644 --- a/lib/rust/ensogl/component/drop-down/src/model.rs +++ b/lib/rust/ensogl/component/drop-down/src/model.rs @@ -37,27 +37,6 @@ const OPEN_ANIMATION_OFFSET: f32 = OPEN_ANIMATION_SCALE - 1.001; -// ========================= -// === Shape Definition === -// ========================= - -mod rounded_rect { - use super::*; - ensogl_core::shape! { - alignment = center; - (style:Style, color_rgba: Vector4, corner_radius: f32) { - let color = Var::::from(color_rgba); - let rect = Rect(Var::canvas_size()).corners_radius(corner_radius.px()); - let out = rect.fill(color); - out.into() - } - } -} - -pub type RoundedRect = rounded_rect::View; - - - // ============= // === Model === // ============= @@ -66,7 +45,7 @@ pub type RoundedRect = rounded_rect::View; #[derivative(Clone(bound = ""))] pub struct Model { display_object: display::object::Instance, - background: RoundedRect, + background: Rectangle, pub grid: Grid, selected_entries: Rc>>, cache: Rc>>, @@ -81,7 +60,7 @@ impl component::Model for Model { fn new(app: &Application) -> Self { let display_object = display::object::Instance::new(); - let background = RoundedRect::new(); + let background = default(); let grid = Grid::new(app); display_object.add_child(&background); display_object.add_child(&grid); @@ -132,7 +111,7 @@ impl Model { self.background.set_size(outer_size); // align the dropdown origin to its top left corner - self.background.set_xy(Vector2(outer_width, -outer_height) / 2.0); + self.background.set_y(-outer_height); self.background.corner_radius.set(CORNER_RADIUS); self.grid.set_xy(Vector2(CLIP_PADDING, -CLIP_PADDING)); @@ -280,7 +259,7 @@ impl Model { /// Set the background color of the dropdown. pub fn set_color(&self, color: Lcha) { - self.background.color_rgba.set(color::Rgba::from(color).into()); + self.background.color.set(color::Rgba::from(color).into()); } } diff --git a/lib/rust/ensogl/core/src/display/scene.rs b/lib/rust/ensogl/core/src/display/scene.rs index 1965a9b816..3b2ff0eaf9 100644 --- a/lib/rust/ensogl/core/src/display/scene.rs +++ b/lib/rust/ensogl/core/src/display/scene.rs @@ -599,7 +599,7 @@ pub struct HardcodedLayers { pub label: Layer, pub above_nodes: Layer, pub above_nodes_text: Layer, - /// Layer containing all panels with fixed position (not moving with the panned scene) + /// `panel` layer contains all panels with fixed position (not moving with the panned scene) /// like status bar, breadcrumbs or similar. pub panel_background: Layer, pub panel: Layer, diff --git a/lib/rust/ensogl/core/src/display/scene/layer.rs b/lib/rust/ensogl/core/src/display/scene/layer.rs index 573921b14f..af23d9bf15 100644 --- a/lib/rust/ensogl/core/src/display/scene/layer.rs +++ b/lib/rust/ensogl/core/src/display/scene/layer.rs @@ -641,7 +641,7 @@ impl LayerModel { /// Consume all dirty flags and update the ordering of elements if needed. Returns [`true`] if /// the layer or its sub-layers were modified during this call. pub fn update(&self) -> bool { - self.update_internal(None) + self.update_internal(default(), default()) } /// Consume all dirty flags and update the ordering of elements if needed. @@ -649,10 +649,11 @@ impl LayerModel { pub(crate) fn update_internal( &self, global_element_depth_order: Option<&DependencyGraph>, + parent_depth_order_changed: bool, ) -> bool { let mut was_dirty = false; - if self.depth_order_dirty.check() { + if self.depth_order_dirty.check() || parent_depth_order_changed { was_dirty = true; self.depth_order_dirty.unset(); self.depth_sort(global_element_depth_order); @@ -662,11 +663,13 @@ impl LayerModel { was_dirty = true; self.sublayers.element_depth_order_dirty.unset(); self.for_each_sublayer(|layer| { - layer.update_internal(Some(&*self.global_element_depth_order.borrow())); + let global_order = self.global_element_depth_order.borrow(); + layer.update_internal(Some(&*global_order), true); }); if let Some(layer) = &*self.mask.borrow() { if let Some(layer) = layer.upgrade() { - layer.update_internal(Some(&*self.global_element_depth_order.borrow())); + let global_order = self.global_element_depth_order.borrow(); + layer.update_internal(Some(&*global_order), true); } } } @@ -777,7 +780,7 @@ impl LayerModel { fn remove_all_sublayers(&self) { for layer in self.sublayers.borrow().layers.iter() { if let Some(layer) = layer.upgrade() { - layer.remove_parent() + layer.unset_parent() } } mem::take(&mut *self.sublayers.model.borrow_mut()); @@ -788,15 +791,24 @@ impl LayerModel { if self.flags.contains(LayerFlags::INHERIT_PARENT_CAMERA) { self.set_camera(parent.camera()); } + // Parent's `global_element_depth_order` is an input to depth order computation. + self.depth_order_dirty.set(); } - fn remove_parent(&self) { + /// Clear the parent field. Note that this doesn't remove the layer from its parent's sublayer + /// list. + fn unset_parent(&self) { *self.parent.borrow_mut() = None; + // Recompute depth order, in case removing a parent resolved a cycle. + self.depth_order_dirty.set(); } + /// Clear the parent field and remove the layer from its parent's sublayer list. fn remove_from_parent(&self) { - if let Some(sublayers) = self.parent.borrow_mut().take() { - sublayers.borrow_mut().remove(self.id()); + if let Some(parent) = self.parent.borrow_mut().take() { + parent.borrow_mut().remove(self.id()); + // Recompute depth order, in case removing a parent resolved a cycle. + self.depth_order_dirty.set(); } } diff --git a/lib/rust/ensogl/core/src/display/shape/compound/rectangle.rs b/lib/rust/ensogl/core/src/display/shape/compound/rectangle.rs index f17bc0e569..f7b717e379 100644 --- a/lib/rust/ensogl/core/src/display/shape/compound/rectangle.rs +++ b/lib/rust/ensogl/core/src/display/shape/compound/rectangle.rs @@ -7,6 +7,10 @@ use crate::prelude::*; use crate::data::color; use crate::display; +use crate::display::shape::StyleWatchFrp; +use crate::display::style::data::DataMatch; +use crate::display::style::Path; + // ============== @@ -25,6 +29,7 @@ pub use shape::Shape; pub mod shape { use super::*; crate::shape! { + pointer_events_instanced = true, ( style: Style, color: Vector4, @@ -157,6 +162,15 @@ impl Rectangle { self.modify_view(|view| view.border_color.set(color.into())) } + /// Set whether the shape interacts with the mouse. + pub fn set_pointer_events(&self, enabled: bool) -> &Self { + let disabled = match enabled { + true => 0.0, + false => 1.0, + }; + self.modify_view(|view| view.disable_pointer_events.set(disabled)) + } + /// Set clipping of the shape. The clipping is normalized, which means, that the value of 0.5 /// means that we are clipping 50% of the shape. For positive clip values, the clipping is /// performed always on the left and on the bottom of the shape. For negative clip values, the @@ -205,6 +219,22 @@ impl Rectangle { pub fn keep_top_left_quarter(&self) -> &Self { self.set_clip(Vector2(-0.5, 0.5)) } + + /// Set the style properties from the given [`StyleWatchFrp`]. + pub fn set_style(&self, path: impl Into, style: &StyleWatchFrp) { + let path = path.into(); + macro_rules! set_property { + ($name:ident: $ty:ident) => {{ + let value = style.get(path.sub(stringify!($name))).value(); + let value = value.and_then(|value| value.$ty()); + if let Some(value) = value { + self.view.$name.set(value.into()); + } + }}; + } + set_property!(corner_radius: number); + set_property!(color: color); + } } impl display::Object for Rectangle { 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 424b440013..bbb9c93cb7 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 @@ -37,10 +37,17 @@ const FRAGMENT_RUNNER: &str = include_str!("../glsl/fragment_runner.glsl"); pub struct Builder {} impl Builder { - /// Returns the final GLSL code. If `pointer_events_enabled` is set to false, the generated - /// shape will be transparent for pointer events and will pass them trough. + /// Returns the final GLSL code. + /// + /// `disable_pointer_events` is a GLSL expression determining whether instances of the shader + /// are transparent to pointer events. It should be one of: + /// - "1.0": Pointer events pass through the object. (As GLSL attributes cannot be `bool`s, this + /// is a floating-point equivalent of `true`.) + /// - "0.0": The object receives pointer events. + /// - The name of an instance variable: The variable determines on a per-instance basis whether + /// pointer events are disabled. #[profile(Detail)] - pub fn run(shape: &S, pointer_events_enabled: bool) -> CodeTemplate { + pub fn run(shape: &S, disable_pointer_events: &str) -> CodeTemplate { let mut canvas = Canvas::default(); let shape_ref = shape.draw(&mut canvas); let shape_header = header("Shape Definition"); @@ -48,8 +55,9 @@ impl Builder { canvas.submit_shape_constructor("run"); let shape_def = overload::allow_overloading(&canvas.to_glsl()); let code = [GLSL_BOILERPLATE.as_str(), "", &shape_header, &shape_def].join("\n\n"); - let main = - format!("bool pointer_events_enabled = {pointer_events_enabled};\n{FRAGMENT_RUNNER}"); + let main = format!( + "bool pointer_events_enabled = ({disable_pointer_events}) == 0.0;\n{FRAGMENT_RUNNER}" + ); CodeTemplate::new(code, main, "") } 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 45c7b9e700..a6b62ce80a 100644 --- a/lib/rust/ensogl/core/src/display/shape/primitive/system.rs +++ b/lib/rust/ensogl/core/src/display/shape/primitive/system.rs @@ -123,7 +123,7 @@ pub trait Shape: 'static + Sized + AsRef { type SystemData: CustomSystemData; type ShapeData: Debug; fn definition_path() -> &'static str; - fn pointer_events() -> bool; + fn pointer_events() -> PointerEvents; /// The alignment of the drawn shape's origin position. When set to `center`, the shape's /// origin will be at the center of its bounding box. The default value is `left_bottom`. fn default_alignment() -> alignment::Dim2 { @@ -160,6 +160,17 @@ impl CustomSystemData for () { fn new(_data: &ShapeSystemStandardData, _shape_data: &S::ShapeData) -> Self {} } +/// Specifies whether pointer events are enabled for a shader's instances. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum PointerEvents { + /// Enable pointer events for all instances. + Enabled, + /// Disable pointer events for all instances. + Disabled, + /// An instance attribute enables or disables pointer events. + PerInstance, +} + // ========================= @@ -348,7 +359,7 @@ pub struct ShapeSystemModel { /// Enables or disables pointer events on this shape system. All shapes of a shape system which /// have pointer events disabled will be completely transparent for the mouse (they will pass /// through all mouse events to shapes behind them). - pub pointer_events: Immutable, + pub pointer_events: Immutable, /// Do not use the provided shape definition to generate material's body. It is rarely needed, /// when a custom material is provided that does not require any additional shape definition /// code. For example, the text system uses this field, as its material fully describes how to @@ -363,7 +374,7 @@ impl ShapeSystemModel { pub fn new( shape: def::AnyShape, alignment: alignment::Dim2, - pointer_events: bool, + pointer_events: PointerEvents, definition_path: &'static str, ) -> Self { let sprite_system = SpriteSystem::new(definition_path, alignment); @@ -478,8 +489,17 @@ impl ShapeSystemModel { warn!("No precompiled shader found for '{path}'. This will affect performance."); } if !self.do_not_use_shape_definition.get() { + let disable_pointer_events: Cow<_> = match *self.pointer_events { + PointerEvents::Enabled => "0.0".into(), + PointerEvents::Disabled => "1.0".into(), + PointerEvents::PerInstance => { + let var = "disable_pointer_events"; + self.material.borrow_mut().add_input_def::(var); + format!("input_{var}").into() + } + }; let code = - shader::builder::Builder::run(&*self.shape.borrow(), *self.pointer_events); + shader::builder::Builder::run(&*self.shape.borrow(), &disable_pointer_events); self.material.borrow_mut().set_code(code); } } @@ -618,6 +638,56 @@ macro_rules! shape { [$style] ($($gpu_param : $gpu_param_type),*){$($body)*} } }; + // Recognize `pointer_events_instanced = true`; in addition to passing it to `_shape!`, insert + // a suitable instance attribute into the list of GPU parameters. + ( + $(type SystemData = $system_data:ident;)? + $(type ShapeData = $shape_data:ident;)? + $(flavor = $flavor:path;)? + $(above = [$($always_above_1:tt $(::$always_above_2:tt)*),*];)? + $(below = [$($always_below_1:tt $(::$always_below_2:tt)*),*];)? + $(pointer_events = $pointer_events:tt;)? + pointer_events_instanced = true, + $(alignment = $alignment:tt;)? + ($style:ident : Style $(,$gpu_param : ident : $gpu_param_type : ty)* $(,)?) {$($body:tt)*} + ) => { + $crate::_shape! { + $(SystemData($system_data))? + $(ShapeData($shape_data))? + $(flavor = [$flavor];)? + $(alignment = $alignment;)? + $(above = [$($always_above_1 $(::$always_above_2)*),*];)? + $(below = [$($always_below_1 $(::$always_below_2)*),*];)? + $(pointer_events = $pointer_events;)? + pointer_events_instanced = true; + [$style] (disable_pointer_events : f32$(,$gpu_param : $gpu_param_type)*){$($body)*} + } + }; + // Recognize `pointer_events_instanced = false`. Only `true` and `false` are allowed, because + // if it were a computed value, we wouldn't know during macro expansion whether to create an + // instance parameter for it. + ( + $(type SystemData = $system_data:ident;)? + $(type ShapeData = $shape_data:ident;)? + $(flavor = $flavor:path;)? + $(above = [$($always_above_1:tt $(::$always_above_2:tt)*),*];)? + $(below = [$($always_below_1:tt $(::$always_below_2:tt)*),*];)? + $(pointer_events = $pointer_events:tt;)? + pointer_events_instanced = false, + $(alignment = $alignment:tt;)? + ($style:ident : Style $(,$gpu_param : ident : $gpu_param_type : ty)* $(,)?) {$($body:tt)*} + ) => { + $crate::_shape! { + $(SystemData($system_data))? + $(ShapeData($shape_data))? + $(flavor = [$flavor];)? + $(alignment = $alignment;)? + $(above = [$($always_above_1 $(::$always_above_2)*),*];)? + $(below = [$($always_below_1 $(::$always_below_2)*),*];)? + $(pointer_events = $pointer_events;)? + [$style] ($($gpu_param : $gpu_param_type),*){$($body)*} + } + }; } // FIXME[WD]: This macro was left in the code because glyphs are not able to use the shader @@ -708,10 +778,14 @@ macro_rules! _shape_old { root_call_path!() } - fn pointer_events() -> bool { + fn pointer_events() -> $crate::display::shape::primitive::system::PointerEvents { + use $crate::display::shape::primitive::system::PointerEvents; let _out = true; $(let _out = $pointer_events;)? - _out + match _out { + true => PointerEvents::Enabled, + false => PointerEvents::Disabled, + } } fn default_alignment() -> $crate::display::layout::alignment::Dim2 { @@ -818,6 +892,7 @@ macro_rules! _shape { $(above = [$($always_above_1:tt $(::$always_above_2:tt)*),*];)? $(below = [$($always_below_1:tt $(::$always_below_2:tt)*),*];)? $(pointer_events = $pointer_events:tt;)? + $(pointer_events_instanced = $pointer_events_instanced:tt;)? [$style:ident] ($($gpu_param : ident : $gpu_param_type : ty),* $(,)?) {$($body:tt)*} @@ -869,10 +944,17 @@ macro_rules! _shape { root_call_path!() } - fn pointer_events() -> bool { - let _out = true; - $(let _out = $pointer_events;)? - _out + fn pointer_events() -> $crate::display::shape::primitive::system::PointerEvents { + use $crate::display::shape::primitive::system::PointerEvents; + let _blanket = true; + $(let _blanket = $pointer_events;)? + let _instanced = false; + $(let _instanced = $pointer_events_instanced;)? + match (_blanket, _instanced) { + (_, true) => PointerEvents::PerInstance, + (true, false) => PointerEvents::Enabled, + (false, false) => PointerEvents::Disabled, + } } $(fn default_alignment() -> $crate::display::layout::alignment::Dim2 { diff --git a/lib/rust/ensogl/examples/mouse-events/src/lib.rs b/lib/rust/ensogl/examples/mouse-events/src/lib.rs index a3bb9d90d2..fc8ccb02c3 100644 --- a/lib/rust/ensogl/examples/mouse-events/src/lib.rs +++ b/lib/rust/ensogl/examples/mouse-events/src/lib.rs @@ -1,4 +1,5 @@ //! Example scene showing simple shape component that logs all its mouse events. +//! A partially-transparent shape partially covering it is transparent to mouse events. #![recursion_limit = "1024"] // === Features === @@ -28,31 +29,15 @@ use ensogl_core::prelude::*; use enso_frp as frp; use ensogl_core::application; use ensogl_core::application::Application; +use ensogl_core::data::color::Rgb; +use ensogl_core::data::color::Rgba; use ensogl_core::display; use ensogl_core::display::navigation::navigator::Navigator; use ensogl_core::display::object::ObjectOps; -use ensogl_core::shape; use ensogl_text_msdf::run_once_initialized; -// ============== -// === Shapes === -// ============== - -mod shape { - use super::*; - - shape! { - alignment = center; - (style: Style) { - Circle(100.px()).fill(color::Rgb(1.0,0.0,0.0)).into() - } - } -} - - - // ============= // === Model === // ============= @@ -61,17 +46,29 @@ mod shape { struct Model { app: Application, display_object: display::object::Instance, - shape: shape::View, + shape: Rectangle, + cover: Rectangle, } impl Model { fn new(app: &Application) -> Self { let app = app.clone_ref(); let display_object = display::object::Instance::new(); - let shape = shape::View::new(); - shape.set_size(Vector2::new(100.0, 100.0)); + let shape: Rectangle = default(); + shape.set_size(Vector2(300.0, 300.0)); + shape.set_color(Rgb(1.0, 0.0, 0.0).into()); + shape.set_corner_radius_max(); display_object.add_child(&shape); - Self { app, display_object, shape } + let cover: Rectangle = default(); + // We need a value that will make the covering shape a bit smaller than the main shape. + // Euclid found a good one. + const INVERSE_PHI: f32 = 0.618_033; + cover.set_size(Vector2(300.0 * INVERSE_PHI, 300.0 * INVERSE_PHI)); + cover.set_color(Rgba(0.0, 0.0, 0.0, 0.5)); + cover.set_corner_radius_max(); + cover.set_pointer_events(false); + display_object.add_child(&cover); + Self { app, display_object, shape, cover } } } @@ -162,7 +159,6 @@ pub fn main() { run_once_initialized(|| { let app = Application::new("root"); let shape: View = app.new_view(); - shape.model.shape.set_size((300.0, 300.0)); app.display.add_child(&shape); let scene = &app.display.default_scene;