mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 11:52:59 +03:00
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:
parent
1dfcf1cafc
commit
285959835f
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
@ -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(());
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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(());
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 ===
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user