feat: add resizable component

This commit is contained in:
2026-11-06 20:13:08 +07:00
parent 5bfde0d382
commit 265d2d129b
6 changed files with 562 additions and 4 deletions
+71 -1
View File
@@ -2,7 +2,7 @@ use std::time::Duration;
use gpui::prelude::FluentBuilder as _;
use gpui::{
App, AppContext as _, Bounds, Context, Entity, FontWeight, InteractiveElement as _,
App, AppContext as _, Axis, Bounds, Context, Entity, FontWeight, InteractiveElement as _,
IntoElement, KeyBinding, ListAlignment, ListState, ParentElement as _, Pixels, Render,
SharedString, Styled as _, UniformListScrollHandle, WeakEntity, Window, WindowBounds,
WindowOptions, actions, div, list, px, size, uniform_list,
@@ -48,6 +48,7 @@ use gpui_native_editor::highlight::Language;
use gpui_native_editor::markdown::{MarkdownBlock, MarkdownViewState};
use gpui_native_icons::{IconAssets, IconRole, icon};
use gpui_native_layout::dock::{Dock, DockSidebar, DockState, PanelDescriptor, SplitDirection};
use gpui_native_layout::resizable::{Resizable, ResizableState};
use gpui_native_layout::resize_handle::ResizeHandle;
use gpui_native_layout::shell::{LayoutState, Shell};
use gpui_native_theme::{
@@ -119,6 +120,7 @@ const CATALOG: &[(&str, &[(&str, &str)])] = &[
("welcome", "Welcome"),
("sidebar", "Sidebar"),
("resize-handle", "Resize handle"),
("resizable", "Resizable"),
("separator", "Separator"),
],
),
@@ -675,6 +677,12 @@ impl Gallery {
}),
cx,
);
dock.register_panel(
PanelDescriptor::new("resizable", "Resizable", |_, cx| {
cx.new(ResizableDemo::new).into()
}),
cx,
);
dock.register_panel(
PanelDescriptor::new("separator", "Separator", |_, cx| {
cx.new(|_| SeparatorDemo).into()
@@ -1706,6 +1714,68 @@ impl Render for SeparatorDemo {
}
}
struct ResizableDemo {
vertical: Entity<ResizableState>,
horizontal: Entity<ResizableState>,
_observations: (gpui::Subscription, gpui::Subscription),
}
impl ResizableDemo {
fn new(cx: &mut Context<Self>) -> Self {
// The panel is a cached dock view: it re-renders only when the size
// entities it reads are observed and notified. Each group owns one.
let vertical = ResizableState::new(cx);
let horizontal = ResizableState::new(cx);
let observations = (
cx.observe(&vertical, |_, _, cx| cx.notify()),
cx.observe(&horizontal, |_, _, cx| cx.notify()),
);
Self {
vertical,
horizontal,
_observations: observations,
}
}
}
impl Render for ResizableDemo {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let colors = cx.theme().colors;
let pane = |text: &'static str| {
div().size_full().bg(colors.muted).p_2().child(
div()
.text_sm()
.text_color(colors.muted_foreground)
.child(text),
)
};
ScrollArea::new("resizable-demo")
.size_full()
.p_4()
.child(demo_section(
"vertical",
colors,
Resizable::new("resizable-demo-vertical", self.vertical.clone())
.size_full()
.h(px(320.))
.pane_with_min(pane("Tree"), px(96.))
.pane(pane("Editor"))
.pane(pane("Terminal")),
))
.child(demo_section(
"horizontal",
colors,
Resizable::new("resizable-demo-horizontal", self.horizontal.clone())
.axis(Axis::Horizontal)
.size_full()
.h(px(120.))
.pane(pane("Primary"))
.pane(pane("Inspector")),
))
}
}
struct BadgeDemo;
impl Render for BadgeDemo {