mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 13:02:07 +03:00
Refactoring mouse events. (#6078)
This commit is contained in:
parent
76409b285d
commit
3f7c4a47da
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -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]]
|
||||
|
@ -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
|
||||
|
@ -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(());
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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(¬_selected);
|
||||
mouse_out_if_not_selected <- model.view.events.mouse_out.gate(¬_selected);
|
||||
mouse_over_if_not_selected <- model.view.events_deprecated.mouse_over.gate(¬_selected);
|
||||
mouse_out_if_not_selected <- model.view.events_deprecated.mouse_out.gate(¬_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(()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 ===
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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){
|
||||
|
@ -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));
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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(());
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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_();
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -8,3 +8,4 @@
|
||||
pub mod events;
|
||||
pub mod from_cache;
|
||||
pub mod path;
|
||||
pub mod rectangle;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
257
lib/rust/ensogl/core/src/display/shape/compound/rectangle.rs
Normal file
257
lib/rust/ensogl/core/src/display/shape/compound/rectangle.rs
Normal 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
|
||||
}
|
@ -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()));
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 ===
|
||||
//
|
||||
|
@ -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" }
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 ===
|
||||
// ===================
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
|
@ -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 }
|
||||
|
17
lib/rust/ensogl/examples/vector-editor/Cargo.toml
Normal file
17
lib/rust/ensogl/examples/vector-editor/Cargo.toml
Normal 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
|
167
lib/rust/ensogl/examples/vector-editor/src/lib.rs
Normal file
167
lib/rust/ensogl/examples/vector-editor/src/lib.rs
Normal 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);
|
||||
}
|
@ -10,4 +10,4 @@ pub mod keyboard;
|
||||
pub mod mouse;
|
||||
pub mod timer;
|
||||
|
||||
pub use mouse::Mouse;
|
||||
pub use mouse::Mouse_DEPRECATED;
|
||||
|
@ -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);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user