Scroll the documentation panel when it is hovered by the mouse (#3968)

[Task link](https://www.pivotaltracker.com/story/show/183970810).

Now documentation panel is being scrolled when only hovered by the cursor (you don't need to click on it beforehand).

Tested on macOS with both the touchpad and the mouse.

https://user-images.githubusercontent.com/6566674/206667769-04aae6b2-91ff-4877-bf10-8c0f0c4c5873.mp4
This commit is contained in:
Ilya Bogdanov 2022-12-14 19:37:04 +03:00 committed by GitHub
parent 1dfcf1cafc
commit 285959835f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 45 deletions

View File

@ -55,7 +55,6 @@ use ensogl_core::data::bounding_box::BoundingBox;
use ensogl_core::data::color; use ensogl_core::data::color;
use ensogl_core::define_endpoints_2; use ensogl_core::define_endpoints_2;
use ensogl_core::display; use ensogl_core::display;
use ensogl_core::display::navigation::navigator::Navigator;
use ensogl_core::display::object::ObjectOps; use ensogl_core::display::object::ObjectOps;
use ensogl_core::display::shape::StyleWatchFrp; use ensogl_core::display::shape::StyleWatchFrp;
use ensogl_derive_theme::FromTheme; use ensogl_derive_theme::FromTheme;
@ -238,14 +237,12 @@ pub struct Model {
pub grid: grid::View, pub grid: grid::View,
pub section_navigator: SectionNavigator, pub section_navigator: SectionNavigator,
pub breadcrumbs: breadcrumbs::Breadcrumbs, pub breadcrumbs: breadcrumbs::Breadcrumbs,
scene_navigator: Rc<RefCell<Option<Navigator>>>,
} }
impl Model { impl Model {
fn new(app: &Application) -> Self { fn new(app: &Application) -> Self {
let app = app.clone_ref(); let app = app.clone_ref();
let display_object = display::object::Instance::new(); let display_object = display::object::Instance::new();
let scene_navigator = default();
let background = background::View::new(); let background = background::View::new();
display_object.add_child(&background); display_object.add_child(&background);
@ -260,7 +257,7 @@ impl Model {
breadcrumbs.set_base_layer(&app.display.default_scene.layers.node_searcher); breadcrumbs.set_base_layer(&app.display.default_scene.layers.node_searcher);
display_object.add_child(&breadcrumbs); display_object.add_child(&breadcrumbs);
Self { display_object, background, grid, section_navigator, scene_navigator, breadcrumbs } Self { display_object, background, grid, section_navigator, breadcrumbs }
} }
fn set_initial_breadcrumbs(&self) { fn set_initial_breadcrumbs(&self) {
@ -279,11 +276,6 @@ impl Model {
self.grid.set_xy(style.grid_pos()); self.grid.set_xy(style.grid_pos());
} }
/// Set the navigator so it can be disabled on hover.
pub fn set_navigator(&self, navigator: Option<Navigator>) {
*self.scene_navigator.borrow_mut() = navigator
}
// Note that this is a workaround for lack of hierarchical mouse over events. // Note that this is a workaround for lack of hierarchical mouse over events.
// We need to know if the mouse is over the panel, but cannot do it via a shape, as // We need to know if the mouse is over the panel, but cannot do it via a shape, as
// sub-components still need to receive all of the mouse events, too. // sub-components still need to receive all of the mouse events, too.
@ -295,23 +287,6 @@ impl Model {
let viewport = BoundingBox::from_center_and_size(default(), size); let viewport = BoundingBox::from_center_and_size(default(), size);
viewport.contains(pos) viewport.contains(pos)
} }
fn on_hover(&self) {
if let Some(navigator) = self.scene_navigator.borrow().as_ref() {
navigator.disable()
} else {
warn!(
"Navigator was not initialised on ComponentBrowserPanel. \
Scroll events will not be handled correctly."
)
}
}
fn on_hover_end(&self) {
if let Some(navigator) = self.scene_navigator.borrow().as_ref() {
navigator.enable()
}
}
} }
impl display::Object for Model { impl display::Object for Model {
@ -345,6 +320,7 @@ define_endpoints_2! {
} }
Output{ Output{
size(Vector2), size(Vector2),
is_hovered(bool),
} }
} }
@ -367,15 +343,13 @@ impl component::Frp<Model> for Frp {
let pos = scene.screen_to_object_space(&model, pos.xy()); let pos = scene.screen_to_object_space(&model, pos.xy());
model.is_hovered(pos) model.is_hovered(pos)
})).gate(&is_visible).on_change(); })).gate(&is_visible).on_change();
output.is_hovered <+ is_hovered;
// TODO[ib] Temporary solution for focus, we grab keyboard events if the // TODO[ib] Temporary solution for focus, we grab keyboard events if the
// component browser is visible. The proper implementation is tracked in // component browser is visible. The proper implementation is tracked in
// https://www.pivotaltracker.com/story/show/180872763 // https://www.pivotaltracker.com/story/show/180872763
model.grid.deprecated_set_focus <+ is_visible; model.grid.deprecated_set_focus <+ is_visible;
on_hover <- is_hovered.on_true();
on_hover_end <- is_hovered.on_false(); on_hover_end <- is_hovered.on_false();
eval_ on_hover ( model.on_hover() );
eval_ on_hover_end ( model.on_hover_end() );
model.grid.unhover_element <+ on_hover_end; model.grid.unhover_element <+ on_hover_end;

