mirror of
https://github.com/enso-org/enso.git
synced 2024-08-17 00:40:34 +03:00
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:
parent
8c9c2d79cd
commit
bc4ed96d71
@ -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
2
.github/CODEOWNERS
vendored
@ -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
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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, "")
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user