add app layout shell to the layout crate

This commit is contained in:
2026-11-02 08:53:18 +07:00
parent 3e03137d1c
commit aa40003a37
15 changed files with 697 additions and 11 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ description = "Live preview of every component against the resolved system theme
[dependencies]
gpui = { workspace = true }
gpui_platform = { workspace = true }
gpui-native-core = { workspace = true }
gpui-native-data = { workspace = true }
gpui-native-core = { workspace = true, features = ["full"] }
gpui-native-data = { workspace = true, features = ["full"] }
gpui-native-layout = { workspace = true }
gpui-native-icons = { workspace = true }
gpui-native-theme = { workspace = true }
+117
View File
@@ -34,6 +34,7 @@ use gpui_native_data::sidebar::{Sidebar, SidebarEntry};
use gpui_native_data::tabs::{Tab, Tabs};
use gpui_native_data::tree::{Tree, TreeItem};
use gpui_native_icons::{IconAssets, IconRole, icon};
use gpui_native_layout::app_layout::{AppLayout, LayoutState};
use gpui_native_layout::resize_handle::ResizeHandle;
use gpui_native_theme::{ActiveTheme as _, Size, Theme};
use gpui_platform::application;
@@ -49,6 +50,8 @@ struct Gallery {
password_state: Entity<InputState>,
notes_state: Entity<InputState>,
split_width: Pixels,
layout_state: Entity<LayoutState>,
shell_selection: Option<usize>,
popover_open: bool,
menu_open: bool,
strategy: Option<Strategy>,
@@ -144,6 +147,82 @@ fn tree_paths(nodes: &[GalleryNode]) -> Vec<Vec<usize>> {
paths
}
impl Gallery {
/// The app shell demo: sidebars around a center region, toggles in the
/// center demonstrating the `LayoutState` API.
fn render_app_shell(&self, gallery: &Entity<Self>, cx: &App) -> impl IntoElement + use<> {
let colors = cx.theme().colors;
let state = self.layout_state.clone();
let state_for_toggle = state.clone();
let toggle = |side: &'static str| {
let state = state_for_toggle.clone();
Button::new(SharedString::from(format!("gallery-shell-toggle-{side}")))
.label(format!("Toggle {}", side))
.variant(ButtonVariant::Secondary)
.small()
.on_click(move |_, _, cx| {
state.update(cx, |state, cx| match side {
"left" => {
let visible = !state.is_left_sidebar_visible();
state.set_left_sidebar_visible(visible, cx);
}
_ => {
let visible = !state.is_right_sidebar_visible();
state.set_right_sidebar_visible(visible, cx);
}
});
})
};
let left_sidebar = Sidebar::new("gallery-shell-sidebar")
.selected(self.shell_selection)
.entries(|_, _| {
vec![
SidebarEntry::header("Repositories"),
SidebarEntry::row("gpui-native-kit").icon(IconRole::Folder),
SidebarEntry::row("signed").icon(IconRole::Folder),
SidebarEntry::header("Explore"),
SidebarEntry::row("Popular"),
SidebarEntry::row("Recent"),
]
})
.on_change({
let gallery = gallery.clone();
move |selection, _, cx| {
gallery.update(cx, |gallery, _| gallery.shell_selection = selection);
}
});
let right_sidebar = v_flex()
.gap_1()
.p_2()
.child("Details")
.child("Signed by the author")
.text_sm()
.text_color(colors.muted_foreground);
let center = v_flex()
.gap_2()
.items_center()
.justify_center()
.size_full()
.child(
h_flex()
.gap_2()
.child(toggle("left"))
.child(toggle("right")),
)
.child("Center \u{2014} the dock lands here (L3)")
.text_color(colors.muted_foreground);
AppLayout::new("gallery-shell", state)
.left_sidebar(left_sidebar)
.center(center)
.right_sidebar(right_sidebar)
}
}
impl Render for Gallery {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme();
@@ -448,6 +527,41 @@ impl Render for Gallery {
)),
),
)
.child(
v_flex()
.gap_2()
.child(section_label(
"layout · app shell \u{2014} NSWindow sidebar + inspector, WinUI SplitView shell",
colors,
))
.child(
div()
.h(px(320.))
.rounded(radius.control)
.border_1()
.border_color(colors.border)
.overflow_hidden()
.child(self.render_app_shell(&gallery, cx)),
)
.child(
div()
.text_xs()
.text_color(colors.muted_foreground)
.child(format!(
"left: {} · right: {} \u{2014} buttons toggle, seams resize",
if self.layout_state.read(cx).is_left_sidebar_visible() {
"on"
} else {
"off"
},
if self.layout_state.read(cx).is_right_sidebar_visible() {
"on"
} else {
"off"
},
)),
),
)
.child(
v_flex()
.gap_2()
@@ -1538,6 +1652,7 @@ fn main() {
password_state.update(cx, |state, cx| state.set_password(true, cx));
let notes_state = InputState::new(cx);
notes_state.update(cx, |state, cx| state.set_multiline(true, cx));
let layout_state = LayoutState::new(cx);
let bounds = Bounds::centered(None, size(px(720.0), px(560.0)), cx);
if let Err(err) = cx.open_window(
WindowOptions {
@@ -1577,6 +1692,8 @@ fn main() {
password_state,
notes_state,
split_width: px(200.),
layout_state,
shell_selection: None,
popover_open: false,
menu_open: false,
strategy: Some(Strategy::Curated),