View File

@ -197,7 +197,6 @@ pub fn main() {
let navigator = Navigator::new(scene, &scene.layers.node_searcher.camera()); let navigator = Navigator::new(scene, &scene.layers.node_searcher.camera());
let panel = app.new_view::<ide_view_component_list_panel::View>(); let panel = app.new_view::<ide_view_component_list_panel::View>();
scene.layers.node_searcher.add(&panel); scene.layers.node_searcher.add(&panel);
panel.model().set_navigator(Some(navigator.clone_ref()));
panel.show(); panel.show();
let network = frp::Network::new("new_component_list_panel_view"); let network = frp::Network::new("new_component_list_panel_view");
//TODO[ao] should be done by panel itself. //TODO[ao] should be done by panel itself.
@ -209,6 +208,11 @@ pub fn main() {
size <- all_with(&init, &panel.size, |(), panel_size| *panel_size); size <- all_with(&init, &panel.size, |(), panel_size| *panel_size);
snap <- all_with(&size, &scene.frp.shape, |sz, sh| snap_to_pixel_offset(*sz, sh)); snap <- all_with(&size, &scene.frp.shape, |sz, sh| snap_to_pixel_offset(*sz, sh));
eval snap((snap) panel.set_xy(*snap)); eval snap((snap) panel.set_xy(*snap));
// === Disable navigator on hover ===
navigator.frp.set_enabled <+ panel.is_hovered.not();
} }
init.emit(()); init.emit(());

View File

@ -2648,11 +2648,9 @@ fn new_graph_editor(app: &Application) -> GraphEditor {
disable_navigator <- any_(&set_navigator_false,&some_vis_selected); disable_navigator <- any_(&set_navigator_false,&some_vis_selected);
enable_navigator <- any_(&set_navigator_true,&no_vis_selected); enable_navigator <- any_(&set_navigator_true,&no_vis_selected);
eval_ disable_navigator ( model.navigator.disable() ); model.navigator.frp.set_enabled <+ bool(&disable_navigator,&enable_navigator);
eval_ enable_navigator ( model.navigator.enable() );
out.navigator_active <+ inputs.set_navigator_disabled out.navigator_active <+ model.navigator.frp.enabled;
|| out.some_visualisation_selected;
} }

View File

