Layer/Rectangle improvements (#6247)

* Use Rectangle for breadcrumbs background.

* Use Rectangle for status bar bg.

* Use Rectangle for dropdown bg.

* Dirty global_element_depth_order invalidates sublayers

* Setting new parent may invalidate depth order

* Support per-instance pointer_events_enabled

* Remove workaround for #6241
This commit is contained in:
Kaz Wesley 2023-04-19 09:06:20 -07:00 committed by GitHub
parent 8c9c2d79cd
commit bc4ed96d71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 206 additions and 208 deletions

View File

@ -1,3 +1,5 @@
# .git-blame-ignore-revs
# Fix prettier config; run prettier
0aa7d7ee4d969ec8e8f9d376e72741dca324fdf6
# Update code style to use rust fmt (#3131)
c822256e6c531e56e894f9f92654654f63cfd6bc

2
.github/CODEOWNERS vendored
View File

@ -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

View File

@ -203,8 +203,6 @@ impl component::Model for Model {
scene.layers.panel.add(&divider);
dropdown.set_label_layer(&scene.layers.panel_text);
dropdown.restore_shape_constraints(app);
Self { display_object, background, play_button, dropdown, inner_root, divider }
}

View File

@ -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::<Pixels>::from("input_size.x");
let height = Var::<Pixels>::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<Breadcrumb> {

View File

@ -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::<Pixels>::from("input_size.x");
let height = Var::<Pixels>::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<RefCell<Vec<event::Label>>>,
processes: Rc<RefCell<HashMap<process::Id, process::Label>>>,
@ -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 {

View File

@ -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;
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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<f32>, corner_radius: f32) {
let color = Var::<color::Rgba>::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<T> {
display_object: display::object::Instance,
background: RoundedRect,
background: Rectangle,
pub grid: Grid,
selected_entries: Rc<RefCell<HashSet<T>>>,
cache: Rc<RefCell<EntryCache<T>>>,
@ -81,7 +60,7 @@ impl<T> component::Model for Model<T> {
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<T: DropdownValue> Model<T> {
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<T: DropdownValue> Model<T> {
/// 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());
}
}

View File

@ -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,

View File

@ -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<LayerItem>>,
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();
}
}

View File

@ -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<Path>, 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 {

View File

@ -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<S: canvas::Draw>(shape: &S, pointer_events_enabled: bool) -> CodeTemplate {
pub fn run<S: canvas::Draw>(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, "")
}

View File

@ -123,7 +123,7 @@ pub trait Shape: 'static + Sized + AsRef<Self::InstanceParams> {
type SystemData: CustomSystemData<Self>;
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<S: Shape> CustomSystemData<S> for () {
fn new(_data: &ShapeSystemStandardData<S>, _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<bool>,
pub pointer_events: Immutable<PointerEvents>,
/// 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::<f32>(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 {

View File

@ -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;