Refactoring mouse events. (#6078)

This commit is contained in:
Wojciech Daniło 2023-03-28 04:41:25 +02:00 committed by GitHub
parent 76409b285d
commit 3f7c4a47da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1092 additions and 661 deletions

10
Cargo.lock generated
View File

@ -2979,6 +2979,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "ensogl-example-vector-editor"
version = "0.1.0"
dependencies = [
"ensogl-core",
"ensogl-hardcoded-theme",
"wasm-bindgen",
]
[[package]]
name = "ensogl-examples"
version = "0.1.0"
@ -3004,6 +3013,7 @@ dependencies = [
"ensogl-example-sprite-system",
"ensogl-example-sprite-system-benchmark",
"ensogl-example-text-area",
"ensogl-example-vector-editor",
]
[[package]]

View File

@ -68,7 +68,7 @@ impl Ide {
fn alive_log_sending_loop(&self) -> impl Future<Output = ()> + 'static {
let network = &self.network;
let scene = &self.ensogl_app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
let keyboard = &scene.keyboard.frp;
enso_frp::extend! { network

View File

@ -344,7 +344,7 @@ impl View {
// === Activation ===
mouse_down_target <- scene.mouse.frp.down.map(f_!(scene.mouse.target.get()));
mouse_down_target <- scene.mouse.frp_deprecated.down.map(f_!(scene.mouse.target.get()));
selected <- mouse_down_target.map(f!([model,visualization] (target){
if !model.overlay.is_this_target(*target) {
visualization.deactivate.emit(());
@ -362,14 +362,14 @@ impl View {
// === Mouse Cursor ===
app.frp.show_system_cursor <+ overlay.events.mouse_over;
app.frp.hide_system_cursor <+ overlay.events.mouse_out;
app.frp.show_system_cursor <+ overlay.events_deprecated.mouse_over;
app.frp.hide_system_cursor <+ overlay.events_deprecated.mouse_out;
// === Hover ===
frp.source.is_hovered <+ model.overlay.events.mouse_over.constant(true);
frp.source.is_hovered <+ model.overlay.events.mouse_out.constant(false);
frp.source.is_hovered <+ model.overlay.events_deprecated.mouse_over.constant(true);
frp.source.is_hovered <+ model.overlay.events_deprecated.mouse_out.constant(false);
}
init.emit(());
style.init.emit(());

View File

@ -253,9 +253,12 @@ pub fn main() {
eval buttons_x((x) buttons.set_x(*x));
eval buttons_y((y) buttons.set_y(*y));
eval_ next.events.mouse_down(wrapper.switch_to_next());
eval_ previous.events.mouse_down(wrapper.switch_to_previous());
button_pressed <- any(&next.events.mouse_down, &previous.events.mouse_down).constant(());
eval_ next.events_deprecated.mouse_down(wrapper.switch_to_next());
eval_ previous.events_deprecated.mouse_down(wrapper.switch_to_previous());
button_pressed <- any(
&next.events_deprecated.mouse_down,
&previous.events_deprecated.mouse_down
).constant(());
update_docs <- any(&button_pressed, &init);
panel.frp.display_documentation <+ update_docs.map(f_!(wrapper.documentation()));
@ -264,7 +267,7 @@ pub fn main() {
caption_visible <- any(...);
caption_visible <+ init.constant(false);
current_state <- caption_visible.sample(&toggle_caption.events.mouse_down);
current_state <- caption_visible.sample(&toggle_caption.events_deprecated.mouse_down);
caption_visible <+ current_state.not();
panel.frp.show_hovered_item_preview_caption <+ caption_visible.on_change();

View File

@ -489,15 +489,15 @@ impl Breadcrumb {
model.deselect(*old,*new);
});
not_selected <- frp.outputs.selected.map(|selected| !selected);
mouse_over_if_not_selected <- model.view.events.mouse_over.gate(&not_selected);
mouse_out_if_not_selected <- model.view.events.mouse_out.gate(&not_selected);
mouse_over_if_not_selected <- model.view.events_deprecated.mouse_over.gate(&not_selected);
mouse_out_if_not_selected <- model.view.events_deprecated.mouse_out.gate(&not_selected);
eval_ mouse_over_if_not_selected(
model.animations.color.set_target_value(hover_color.into())
);
eval_ mouse_out_if_not_selected(
model.animations.color.set_target_value(model.deselected_color().into())
);
eval_ model.view.events.mouse_down_primary(frp.outputs.clicked.emit(()));
eval_ model.view.events_deprecated.mouse_down_primary(frp.outputs.clicked.emit(()));
}

View File

@ -267,10 +267,12 @@ impl ProjectName {
// === Mouse IO ===
let mouse_down = model.view.events.mouse_down_primary.clone_ref();
output.is_hovered <+ bool(&model.view.events.mouse_out,
&model.view.events.mouse_over);
output.mouse_down <+ model.view.events.mouse_down_primary;
let mouse_down = model.view.events_deprecated.mouse_down_primary.clone_ref();
output.is_hovered <+ bool(
&model.view.events_deprecated.mouse_out,
&model.view.events_deprecated.mouse_over
);
output.mouse_down <+ model.view.events_deprecated.mouse_down_primary;
text_color <- all3(
&frp.output.selected,

View File

@ -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::PointerTarget_DEPRECATED;
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) -> &PointerTarget_DEPRECATED;
fn set_color(&self, color: color::Rgba);
fn set_color_focus(&self, color: color::Rgba);
@ -376,8 +376,8 @@ macro_rules! define_corner_start {
self.focus_split_angle.set(angle);
}
fn events(&self) -> &PointerTarget {
&self.events
fn events(&self) -> &PointerTarget_DEPRECATED {
&self.events_deprecated
}
fn set_color(&self, color: color::Rgba) {
@ -473,8 +473,8 @@ macro_rules! define_corner_end {
self.focus_split_angle.set(angle);
}
fn events(&self) -> &PointerTarget {
&self.events
fn events(&self) -> &PointerTarget_DEPRECATED {
&self.events_deprecated
}
fn set_color(&self, color: color::Rgba) {
@ -558,8 +558,8 @@ macro_rules! define_line {
self.focus_split_angle.set(angle);
}
fn events(&self) -> &PointerTarget {
&self.events
fn events(&self) -> &PointerTarget_DEPRECATED {
&self.events_deprecated
}
fn set_color(&self, color: color::Rgba) {
@ -635,8 +635,8 @@ macro_rules! define_arrow { () => {
self.focus_split_angle.set(angle);
}
fn events(&self) -> &PointerTarget {
&self.events
fn events(&self) -> &PointerTarget_DEPRECATED {
&self.events_deprecated
}
fn set_color(&self, color:color::Rgba) {
@ -765,7 +765,7 @@ macro_rules! define_components {
#[allow(missing_docs)]
pub struct $name {
pub display_object : display::object::Instance,
pub shape_view_events : Rc<Vec<PointerTarget>>,
pub shape_view_events : Rc<Vec<PointerTarget_DEPRECATED>>,
shape_type_map : Rc<HashMap<display::object::Id,ShapeRole>>,
$(pub $field : $field_type),*
}
@ -777,8 +777,8 @@ macro_rules! define_components {
let display_object = display::object::Instance::new();
$(let $field = <$field_type>::new();)*
$(display_object.add_child(&$field);)*
let mut shape_view_events:Vec<PointerTarget> = Vec::default();
$(shape_view_events.push($field.events.clone_ref());)*
let mut shape_view_events:Vec<PointerTarget_DEPRECATED> = Vec::default();
$(shape_view_events.push($field.events_deprecated.clone_ref());)*
let shape_view_events = Rc::new(shape_view_events);
let mut shape_type_map:HashMap<display::object::Id,ShapeRole> = default();

View File

@ -722,7 +722,7 @@ impl Node {
// ths user hovers the drag area. The input port manager merges this information with
// port hover events and outputs the final hover event for any part inside of the node.
let drag_area = &model.drag_area.events;
let drag_area = &model.drag_area.events_deprecated;
drag_area_hover <- bool(&drag_area.mouse_out,&drag_area.mouse_over);
model.input.set_hover <+ drag_area_hover;
model.output.set_hover <+ model.input.body_hover;
@ -731,7 +731,7 @@ impl Node {
// === Background Press ===
out.background_press <+ model.drag_area.events.mouse_down_primary;
out.background_press <+ model.drag_area.events_deprecated.mouse_down_primary;
out.background_press <+ model.input.on_background_press;

View File

@ -357,7 +357,7 @@ mod test {
assert_eq!(app.frp.tooltip.value().content(), None);
// Move the mouse over the visibility button
visibility_icon.view().events.mouse_over.emit(());
visibility_icon.view().events_deprecated.mouse_over.emit(());
// We expect the button to be hovered by the mouse
assert!(visibility_icon.frp.is_hovered.value());
@ -366,7 +366,7 @@ mod test {
assert_eq!(app.frp.tooltip.value().content(), Some("Show preview"));
// Move the mouse away again
visibility_icon.view().events.mouse_out.emit(());
visibility_icon.view().events_deprecated.mouse_out.emit(());
// We expect the tooltip to be gone
assert_eq!(app.frp.tooltip.value().content(), None);

View File

@ -456,9 +456,9 @@ impl Model {
// === Aliases ===
let mouse_over_raw = port_shape.hover.events.mouse_over.clone_ref();
let mouse_out = port_shape.hover.events.mouse_out.clone_ref();
let mouse_down_raw = port_shape.hover.events.mouse_down_primary.clone_ref();
let mouse_over_raw = port_shape.hover.events_deprecated.mouse_over.clone_ref();
let mouse_out = port_shape.hover.events_deprecated.mouse_out.clone_ref();
let mouse_down_raw = port_shape.hover.events_deprecated.mouse_down_primary.clone_ref();
// === Body Hover ===

View File

@ -390,7 +390,7 @@ impl SingleChoiceModel {
let dropdown = Rc::new(RefCell::new(dropdown));
frp::extend! { network
let dot_clicked = activation_shape.events.mouse_down_primary.clone_ref();
let dot_clicked = activation_shape.events_deprecated.mouse_down_primary.clone_ref();
toggle_focus <- dot_clicked.map(f!([display_object](()) !display_object.is_focused()));
set_focused <- any(toggle_focus, frp.set_focused);
eval set_focused([display_object](focus) match focus {

View File

@ -405,10 +405,10 @@ impl PortShapeView {
set_padding_right (this,t:f32) { this.padding_right.set(t) }
}
fn events(&self) -> &component::PointerTarget {
fn events(&self) -> &component::PointerTarget_DEPRECATED {
match self {
Self::Single(t) => &t.events,
Self::Multi(t) => &t.events,
Self::Single(t) => &t.events_deprecated,
Self::Multi(t) => &t.events_deprecated,
}
}
}

View File

@ -561,7 +561,7 @@ impl Container {
// === Selecting Visualization ===
frp::extend! { network
mouse_down_target <- scene.mouse.frp.down.map(f_!(scene.mouse.target.get()));
mouse_down_target <- scene.mouse.frp_deprecated.down.map(f_!(scene.mouse.target.get()));
selected_by_click <= mouse_down_target.map(f!([model] (target){
let vis = &model.visualization;
let activate = || vis.borrow().as_ref().map(|v| v.activate.clone_ref());

View File

@ -355,7 +355,7 @@ impl ActionBar {
let network = &self.frp.network;
let frp = &self.frp;
let model = &self.model;
let mouse = &app.display.default_scene.mouse.frp;
let mouse = &app.display.default_scene.mouse.frp_deprecated;
let visualization_chooser = &model.visualization_chooser.frp;
frp::extend! { network
@ -389,11 +389,11 @@ impl ActionBar {
frp.source.visualisation_selection <+ visualization_chooser.chosen_entry;
let reset_position_icon = &model.icons.reset_position_icon.events;
let reset_position_icon = &model.icons.reset_position_icon.events_deprecated;
let reset_position_icon_down = reset_position_icon.mouse_down_primary.clone_ref();
frp.source.on_container_reset_position <+ reset_position_icon_down;
let drag_icon = &model.icons.drag_icon.events;
let drag_icon = &model.icons.drag_icon.events_deprecated;
let start_dragging = &drag_icon.mouse_down_primary;
end_dragging <- mouse.up.gate(&frp.source.container_drag_state);
should_drag <- bool(&end_dragging,start_dragging);

View File

@ -1307,7 +1307,7 @@ pub struct TouchNetwork<T: frp::Data> {
impl<T: frp::Data> TouchNetwork<T> {
#[allow(missing_docs)] // FIXME[everyone] All pub functions should have docs.
pub fn new(network: &frp::Network, mouse: &frp::io::Mouse) -> Self {
pub fn new(network: &frp::Network, mouse: &frp::io::Mouse_DEPRECATED) -> Self {
frp::extend! { network
down <- source::<T> ();
is_down <- bool(&mouse.up_primary,&down);
@ -1337,7 +1337,7 @@ pub struct TouchState {
impl TouchState {
#[allow(missing_docs)] // FIXME[everyone] All pub functions should have docs.
pub fn new(network: &frp::Network, mouse: &frp::io::Mouse) -> Self {
pub fn new(network: &frp::Network, mouse: &frp::io::Mouse_DEPRECATED) -> Self {
let nodes = TouchNetwork::<NodeId>::new(network, mouse);
let background = TouchNetwork::<()>::new(network, mouse);
Self { nodes, background }
@ -1756,7 +1756,7 @@ impl GraphEditorModel {
let edges = Edges::new();
let vis_registry = visualization::Registry::with_default_visualizations();
let visualisations = default();
let touch_state = TouchState::new(network, &scene.mouse.frp);
let touch_state = TouchState::new(network, &scene.mouse.frp_deprecated);
let breadcrumbs = component::Breadcrumbs::new(app.clone_ref());
let app = app.clone_ref();
let frp = frp.clone_ref();
@ -1768,8 +1768,13 @@ impl GraphEditorModel {
let drop_manager =
ensogl_drop_manager::Manager::new(&scene.dom.root.clone_ref().into(), scene);
let styles_frp = StyleWatchFrp::new(&scene.style_sheet);
let selection_controller =
selection::Controller::new(&frp, &app.cursor, &scene.mouse.frp, &touch_state, &nodes);
let selection_controller = selection::Controller::new(
&frp,
&app.cursor,
&scene.mouse.frp_deprecated,
&touch_state,
&nodes,
);
Self {
display_object,
@ -2697,7 +2702,7 @@ fn new_graph_editor(app: &Application) -> GraphEditor {
let nodes = &model.nodes;
let edges = &model.edges;
let inputs = &model.frp;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
let touch = &model.touch_state;
let vis_registry = &model.vis_registry;
let out = &frp.private.output;
@ -3936,13 +3941,13 @@ mod tests {
graph_editor.stop_editing();
// Creating edge.
let port = node_1.model().output_port_shape().expect("No output port.");
port.events.emit_mouse_down(PrimaryButton);
port.events.emit_mouse_up(PrimaryButton);
port.events_deprecated.emit_mouse_down(PrimaryButton);
port.events_deprecated.emit_mouse_up(PrimaryButton);
assert_eq!(graph_editor.edges().len(), 1);
// Dropping edge.
let mouse = &app.display.default_scene.mouse;
let click_pos = Vector2(300.0, 300.0);
mouse.frp.position.emit(click_pos);
mouse.frp_deprecated.position.emit(click_pos);
let click_on_background = |_: &GraphEditor| mouse.click_on_background();
let (_, node_2) = graph_editor.add_node_by(&click_on_background);
graph_editor.assert(Case { node_source: Some(node_1_id), should_edit: true });
@ -3963,8 +3968,8 @@ mod tests {
graph_editor.stop_editing();
// Creating edge.
let port = node_1.model().output_port_shape().expect("No output port.");
port.events.emit_mouse_down(PrimaryButton);
port.events.emit_mouse_up(PrimaryButton);
port.events_deprecated.emit_mouse_down(PrimaryButton);
port.events_deprecated.emit_mouse_up(PrimaryButton);
let edge_id = graph_editor.on_edge_add.value();
let edge = edges.get_cloned_ref(&edge_id).expect("Edge was not added.");
assert_eq!(edge.source().map(|e| e.node_id), Some(node_id_1));
@ -3974,8 +3979,8 @@ mod tests {
// We need to enable ports. Normally it is done by hovering the node.
node_2.model().input.frp.set_ports_active(true, None);
let port = node_2.model().input_port_shape().expect("No input port.");
port.hover.events.emit_mouse_down(PrimaryButton);
port.hover.events.emit_mouse_up(PrimaryButton);
port.hover.events_deprecated.emit_mouse_down(PrimaryButton);
port.hover.events_deprecated.emit_mouse_up(PrimaryButton);
assert_eq!(edge.source().map(|e| e.node_id), Some(node_id_1));
assert_eq!(edge.target().map(|e| e.node_id), Some(node_id_2));
}
@ -3990,7 +3995,7 @@ mod tests {
editor: &GraphEditor,
mouse_pos: Vector2,
) {
scene.mouse.frp.position.emit(mouse_pos);
scene.mouse.frp_deprecated.position.emit(mouse_pos);
press_add_node_shortcut(editor);
}
@ -4140,7 +4145,7 @@ mod tests {
app.set_screen_size_for_tests();
let graph_editor = new_graph_editor(&app);
let mouse = &app.display.default_scene.mouse;
mouse.frp.position.emit(Vector2::zeros());
mouse.frp_deprecated.position.emit(Vector2::zeros());
(app, graph_editor)
}
}

View File

@ -310,7 +310,7 @@ impl Controller {
pub fn new(
editor: &crate::Frp,
cursor: &Cursor,
mouse: &frp::io::Mouse,
mouse: &frp::io::Mouse_DEPRECATED,
touch: &TouchState,
nodes: &Nodes,
) -> Self {

View File

@ -555,7 +555,7 @@ impl View {
eval_ frp.show_project_list (model.show_project_list());
project_chosen <- project_list.grid.entry_selected.constant(());
mouse_down <- scene.mouse.frp.down.constant(());
mouse_down <- scene.mouse.frp_deprecated.down.constant(());
clicked_on_bg <- mouse_down.filter(f_!(scene.mouse.target.get().is_background()));
should_be_closed <- any(frp.hide_project_list,project_chosen,clicked_on_bg);
eval_ should_be_closed (model.hide_project_list());

View File

@ -248,7 +248,10 @@ impl View {
frp.source.size <+ need_relayout.map(f!((layout) model.set_layout(*layout)));
// Handle the panel-wide hover
mouse_near_buttons <- bool(&model.shape.events.mouse_out,&model.shape.events.mouse_over);
mouse_near_buttons <- bool(
&model.shape.events_deprecated.mouse_out,
&model.shape.events_deprecated.mouse_over
);
mouse_on_any_buttton <- model.close.is_hovered.or(&model.fullscreen.is_hovered);
mouse_nearby <- mouse_near_buttons.or(&mouse_on_any_buttton);
model.close.mouse_nearby <+ mouse_nearby;

View File

@ -167,7 +167,7 @@ async fn adding_node_by_clicking_on_the_output_port() {
let method = |editor: &GraphEditor| {
let port = node_1.model().output_port_shape().expect("No output port");
port.events.mouse_over.emit(());
port.events_deprecated.mouse_over.emit(());
editor.start_node_creation_from_port();
};
let (_, source, node_2) = add_node(&graph_editor, "+ 1", method).await;
@ -285,7 +285,7 @@ async fn mouse_oriented_node_placement() {
}
fn check_tab_key(&self) {
self.scene.mouse.frp.position.emit(self.mouse_position);
self.scene.mouse.frp_deprecated.position.emit(self.mouse_position);
let added_node = self.graph_editor.node_added.next_event();
self.graph_editor.start_node_creation();
self.check_searcher_opening_place(added_node);
@ -293,9 +293,9 @@ async fn mouse_oriented_node_placement() {
fn check_edge_drop(&self) {
let port = self.source_node.view.model().output_port_shape().unwrap();
port.events.emit_mouse_down(PrimaryButton);
port.events.emit_mouse_up(PrimaryButton);
self.scene.mouse.frp.position.emit(self.mouse_position);
port.events_deprecated.emit_mouse_down(PrimaryButton);
port.events_deprecated.emit_mouse_up(PrimaryButton);
self.scene.mouse.frp_deprecated.position.emit(self.mouse_position);
assert!(
self.graph_editor.has_detached_edge.value(),
"No detached edge after clicking port"

View File

@ -280,7 +280,7 @@ impl<Shape: ButtonShape> View<Shape> {
let network = &frp.network;
let scene = &app.display.default_scene;
let style = StyleWatchFrp::new(&scene.style_sheet);
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
// Icon color initialization
let default_icon_color_path = Shape::icon_color_path(State::Unconcerned);
@ -310,7 +310,7 @@ impl<Shape: ButtonShape> View<Shape> {
model.set_background_color(background_unconcerned_color.value());
model.set_icon_color(icon_unconcerned_color.value());
let events = &model.shape.events;
let events = &model.shape.events_deprecated;
frp::extend! { network
eval frp.set_size ((&size) model.shape.set_size(size););
@ -318,7 +318,7 @@ impl<Shape: ButtonShape> View<Shape> {
// Mouse
frp.source.is_hovered <+ bool(&events.mouse_out,&events.mouse_over);
pressed_on_me <- model.shape.events.mouse_down_primary.gate(&frp.is_hovered);
pressed_on_me <- model.shape.events_deprecated.mouse_down_primary.gate(&frp.is_hovered);
tracking_for_release <- bool(&mouse.up_primary,&pressed_on_me);
mouse_released_on_me <- mouse.up_primary.gate(&frp.is_hovered);
was_clicked <- tracking_for_release.previous();

View File

@ -229,7 +229,7 @@ impl DropDownMenu {
let model = &self.model;
let scene = &app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
frp::extend! { network
@ -347,14 +347,14 @@ impl DropDownMenu {
// === Menu Toggle Through Mouse Interaction ===
icon_hovered <- source::<bool>();
eval_ model.icon_overlay.events.mouse_over ( icon_hovered.emit(true) );
eval_ model.icon_overlay.events.mouse_out ( icon_hovered.emit(false) );
eval_ model.icon_overlay.events_deprecated.mouse_over ( icon_hovered.emit(true) );
eval_ model.icon_overlay.events_deprecated.mouse_out ( icon_hovered.emit(false) );
frp.source.icon_mouse_over <+ model.icon_overlay.events.mouse_over;
frp.source.icon_mouse_out <+ model.icon_overlay.events.mouse_out;
frp.source.icon_mouse_over <+ model.icon_overlay.events_deprecated.mouse_over;
frp.source.icon_mouse_out <+ model.icon_overlay.events_deprecated.mouse_out;
let icon_mouse_down = model.icon_overlay.events.mouse_down_primary.clone_ref();
let icon_mouse_down = model.icon_overlay.events_deprecated.mouse_down_primary.clone_ref();
visibility_on_mouse_down <- frp.source.menu_visible.sample(&icon_mouse_down) ;
eval visibility_on_mouse_down ([show_menu,hide_menu](is_visible){

View File

@ -71,7 +71,7 @@ impl component::Frp<Model> for Frp {
model: &Model,
_style: &StyleWatchFrp,
) {
let background = &model.background.events;
let background = &model.background.events_deprecated;
frp::extend! { network
eval api.input.set_content((t) model.set_content(t));
eval api.input.set_size((size) model.set_size(*size));

View File

@ -75,7 +75,7 @@ impl component::Frp<Model> for Frp {
model: &Model,
_style: &StyleWatchFrp,
) {
let background = &model.background.events;
let background = &model.background.events_deprecated;
frp::extend! { network
eval api.input.set_content((t) model.set_content(t));

View File

@ -94,7 +94,7 @@ where EntryParams: frp::node::Data
let init = if let Some(network) = self.network.upgrade_or_warn() {
let entry_frp = entry.frp();
let entry_network = entry_frp.network();
let mouse = &self.app.display.default_scene.mouse.frp;
let mouse = &self.app.display.default_scene.mouse.frp_deprecated;
frp::new_bridge_network! { [network, entry_network] grid_view_entry_bridge
init <- source_();
entry_frp.set_size <+ all(init, self.set_entry_size)._1();
@ -104,7 +104,7 @@ where EntryParams: frp::node::Data
contour_offset <- all(init, entry_frp.contour_offset)._1();
eval contour_offset ((off) overlay.set_xy(*off));
let events = &overlay.events;
let events = &overlay.events_deprecated;
let disabled = &entry_frp.disabled;
let location = entry_frp.set_location.clone_ref();
self.entry_contour <+ all_with(&location, &contour, |&(r, c), &cont| (r, c, cont));

View File

@ -472,7 +472,7 @@ where E::Model: Default
let network = &frp.network;
let model = &self.model;
let scene = &app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
let view_y = Animation::<f32>::new(network);
let selection_y = Animation::<f32>::new(network);
let selection_height = Animation::<f32>::new(network);
@ -498,7 +498,7 @@ where E::Model: Default
// === Mouse Position ===
let overlay_events = &model.overlay.events;
let overlay_events = &model.overlay.events_deprecated;
mouse_in <- bool(&overlay_events.mouse_out, &overlay_events.mouse_over);
frp.source.is_mouse_over <+ mouse_in;
mouse_moved <- mouse.distance.map(|dist| *dist > MOUSE_MOVE_THRESHOLD ).on_true();

View File

@ -360,7 +360,7 @@ impl ScrollArea {
let mouse = &scene.mouse;
frp::extend! { network
hovering <- all_with(&mouse.frp.position,&frp.resize,
hovering <- all_with(&mouse.frp_deprecated.position, &frp.resize,
f!([scene,model](&pos,&size) {
let local_pos = scene.screen_to_object_space(&*model.display_object,pos);
(0.0..=size.x).contains(&local_pos.x) && (-size.y..=0.0).contains(&local_pos.y)
@ -369,7 +369,7 @@ impl ScrollArea {
}
let mouse_manager = &mouse.mouse_manager;
let scroll_handler = f!([model](event:&mouse::OnWheel)
let scroll_handler = f!([model](event: &mouse::Wheel)
if hovering.value() {
model.h_scrollbar.scroll_by(event.delta_x() as f32);
model.v_scrollbar.scroll_by(event.delta_y() as f32);

View File

@ -102,7 +102,7 @@ impl Frp {
let frp = &self;
let network = &frp.network;
let scene = &app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
let thumb_position = OvershootAnimation::new(network);
let thumb_color = color::Animation::new(network);
let background_color = color::Animation::new(network);

View File

@ -7,7 +7,7 @@ use crate::shape::relative_shape_down_position;
use crate::shape::shape_is_dragged;
use enso_frp as frp;
use enso_frp::io::Mouse;
use enso_frp::io::Mouse_DEPRECATED;
use enso_frp::Network;
use ensogl_core::display::object::ObjectOps;
use ensogl_core::display::shape::StyleWatchFrp;
@ -73,20 +73,24 @@ impl Frp {
style: &StyleWatchFrp,
network: &Network,
size: frp::Stream<Vector2>,
mouse: &Mouse,
mouse: &Mouse_DEPRECATED,
) -> Frp {
let net = &network;
let scene = &model.app.display.default_scene;
let shadow = shadow::frp_from_style(style, theme::shadow);
let text_size = style.get_number(theme::text::size);
let is_dragging_left_overflow = shape_is_dragged(net, &model.left_overflow.events, mouse);
let is_dragging_right_overflow = shape_is_dragged(net, &model.right_overflow.events, mouse);
let is_dragging_track = shape_is_dragged(net, &model.track.events, mouse);
let is_dragging_background = shape_is_dragged(net, &model.background.events, mouse);
let is_dragging_left_handle = shape_is_dragged(net, &model.track_handle_left.events, mouse);
let is_dragging_left_overflow =
shape_is_dragged(net, &model.left_overflow.events_deprecated, mouse);
let is_dragging_right_overflow =
shape_is_dragged(net, &model.right_overflow.events_deprecated, mouse);
let is_dragging_track = shape_is_dragged(net, &model.track.events_deprecated, mouse);
let is_dragging_background =
shape_is_dragged(net, &model.background.events_deprecated, mouse);
let is_dragging_left_handle =
shape_is_dragged(net, &model.track_handle_left.events_deprecated, mouse);
let is_dragging_right_handle =
shape_is_dragged(net, &model.track_handle_right.events, mouse);
shape_is_dragged(net, &model.track_handle_right.events_deprecated, mouse);
let background_click = relative_shape_down_position(net, scene, &model.background);
let track_click = relative_shape_down_position(net, scene, &model.track);
@ -136,7 +140,10 @@ impl Frp {
&is_dragging_handle,
);
track_hover <- bool(&model.track.events.mouse_out,&model.track.events.mouse_over);
track_hover <- bool(
&model.track.events_deprecated.mouse_out,
&model.track.events_deprecated.mouse_over
);
}
init_shadow_padding.emit(());

View File

@ -45,7 +45,7 @@ impl Frp {
let frp = &self;
let network = &frp.network;
let scene = &app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
model.show_background(true);

View File

@ -42,7 +42,7 @@ impl Frp {
let frp = &self;
let network = &frp.network;
let scene = &app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
let base_frp = super::Frp::new(model, style, network, frp.resize.clone().into(), mouse);

View File

@ -198,8 +198,8 @@ pub mod right_overflow {
// =======================
use enso_frp::Network;
use ensogl_core::frp::io::Mouse;
use ensogl_core::gui::component::PointerTarget;
use ensogl_core::frp::io::Mouse_DEPRECATED;
use ensogl_core::gui::component::PointerTarget_DEPRECATED;
use ensogl_core::gui::component::ShapeView;
pub use super::frp::*;
@ -211,8 +211,8 @@ use ensogl_core::display::Scene;
/// Dragging is ended by a mouse up.
pub fn shape_is_dragged(
network: &Network,
shape: &PointerTarget,
mouse: &Mouse,
shape: &PointerTarget_DEPRECATED,
mouse: &Mouse_DEPRECATED,
) -> enso_frp::Stream<bool> {
enso_frp::extend! { network
mouse_up <- mouse.up.constant(());
@ -231,10 +231,13 @@ pub fn relative_shape_down_position<T: 'static + Shape>(
scene: &Scene,
shape: &ShapeView<T>,
) -> enso_frp::Stream<Vector2> {
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
enso_frp::extend! { network
mouse_down <- mouse.down.constant(());
over_shape <- bool(&shape.events.mouse_out,&shape.events.mouse_over);
over_shape <- bool(
&shape.events_deprecated.mouse_out,
&shape.events_deprecated.mouse_over
);
mouse_down_over_shape <- mouse_down.gate(&over_shape);
click_positon <- mouse.position.sample(&mouse_down_over_shape);
click_positon <- click_positon.map(f!([scene,shape](pos)
@ -261,8 +264,8 @@ mod tests {
#[test]
fn test_shape_is_dragged() {
let network = enso_frp::Network::new("TestNetwork");
let mouse = enso_frp::io::Mouse::default();
let shape = PointerTarget::default();
let mouse = enso_frp::io::Mouse_DEPRECATED::default();
let shape = PointerTarget_DEPRECATED::default();
let is_dragged = shape_is_dragged(&network, &shape, &mouse);
let _watch = is_dragged.register_watch();

View File

@ -61,7 +61,7 @@ impl component::Frp<Model> for Frp {
model: &Model,
_style: &StyleWatchFrp,
) {
let line = &model.line.events;
let line = &model.line.events_deprecated;
frp::extend! { network
eval api.input.set_size((size) model.set_size(*size));
eval api.input.set_color((color) model.set_color(*color));

View File

@ -375,9 +375,9 @@ impl Slider {
let output = &self.frp.private.output;
let model = &self.model;
let scene = &self.app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
let keyboard = &scene.keyboard.frp;
let component_events = &model.background.events;
let component_events = &model.background.events_deprecated;
frp::extend! { network
@ -621,7 +621,7 @@ impl Slider {
let input = &self.frp.input;
let output = &self.frp.private.output;
let model = &self.model;
let component_events = &model.background.events;
let component_events = &model.background.events_deprecated;
let popup_anim = DelayedAnimation::new(network);
frp::extend! { network
@ -655,7 +655,7 @@ impl Slider {
let input = &self.frp.input;
let output = &self.frp.private.output;
let model = &self.model;
let component_events = &model.background.events;
let component_events = &model.background.events_deprecated;
let tooltip_anim = DelayedAnimation::new(network);
frp::extend! { network

View File

@ -402,7 +402,7 @@ impl Text {
let network = self.frp.network();
let input = &self.frp.input;
let scene = &m.app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
frp::extend! { network
@ -482,7 +482,7 @@ impl Text {
fn init_selections(&self) {
let m = &self.data;
let scene = &m.app.display.default_scene;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
let network = self.frp.network();
let input = &self.frp.input;

View File

@ -217,7 +217,7 @@ impl<Shape: ColorableShape + 'static> ToggleButton<Shape> {
let frp = &self.frp;
let model = &self.model;
let color = color::Animation::new(network);
let icon = &model.icon.events;
let icon = &model.icon.events_deprecated;
// Explicitly define the tooltip placement if none was set. This ensures that this tooltip
// is always correctly placed even when other components use tooltips as well. Otherwise,

View File

@ -89,7 +89,8 @@ impl Application {
let scene = &display.default_scene;
scene.display_in(dom);
let commands = command::Registry::create();
let shortcuts = shortcut::Registry::new(&scene.mouse.frp, &scene.keyboard.frp, &commands);
let shortcuts =
shortcut::Registry::new(&scene.mouse.frp_deprecated, &scene.keyboard.frp, &commands);
let views = view::Registry::create(&display, &commands, &shortcuts);
let cursor = Cursor::new(&display.default_scene);
display.add_child(&cursor);

View File

@ -5,7 +5,7 @@ use enso_shortcuts::traits::*;
use crate::frp;
use crate::frp::io::keyboard;
use crate::frp::io::mouse::Mouse;
use crate::frp::io::mouse::Mouse_DEPRECATED;
use super::command;
use enso_shortcuts as shortcuts;
@ -233,7 +233,7 @@ pub struct Registry {
#[derive(Clone, CloneRef, Debug)]
pub struct RegistryModel {
keyboard: keyboard::Keyboard,
mouse: Mouse,
mouse: Mouse_DEPRECATED,
command_registry: command::Registry,
shortcuts_registry: shortcuts::HashSetRegistry<Shortcut>,
}
@ -248,7 +248,7 @@ impl Deref for Registry {
impl Registry {
/// Constructor.
pub fn new(
mouse: &Mouse,
mouse: &Mouse_DEPRECATED,
keyboard: &keyboard::Keyboard,
cmd_registry: &command::Registry,
) -> Self {
@ -270,7 +270,7 @@ impl Registry {
impl RegistryModel {
/// Constructor.
pub fn new(
mouse: &Mouse,
mouse: &Mouse_DEPRECATED,
keyboard: &keyboard::Keyboard,
command_registry: &command::Registry,
) -> Self {

View File

@ -123,10 +123,10 @@ fn event_listener_options() -> web::AddEventListenerOptions {
}
define_bindings! { target, gloabl_target,
MouseEvent::mousedown => on_down (target, OnDown),
MouseEvent::mouseup => on_up (gloabl_target, OnUp),
MouseEvent::mousemove => on_move (gloabl_target, OnMove),
MouseEvent::mouseleave => on_leave (target, OnLeave),
MouseEvent::mouseenter => on_enter (target, OnEnter),
WheelEvent::wheel => on_wheel (target, OnWheel),
MouseEvent::mousedown => on_down (target, Down),
MouseEvent::mouseup => on_up (gloabl_target, Up),
MouseEvent::mousemove => on_move (gloabl_target, Move),
MouseEvent::mouseleave => on_leave (target, Leave),
MouseEvent::mouseenter => on_enter (target, Enter),
WheelEvent::wheel => on_wheel (target, Wheel),
}

View File

@ -14,90 +14,244 @@ use web::dom::Shape;
// === Event ===
// =============
/// Mouse event wrapper.
#[derive(Debug, Clone, Derivative)]
#[derivative(Default(bound = ""))]
pub struct Event<EventType, JsEvent> {
js_event: Option<JsEvent>,
shape: Shape,
event_type: PhantomData<EventType>,
}
/// Trait allowing extracting the phantom type of [`Event`].
#[allow(missing_docs)]
pub trait IsEvent {
type PhantomType;
}
impl<EventType, JsEvent> IsEvent for Event<EventType, JsEvent> {
type PhantomType = EventType;
}
/// Extract the phantom type of [`Event`].
pub type EventPhantomType<T> = <T as IsEvent>::PhantomType;
impl<EventType, JsEvent> Event<EventType, JsEvent>
where JsEvent: AsRef<web::MouseEvent>
{
/// Constructor.
pub fn new(js_event: JsEvent, shape: Shape) -> Self {
let js_event = Some(js_event);
let event_type = default();
Self { js_event, shape, event_type }
}
/// The horizontal coordinate within the application's viewport at which the event occurred (as
/// opposed to the coordinate within the page).
///
/// For example, clicking on the left edge of the viewport will always result in a mouse event
/// with a [`client_x`] value of 0, regardless of whether the page is scrolled horizontally.
pub fn client_x(&self) -> i32 {
self.js_event.as_ref().map(|t| t.as_ref().client_x()).unwrap_or_default()
}
/// The vertical coordinate within the application's viewport at which the event occurred (as
/// opposed to the coordinate within the page).
///
/// For example, clicking on the bottom edge of the viewport will always result in a mouse event
/// with a [`client_y`] value of 0, regardless of whether the page is scrolled horizontally.
pub fn client_y(&self) -> i32 {
self.shape.height as i32
- self.js_event.as_ref().map(|t| t.as_ref().client_y()).unwrap_or_default()
}
/// The horizontal coordinate (offset) of the mouse pointer in global (screen) coordinates.
pub fn screen_x(&self) -> i32 {
self.js_event.as_ref().map(|t| t.as_ref().screen_x()).unwrap_or_default()
}
/// The vertical coordinate (offset) of the mouse pointer in global (screen) coordinates.
pub fn screen_y(&self) -> i32 {
self.shape.height as i32
- self.js_event.as_ref().map(|t| t.as_ref().screen_y()).unwrap_or_default()
}
/// Indicates which button was pressed on the mouse to trigger the event.
pub fn button(&self) -> mouse::Button {
mouse::Button::from_code(
self.js_event.as_ref().map(|t| t.as_ref().button().into()).unwrap_or_default(),
)
}
/// Return the position relative to the event handler that was used to catch the event. If the
/// event handler does not have a position in the DOM, the returned position will be relative to
/// the viewport. This can happen if the event handler is, for example, the window.
///
/// Note: may cause reflow of the JS layout.
pub fn position_relative_to_event_handler(&self) -> Vector2<f32> {
if let Some(element) = self.try_get_current_target_element() {
self.relative_position_with_reflow(&element)
} else {
Vector2::new(self.client_x() as f32, self.client_y() as f32)
}
}
/// Return the event handler that caught this event if it exists and if it is an HTML element.
/// Returns `None` if the event was caught, for example, byt the window.
fn try_get_current_target_element(&self) -> Option<web::Element> {
let target = self.js_event.as_ref().and_then(|t| t.as_ref().current_target())?;
target.value_of().dyn_into::<web::Element>().ok()
}
/// Return the position relative to the given element.
///
/// Note: causes reflow of the JS layout.
pub fn relative_position_with_reflow(&self, element: &web::Element) -> Vector2<f32> {
let rect = element.get_bounding_client_rect();
let x = self.client_x() as f64 - rect.left();
let y = self.client_y() as f64 - rect.top();
Vector2::new(x as f32, y as f32)
}
/// Check whether the `ctrl` key was pressed when the event was triggered.
pub fn ctrl_key(&self) -> bool {
self.js_event.as_ref().map(|t| t.as_ref().ctrl_key()).unwrap_or_default()
}
/// Prevent the default action of the event.
pub fn prevent_default(&self) {
self.js_event.as_ref().map(|t| t.as_ref().prevent_default());
}
/// Convert the event to a different type. No checks will be performed during this action.
pub fn unchecked_convert_to<NewEventType: IsEvent>(
self,
) -> Event<EventPhantomType<NewEventType>, JsEvent> {
let js_event = self.js_event;
let shape = self.shape;
let event_type = default();
Event { js_event, shape, event_type }
}
}
// ==============
// === Events ===
// ==============
macro_rules! define_events {
( $( $js_event:ident :: $name:ident ),* $(,)? ) => {$(
/// Mouse event wrapper.
#[derive(Debug, Clone, From, Deref, AsRef)]
pub struct $name {
#[deref]
raw : web::$js_event,
shape : Shape,
}
impl $name {
( $( $(#$meta:tt)* $name:ident <$js_event:ident> ),* $(,)? ) => {paste!{
$(
$(#$meta)*
#[derive(Copy, Clone, Debug, Default)]
pub struct [<Phantom $name>];
/// Constructor.
pub fn new(raw:web::$js_event,shape:Shape) -> Self {
Self {raw,shape}
}
/// The Y coordinate of the mouse pointer relative to the position of the padding edge
/// of the target node.
pub fn offset_y(&self) -> i32 {
self.shape.height as i32 - self.raw.offset_y()
}
/// The Y coordinate of the mouse pointer in local (DOM content) coordinates.
pub fn client_y(&self) -> i32 {
self.shape.height as i32 - self.raw.client_y()
}
/// The Y coordinate of the mouse pointer in global (screen) coordinates.
pub fn screen_y(&self) -> i32 {
self.shape.height as i32 - self.raw.screen_y()
}
/// Translation of the button property to Rust `Button` enum.
pub fn button(&self) -> mouse::Button {
mouse::Button::from_code(self.raw.button().into())
}
/// Return the position relative to the event handler that was used to catch the event.
/// If the event handler does not have a position in the DOM, the returned position
/// will be relative to the viewport. This can happen if the event handler is, for
/// example, the window.
///
/// Note: may cause reflow of the JS layout.
pub fn position_relative_to_event_handler(&self) -> Vector2<f32> {
if let Some(element) = self.try_get_current_target_element() {
self.relative_position_with_reflow(&element)
} else {
Vector2::new(self.client_x() as f32,self.client_y() as f32)
}
}
/// Return the event handler that caught this event if it exists and if it is an
/// html element. Returns `None` if the event was caught, for example, byt the window.
fn try_get_current_target_element(&self) -> Option<web::Element> {
let target = self.current_target()?;
target.value_of().dyn_into::<web::Element>().ok()
}
/// Return the position relative to the given element.
///
/// Note: causes reflow of the JS layout.
pub fn relative_position_with_reflow(&self, element:&web::Element) -> Vector2<f32> {
let rect = element.get_bounding_client_rect();
let x = self.client_x() as f64 - rect.left();
let y = self.client_y() as f64 - rect.top();
Vector2::new(x as f32,y as f32)
}
}
impl AsRef<web::Event> for $name {
fn as_ref(&self) -> &web::Event {
let js_event = AsRef::<web::$js_event>::as_ref(self);
js_event.as_ref()
}
}
)*};
$(#$meta)*
pub type $name = Event<[<Phantom $name>], web::$js_event>;
)*
}};
}
define_events! {
MouseEvent::OnDown,
MouseEvent::OnUp,
MouseEvent::OnMove,
MouseEvent::OnLeave,
MouseEvent::OnEnter,
WheelEvent::OnWheel,
// ======================
// === JS-like Events ===
// ======================
// These events are counterpart of the JavaScript events. They have the same behavior in the
// EnsoGL display object hierarchy. To learn more about them, see:
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/mousedown_event
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseout_event
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseover_event
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
/// The [`Down`] event is fired at an element when a button on a pointing device (such as a
/// mouse or trackpad) is pressed while the pointer is inside the element.
///
/// The [`Down`] event is the counterpoint to the [`Up`] event.
Down<MouseEvent>,
/// The [`Up`] event is fired at an element when a button on a pointing device (such as a mouse
/// or trackpad) is released while the pointer is located inside it.
///
/// The [`Up`] event is the counterpoint to the [`Down`] event.
Up<MouseEvent>,
/// The [`Move`] event is fired at an element when a pointing device (such as a mouse or
/// trackpad) is moved while the cursor's hotspot is inside it.
Move<MouseEvent>,
/// The [`Enter`] event is fired at an element when the cursor of a pointing device (such as a
/// mouse or trackpad) is initially moved so that its hotspot is within the element at which the
/// event was fired.
///
/// Both [`Enter`] and [`Over`] events are similar but differ in that [`Enter`] does not bubble
/// and [`Over`] does. This means that [`Enter`] is fired when the pointer has entered the
/// element and all of its descendants, whereas [`Over`] is fired when the pointer enters the
/// element or enters one of the element's descendants (even if the pointer was already within
/// the element).
Enter<MouseEvent>,
/// The [`Leave`] event is fired at an element when the cursor of a pointing device (such as a
/// mouse or trackpad) is moved out of it.
///
/// Both [`Leave`] and [`Out`] events are similar but differ in that [`Leave`] does not bubble
/// and [`Out`] does. This means that [`Leave`] is fired when the pointer has exited the element
/// and all of its descendants, whereas [`Out`] is fired when the pointer leaves the element or
/// leaves one of the element's descendants (even if the pointer is still within the element).
Leave<MouseEvent>,
/// The [`Over`] event is fired at an element when the cursor of a pointing device (such as a
/// mouse or trackpad) is moved onto the element or one of its child elements
///
/// Both [`Enter`] and [`Over`] events are similar but differ in that [`Enter`] does not bubble
/// and [`Over`] does. This means that [`Enter`] is fired when the pointer has entered the
/// element and all of its descendants, whereas [`Over`] is fired when the pointer enters the
/// element or enters one of the element's descendants (even if the pointer was already within
/// the element).
Over<MouseEvent>,
/// The [`Out`] event is fired at an element when the cursor of a pointing device (such as a
/// mouse or trackpad) is moved so that it is no longer contained within the element or one of
/// its children.
///
/// Both [`Leave`] and [`Out`] events are similar but differ in that [`Leave`] does not bubble
/// and [`Out`] does. This means that [`Leave`] is fired when the pointer has exited the element
/// and all of its descendants, whereas [`Out`] is fired when the pointer leaves the element or
/// leaves one of the element's descendants (even if the pointer is still within the element).
Out<MouseEvent>,
/// The wheel event fires when the user rotates a wheel button on a pointing device
/// (typically a mouse).
Wheel<WheelEvent>,
// ==========================
// === Non JS-like Events ===
// ==========================
// These events do not have their JavaScript counterpart and are EnsoGL-specific extensions to
// the mouse event family
/// The [`Release`] event is fired at an element when a button on a pointing device (such as a
/// mouse or trackpad) is released anywhere in the scene, if it was previously pressed on that
/// element.
///
/// The [`Release`] event is similar to the [`Up`] event, but fires even if the mouse is outside
/// of the element it was initially pressed on.
Release<MouseEvent>,
}
impl Wheel {
/// The horizontal scroll amount.
pub fn delta_x(&self) -> f64 {
self.js_event.as_ref().map(|t| t.delta_x()).unwrap_or_default()
}
/// The vertical scroll amount.
pub fn delta_y(&self) -> f64 {
self.js_event.as_ref().map(|t| t.delta_y()).unwrap_or_default()
}
}

View File

@ -487,7 +487,7 @@ impl Monitor {
let config = renderer.borrow().user_config.clone();
let scene = scene();
let network = &self.frp.network;
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
let label_width = config.outer_margin + 2.0 * config.margin + config.labels_width;
enso_frp::extend! { network
init <- source_();

View File

@ -236,7 +236,7 @@ impl NavigatorEvents {
fn initialize_wheel_zoom(&mut self) {
let data = Rc::downgrade(&self.data);
let listener = self.mouse_manager.on_wheel.add(move |event: &mouse::OnWheel| {
let listener = self.mouse_manager.on_wheel.add(move |event: &mouse::Wheel| {
if let Some(data) = data.upgrade() {
if event.ctrl_key() {
// Prevent zoom event to be handed to the browser. This avoids browser scaling
@ -265,7 +265,7 @@ impl NavigatorEvents {
fn initialize_mouse_start_event(&mut self) {
let data = Rc::downgrade(&self.data);
let listener = self.mouse_manager.on_down.add(move |event: &mouse::OnDown| {
let listener = self.mouse_manager.on_down.add(move |event: &mouse::Down| {
if let Some(data) = data.upgrade() {
if data.is_navigator_enabled() {
event.prevent_default();
@ -285,7 +285,7 @@ impl NavigatorEvents {
fn initialize_mouse_end_event(&mut self) {
let data = Rc::downgrade(&self.data);
let listener = self.mouse_manager.on_up.add(move |event: &mouse::OnUp| {
let listener = self.mouse_manager.on_up.add(move |event: &mouse::Up| {
if let Some(data) = data.upgrade() {
if data.is_navigator_enabled() {
event.prevent_default();
@ -296,7 +296,7 @@ impl NavigatorEvents {
self.mouse_up = Some(listener);
let data = Rc::downgrade(&self.data);
let listener = self.mouse_manager.on_leave.add(move |event: &mouse::OnLeave| {
let listener = self.mouse_manager.on_leave.add(move |event: &mouse::Leave| {
if let Some(data) = data.upgrade() {
if data.is_navigator_enabled() {
event.prevent_default();
@ -309,7 +309,7 @@ impl NavigatorEvents {
fn initialize_mouse_move_event(&mut self) {
let data = Rc::downgrade(&self.data);
let listener = self.mouse_manager.on_move.add(move |event: &mouse::OnMove| {
let listener = self.mouse_manager.on_move.add(move |event: &mouse::Move| {
if let Some(data) = data.upgrade() {
let position = event.position_relative_to_event_handler();
data.set_mouse_position(position);

View File

@ -64,6 +64,11 @@ impl SomeEvent {
pub fn is_cancelled(&self) -> bool {
self.state() == State::Cancelled
}
/// Enables or disables bubbling for this event.
pub fn set_bubbling(&self, value: bool) {
self.bubbles.set(value);
}
}
impl Default for SomeEvent {

View File

@ -2135,7 +2135,15 @@ impl InstanceDef {
fn emit_event<T>(&self, payload: T)
where T: 'static {
self.event.source.emit(event::SomeEvent::new(Some(self.downgrade()), payload));
let event = self.new_event(payload);
self.event.source.emit(event);
}
fn emit_event_without_bubbling<T>(&self, payload: T)
where T: 'static {
let event = self.new_event(payload);
event.set_bubbling(false);
self.event.source.emit(event);
}
fn focused_descendant(&self) -> Option<Instance> {
@ -3773,6 +3781,13 @@ pub trait ObjectOps: Object + AutoLayoutOps + LayoutOps {
self.display_object().def.emit_event(event)
}
/// Emit a new event that does not participate in the bubbling propagation phase. See docs of
/// [`event::Event`] to learn more.
fn emit_event_without_bubbling<T>(&self, event: T)
where T: 'static {
self.display_object().def.emit_event_without_bubbling(event)
}
/// Get event stream for bubbling events. See docs of [`event::Event`] to learn more.
fn on_event<T>(&self) -> frp::Stream<event::Event<T>>
where T: frp::Data {

View File

@ -30,7 +30,6 @@ use crate::system::web;
use crate::system::web::EventListenerHandle;
use enso_frp as frp;
use enso_frp::io::js::JsEvent;
use enso_shapely::shared;
use std::any::TypeId;
use web::HtmlElement;
@ -49,8 +48,8 @@ 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 use pointer_target::PointerTarget_DEPRECATED;
@ -61,18 +60,18 @@ pub use pointer_target::PointerTargetId;
shared! { PointerTargetRegistry
#[derive(Debug)]
pub struct ShapeRegistryData {
mouse_target_map : HashMap<PointerTargetId, PointerTarget>,
mouse_target_map : HashMap<PointerTargetId, (PointerTarget_DEPRECATED, display::object::Instance)>,
}
impl {
fn new(background: &PointerTarget) -> Self {
fn new(background_pointer_target: &PointerTarget_DEPRECATED, background: &display::object::Instance) -> Self {
let mouse_target_map = default();
Self {mouse_target_map} . init(background)
Self {mouse_target_map} . init(background_pointer_target, background)
}
pub fn insert
(&mut self, id:impl Into<PointerTargetId>, target:impl Into<PointerTarget>) {
self.mouse_target_map.insert(id.into(),target.into());
(&mut self, id:impl Into<PointerTargetId>, target:impl Into<PointerTarget_DEPRECATED>, display_object:&display::object::Instance) {
self.mouse_target_map.insert(id.into(),(target.into(), display_object.clone_ref()));
}
pub fn remove
@ -80,32 +79,39 @@ impl {
self.mouse_target_map.remove(&id.into());
}
pub fn get(&self, target:PointerTargetId) -> Option<PointerTarget> {
pub fn get(&self, target:PointerTargetId) -> Option<(PointerTarget_DEPRECATED, display::object::Instance)> {
self.mouse_target_map.get(&target).cloned()
}
}}
impl ShapeRegistryData {
fn init(mut self, background: &PointerTarget) -> Self {
self.mouse_target_map.insert(PointerTargetId::Background, background.clone_ref());
fn init(
mut self,
background: &PointerTarget_DEPRECATED,
display_object: &display::object::Instance,
) -> Self {
self.mouse_target_map.insert(
PointerTargetId::Background,
(background.clone_ref(), display_object.clone_ref()),
);
self
}
}
impl PointerTargetRegistry {
/// Runs the provided function on the [`PointerTarget`] associated with the provided
/// [`PointerTargetId`]. Please note that the [`PointerTarget`] will be cloned because during
/// evaluation of the provided function this registry might be changed, which would result in
/// double borrow mut otherwise.
/// Runs the provided function on the [`PointerTarget_DEPRECATED`] associated with the provided
/// [`PointerTargetId`]. Please note that the [`PointerTarget_DEPRECATED`] will be cloned
/// because during evaluation of the provided function this registry might be changed, which
/// would result in double borrow mut otherwise.
pub fn with_mouse_target<T>(
&self,
target: PointerTargetId,
f: impl FnOnce(&PointerTarget) -> T,
target_id: PointerTargetId,
f: impl FnOnce(&PointerTarget_DEPRECATED, &display::object::Instance) -> T,
) -> Option<T> {
match self.get(target) {
Some(target) => Some(f(&target)),
match self.get(target_id) {
Some(t) => Some(f(&t.0, &t.1)),
None => {
warn!("Internal error. Symbol ID {target:?} is not registered.");
warn!("Internal error. Symbol ID {target_id:?} is not registered.");
None
}
}
@ -120,15 +126,25 @@ impl PointerTargetRegistry {
#[derive(Clone, CloneRef, Debug)]
pub struct Mouse {
pub mouse_manager: MouseManager,
pub last_position: Rc<Cell<Vector2<i32>>>,
pub position: Uniform<Vector2<i32>>,
pub click_count: Uniform<i32>,
pub hover_rgba: Uniform<Vector4<u32>>,
pub target: Rc<Cell<PointerTargetId>>,
pub handles: Rc<[callback::Handle; 6]>,
pub frp: enso_frp::io::Mouse,
pub scene_frp: Frp,
pub mouse_manager: MouseManager,
pub last_position: Rc<Cell<Vector2<i32>>>,
pub position: Uniform<Vector2<i32>>,
pub click_count: Uniform<i32>,
/// The encoded value of pointer target ID. The offscreen canvas containing the encoded IDs is
/// sampled and the most recent sample is stored here. Please note, that this sample may be
/// from a few frames back, as it is not guaranteed when we will receive the data from GPU.
pub pointer_target_encoded: Uniform<Vector4<u32>>,
pub target: Rc<Cell<PointerTargetId>>,
pub handles: Rc<[callback::Handle; 6]>,
pub scene_frp: Frp,
/// Stored in order to be converted to [`mouse::Over`], [`mouse::Out`], [`mouse::Enter`], and
/// [`mouse::Leave`] when the mouse enters or leaves an element.
pub last_move_event: Rc<RefCell<Option<mouse::Move>>>,
/// # Deprecated
/// This API is deprecated. Instead, use the display object's event API. For example, to get an
/// FRP endpoint for mouse event, you can use the [`crate::display::Object::on_event`]
/// function.
pub frp_deprecated: enso_frp::io::Mouse_DEPRECATED,
}
impl Mouse {
@ -136,21 +152,26 @@ impl Mouse {
scene_frp: &Frp,
root: &web::dom::WithKnownShape<web::HtmlDivElement>,
variables: &UniformScope,
js_event: &JsEvent,
display_mode: &Rc<Cell<glsl::codes::DisplayModes>>,
pointer_target_registry: &PointerTargetRegistry,
) -> Self {
let last_pressed_elem: Rc<RefCell<HashMap<mouse::Button, PointerTargetId>>> = default();
let scene_frp = scene_frp.clone_ref();
let target = PointerTargetId::default();
let last_position = Rc::new(Cell::new(Vector2::default()));
let position = variables.add_or_panic("mouse_position", Vector2::default());
let click_count = variables.add_or_panic("mouse_click_count", 0);
let hover_rgba = variables.add_or_panic("mouse_hover_ids", Vector4::default());
let pointer_target_encoded = variables.add_or_panic("mouse_hover_ids", Vector4::default());
let target = Rc::new(Cell::new(target));
let shaped_dom = root.clone_ref().into();
let mouse_manager = MouseManager::new(&shaped_dom, root, &web::window);
let frp = frp::io::Mouse::new();
let on_move = mouse_manager.on_move.add(js_event.handler(
f!([frp, scene_frp, position, last_position] (event: &mouse::OnMove) {
let frp_deprecated = frp::io::Mouse_DEPRECATED::new();
let last_move_event = Rc::new(RefCell::new(None));
let on_move = mouse_manager.on_move.add(
f!([pointer_target_registry, target, frp_deprecated, scene_frp, position, last_position, last_move_event]
(event: &mouse::Move) {
last_move_event.borrow_mut().replace(event.clone());
let shape = scene_frp.shape.value();
let pixel_ratio = shape.pixel_ratio;
let screen_x = event.client_x();
@ -164,39 +185,71 @@ impl Mouse {
let position_bottom_left = Vector2(new_pos.x as f32, new_pos.y as f32);
let position_top_left = Vector2(new_pos.x as f32, shape.height - new_pos.y as f32);
let position = position_bottom_left - shape.center();
frp.position_bottom_left.emit(position_bottom_left);
frp.position_top_left.emit(position_top_left);
frp.position.emit(position);
frp_deprecated.position_bottom_left.emit(position_bottom_left);
frp_deprecated.position_top_left.emit(position_top_left);
frp_deprecated.position.emit(position);
pointer_target_registry.with_mouse_target(target.get(), |_, d| {
d.emit_event(event.clone());
});
}
}),
));
let on_down = mouse_manager.on_down.add(js_event.handler(
f!([frp, click_count, display_mode] (event:&mouse::OnDown) {
);
let on_down = mouse_manager.on_down.add(
f!([pointer_target_registry, target, last_pressed_elem, frp_deprecated, click_count, display_mode]
(event:&mouse::Down) {
click_count.modify(|v| *v += 1);
if display_mode.get().allow_mouse_events() {
frp.down.emit(event.button());
}
}),
));
let on_up = mouse_manager.on_up.add(js_event.handler(
f!([frp, display_mode] (event:&mouse::OnUp) {
if display_mode.get().allow_mouse_events() {
frp.up.emit(event.button())
}
}),
));
let on_wheel = mouse_manager.on_wheel.add(js_event.handler(f_!([frp, display_mode] {
if display_mode.get().allow_mouse_events() {
frp.wheel.emit(())
}
})));
let button = event.button();
frp_deprecated.down.emit(button);
let on_leave = mouse_manager.on_leave.add(js_event.handler(f_!(
let current_target = target.get();
last_pressed_elem.borrow_mut().insert(button, current_target);
pointer_target_registry.with_mouse_target(current_target, |t, d| {
t.emit_mouse_down(button);
d.emit_event(event.clone());
});
}
}),
);
let on_up = mouse_manager.on_up.add(
f!([pointer_target_registry, target, last_pressed_elem, frp_deprecated, display_mode]
(event: &mouse::Up) {
if display_mode.get().allow_mouse_events() {
let button = event.button();
frp_deprecated.up.emit(button);
let current_target = target.get();
if let Some(last_target) = last_pressed_elem.borrow_mut().remove(&button) {
pointer_target_registry.with_mouse_target(last_target, |t, d| {
t.emit_mouse_release(button);
d.emit_event(event.clone().unchecked_convert_to::<mouse::Release>());
});
}
pointer_target_registry.with_mouse_target(current_target, |t, d| {
t.emit_mouse_up(button);
d.emit_event(event.clone());
});
}
}),
);
let on_wheel = mouse_manager.on_wheel.add(
f!([pointer_target_registry, target, frp_deprecated, display_mode] (event: &mouse::Wheel) {
if display_mode.get().allow_mouse_events() {
frp_deprecated.wheel.emit(());
pointer_target_registry.with_mouse_target(target.get(), |_, d| {
d.emit_event(event.clone());
});
}
}),
);
let on_leave = mouse_manager.on_leave.add(f!((_event: &mouse::Leave)
scene_frp.focused_source.emit(false);
)));
let on_enter = mouse_manager.on_enter.add(js_event.handler(f_!(
));
let on_enter = mouse_manager.on_enter.add(f!((_event: &mouse::Enter)
scene_frp.focused_source.emit(true);
)));
));
let handles = Rc::new([on_move, on_down, on_up, on_wheel, on_leave, on_enter]);
Self {
@ -204,11 +257,12 @@ impl Mouse {
last_position,
position,
click_count,
hover_rgba,
pointer_target_encoded,
target,
handles,
frp,
frp_deprecated,
scene_frp,
last_move_event,
}
}
@ -236,7 +290,7 @@ impl Mouse {
let shape = self.scene_frp.shape.value();
let new_pos = self.last_position.get();
let position = Vector2(new_pos.x as f32, new_pos.y as f32) - shape.center();
self.frp.position.emit(position);
self.frp_deprecated.position.emit(position);
}
}
@ -253,13 +307,19 @@ pub struct Keyboard {
}
impl Keyboard {
pub fn new(current_event: &JsEvent) -> Self {
pub fn new() -> Self {
let frp = enso_frp::io::keyboard::Keyboard::default();
let bindings = Rc::new(enso_frp::io::keyboard::DomBindings::new(&frp, current_event));
let bindings = Rc::new(enso_frp::io::keyboard::DomBindings::new(&frp));
Self { frp, bindings }
}
}
impl Default for Keyboard {
fn default() -> Self {
Self::new()
}
}
// ===========
@ -717,11 +777,10 @@ pub struct SceneData {
pub context: Rc<RefCell<Option<Context>>>,
pub context_lost_handler: Rc<RefCell<Option<ContextLostHandler>>>,
pub variables: UniformScope,
pub js_event: JsEvent,
pub mouse: Mouse,
pub keyboard: Keyboard,
pub uniforms: Uniforms,
pub background: PointerTarget,
pub background: PointerTarget_DEPRECATED,
pub pointer_target_registry: PointerTargetRegistry,
pub stats: Stats,
pub dirty: Dirty,
@ -754,16 +813,16 @@ impl SceneData {
let dirty = Dirty::new(on_mut);
let layers = world::with_context(|t| t.layers.clone_ref());
let stats = stats.clone();
let background = PointerTarget::new();
let pointer_target_registry = PointerTargetRegistry::new(&background);
let background = PointerTarget_DEPRECATED::new();
let pointer_target_registry = PointerTargetRegistry::new(&background, &display_object);
let uniforms = Uniforms::new(&variables);
let renderer = Renderer::new(&dom, &variables);
let style_sheet = world::with_context(|t| t.style_sheet.clone_ref());
let js_event = JsEvent::new();
let frp = Frp::new(&dom.root.shape);
let mouse = Mouse::new(&frp, &dom.root, &variables, &js_event, &display_mode);
let mouse =
Mouse::new(&frp, &dom.root, &variables, &display_mode, &pointer_target_registry);
let disable_context_menu = Rc::new(web::ignore_context_menu(&dom.root));
let keyboard = Keyboard::new(&js_event);
let keyboard = Keyboard::new();
let network = &frp.network;
let extensions = Extensions::default();
let bg_color_var = style_sheet.var("application.background");
@ -792,7 +851,6 @@ impl SceneData {
context,
context_lost_handler,
variables,
js_event,
mouse,
keyboard,
uniforms,
@ -816,7 +874,7 @@ impl SceneData {
}
fn init(self) -> Self {
self.init_mouse_down_and_up_events();
self.init_pointer_position_changed_check();
self
}
@ -963,43 +1021,18 @@ 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) {
fn init_pointer_position_changed_check(&self) {
let network = &self.frp.network;
let pointer_target_registry = &self.pointer_target_registry;
let target = &self.mouse.target;
let pointer_position_changed = &self.pointer_position_changed;
let pressed: Rc<RefCell<HashMap<mouse::Button, PointerTargetId>>> = default();
frp::extend! { network
eval self.mouse.frp.down ([pointer_target_registry, target, pressed](button) {
let current_target = target.get();
pressed.borrow_mut().insert(*button,current_target);
pointer_target_registry.with_mouse_target(current_target, |t|
t.emit_mouse_down(*button)
);
});
eval self.mouse.frp.up ([pointer_target_registry, target, pressed](button) {
let current_target = target.get();
if let Some(last_target) = pressed.borrow_mut().remove(button) {
pointer_target_registry.with_mouse_target(last_target, |t|
t.emit_mouse_release(*button)
);
}
pointer_target_registry.with_mouse_target(current_target, |t|
t.emit_mouse_up(*button)
);
});
eval_ self.mouse.frp.position (pointer_position_changed.set(true));
eval_ self.mouse.frp_deprecated.position (pointer_position_changed.set(true));
}
}
/// 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 opt_new_target =
PointerTargetId::decode_from_rgba(self.mouse.pointer_target_encoded.get());
let new_target = opt_new_target.unwrap_or_else(|err| {
error!("{err}");
default()
@ -1007,10 +1040,29 @@ impl SceneData {
let current_target = self.mouse.target.get();
if new_target != current_target {
self.mouse.target.set(new_target);
self.pointer_target_registry
.with_mouse_target(current_target, |t| t.mouse_out.emit(()));
self.pointer_target_registry.with_mouse_target(new_target, |t| t.mouse_over.emit(()));
self.mouse.re_emit_position_event(); // See docs to learn why.
if let Some(event) = (*self.mouse.last_move_event.borrow()).clone() {
self.pointer_target_registry.with_mouse_target(current_target, |t, d| {
t.mouse_out.emit(());
let out_event = event.clone().unchecked_convert_to::<mouse::Out>();
let leave_event = event.clone().unchecked_convert_to::<mouse::Leave>();
d.emit_event(out_event);
d.emit_event_without_bubbling(leave_event);
});
self.pointer_target_registry.with_mouse_target(new_target, |t, d| {
t.mouse_over.emit(());
let over_event = event.clone().unchecked_convert_to::<mouse::Over>();
let enter_event = event.clone().unchecked_convert_to::<mouse::Enter>();
d.emit_event(over_event);
d.emit_event_without_bubbling(enter_event);
});
// Re-emitting position event. See the docs of [`re_emit_position_event`] to learn
// why.
self.pointer_target_registry.with_mouse_target(new_target, |_, d| {
d.emit_event(event.clone());
});
self.mouse.re_emit_position_event();
}
}
}
}
@ -1270,8 +1322,8 @@ pub mod test_utils {
fn click_on_background(&self) {
self.target.set(PointerTargetId::Background);
let left_mouse_button = frp::io::mouse::Button::Button0;
self.frp.down.emit(left_mouse_button);
self.frp.up.emit(left_mouse_button);
self.frp_deprecated.down.emit(left_mouse_button);
self.frp_deprecated.up.emit(left_mouse_button);
}
}
}

View File

@ -10,14 +10,19 @@ use enso_frp as frp;
// =====================
// === PointerTarget ===
// =====================
// ================================
// === PointerTarget_DEPRECATED ===
// ================================
/// Abstraction for objects that can interact with a mouse.
///
/// # Deprecated
/// This API is deprecated. Instead, use the display object's event API. For example, to get an FRP
/// endpoint for mouse event, you can use the [`crate::display::Object::on_event`] function.
#[derive(Clone, CloneRef, Debug)]
#[allow(missing_docs)]
pub struct PointerTarget {
#[allow(non_camel_case_types)]
pub struct PointerTarget_DEPRECATED {
network: frp::Network,
/// Mouse button was pressed while the pointer was hovering this object.
pub mouse_down: frp::Source<mouse::Button>,
@ -52,7 +57,7 @@ pub struct PointerTarget {
pub on_drop: frp::Source,
}
impl PointerTarget {
impl PointerTarget_DEPRECATED {
/// Constructor.
pub fn new() -> Self {
frp::new_network! { network
@ -130,7 +135,7 @@ impl PointerTarget {
}
}
impl Default for PointerTarget {
impl Default for PointerTarget_DEPRECATED {
fn default() -> Self {
Self::new()
}

View File

@ -1,4 +1,9 @@
//! This is a root module for shapes, 2-dimensional graphical elements.
//!
//! Please note that this module does not re-export `compound::rectangle::Circle` as the name
//! collides with `primitive::Circle`. It is imported in many places in the code and should be one
//! day refactored in such a way, that shape definitions would import `primitive::*` automatically,
//! while this module would not re-export `primitive::*` at all.
// ==============
@ -9,6 +14,8 @@ pub mod compound;
pub mod constants;
pub mod primitive;
pub use compound::rectangle::Rectangle;
pub use compound::rectangle::RoundedRectangle;
pub use constants::*;
pub use primitive::*;

View File

@ -8,3 +8,4 @@
pub mod events;
pub mod from_cache;
pub mod path;
pub mod rectangle;

View File

@ -52,8 +52,8 @@ impl MouseEvents {
/// Connect the given [`PointerTarget`] to the [`Events`] output.
pub fn add_sub_shape<S: Shape>(&self, sub_shape: &ShapeView<S>) {
frp::extend! { network
self.frp.source.mouse_over <+ sub_shape.events.mouse_over;
self.frp.source.mouse_out <+ sub_shape.events.mouse_out;
self.frp.source.mouse_over <+ sub_shape.events_deprecated.mouse_over;
self.frp.source.mouse_out <+ sub_shape.events_deprecated.mouse_out;
}
}
}

View File

@ -0,0 +1,257 @@
//! A rectangle shape with numerous parameters allowing drawing diverse range of shapes,
//! such as circles, rings, or ring segments. The advantage of having a singular shape for these
//! cases is that a single draw call can be used to render multiple GUI elements, which ultimately
//! enhances performance.
use crate::prelude::*;
use crate::data::color;
use crate::display;
// =============
// === Shape ===
// =============
mod shape {
use super::*;
crate::shape! {(
style: Style,
color: Vector4,
corner_radius: f32,
inset: f32,
border: f32,
border_color: Vector4,
clip: Vector2,
) {
// === Canvas ===
let canvas_width = Var::<Pixels>::from("input_size.x");
let canvas_height = Var::<Pixels>::from("input_size.y");
// === Clip ===
// Clipping scales the shape in such a way, that the visible part will occupy whole
// canvas area. Thus, we need to recompute the new canvas size for the scaled shape.
let canvas_clip_height_diff = &canvas_height * (clip.y() * 2.0);
let canvas_clip_width_diff = &canvas_width * (clip.x() * 2.0);
let canvas_height = canvas_height + &canvas_clip_height_diff;
let canvas_width = canvas_width + &canvas_clip_width_diff;
// === Body ===
let inset2 = (&inset * 2.0).px();
let width = &canvas_width - &inset2;
let height = &canvas_height - &inset2;
let color = Var::<color::Rgba>::from(color);
let body = Rect((&width, &height)).corners_radius(corner_radius.px());
let body = body.fill(color);
// === Border ===
let border = body.grow(border.px());
let border_color = Var::<color::Rgba>::from(border_color);
let border = border.fill(border_color);
// === Shape ===
let shape = border.union_exclusive(&body);
// === Clip Adjustment ===
let shape = shape.translate((-canvas_clip_width_diff/2.0, -canvas_clip_height_diff/2.0));
shape.into()
}
}
}
// =================
// === Rectangle ===
// =================
/// A rectangle shape with the following configurable properties:
/// - The body color of the shape.
/// - The corner radius of the shape.
/// - The inset, padding between edge of the frame and shape itself.
/// - The border width and color.
/// - The clipping of the shape (e.g. clipping bottom half of the shape).
///
/// # Performance
/// This shape has been specifically designed to be utilized across various sections of the GUI. Its
/// numerous parameters enable a highly adaptable approach to drawing a diverse range of shapes,
/// such as circles, rings, or ring segments. The advantage of having a singular shape for these
/// cases is that a single draw call can be used to render multiple GUI elements, which ultimately
/// enhances performance.
#[derive(Clone, CloneRef, Debug, Deref, Default)]
#[allow(missing_docs)]
pub struct Rectangle {
pub view: shape::View,
}
impl Rectangle {
fn modify_view(&self, f: impl FnOnce(&shape::View)) -> &Self {
f(&self.view);
self
}
/// Constructor.
pub fn new() -> Self {
Self::default()
}
/// Builder-style modifier, allowing setting shape properties without creating a temporary
/// variable after its construction.
pub fn build(self, f: impl FnOnce(&Self)) -> Self {
f(&self);
self
}
/// Set the color of the body of the shape.
pub fn set_color(&self, color: color::Rgba) -> &Self {
self.modify_view(|view| view.color.set(color.into()))
}
/// Set the corner radius. If the corner radius will be larger than possible (e.g. larger than
/// the shape dimension), it will be clamped to the highest possible value.
pub fn set_corner_radius(&self, radius: f32) -> &Self {
self.modify_view(|view| view.corner_radius.set(radius))
}
/// Set the corner radius to maximum. If the width and height of the shape are equal, it will
/// result in a circle.
pub fn set_corner_radius_max(&self) -> &Self {
// We are using here a value bigger than anything we will ever need. We are not using
// biggest possible GLSL float value in order not to get rendering artifacts.
let max_radius = 1000000.0;
self.set_corner_radius(max_radius)
}
/// Set the padding between edge of the frame and shape itself. If you want to use border, you
/// should always set the inset at least of the size of the border. If you do not want the
/// border to be animated, you can use [`Self::set_inset_border`] instead.
pub fn set_inset(&self, inset: f32) -> &Self {
self.modify_view(|view| view.inset.set(inset))
}
/// Set the border size of the shape. If you want to use border, you should always set the inset
/// at least of the size of the border. If you do not want the border to be animated, you can
/// use [`Self::set_inset_border`] instead.
pub fn set_border(&self, border: f32) -> &Self {
self.modify_view(|view| view.border.set(border))
}
/// Set both the inset and border at once. See documentation of [`Self::set_border`] and
/// [`Self::set_inset`] to learn more.
pub fn set_inset_border(&self, border: f32) -> &Self {
self.set_inset(border).set_border(border)
}
/// Set the border color.
pub fn set_border_color(&self, color: color::Rgba) -> &Self {
self.modify_view(|view| view.border_color.set(color.into()))
}
/// 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. The clipping is performed always on the left
/// and on the bottom of the shape. If you want to clip other sides of the shape, you can rotate
/// it after clipping or use one of the predefined helper functions, such as
/// [`Self::keep_bottom_half`].
pub fn set_clip(&self, clip: Vector2) -> &Self {
self.modify_view(|view| view.clip.set(clip))
}
/// Keep only the top half of the shape.
pub fn keep_top_half(&self) -> &Self {
self.set_clip(Vector2(0.0, 0.5))
}
/// Keep only the bottom half of the shape.
pub fn keep_bottom_half(&self) -> &Self {
self.keep_top_half().flip()
}
/// Keep only the right half of the shape.
pub fn keep_right_half(&self) -> &Self {
self.set_clip(Vector2(0.5, 0.0))
}
/// Keep only the left half of the shape.
pub fn keep_left_half(&self) -> &Self {
self.keep_right_half().flip()
}
/// Keep only the top right quarter of the shape.
pub fn keep_top_right_quarter(&self) -> &Self {
self.set_clip(Vector2(0.5, 0.5))
}
/// Keep only the bottom right quarter of the shape.
pub fn keep_bottom_right_quarter(&self) -> &Self {
self.keep_top_right_quarter().rotate_90()
}
/// Keep only the bottom left quarter of the shape.
pub fn keep_bottom_left_quarter(&self) -> &Self {
self.keep_bottom_right_quarter().rotate_180()
}
/// Keep only the top left quarter of the shape.
pub fn keep_top_left_quarter(&self) -> &Self {
self.keep_bottom_left_quarter().rotate_270()
}
/// Flip the shape via its center. This is equivalent to rotating the shape by 180 degrees.
pub fn flip(&self) -> &Self {
self.rotate_180()
}
/// Rotate the shape by 90 degrees.
pub fn rotate_90(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(-std::f32::consts::PI / 2.0))
}
/// Counter rotate the shape by 90 degrees.
pub fn counter_rotate_90(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(std::f32::consts::PI / 2.0))
}
/// Rotate the shape by 180 degrees.
pub fn rotate_180(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(-std::f32::consts::PI))
}
/// Counter rotate the shape by 180 degrees.
pub fn rotate_270(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(-3.0 / 2.0 * std::f32::consts::PI))
}
/// Counter rotate the shape by 270 degrees.
pub fn counter_rotate_270(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(3.0 / 2.0 * std::f32::consts::PI))
}
}
impl display::Object for Rectangle {
fn display_object(&self) -> &display::object::Instance {
self.view.display_object()
}
}
/// Rectangle constructor.
#[allow(non_snake_case)]
pub fn Rectangle() -> Rectangle {
Rectangle::default()
}
/// Rounded rectangle constructor. It is a wrapper around [`Rectangle`] with a corner radius set.
#[allow(non_snake_case)]
pub fn RoundedRectangle(radius: f32) -> Rectangle {
let shape = Rectangle();
shape.set_corner_radius(radius);
shape
}
/// Circle constructor. It is a wrapper around [`Rectangle`] with a corner radius set to maximum.
#[allow(non_snake_case)]
pub fn Circle() -> Rectangle {
let shape = Rectangle();
shape.set_corner_radius_max();
shape
}

View File

@ -510,11 +510,11 @@ impl WorldData {
}
fn init_composer(&self) {
let mouse_hover_rgba = self.default_scene.mouse.hover_rgba.clone_ref();
let pointer_target_encoded = self.default_scene.mouse.pointer_target_encoded.clone_ref();
let garbage_collector = &self.garbage_collector;
let mut pixel_read_pass = PixelReadPass::<u8>::new(&self.default_scene.mouse.position);
pixel_read_pass.set_callback(f!([garbage_collector](v) {
mouse_hover_rgba.set(Vector4::from_iterator(v.iter().map(|value| *value as u32)));
pointer_target_encoded.set(Vector4::from_iterator(v.iter().map(|value| *value as u32)));
garbage_collector.pixel_updated();
}));
pixel_read_pass.set_sync_callback(f!(garbage_collector.pixel_synced()));

View File

@ -21,7 +21,7 @@ use crate::frp;
// === Export ===
// ==============
pub use crate::display::scene::PointerTarget;
pub use crate::display::scene::PointerTarget_DEPRECATED;
@ -130,16 +130,20 @@ where
#[allow(missing_docs)]
pub struct ShapeViewModel<S: Shape> {
#[deref]
shape: ShapeInstance<S>,
pub data: RefCell<S::ShapeData>,
pub events: PointerTarget,
pub pointer_targets: RefCell<Vec<symbol::GlobalInstanceId>>,
shape: ShapeInstance<S>,
pub data: RefCell<S::ShapeData>,
/// # Deprecated
/// This API is deprecated. Instead, use the display object's event API. For example, to get an
/// FRP endpoint for mouse event, you can use the [`crate::display::Object::on_event`]
/// function.
pub events_deprecated: PointerTarget_DEPRECATED,
pub pointer_targets: RefCell<Vec<symbol::GlobalInstanceId>>,
}
impl<S: Shape> Drop for ShapeViewModel<S> {
fn drop(&mut self) {
self.unregister_existing_mouse_targets();
self.events.on_drop.emit(());
self.events_deprecated.on_drop.emit(());
}
}
@ -147,10 +151,10 @@ impl<S: Shape> ShapeViewModel<S> {
/// Constructor.
pub fn new_with_data(data: S::ShapeData) -> Self {
let (shape, _) = world::with_context(|t| t.layers.DETACHED.instantiate(&data));
let events = PointerTarget::new();
let events_deprecated = PointerTarget_DEPRECATED::new();
let pointer_targets = default();
let data = RefCell::new(data);
ShapeViewModel { shape, data, events, pointer_targets }
ShapeViewModel { shape, data, events_deprecated, pointer_targets }
}
#[profile(Debug)]
@ -189,7 +193,11 @@ impl<S: Shape> ShapeViewModel<S> {
impl<S: Shape> ShapeViewModel<S> {
fn add_to_scene_layer(&self, scene: &Scene, layer: &scene::Layer) {
let (shape, instance) = layer.instantiate(&*self.data.borrow());
scene.pointer_target_registry.insert(instance.global_instance_id, self.events.clone_ref());
scene.pointer_target_registry.insert(
instance.global_instance_id,
self.events_deprecated.clone_ref(),
self.display_object(),
);
self.pointer_targets.borrow_mut().push(instance.global_instance_id);
self.shape.swap(&shape);
}

View File

@ -241,7 +241,7 @@ impl Cursor {
let frp = Frp::new();
let network = frp.network();
let model = CursorModel::new(scene);
let mouse = &scene.mouse.frp;
let mouse = &scene.mouse.frp_deprecated;
// === Animations ===
//

View File

@ -29,3 +29,4 @@ ensogl-example-slider = { path = "slider" }
ensogl-example-sprite-system = { path = "sprite-system" }
ensogl-example-sprite-system-benchmark = { path = "sprite-system-benchmark" }
ensogl-example-text-area = { path = "text-area" }
ensogl-example-vector-editor = { path = "vector-editor" }

View File

@ -6,12 +6,12 @@
#![allow(clippy::bool_to_int_with_if)]
#![allow(clippy::let_and_return)]
use ensogl_core::animation;
use ensogl_core::data::color::Rgba;
use ensogl_core::display::shape::*;
use ensogl_core::display::world::*;
use ensogl_core::prelude::*;
use ensogl_core::animation;
use ensogl_core::data::color::Rgba;
use ensogl_core::display::navigation::navigator::Navigator;
use ensogl_core::display::object::ObjectOps;

View File

@ -6,7 +6,7 @@
#![allow(clippy::bool_to_int_with_if)]
#![allow(clippy::let_and_return)]
use ensogl_core::display::shape::*;
use ensogl_core::display::shape::compound::rectangle::*;
use ensogl_core::display::world::*;
use ensogl_core::prelude::*;
@ -17,248 +17,6 @@ use ensogl_core::display::object::ObjectOps;
// ==============
// === Shapes ===
// ==============
mod rectangle {
use super::*;
ensogl_core::shape! {(
style: Style,
color: Vector4,
corner_radius: f32,
inset: f32,
border: f32,
border_color: Vector4,
clip: Vector2,
) {
// === Canvas ===
let canvas_width = Var::<Pixels>::from("input_size.x");
let canvas_height = Var::<Pixels>::from("input_size.y");
// === Clip ===
// Clipping scales the shape in such a way, that the visible part will occupy whole
// canvas area. Thus, we need to recompute the new canvas size for the scaled shape.
let canvas_clip_height_diff = &canvas_height * (clip.y() * 2.0);
let canvas_clip_width_diff = &canvas_width * (clip.x() * 2.0);
let canvas_height = canvas_height + &canvas_clip_height_diff;
let canvas_width = canvas_width + &canvas_clip_width_diff;
// === Body ===
let inset2 = (&inset * 2.0).px();
let width = &canvas_width - &inset2;
let height = &canvas_height - &inset2;
let color = Var::<color::Rgba>::from(color);
let body = Rect((&width, &height)).corners_radius(corner_radius.px());
let body = body.fill(color);
// === Border ===
let border = body.grow(border.px());
let border_color = Var::<color::Rgba>::from(border_color);
let border = border.fill(border_color);
// === Shape ===
let shape = border.union_exclusive(&body);
// === Clip Adjustment ===
let shape = shape.translate((-canvas_clip_width_diff/2.0, -canvas_clip_height_diff/2.0));
shape.into()
}
}
}
/// A rectangle shape with the following configurable properties:
/// - The body color of the shape.
/// - The corner radius of the shape.
/// - The inset, padding between edge of the frame and shape itself.
/// - The border width and color.
/// - The clipping of the shape (e.g. clipping bottom half of the shape).
///
/// # Performance
/// This shape has been specifically designed to be utilized across various sections of the GUI. Its
/// numerous parameters enable a highly adaptable approach to drawing a diverse range of shapes,
/// such as circles, rings, or ring segments. The advantage of having a singular shape for these
/// cases is that a single draw call can be used to render multiple GUI elements, which ultimately
/// enhances performance.
#[derive(Clone, CloneRef, Debug, Deref, Default)]
#[allow(missing_docs)]
pub struct Rectangle {
pub view: rectangle::View,
}
impl Rectangle {
fn modify_view(&self, f: impl FnOnce(&rectangle::View)) -> &Self {
f(&self.view);
self
}
/// Constructor.
pub fn new() -> Self {
Self::default()
}
/// Builder-style modifier, allowing setting shape properties without creating a temporary
/// variable after its construction.
pub fn build(self, f: impl FnOnce(&Self)) -> Self {
f(&self);
self
}
/// Set the color of the body of the shape.
pub fn set_color(&self, color: color::Rgba) -> &Self {
self.modify_view(|view| view.color.set(color.into()))
}
/// Set the corner radius. If the corner radius will be larger than possible (e.g. larger than
/// the shape dimension), it will be clamped to the highest possible value.
pub fn set_corner_radius(&self, radius: f32) -> &Self {
self.modify_view(|view| view.corner_radius.set(radius))
}
/// Set the corner radius to maximum. If the width and height of the shape are equal, it will
/// result in a circle.
pub fn set_corner_radius_max(&self) -> &Self {
// We are using here a value bigger than anything we will ever need. We are not using
// biggest possible GLSL float value in order not to get rendering artifacts.
let max_radius = 1000000.0;
self.set_corner_radius(max_radius)
}
/// Set the padding between edge of the frame and shape itself. If you want to use border, you
/// should always set the inset at least of the size of the border. If you do not want the
/// border to be animated, you can use [`Self::set_inset_border`] instead.
pub fn set_inset(&self, inset: f32) -> &Self {
self.modify_view(|view| view.inset.set(inset))
}
/// Set the border size of the shape. If you want to use border, you should always set the inset
/// at least of the size of the border. If you do not want the border to be animated, you can
/// use [`Self::set_inset_border`] instead.
pub fn set_border(&self, border: f32) -> &Self {
self.modify_view(|view| view.border.set(border))
}
/// Set both the inset and border at once. See documentation of [`Self::set_border`] and
/// [`Self::set_inset`] to learn more.
pub fn set_inset_border(&self, border: f32) -> &Self {
self.set_inset(border).set_border(border)
}
/// Set the border color.
pub fn set_border_color(&self, color: color::Rgba) -> &Self {
self.modify_view(|view| view.border_color.set(color.into()))
}
/// 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. The clipping is performed always on the left
/// and on the bottom of the shape. If you want to clip other sides of the shape, you can rotate
/// it after clipping or use one of the predefined helper functions, such as
/// [`Self::keep_bottom_half`].
pub fn set_clip(&self, clip: Vector2) -> &Self {
self.modify_view(|view| view.clip.set(clip))
}
/// Keep only the top half of the shape.
pub fn keep_top_half(&self) -> &Self {
self.set_clip(Vector2(0.0, 0.5))
}
/// Keep only the bottom half of the shape.
pub fn keep_bottom_half(&self) -> &Self {
self.keep_top_half().flip()
}
/// Keep only the right half of the shape.
pub fn keep_right_half(&self) -> &Self {
self.set_clip(Vector2(0.5, 0.0))
}
/// Keep only the left half of the shape.
pub fn keep_left_half(&self) -> &Self {
self.keep_right_half().flip()
}
/// Keep only the top right quarter of the shape.
pub fn keep_top_right_quarter(&self) -> &Self {
self.set_clip(Vector2(0.5, 0.5))
}
/// Keep only the bottom right quarter of the shape.
pub fn keep_bottom_right_quarter(&self) -> &Self {
self.keep_top_right_quarter().rotate_90()
}
/// Keep only the bottom left quarter of the shape.
pub fn keep_bottom_left_quarter(&self) -> &Self {
self.keep_bottom_right_quarter().rotate_180()
}
/// Keep only the top left quarter of the shape.
pub fn keep_top_left_quarter(&self) -> &Self {
self.keep_bottom_left_quarter().rotate_270()
}
/// Flip the shape via its center. This is equivalent to rotating the shape by 180 degrees.
pub fn flip(&self) -> &Self {
self.rotate_180()
}
/// Rotate the shape by 90 degrees.
pub fn rotate_90(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(-std::f32::consts::PI / 2.0))
}
/// Counter rotate the shape by 90 degrees.
pub fn counter_rotate_90(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(std::f32::consts::PI / 2.0))
}
/// Rotate the shape by 180 degrees.
pub fn rotate_180(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(-std::f32::consts::PI))
}
/// Counter rotate the shape by 180 degrees.
pub fn rotate_270(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(-3.0 / 2.0 * std::f32::consts::PI))
}
/// Counter rotate the shape by 270 degrees.
pub fn counter_rotate_270(&self) -> &Self {
self.modify_view(|view| view.set_rotation_z(3.0 / 2.0 * std::f32::consts::PI))
}
}
impl display::Object for Rectangle {
fn display_object(&self) -> &display::object::Instance {
self.view.display_object()
}
}
/// Rectangle constructor.
#[allow(non_snake_case)]
fn Rectangle() -> Rectangle {
Rectangle::default()
}
/// Rounded rectangle constructor. It is a wrapper around [`Rectangle`] with a corner radius set.
#[allow(non_snake_case)]
fn RoundedRectangle(radius: f32) -> Rectangle {
let shape = Rectangle();
shape.set_corner_radius(radius);
shape
}
/// Circle constructor. It is a wrapper around [`Rectangle`] with a corner radius set to maximum.
#[allow(non_snake_case)]
fn Circle() -> Rectangle {
let shape = Rectangle();
shape.set_corner_radius_max();
shape
}
// ===================
// === Entry Point ===
// ===================

View File

@ -58,9 +58,9 @@ pub fn main() {
world.keep_alive_forever();
frp::new_network! { network
trace view.events.mouse_over;
trace view.events.mouse_out;
trace view.events.mouse_down;
trace view.events_deprecated.mouse_over;
trace view.events_deprecated.mouse_out;
trace view.events_deprecated.mouse_down;
}
let mut i = 0;

View File

@ -69,7 +69,7 @@ fn define_rect(width: f32, height: f32, network: &frp::Network) -> rectangle::Vi
// Please note that this clones [`rect`] refs to closures, so [`network`] keeps them alive.
frp::extend! { network
eval_ rect.events.mouse_down (rect.focus());
eval_ rect.events_deprecated.mouse_down (rect.focus());
eval border_size.value ((size) rect.border_size.set(*size));
eval border_color.value ((color) rect.border_color.set(color::Rgba::from(color).into()));

View File

@ -106,12 +106,12 @@ impl View {
let model = Model::new(app);
let network = &frp.network;
frp::extend! { network
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;
trace model.shape.events.on_drop;
trace model.shape.events_deprecated.mouse_up;
trace model.shape.events_deprecated.mouse_release;
trace model.shape.events_deprecated.mouse_down;
trace model.shape.events_deprecated.mouse_over;
trace model.shape.events_deprecated.mouse_out;
trace model.shape.events_deprecated.on_drop;
}
Self { frp, model }

View File

@ -0,0 +1,17 @@
[package]
name = "ensogl-example-vector-editor"
version = "0.1.0"
authors = ["Enso Team <contact@enso.org>"]
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
ensogl-core = { path = "../../core" }
wasm-bindgen = { workspace = true }
ensogl-hardcoded-theme = { path = "../../../ensogl/app/theme/hardcoded" }
# Stop wasm-pack from running wasm-opt, because we run it from our build scripts in order to customize options.
[package.metadata.wasm-pack.profile.release]
wasm-opt = false

View File

@ -0,0 +1,167 @@
//! Example scene showing the usage of built-in vector editor component.
//!
//! TODO[WD]: This is work in progress and will be changed in the upcoming PRs.
// === Standard Linter Configuration ===
#![deny(non_ascii_idents)]
#![warn(unsafe_code)]
#![allow(clippy::bool_to_int_with_if)]
#![allow(clippy::let_and_return)]
use ensogl_core::display::shape::compound::rectangle::*;
use ensogl_core::display::world::*;
use ensogl_core::prelude::*;
use ensogl_core::control::io::mouse;
use ensogl_core::data::color;
use ensogl_core::display;
use ensogl_core::display::navigation::navigator::Navigator;
use ensogl_core::display::object::ObjectOps;
// ==============
// === Events ===
// ==============
#[derive(Clone, CloneRef, Debug, Default)]
pub struct MouseOver;
// ============
// === Glob ===
// ============
pub mod glob {
use super::*;
ensogl_core::define_endpoints_2! {
Input {
}
Output {
}
}
}
// ===========
// === FRP ===
// ===========
ensogl_core::define_endpoints_2! {
Input {
}
Output {
}
}
#[derive(Derivative, CloneRef, Debug, Deref)]
#[derivative(Clone(bound = ""))]
pub struct VectorEditor<T> {
#[deref]
pub frp: Frp,
display_object: display::object::Instance,
model: Rc<RefCell<Model<T>>>,
}
#[derive(Debug, Derivative)]
#[derivative(Default(bound = ""))]
pub struct Model<T> {
items: Vec<T>,
}
impl<T> VectorEditor<T> {
pub fn new() -> Self {
let frp = Frp::new();
let display_object = display::object::Instance::new();
let model = default();
display_object.use_auto_layout().set_gap((10.0, 10.0));
Self { frp, display_object, model }.init()
}
fn init(self) -> Self {
let network = self.frp.network();
let event_handler = self.display_object.on_event::<mouse::Up>();
frp::extend! { network
eval_ event_handler ([] {
warn!("Mouse up in parent");
});
}
self
}
}
impl<T: display::Object> VectorEditor<T> {
fn append(&self, item: T) {
self.add_child(&item);
self.model.borrow_mut().items.push(item);
}
}
impl<T> display::Object for VectorEditor<T> {
fn display_object(&self) -> &display::object::Instance {
&self.display_object
}
}
impl<T> Default for VectorEditor<T> {
fn default() -> Self {
Self::new()
}
}
// ===================
// === 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 vector_editor = VectorEditor::<Rectangle>::new();
let shape1 = Circle().build(|t| {
t.set_size(Vector2::new(100.0, 100.0))
.set_color(color::Rgba::new(0.5, 0.0, 0.0, 0.3))
.set_inset_border(5.0)
.set_border_color(color::Rgba::new(0.0, 0.0, 1.0, 1.0))
.keep_bottom_left_quarter();
});
let shape2 = RoundedRectangle(10.0).build(|t| {
t.set_size(Vector2::new(100.0, 100.0))
.set_color(color::Rgba::new(0.5, 0.0, 0.0, 0.3))
.set_inset_border(5.0)
.set_border_color(color::Rgba::new(0.0, 0.0, 1.0, 1.0));
});
let glob_frp = glob::Frp::new();
let glob_frp_network = glob_frp.network();
let shape1_over = shape1.on_event::<mouse::Over>();
frp::extend! { glob_frp_network
eval_ shape1_over ([] {
warn!("Shape 1 over");
});
}
vector_editor.append(shape1);
vector_editor.append(shape2);
let root = display::object::Instance::new();
root.set_size(Vector2::new(300.0, 100.0));
root.add_child(&vector_editor);
world.add_child(&root);
world.keep_alive_forever();
mem::forget(glob_frp);
mem::forget(navigator);
mem::forget(root);
mem::forget(vector_editor);
}

View File

@ -10,4 +10,4 @@ pub mod keyboard;
pub mod mouse;
pub mod timer;
pub use mouse::Mouse;
pub use mouse::Mouse_DEPRECATED;

View File

@ -3,9 +3,6 @@
use crate::prelude::*;
use enso_web::prelude::*;
use crate as frp;
use enso_profiler as profiler;
use enso_web as web;
@ -78,51 +75,3 @@ fn event_listener_options() -> enso_web::AddEventListenerOptions {
options.passive(false);
options
}
// ======================
// === JsEventHandler ===
// ======================
/// FRP wrapper for JS events. You can use the [`Self::handler`] method to generate a new event
/// handler closure. When event is fired, it will be emitted as [`Self::event`] stream. After the
/// event stops propagating, [`None`] will be emitted instead.
#[allow(missing_docs)]
#[derive(Clone, CloneRef, Debug)]
pub struct JsEvent {
pub event: frp::Stream<Option<enso_web::Event>>,
event_source: frp::Source<Option<enso_web::Event>>,
network: frp::Network,
}
impl Default for JsEvent {
fn default() -> Self {
Self::new()
}
}
impl JsEvent {
/// Constructor
pub fn new() -> Self {
frp::new_network! { network
event_source <- source();
}
let event = event_source.clone().into();
Self { event, event_source, network }
}
/// Creates an event handler which wraps the event in an FRP network. The event will be emitted
/// on the `event` output stream. After the event is emitted, `None` will be emitted.
pub fn handler<Event>(&self, mut processing_fn: impl FnMut(&Event)) -> impl FnMut(&Event)
where Event: AsRef<enso_web::Event> {
let event_source = &self.event_source;
f!([event_source] (event) {
let _profiler = profiler::start_debug!(profiler::APP_LIFETIME, "event_handler");
let js_event = event.as_ref().clone();
event_source.emit(Some(js_event));
processing_fn(event);
event_source.emit(None);
})
}
}

View File

@ -3,7 +3,6 @@
use crate::prelude::*;
use crate as frp;
use crate::io::js::JsEvent;
use crate::io::js::Listener;
use enso_web::KeyboardEvent;
@ -440,17 +439,14 @@ pub struct DomBindings {
impl DomBindings {
/// Create new Keyboard and Frp bindings.
pub fn new(keyboard: &Keyboard, current_event: &JsEvent) -> Self {
let key_down = Listener::new_key_down(current_event.handler(
pub fn new(keyboard: &Keyboard) -> Self {
let key_down = Listener::new_key_down(
f!((event:&KeyboardEvent) keyboard.source.down.emit(KeyWithCode::from(event))),
));
let key_up =
Listener::new_key_up(current_event.handler(
f!((event:&KeyboardEvent) keyboard.source.up.emit(KeyWithCode::from(event))),
));
let blur = Listener::new_blur(
current_event.handler(f_!(keyboard.source.window_defocused.emit(()))),
);
let key_up = Listener::new_key_up(
f!((event:&KeyboardEvent) keyboard.source.up.emit(KeyWithCode::from(event))),
);
let blur = Listener::new_blur(f_!(keyboard.source.window_defocused.emit(())));
Self { key_down, key_up, blur }
}
}

View File

@ -182,9 +182,14 @@ impl From<&ButtonMask> for ButtonMask {
// =============
/// Mouse FRP bindings.
///
/// # Deprecated
/// This API is deprecated. Instead, use the display object's event API. For example, to get an FRP
/// endpoint for mouse event, you can use the [`crate::display::Object::on_event`] function.
#[derive(Clone, CloneRef, Debug)]
#[allow(missing_docs)]
pub struct Mouse {
#[allow(non_camel_case_types)]
pub struct Mouse_DEPRECATED {
pub network: frp::Network,
pub up: frp::Source<Button>,
pub down: frp::Source<Button>,
@ -232,7 +237,7 @@ pub struct Mouse {
pub prev_button_mask: frp::Stream<ButtonMask>,
}
impl Mouse {
impl Mouse_DEPRECATED {
/// Smart accessor for `up_X` field.
pub fn up(&self, button: Button) -> &frp::Stream {
match button {
@ -278,7 +283,7 @@ impl Mouse {
}
}
impl Default for Mouse {
impl Default for Mouse_DEPRECATED {
fn default() -> Self {
frp::new_network! { network
up <- source();
@ -397,7 +402,7 @@ impl Default for Mouse {
}
}
impl Mouse {
impl Mouse_DEPRECATED {
/// Constructor.
pub fn new() -> Self {
default()