@ -91,6 +91,7 @@ ensogl::define_endpoints_2! {
is_visible(bool), is_visible(bool),
size(Vector2), size(Vector2),
expression_input_position(Vector2), expression_input_position(Vector2),
is_hovered(bool),
} }
} }
@ -138,6 +139,9 @@ impl component::Frp<Model> for Frp {
&snap, &snap,
Model::expression_input_position Model::expression_input_position
); );
out.is_hovered <+ list_panel.is_hovered || documentation.frp.is_hovered;
out.is_hovered <+ input.hide.constant(false);
} }
init.emit(()); init.emit(());
} }

View File

@ -238,6 +238,8 @@ ensogl::define_endpoints! {
/// Indicates whether the documentation panel has been selected through clicking into /// Indicates whether the documentation panel has been selected through clicking into
/// it, or deselected by clicking somewhere else. /// it, or deselected by clicking somewhere else.
is_selected(bool), is_selected(bool),
/// Indicates whether the documentation panel has been hovered.
is_hovered(bool),
} }
} }
@ -328,6 +330,19 @@ impl View {
app.frp.show_system_cursor <+ overlay.events.mouse_over; app.frp.show_system_cursor <+ overlay.events.mouse_over;
app.frp.hide_system_cursor <+ overlay.events.mouse_out; app.frp.hide_system_cursor <+ overlay.events.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);
let mouse_up = scene.mouse.frp.up.clone_ref();
let mouse_down = scene.mouse.frp.down.clone_ref();
let mouse_wheel = scene.mouse.frp.wheel.clone_ref();
let mouse_position = scene.mouse.frp.position.clone_ref();
caught_mouse <- any_(mouse_up,mouse_down,mouse_wheel,mouse_position);
pass_to_dom <- caught_mouse.gate(&frp.source.is_hovered);
eval_ pass_to_dom(scene.current_js_event.pass_to_dom.emit(()));
} }
visualization.pass_events_to_dom_if_active(scene, network); visualization.pass_events_to_dom_if_active(scene, network);
self self

View File

