dock: Add add_panel method to add panel to DockArea. (#440)
Ref #438 https://github.com/user-attachments/assets/93b40aed-39f3-4d16-a442-13aa7a931895
This commit is contained in:
parent
0119c80120
commit
142d092867
10 changed files with 205 additions and 8 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -2355,6 +2355,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"gpui",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
|||
24
assets/icons/layout-dashboard.svg
Normal file
24
assets/icons/layout-dashboard.svg
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-layout-dashboard"
|
||||
><rect width="7" height="9" x="3" y="3" rx="1" /><rect
|
||||
width="7"
|
||||
height="5"
|
||||
x="14"
|
||||
y="3"
|
||||
rx="1"
|
||||
/><rect width="7" height="9" x="14" y="12" rx="1" /><rect
|
||||
width="7"
|
||||
height="5"
|
||||
x="3"
|
||||
y="16"
|
||||
rx="1"
|
||||
/></svg>
|
||||
|
After Width: | Height: | Size: 552 B |
|
|
@ -12,6 +12,7 @@ ui.workspace = true
|
|||
story.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
rand = "0.8"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use story::{
|
|||
use ui::{
|
||||
button::{Button, ButtonStyled as _},
|
||||
color_picker::{ColorPicker, ColorPickerEvent},
|
||||
dock::{DockArea, DockAreaState, DockEvent, DockItem},
|
||||
dock::{DockArea, DockAreaState, DockEvent, DockItem, DockPlacement},
|
||||
h_flex,
|
||||
popup_menu::PopupMenuExt,
|
||||
theme::{ActiveTheme, Theme},
|
||||
|
|
@ -31,7 +31,10 @@ struct SelectLocale(SharedString);
|
|||
#[derive(Clone, PartialEq, Eq, Deserialize)]
|
||||
struct SelectFont(usize);
|
||||
|
||||
impl_actions!(story, [SelectLocale, SelectFont]);
|
||||
#[derive(Clone, PartialEq, Eq, Deserialize)]
|
||||
struct AddPanel(DockPlacement);
|
||||
|
||||
impl_actions!(story, [SelectLocale, SelectFont, AddPanel]);
|
||||
|
||||
actions!(workspace, [Open, CloseWindow]);
|
||||
|
||||
|
|
@ -352,6 +355,35 @@ impl StoryWorkspace {
|
|||
Ok(window)
|
||||
})
|
||||
}
|
||||
|
||||
fn on_action_add_panel(&mut self, action: &AddPanel, cx: &mut ViewContext<Self>) {
|
||||
// Random pick up a panel to add
|
||||
let panel = match rand::random::<usize>() % 18 {
|
||||
0 => Arc::new(StoryContainer::panel::<ButtonStory>(cx)),
|
||||
1 => Arc::new(StoryContainer::panel::<InputStory>(cx)),
|
||||
2 => Arc::new(StoryContainer::panel::<DropdownStory>(cx)),
|
||||
3 => Arc::new(StoryContainer::panel::<TextStory>(cx)),
|
||||
4 => Arc::new(StoryContainer::panel::<ModalStory>(cx)),
|
||||
5 => Arc::new(StoryContainer::panel::<PopupStory>(cx)),
|
||||
6 => Arc::new(StoryContainer::panel::<SwitchStory>(cx)),
|
||||
7 => Arc::new(StoryContainer::panel::<ProgressStory>(cx)),
|
||||
8 => Arc::new(StoryContainer::panel::<TableStory>(cx)),
|
||||
9 => Arc::new(StoryContainer::panel::<ImageStory>(cx)),
|
||||
10 => Arc::new(StoryContainer::panel::<IconStory>(cx)),
|
||||
11 => Arc::new(StoryContainer::panel::<TooltipStory>(cx)),
|
||||
12 => Arc::new(StoryContainer::panel::<ProgressStory>(cx)),
|
||||
13 => Arc::new(StoryContainer::panel::<CalendarStory>(cx)),
|
||||
14 => Arc::new(StoryContainer::panel::<ResizableStory>(cx)),
|
||||
15 => Arc::new(StoryContainer::panel::<ScrollableStory>(cx)),
|
||||
16 => Arc::new(StoryContainer::panel::<AccordionStory>(cx)),
|
||||
// 17 => Arc::new(StoryContainer::panel::<WebViewStory>(cx)),
|
||||
_ => Arc::new(StoryContainer::panel::<ButtonStory>(cx)),
|
||||
};
|
||||
|
||||
self.dock_area.update(cx, |dock_area, cx| {
|
||||
dock_area.add_panel(panel, action.0, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_new(
|
||||
|
|
@ -377,6 +409,8 @@ impl Render for StoryWorkspace {
|
|||
let notifications_count = cx.notifications().len();
|
||||
|
||||
div()
|
||||
.id("story-workspace")
|
||||
.on_action(cx.listener(Self::on_action_add_panel))
|
||||
.relative()
|
||||
.size_full()
|
||||
.flex()
|
||||
|
|
@ -393,6 +427,32 @@ impl Render for StoryWorkspace {
|
|||
.px_2()
|
||||
.gap_2()
|
||||
.child(self.theme_color_picker.clone())
|
||||
.child(
|
||||
Button::new("add-panel")
|
||||
.icon(IconName::LayoutDashboard)
|
||||
.small()
|
||||
.ghost()
|
||||
.popup_menu(|menu, _| {
|
||||
menu.menu(
|
||||
"Add Panel to Center",
|
||||
Box::new(AddPanel(DockPlacement::Center)),
|
||||
)
|
||||
.separator()
|
||||
.menu(
|
||||
"Add Panel to Left",
|
||||
Box::new(AddPanel(DockPlacement::Left)),
|
||||
)
|
||||
.menu(
|
||||
"Add Panel to Right",
|
||||
Box::new(AddPanel(DockPlacement::Right)),
|
||||
)
|
||||
.menu(
|
||||
"Add Panel to Bottom",
|
||||
Box::new(AddPanel(DockPlacement::Bottom)),
|
||||
)
|
||||
})
|
||||
.anchor(AnchorCorner::TopRight),
|
||||
)
|
||||
.child(
|
||||
Button::new("theme-mode")
|
||||
.map(|this| {
|
||||
|
|
@ -414,7 +474,9 @@ impl Render for StoryWorkspace {
|
|||
.small()
|
||||
.ghost()
|
||||
.on_click(|_, cx| {
|
||||
cx.open_url("https://github.com/huacnlee/gpui-component")
|
||||
cx.open_url(
|
||||
"https://github.com/longbridgeapp/gpui-component",
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ impl Render for PopupStory {
|
|||
menu.link_with_icon(
|
||||
"GitHub Repository",
|
||||
IconName::GitHub,
|
||||
"https://github.com/huacnlee/gpui-component",
|
||||
"https://github.com/longbridgeapp/gpui-component",
|
||||
)
|
||||
.separator()
|
||||
.link("GPUI", "https://gpui.rs")
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ impl Render for TextStory {
|
|||
.child(Link::new("link3").child(h_flex().gap_1().child(IconName::GitHub).child("GitHub")).on_click(cx.listener(|_, _, cx| {
|
||||
cx.open_url("https://google.com")
|
||||
})))
|
||||
.child(div().w(px(250.)).child(Link::new("link4").child("https://github.com/huacnlee/gpui-component").href("https://github.com/huacnlee/gpui-component")))
|
||||
.child(div().w(px(250.)).child(Link::new("link4").child("https://github.com/longbridgeapp/gpui-component").href("https://github.com/longbridgeapp/gpui-component")))
|
||||
)
|
||||
)
|
||||
.child(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
//! Dock is a fixed container that places at left, bottom, right of the Windows.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, Axis, Element, InteractiveElement as _, IntoElement,
|
||||
MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render,
|
||||
|
|
@ -14,13 +16,15 @@ use crate::{
|
|||
AxisExt as _, StyledExt,
|
||||
};
|
||||
|
||||
use super::{DockArea, DockItem, TabPanel};
|
||||
use super::{DockArea, DockItem, PanelView, TabPanel};
|
||||
|
||||
#[derive(Clone, Render)]
|
||||
struct ResizePanel;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum DockPlacement {
|
||||
#[serde(rename = "center")]
|
||||
Center,
|
||||
#[serde(rename = "left")]
|
||||
Left,
|
||||
#[serde(rename = "bottom")]
|
||||
|
|
@ -34,6 +38,7 @@ impl DockPlacement {
|
|||
match self {
|
||||
Self::Left | Self::Right => Axis::Horizontal,
|
||||
Self::Bottom => Axis::Vertical,
|
||||
Self::Center => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,6 +209,12 @@ impl Dock {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
/// Add item to the Dock.
|
||||
pub fn add_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
|
||||
self.panel.add_panel(panel, &self.dock_area, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_resize_handle(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let axis = self.placement.axis();
|
||||
let neg_offset = -HANDLE_PADDING;
|
||||
|
|
@ -270,6 +281,7 @@ impl Dock {
|
|||
DockPlacement::Left => mouse_position.x - area_bounds.left(),
|
||||
DockPlacement::Right => area_bounds.right() - mouse_position.x,
|
||||
DockPlacement::Bottom => area_bounds.bottom() - mouse_position.y,
|
||||
DockPlacement::Center => unreachable!(),
|
||||
};
|
||||
|
||||
self.size = size.max(PANEL_MIN_SIZE);
|
||||
|
|
@ -293,6 +305,7 @@ impl Render for Dock {
|
|||
.map(|this| match self.placement {
|
||||
DockPlacement::Left | DockPlacement::Right => this.h_flex().h_full().w(self.size),
|
||||
DockPlacement::Bottom => this.w_full().h(self.size),
|
||||
DockPlacement::Center => unreachable!(),
|
||||
})
|
||||
// Bottom Dock should keep the title bar, then user can click the Toggle button
|
||||
.when(!self.open && self.placement.is_bottom(), |this| {
|
||||
|
|
|
|||
|
|
@ -202,6 +202,41 @@ impl DockItem {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add a panel to the dock item.
|
||||
pub fn add_panel(
|
||||
&mut self,
|
||||
panel: Arc<dyn PanelView>,
|
||||
dock_area: &WeakView<DockArea>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
match self {
|
||||
Self::Tabs { view, items, .. } => {
|
||||
items.push(panel.clone());
|
||||
view.update(cx, |tab_panel, cx| {
|
||||
tab_panel.add_panel(panel, cx);
|
||||
});
|
||||
}
|
||||
Self::Split { view, items, .. } => {
|
||||
// Iter items to add panel to the first tabs
|
||||
for item in items.into_iter() {
|
||||
if let DockItem::Tabs { view, .. } = item {
|
||||
view.update(cx, |tab_panel, cx| {
|
||||
tab_panel.add_panel(panel.clone(), cx);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Unable to find tabs, create new tabs
|
||||
let new_item = Self::tabs(vec![panel.clone()], None, dock_area, cx);
|
||||
items.push(new_item.clone());
|
||||
view.update(cx, |stack_panel, cx| {
|
||||
stack_panel.add_panel(new_item.view(), None, dock_area.clone(), cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_collapsed(&self, collapsed: bool, cx: &mut WindowContext) {
|
||||
match self {
|
||||
DockItem::Tabs { view, .. } => {
|
||||
|
|
@ -364,6 +399,7 @@ impl DockArea {
|
|||
DockPlacement::Left => self.left_dock.is_some(),
|
||||
DockPlacement::Bottom => self.bottom_dock.is_some(),
|
||||
DockPlacement::Right => self.right_dock.is_some(),
|
||||
DockPlacement::Center => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -385,6 +421,7 @@ impl DockArea {
|
|||
.as_ref()
|
||||
.map(|dock| dock.read(cx).is_open())
|
||||
.unwrap_or(false),
|
||||
DockPlacement::Center => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +430,9 @@ impl DockArea {
|
|||
DockPlacement::Left => &self.left_dock,
|
||||
DockPlacement::Bottom => &self.bottom_dock,
|
||||
DockPlacement::Right => &self.right_dock,
|
||||
DockPlacement::Center => return,
|
||||
};
|
||||
|
||||
if let Some(dock) = dock {
|
||||
dock.update(cx, |view, cx| {
|
||||
view.toggle_open(cx);
|
||||
|
|
@ -401,6 +440,59 @@ impl DockArea {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add a panel item to the dock area at the given placement.
|
||||
///
|
||||
/// If the left, bottom, right dock is not present, it will set the dock at the placement.
|
||||
pub fn add_panel(
|
||||
&mut self,
|
||||
panel: Arc<dyn PanelView>,
|
||||
placement: DockPlacement,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let weak_self = cx.view().downgrade();
|
||||
match placement {
|
||||
DockPlacement::Left => {
|
||||
if let Some(dock) = self.left_dock.as_ref() {
|
||||
dock.update(cx, |dock, cx| dock.add_panel(panel, cx))
|
||||
} else {
|
||||
self.set_left_dock(
|
||||
DockItem::tabs(vec![panel], None, &weak_self, cx),
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
DockPlacement::Bottom => {
|
||||
if let Some(dock) = self.bottom_dock.as_ref() {
|
||||
dock.update(cx, |dock, cx| dock.add_panel(panel, cx))
|
||||
} else {
|
||||
self.set_bottom_dock(
|
||||
DockItem::tabs(vec![panel], None, &weak_self, cx),
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
DockPlacement::Right => {
|
||||
if let Some(dock) = self.right_dock.as_ref() {
|
||||
dock.update(cx, |dock, cx| dock.add_panel(panel, cx))
|
||||
} else {
|
||||
self.set_right_dock(
|
||||
DockItem::tabs(vec![panel], None, &weak_self, cx),
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
DockPlacement::Center => {
|
||||
self.items.add_panel(panel, &cx.view().downgrade(), cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Load the state of the DockArea from the DockAreaState.
|
||||
///
|
||||
/// See also [DockeArea::dump].
|
||||
|
|
|
|||
|
|
@ -400,6 +400,7 @@ impl TabPanel {
|
|||
dock_area.bottom_dock.is_some()
|
||||
&& toggle_button_panels.bottom == Some(view_entity_id)
|
||||
}
|
||||
DockPlacement::Center => unreachable!(),
|
||||
} {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -428,6 +429,7 @@ impl TabPanel {
|
|||
IconName::PanelBottomOpen
|
||||
}
|
||||
}
|
||||
DockPlacement::Center => unreachable!(),
|
||||
};
|
||||
|
||||
Some(
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ pub enum IconName {
|
|||
HeartOff,
|
||||
Inbox,
|
||||
Info,
|
||||
LayoutDashboard,
|
||||
Loader,
|
||||
LoaderCircle,
|
||||
Maximize,
|
||||
|
|
@ -60,10 +61,10 @@ pub enum IconName {
|
|||
ThumbsDown,
|
||||
ThumbsUp,
|
||||
TriangleAlert,
|
||||
WindowClose,
|
||||
WindowMaximize,
|
||||
WindowMinimize,
|
||||
WindowRestore,
|
||||
WindowClose,
|
||||
}
|
||||
|
||||
impl IconName {
|
||||
|
|
@ -99,6 +100,7 @@ impl IconName {
|
|||
IconName::HeartOff => "icons/heart-off.svg",
|
||||
IconName::Inbox => "icons/inbox.svg",
|
||||
IconName::Info => "icons/info.svg",
|
||||
IconName::LayoutDashboard => "icons/layout-dashboard.svg",
|
||||
IconName::Loader => "icons/loader.svg",
|
||||
IconName::LoaderCircle => "icons/loader-circle.svg",
|
||||
IconName::Maximize => "icons/maximize.svg",
|
||||
|
|
@ -123,10 +125,10 @@ impl IconName {
|
|||
IconName::ThumbsDown => "icons/thumbs-down.svg",
|
||||
IconName::ThumbsUp => "icons/thumbs-up.svg",
|
||||
IconName::TriangleAlert => "icons/triangle-alert.svg",
|
||||
IconName::WindowClose => "icons/window-close.svg",
|
||||
IconName::WindowMaximize => "icons/window-maximize.svg",
|
||||
IconName::WindowMinimize => "icons/window-minimize.svg",
|
||||
IconName::WindowRestore => "icons/window-restore.svg",
|
||||
IconName::WindowClose => "icons/window-close.svg",
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue