diff --git a/crates/ely_app/src/shell/chrome/appearance_form.rs b/crates/ely_app/src/shell/chrome/appearance_form.rs index 5040285..c690ff2 100644 --- a/crates/ely_app/src/shell/chrome/appearance_form.rs +++ b/crates/ely_app/src/shell/chrome/appearance_form.rs @@ -8,6 +8,8 @@ use gpui::{ }; use gpui_component::{IconName, scroll::ScrollableElement}; +use super::appearance_layout_cards::render_sidebar_layout_section; + use crate::shell::ElyShell; use crate::shell::chrome::SERIF_FAMILY; @@ -31,7 +33,8 @@ pub(crate) fn render_appearance_form( .gap(px(28.0)) .child(render_header()) .child(render_wallpaper_grid(snapshot, cx)) - .child(render_appearance_rows(snapshot, cx)), + .child(render_appearance_rows(snapshot, cx)) + .child(render_sidebar_layout_section(snapshot, cx)), ) .into_any_element() } diff --git a/crates/ely_app/src/shell/chrome/appearance_layout_cards.rs b/crates/ely_app/src/shell/chrome/appearance_layout_cards.rs new file mode 100644 index 0000000..40e954b --- /dev/null +++ b/crates/ely_app/src/shell/chrome/appearance_layout_cards.rs @@ -0,0 +1,163 @@ +use ely_browser_core::BrowserSnapshot; +use ely_design_system::colors; +use ely_domain::{COLLAPSED_SIDEBAR_WIDTH_PX, DEFAULT_SIDEBAR_WIDTH_PX}; +use gpui::{ + AnyElement, Context, FontWeight, InteractiveElement, IntoElement, ParentElement, SharedString, + StatefulInteractiveElement, Styled, div, prelude::FluentBuilder, px, rgb, rgba, +}; + +use crate::shell::ElyShell; +use crate::shell::chrome::SERIF_FAMILY; + +pub(crate) fn render_sidebar_layout_section( + snapshot: &BrowserSnapshot, + cx: &mut Context, +) -> AnyElement { + let active_width = snapshot + .spaces + .iter() + .find(|space| space.id() == &snapshot.active_space_id) + .map(|space| space.sidebar_width_px()) + .unwrap_or(DEFAULT_SIDEBAR_WIDTH_PX); + let compact = active_width <= COLLAPSED_SIDEBAR_WIDTH_PX; + + div() + .flex() + .flex_col() + .gap(px(14.0)) + .pt(px(8.0)) + .child( + div() + .text_size(px(11.0)) + .text_color(rgb(colors::INK_4)) + .child("SIDEBAR"), + ) + .child( + div() + .font_family(SERIF_FAMILY) + .text_size(px(22.0)) + .font_weight(FontWeight(400.0)) + .text_color(rgb(colors::INK)) + .child("Layout"), + ) + .child( + div() + .grid() + .grid_cols(2) + .gap(px(10.0)) + .child(render_layout_card( + "Single column", + "Default ยท workspace + tabs.", + !compact, + LayoutMode::Single, + cx, + )) + .child(render_layout_card( + "Compact", + "Icons-only with launcher rail.", + compact, + LayoutMode::Compact, + cx, + )), + ) + .into_any_element() +} + +#[derive(Clone, Copy)] +enum LayoutMode { + Single, + Compact, +} + +fn render_layout_card( + title: &'static str, + detail: &'static str, + selected: bool, + mode: LayoutMode, + cx: &mut Context, +) -> AnyElement { + let id = SharedString::from(match mode { + LayoutMode::Single => "layout-single", + LayoutMode::Compact => "layout-compact", + }); + + div() + .id(id) + .p(px(14.0)) + .rounded(px(14.0)) + .bg(rgba(LAYOUT_CARD_BG)) + .when(selected, |el| el.border_2().border_color(rgb(colors::ACCENT))) + .when(!selected, |el| el.border_1().border_color(rgba(colors::STROKE))) + .flex() + .flex_col() + .gap(px(10.0)) + .cursor_pointer() + .hover(|style| style.opacity(0.94)) + .active(|style| style.opacity(0.85)) + .on_click(cx.listener(move |shell, _, _, cx| { + let width = match mode { + LayoutMode::Single => DEFAULT_SIDEBAR_WIDTH_PX, + LayoutMode::Compact => COLLAPSED_SIDEBAR_WIDTH_PX, + }; + shell.set_active_sidebar_width(width, cx); + })) + .child(render_layout_preview(mode)) + .child( + div() + .flex() + .flex_col() + .gap(px(2.0)) + .child( + div() + .text_size(px(12.5)) + .font_weight(FontWeight(500.0)) + .text_color(rgb(colors::INK)) + .child(title), + ) + .child( + div() + .text_size(px(11.0)) + .text_color(rgb(colors::INK_4)) + .child(detail), + ), + ) + .into_any_element() +} + +fn render_layout_preview(mode: LayoutMode) -> AnyElement { + let sidebar_width = match mode { + LayoutMode::Single => 56.0, + LayoutMode::Compact => 18.0, + }; + + div() + .h(px(96.0)) + .rounded(px(8.0)) + .bg(rgb(0xffffff)) + .relative() + .child( + div() + .absolute() + .left(px(6.0)) + .top(px(6.0)) + .bottom(px(6.0)) + .w(px(sidebar_width)) + .rounded(px(5.0)) + .bg(rgba(LAYOUT_PANEL_BG)), + ) + .child( + div() + .absolute() + .left(px(sidebar_width + 12.0)) + .top(px(6.0)) + .right(px(6.0)) + .bottom(px(6.0)) + .rounded(px(5.0)) + .bg(rgba(LAYOUT_CANVAS_BG)), + ) + .into_any_element() +} + +const LAYOUT_CARD_BG: u32 = 0xffffffd9; +const LAYOUT_PANEL_BG: u32 = 0x281e1414; +const LAYOUT_CANVAS_BG: u32 = 0x281e140a; diff --git a/crates/ely_app/src/shell/chrome/mod.rs b/crates/ely_app/src/shell/chrome/mod.rs index 1b83ee0..54632be 100644 --- a/crates/ely_app/src/shell/chrome/mod.rs +++ b/crates/ely_app/src/shell/chrome/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod appearance_form; +pub(crate) mod appearance_layout_cards; pub(crate) mod brand_glyph; pub(crate) mod command_footer; pub(crate) mod command_overlay; diff --git a/crates/ely_app/src/shell/sidebar.rs b/crates/ely_app/src/shell/sidebar.rs index 9a86ba6..b4cdf8d 100644 --- a/crates/ely_app/src/shell/sidebar.rs +++ b/crates/ely_app/src/shell/sidebar.rs @@ -112,6 +112,20 @@ impl ElyShell { } } + pub(crate) fn set_active_sidebar_width(&mut self, width_px: u16, cx: &mut Context) { + let ShellState::Ready(core) = &mut self.state else { + return; + }; + let active_space_id = match core.snapshot() { + Ok(snapshot) => snapshot.active_space_id, + Err(_) => return, + }; + + if core.set_space_sidebar_width(&active_space_id, width_px).is_ok() { + cx.notify(); + } + } + fn render_compact_space_button( &mut self, index: usize,