Auto-reveal hidden sidebar on cursor reach
Cursor-reach reveal now fulfils the "Slide in on cursor reach" description on the design's Hidden-on-hover layout card. - BrowserCore::active_space_sidebar_width() returns the current sidebar tier without cloning a full BrowserSnapshot, so the mouse_move hot path stays cheap. - on_window_mouse_move on the shell root hits-tests the cursor x against REVEAL_THRESHOLD_PX (24 px from the left edge). Outside the reveal/collapse zones it returns immediately, so 99 % of mouse moves never even read the snapshot. - When in HIDDEN mode and the cursor crosses the reveal threshold, expand_hidden_sidebar fires (its early-return on already-expanded state prevents notify spam). When the cursor passes COLLAPSE_THRESHOLD_PX (shell inset + default sidebar width + 24 px buffer), collapse_hidden_sidebar fires. - Click-to-expand on the rail and click-on-backdrop-to-collapse remain as predictable fallbacks.
This commit is contained in:
@@ -2,8 +2,9 @@ use ely_browser_core::BrowserSnapshot;
|
||||
use ely_design_system::{colors, spacing};
|
||||
use ely_domain::{BrowserTab, DEFAULT_SIDEBAR_WIDTH_PX, HIDDEN_SIDEBAR_WIDTH_PX};
|
||||
use gpui::{
|
||||
AnyElement, Context, InteractiveElement, IntoElement, ParentElement, Render, SharedString,
|
||||
StatefulInteractiveElement, Styled, Window, div, prelude::FluentBuilder, px, rgb, rgba,
|
||||
AnyElement, Context, InteractiveElement, IntoElement, MouseMoveEvent, ParentElement, Render,
|
||||
SharedString, StatefulInteractiveElement, Styled, Window, div, prelude::FluentBuilder, px,
|
||||
rgb, rgba,
|
||||
};
|
||||
|
||||
use super::chrome::{
|
||||
@@ -65,6 +66,7 @@ impl ElyShell {
|
||||
.on_action(cx.listener(Self::on_toggle_sidebar))
|
||||
.on_action(cx.listener(Self::on_zoom_in))
|
||||
.on_action(cx.listener(Self::on_zoom_out))
|
||||
.on_mouse_move(cx.listener(Self::on_window_mouse_move))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(render_wallpaper(snapshot.appearance.wallpaper()))
|
||||
.child(
|
||||
@@ -165,6 +167,35 @@ impl ElyShell {
|
||||
self.render_expanded_sidebar(snapshot, sidebar_width, cx)
|
||||
}
|
||||
|
||||
fn on_window_mouse_move(
|
||||
&mut self,
|
||||
event: &MouseMoveEvent,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let cursor_x = f32::from(event.position.x);
|
||||
|
||||
if cursor_x >= REVEAL_THRESHOLD_PX && cursor_x <= COLLAPSE_THRESHOLD_PX {
|
||||
return;
|
||||
}
|
||||
|
||||
let ShellState::Ready(core) = &self.state else {
|
||||
return;
|
||||
};
|
||||
let Some(width) = core.active_space_sidebar_width() else {
|
||||
return;
|
||||
};
|
||||
if width > HIDDEN_SIDEBAR_WIDTH_PX {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.sidebar_hover_expanded && cursor_x < REVEAL_THRESHOLD_PX {
|
||||
self.expand_hidden_sidebar(cx);
|
||||
} else if self.sidebar_hover_expanded && cursor_x > COLLAPSE_THRESHOLD_PX {
|
||||
self.collapse_hidden_sidebar(cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_hidden_sidebar_rail(&mut self, cx: &mut Context<Self>) -> AnyElement {
|
||||
div()
|
||||
.id(SharedString::from("hidden-sidebar-rail"))
|
||||
@@ -180,6 +211,17 @@ impl ElyShell {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cursor x within this px from the left edge auto-reveals the hidden sidebar.
|
||||
/// Sized so the user only triggers the reveal when they actually approach the
|
||||
/// rail, not when they hover the page content.
|
||||
const REVEAL_THRESHOLD_PX: f32 = 24.0;
|
||||
|
||||
/// Once revealed, cursor x past this px collapses the hidden sidebar back to
|
||||
/// the rail. Sits past the right edge of the expanded sidebar plus a small
|
||||
/// buffer so brief overshoots don't ping-pong the state.
|
||||
const COLLAPSE_THRESHOLD_PX: f32 =
|
||||
spacing::SHELL_INSET + DEFAULT_SIDEBAR_WIDTH_PX as f32 + 24.0;
|
||||
|
||||
fn render_error(message: String) -> AnyElement {
|
||||
div()
|
||||
.size_full()
|
||||
|
||||
@@ -465,6 +465,11 @@ impl BrowserCore {
|
||||
.ok_or_else(|| CoreError::SpaceNotFound { id: self.active_space_id.clone() })
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn active_space_sidebar_width(&self) -> Option<u16> {
|
||||
self.active_space().ok().map(|space| space.sidebar_width_px())
|
||||
}
|
||||
|
||||
fn favorites(&self) -> Vec<BrowserTab> {
|
||||
self.tabs.iter().filter(|tab| tab.flags().favorite).cloned().collect()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user