Files
ELY-Browser/crates/ely_domain/src/space.rs
T
ZacharyZhang-NY 9a2f9f2d74 Add hidden-on-hover sidebar mode
Domain ships HIDDEN_SIDEBAR_WIDTH_PX = 8 alongside the existing
collapsed/default tiers. ElyShell tracks sidebar_hover_expanded with
expand_hidden_sidebar / collapse_hidden_sidebar helpers; switching
back to a non-hidden width via the layout cards or core API resets
the flag automatically so the sidebar can never be both hidden and
expanded after a mode change.

Renderer:
- render_sidebar takes a sidebar_hidden flag and routes to a thin
  8 px clickable rail (hover bg + click expands) when the active
  space's width is at HIDDEN.
- While the rail is expanded, render_browser overlays a transparent
  backdrop + the full default-width sidebar absolutely positioned in
  the shell inset, so the main pane content never reflows.
- collapsed_sidebar_active still drives the COLLAPSED-tier compact
  sidebar; the hidden tier is opted out of that path.

Appearance form:
- Layout cards section gains the design's third "Hidden on hover"
  card with a 6 px sliver preview that mutates the active space to
  HIDDEN_SIDEBAR_WIDTH_PX. LayoutMode now derives id/width/preview
  from a small enum so adding a fourth mode would be one match arm.

The hover-expanded state never persists into the domain; once the
user switches modes or clicks the backdrop, it collapses cleanly.
2026-05-09 20:01:31 -04:00

129 lines
2.9 KiB
Rust

use std::time::{Duration, SystemTime};
use crate::{ProfileId, SpaceId};
pub const DEFAULT_SIDEBAR_WIDTH_PX: u16 = 280;
pub const COLLAPSED_SIDEBAR_WIDTH_PX: u16 = 56;
pub const HIDDEN_SIDEBAR_WIDTH_PX: u16 = 8;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ArchivePolicy {
Manual,
IdleDays(u16),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Space {
id: SpaceId,
name: String,
icon: String,
accent_hex: u32,
default_profile_id: ProfileId,
archive_policy: ArchivePolicy,
sidebar_width_px: u16,
sort_key: u64,
created_at: SystemTime,
updated_at: SystemTime,
}
impl Space {
#[must_use]
pub fn new(
name: impl Into<String>,
icon: impl Into<String>,
accent_hex: u32,
default_profile_id: ProfileId,
sort_key: u64,
) -> Self {
let created_at = SystemTime::now();
Self {
id: SpaceId::new(),
name: name.into(),
icon: icon.into(),
accent_hex,
default_profile_id,
archive_policy: ArchivePolicy::Manual,
sidebar_width_px: DEFAULT_SIDEBAR_WIDTH_PX,
sort_key,
created_at,
updated_at: created_at,
}
}
#[must_use]
pub fn id(&self) -> &SpaceId {
&self.id
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn icon(&self) -> &str {
&self.icon
}
#[must_use]
pub fn accent_hex(&self) -> u32 {
self.accent_hex
}
#[must_use]
pub fn default_profile_id(&self) -> &ProfileId {
&self.default_profile_id
}
pub fn set_default_profile_id(&mut self, profile_id: ProfileId) {
self.default_profile_id = profile_id;
self.record_update();
}
#[must_use]
pub fn archive_policy(&self) -> &ArchivePolicy {
&self.archive_policy
}
pub fn set_archive_policy(&mut self, archive_policy: ArchivePolicy) {
self.archive_policy = archive_policy;
self.record_update();
}
#[must_use]
pub fn sidebar_width_px(&self) -> u16 {
self.sidebar_width_px
}
pub fn set_sidebar_width_px(&mut self, sidebar_width_px: u16) {
self.sidebar_width_px = sidebar_width_px;
self.record_update();
}
#[must_use]
pub fn sort_key(&self) -> u64 {
self.sort_key
}
pub fn set_sort_key(&mut self, sort_key: u64) {
self.sort_key = sort_key;
self.record_update();
}
#[must_use]
pub fn created_at(&self) -> SystemTime {
self.created_at
}
#[must_use]
pub fn updated_at(&self) -> SystemTime {
self.updated_at
}
fn record_update(&mut self) {
let now = SystemTime::now();
self.updated_at =
if now > self.updated_at { now } else { self.updated_at + Duration::from_nanos(1) };
}
}