Add Sidebar / Layout cards to appearance form
Below the appearance rows the Appearance form now exposes the design's "Sidebar / Layout" section: serif "Layout" sub-headline plus a 2-up preview-card grid wired to the active space's sidebar_width — Single column lands on DEFAULT_SIDEBAR_WIDTH_PX, Compact lands on COLLAPSED_SIDEBAR_WIDTH_PX. Each card draws a miniature sidebar + canvas preview so the choice reads at a glance. The shell exposes a new set_active_sidebar_width helper alongside the existing toggle so the cards mutate state without re-implementing the toggle logic. The "Hidden on hover" preset from the design is omitted until that mode actually ships — no placeholder option. The cards live in chrome::appearance_layout_cards so appearance_form stays under 500 lines.
This commit is contained in:
@@ -8,6 +8,8 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use gpui_component::{IconName, scroll::ScrollableElement};
|
use gpui_component::{IconName, scroll::ScrollableElement};
|
||||||
|
|
||||||
|
use super::appearance_layout_cards::render_sidebar_layout_section;
|
||||||
|
|
||||||
use crate::shell::ElyShell;
|
use crate::shell::ElyShell;
|
||||||
use crate::shell::chrome::SERIF_FAMILY;
|
use crate::shell::chrome::SERIF_FAMILY;
|
||||||
|
|
||||||
@@ -31,7 +33,8 @@ pub(crate) fn render_appearance_form(
|
|||||||
.gap(px(28.0))
|
.gap(px(28.0))
|
||||||
.child(render_header())
|
.child(render_header())
|
||||||
.child(render_wallpaper_grid(snapshot, cx))
|
.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()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<ElyShell>,
|
||||||
|
) -> 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<ElyShell>,
|
||||||
|
) -> 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;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pub(crate) mod appearance_form;
|
pub(crate) mod appearance_form;
|
||||||
|
pub(crate) mod appearance_layout_cards;
|
||||||
pub(crate) mod brand_glyph;
|
pub(crate) mod brand_glyph;
|
||||||
pub(crate) mod command_footer;
|
pub(crate) mod command_footer;
|
||||||
pub(crate) mod command_overlay;
|
pub(crate) mod command_overlay;
|
||||||
|
|||||||
@@ -112,6 +112,20 @@ impl ElyShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_active_sidebar_width(&mut self, width_px: u16, cx: &mut Context<Self>) {
|
||||||
|
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(
|
fn render_compact_space_button(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
|
|||||||
Reference in New Issue
Block a user