@ -24,7 +24,6 @@ use ensogl::application;
use ensogl::application::shortcut; use ensogl::application::shortcut;
use ensogl::application::Application; use ensogl::application::Application;
use ensogl::display; use ensogl::display;
use ensogl::display::navigation::navigator::Navigator;
use ensogl::system::web; use ensogl::system::web;
use ensogl::system::web::dom; use ensogl::system::web::dom;
use ensogl::Animation; use ensogl::Animation;
@ -143,6 +142,7 @@ struct SearcherFrp {
editing_committed: frp::Stream, editing_committed: frp::Stream,
is_visible: frp::Stream<bool>, is_visible: frp::Stream<bool>,
is_empty: frp::Stream<bool>, is_empty: frp::Stream<bool>,
is_hovered: frp::Stream<bool>,
} }
/// A structure containing the Searcher View: the old Node Searcher of a new Component Browser. /// A structure containing the Searcher View: the old Node Searcher of a new Component Browser.
@ -166,12 +166,6 @@ impl SearcherVariant {
} }
} }
fn set_navigator(&self, navigator: Navigator) {
if let Self::ComponentBrowser(browser) = self {
browser.model().list.model().set_navigator(Some(navigator))
}
}
fn frp(&self, project_view_network: &frp::Network) -> SearcherFrp { fn frp(&self, project_view_network: &frp::Network) -> SearcherFrp {
match self { match self {
SearcherVariant::ComponentBrowser(view) => { SearcherVariant::ComponentBrowser(view) => {
@ -185,9 +179,11 @@ impl SearcherVariant {
editing_committed, editing_committed,
is_visible: view.output.is_visible.clone_ref().into(), is_visible: view.output.is_visible.clone_ref().into(),
is_empty: is_empty.into(), is_empty: is_empty.into(),
is_hovered: view.output.is_hovered.clone_ref().into(),
} }
} }
SearcherVariant::OldNodeSearcher(view) => { SearcherVariant::OldNodeSearcher(view) => {
let documentation = view.documentation();
frp::extend! {project_view_network frp::extend! {project_view_network
editing_committed <- view.editing_committed.constant(()); editing_committed <- view.editing_committed.constant(());
} }
@ -195,6 +191,7 @@ impl SearcherVariant {
editing_committed, editing_committed,
is_visible: view.output.is_visible.clone_ref().into(), is_visible: view.output.is_visible.clone_ref().into(),
is_empty: view.output.is_empty.clone_ref().into(), is_empty: view.output.is_empty.clone_ref().into(),
is_hovered: documentation.frp.is_hovered.clone_ref().into(),
} }
} }
} }
@ -276,7 +273,6 @@ impl Model {
let display_object = display::object::Instance::new(); let display_object = display::object::Instance::new();
let searcher = SearcherVariant::new(app); let searcher = SearcherVariant::new(app);
let graph_editor = app.new_view::<GraphEditor>(); let graph_editor = app.new_view::<GraphEditor>();
searcher.set_navigator(graph_editor.model.navigator.clone_ref());
let code_editor = app.new_view::<code_editor::View>(); let code_editor = app.new_view::<code_editor::View>();
let fullscreen_vis = default(); let fullscreen_vis = default();
let prompt_background = prompt_background::View::new(); let prompt_background = prompt_background::View::new();
@ -749,7 +745,8 @@ impl View {
// === Disabling Navigation === // === Disabling Navigation ===
let documentation = model.searcher.documentation(); let documentation = model.searcher.documentation();
disable_navigation <- documentation.frp.is_selected || frp.open_dialog_shown; searcher_active <- searcher.is_hovered || documentation.frp.is_selected;
disable_navigation <- searcher_active || frp.open_dialog_shown;
graph.set_navigator_disabled <+ disable_navigation; graph.set_navigator_disabled <+ disable_navigation;
// === Disabling Dropping === // === Disabling Dropping ===

View File

@ -7,6 +7,7 @@ use crate::prelude::*;
use crate::animation::physics; use crate::animation::physics;
use crate::control::callback; use crate::control::callback;
use crate::define_endpoints_2;
use crate::display::camera::Camera2d; use crate::display::camera::Camera2d;
use crate::display::navigation::navigator::events::NavigatorEvents; use crate::display::navigation::navigator::events::NavigatorEvents;
use crate::display::object::traits::*; use crate::display::object::traits::*;
@ -238,6 +239,20 @@ impl NavigatorModel {
// ===========
// === FRP ===
// ===========
define_endpoints_2! {
Input {
set_enabled(bool),
}
Output {
enabled(bool),
}
}
// ================= // =================
// === Navigator === // === Navigator ===
// ================= // =================
@ -245,6 +260,8 @@ impl NavigatorModel {
/// Navigator enables camera navigation with mouse interactions. /// Navigator enables camera navigation with mouse interactions.
#[derive(Clone, CloneRef, Debug, Shrinkwrap)] #[derive(Clone, CloneRef, Debug, Shrinkwrap)]
pub struct Navigator { pub struct Navigator {
#[allow(missing_docs)]
pub frp: Frp,
#[shrinkwrap(main_field)] #[shrinkwrap(main_field)]
model: Rc<NavigatorModel>, model: Rc<NavigatorModel>,
} }
@ -252,7 +269,19 @@ pub struct Navigator {
impl Navigator { impl Navigator {
pub fn new(scene: &Scene, camera: &Camera2d) -> Self { pub fn new(scene: &Scene, camera: &Camera2d) -> Self {
let model = Rc::new(NavigatorModel::new(scene, camera)); let model = Rc::new(NavigatorModel::new(scene, camera));
Navigator { model } let frp = Frp::new();
let out = &frp.private.output;
let network = frp.network();
frp::extend! { network
enable <- frp.set_enabled.on_true();
disable <- frp.set_enabled.on_false();
eval_ enable(model.enable());
eval_ disable(model.disable());
out.enabled <+ frp.set_enabled;
}
Navigator { model, frp }
} }
} }