Files
Ely-GPUI-Components/src/layout/sidebar.rs
T

96 lines
2.8 KiB
Rust

use std::time::Instant;
use gpui::{
Animation, AnimationExt, AnyElement, App, ElementId, InteractiveElement, IntoElement,
ParentElement, RenderOnce, Styled, Window, div,
};
use smallvec::SmallVec;
use crate::{motion, theme::ActiveTheme};
struct Width {
collapsed: bool,
generation: u64,
changed: Instant,
}
/// Side column that folds to an icon rail, width animated.
#[derive(IntoElement)]
pub struct Sidebar {
id: ElementId,
collapsed: bool,
body: SmallVec<[AnyElement; 4]>,
}
impl Sidebar {
/// Children should render their own compact form when `collapsed`.
pub fn new(id: impl Into<ElementId>, collapsed: bool) -> Self {
Self {
id: id.into(),
collapsed,
body: SmallVec::new(),
}
}
}
impl ParentElement for Sidebar {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.body.extend(elements);
}
}
impl RenderOnce for Sidebar {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let collapsed = self.collapsed;
let state = window.use_keyed_state(self.id.clone(), cx, |_, _| Width {
collapsed,
generation: 0,
changed: Instant::now(),
});
if state.read(cx).collapsed != collapsed {
state.update(cx, |width, _| {
width.collapsed = collapsed;
width.generation += 1;
width.changed = Instant::now();
});
}
let (generation, changed) = {
let width = state.read(cx);
(width.generation, width.changed)
};
let theme = cx.theme();
let (wide, narrow) = (theme.sidebar_width(false), theme.sidebar_width(true));
let duration = motion::duration(motion::BASE, cx);
let column = div()
.id(self.id)
.flex()
.flex_col()
.flex_none()
.h_full()
.overflow_hidden()
.border_r_1()
.border_color(theme.colors.border)
.bg(theme.colors.bg)
.children(self.body);
if generation == 0 || changed.elapsed() >= duration {
return column
.w(if collapsed { narrow } else { wide })
.into_any_element();
}
column
.with_animation(
("width", generation),
Animation::new(duration).with_easing(motion::ease_in_out_cubic),
move |column, t| {
let (from, to) = if collapsed {
(wide, narrow)
} else {
(narrow, wide)
};
column.w(from + (to - from) * t)
},
)
.into_any_element()
}
}