Update dock layout
This commit is contained in:
parent
19930e46db
commit
f78d50b5e9
13 changed files with 279 additions and 159 deletions
|
|
@ -4,7 +4,7 @@ use story::{
|
|||
ButtonStory, CheckboxStory, DropdownStory, ImageStory, InputStory, ListStory, PickerStory,
|
||||
PopoverStory, StoryContainer, SwitchStory, TooltipStory,
|
||||
};
|
||||
use workspace::{TitleBar, Workspace};
|
||||
use workspace::{dock::DockPosition, TitleBar, Workspace};
|
||||
|
||||
use std::sync::Arc;
|
||||
use ui::{
|
||||
|
|
@ -40,7 +40,7 @@ impl StoryWorkspace {
|
|||
})
|
||||
.detach();
|
||||
|
||||
StoryContainer::open(
|
||||
StoryContainer::add_pane(
|
||||
"Buttons",
|
||||
"Displays a button or a component that looks like a button.",
|
||||
ButtonStory::view(cx).into(),
|
||||
|
|
@ -49,25 +49,23 @@ impl StoryWorkspace {
|
|||
)
|
||||
.detach();
|
||||
|
||||
StoryContainer::open(
|
||||
"Inputs",
|
||||
"Displays a text input field.",
|
||||
StoryContainer::add_panel(
|
||||
InputStory::view(cx).into(),
|
||||
workspace.clone(),
|
||||
DockPosition::Right,
|
||||
px(500.0),
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
);
|
||||
|
||||
StoryContainer::open(
|
||||
"Checkbox",
|
||||
"A control that allows the user to toggle between checked and not checked.",
|
||||
StoryContainer::add_panel(
|
||||
CheckboxStory::view(cx).into(),
|
||||
workspace.clone(),
|
||||
DockPosition::Bottom,
|
||||
px(300.),
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
);
|
||||
|
||||
StoryContainer::open(
|
||||
StoryContainer::add_pane(
|
||||
"Switch",
|
||||
"A control that allows the user to toggle between two states.",
|
||||
SwitchStory::view(cx).into(),
|
||||
|
|
@ -76,7 +74,7 @@ impl StoryWorkspace {
|
|||
)
|
||||
.detach();
|
||||
|
||||
StoryContainer::open(
|
||||
StoryContainer::add_pane(
|
||||
"Dropdowns",
|
||||
"Displays a list of options for the user to pick from—triggered by a button.",
|
||||
DropdownStory::new(cx).into(),
|
||||
|
|
@ -85,7 +83,7 @@ impl StoryWorkspace {
|
|||
)
|
||||
.detach();
|
||||
|
||||
StoryContainer::open(
|
||||
StoryContainer::add_pane(
|
||||
"Picker",
|
||||
"Picker is a component that allows the user to select an item from a list of options.",
|
||||
PickerStory::view(cx).into(),
|
||||
|
|
@ -94,7 +92,7 @@ impl StoryWorkspace {
|
|||
)
|
||||
.detach();
|
||||
|
||||
StoryContainer::open(
|
||||
StoryContainer::add_pane(
|
||||
"Popover",
|
||||
"Displays rich content in a portal, triggered by a button.",
|
||||
PopoverStory::view(cx).into(),
|
||||
|
|
@ -103,24 +101,24 @@ impl StoryWorkspace {
|
|||
)
|
||||
.detach();
|
||||
|
||||
StoryContainer::open(
|
||||
StoryContainer::add_pane(
|
||||
"Tooltip",
|
||||
"A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
|
||||
"Displays a short message when users hover over an element.",
|
||||
TooltipStory::view(cx).into(),
|
||||
workspace.clone(),
|
||||
cx,
|
||||
).detach();
|
||||
|
||||
StoryContainer::open(
|
||||
"List",
|
||||
"A complex list example, includes use Picker, Scrollbar, and more.",
|
||||
ListStory::view(cx).into(),
|
||||
workspace.clone(),
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
|
||||
StoryContainer::open(
|
||||
StoryContainer::add_panel(
|
||||
ListStory::view(cx).into(),
|
||||
workspace.clone(),
|
||||
DockPosition::Left,
|
||||
px(360.),
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_pane(
|
||||
"Image",
|
||||
"Render SVG image and Chart",
|
||||
ImageStory::view(cx).into(),
|
||||
|
|
@ -136,7 +134,7 @@ impl StoryWorkspace {
|
|||
app_state: Arc<AppState>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<anyhow::Result<WindowHandle<Self>>> {
|
||||
let window_bounds = Bounds::centered(None, size(px(1200.0), px(900.0)), cx);
|
||||
let window_bounds = Bounds::centered(None, size(px(1600.0), px(1200.0)), cx);
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
let options = WindowOptions {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ impl CheckboxStory {
|
|||
impl Render for CheckboxStory {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.p_4()
|
||||
.gap_6()
|
||||
.child(
|
||||
v_flex().items_start().justify_start().gap_6().child(
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ impl Render for InputStory {
|
|||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.size_full()
|
||||
.p_4()
|
||||
.justify_start()
|
||||
.gap_3()
|
||||
.child(
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@ pub use switch_story::SwitchStory;
|
|||
pub use tooltip_story::TooltipStory;
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, AnyElement, AnyView, AppContext, Div, EventEmitter,
|
||||
FocusableView, IntoElement, ParentElement, Render, SharedString, Styled as _, Task, View,
|
||||
ViewContext, VisualContext, WindowContext,
|
||||
div, prelude::FluentBuilder as _, px, AnyElement, AnyView, AppContext, Div, Element,
|
||||
EventEmitter, FocusableView, InteractiveElement, IntoElement, ParentElement, Pixels, Render,
|
||||
ScrollHandle, SharedString, StatefulInteractiveElement, Styled as _, Task, View, ViewContext,
|
||||
VisualContext, WindowContext,
|
||||
};
|
||||
use workspace::{
|
||||
dock::{DockPosition, Panel, PanelEvent},
|
||||
|
|
@ -32,7 +33,7 @@ use workspace::{
|
|||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use ui::{divider::Divider, h_flex, label::Label, v_flex};
|
||||
use ui::{divider::Divider, h_flex, label::Label, v_flex, StyledExt};
|
||||
|
||||
pub fn story_case(
|
||||
name: &'static str,
|
||||
|
|
@ -61,6 +62,7 @@ pub fn section(title: impl Into<SharedString>, cx: &WindowContext) -> Div {
|
|||
|
||||
pub struct StoryContainer {
|
||||
focus_handle: gpui::FocusHandle,
|
||||
scroll_handle: gpui::ScrollHandle,
|
||||
name: SharedString,
|
||||
description: SharedString,
|
||||
position: DockPosition,
|
||||
|
|
@ -76,48 +78,6 @@ impl FocusableView for StoryContainer {
|
|||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<PanelEvent> for StoryContainer {}
|
||||
|
||||
impl Panel for StoryContainer {
|
||||
fn persistent_name() -> &'static str {
|
||||
"story-container"
|
||||
}
|
||||
|
||||
fn position(&self, _: &WindowContext) -> workspace::dock::DockPosition {
|
||||
self.position
|
||||
}
|
||||
|
||||
fn can_position(&self, _: workspace::dock::DockPosition, _: &WindowContext) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_position(&mut self, position: workspace::dock::DockPosition, _: &mut WindowContext) {
|
||||
self.position = position;
|
||||
}
|
||||
|
||||
fn size(&self, _: &WindowContext) -> gpui::Pixels {
|
||||
match self.position {
|
||||
DockPosition::Left | DockPosition::Right => self.width.unwrap_or(px(360.)),
|
||||
DockPosition::Bottom => self.height.unwrap_or(px(360.)),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: Option<gpui::Pixels>, _: &mut WindowContext) {
|
||||
match self.position {
|
||||
DockPosition::Left | DockPosition::Right => self.width = size,
|
||||
DockPosition::Bottom => self.height = size,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_active(&mut self, active: bool, _: &mut WindowContext) {
|
||||
self.active = active;
|
||||
}
|
||||
|
||||
fn starts_open(&self, _cx: &WindowContext) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ContainerEvent {
|
||||
Close,
|
||||
|
|
@ -172,6 +132,7 @@ impl StoryContainer {
|
|||
|
||||
Self {
|
||||
focus_handle,
|
||||
scroll_handle: ScrollHandle::new(),
|
||||
name: name.into(),
|
||||
description: description.into(),
|
||||
width: None,
|
||||
|
|
@ -182,7 +143,7 @@ impl StoryContainer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn open(
|
||||
pub fn add_pane(
|
||||
name: impl Into<SharedString>,
|
||||
description: impl Into<SharedString>,
|
||||
story: AnyView,
|
||||
|
|
@ -203,6 +164,19 @@ impl StoryContainer {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn add_panel(
|
||||
story: AnyView,
|
||||
workspace: View<Workspace>,
|
||||
position: DockPosition,
|
||||
size: gpui::Pixels,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let panel = cx.new_view(|cx| MyPanel::new(story, position, size, cx));
|
||||
workspace.add_panel(panel, cx)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn width(mut self, width: gpui::Pixels) -> Self {
|
||||
self.width = Some(width);
|
||||
self
|
||||
|
|
@ -226,21 +200,112 @@ impl StoryContainer {
|
|||
|
||||
impl Render for StoryContainer {
|
||||
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
div()
|
||||
.id("story-container")
|
||||
.overflow_y_scroll()
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.size_full()
|
||||
.gap_6()
|
||||
.p_2()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
.child(Label::new(self.name.clone()).text_size(px(24.0)))
|
||||
.child(Label::new(self.description.clone()).text_size(px(16.0))),
|
||||
v_flex()
|
||||
.size_full()
|
||||
.gap_6()
|
||||
.p_4()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
.child(Label::new(self.name.clone()).text_size(px(24.0)))
|
||||
.child(Label::new(self.description.clone()).text_size(px(16.0))),
|
||||
)
|
||||
.child(Divider::horizontal())
|
||||
.when_some(self.story.clone(), |this, story| {
|
||||
this.child(v_flex().size_full().child(story))
|
||||
}),
|
||||
)
|
||||
.child(Divider::horizontal())
|
||||
.when_some(self.story.clone(), |this, story| {
|
||||
this.child(v_flex().size_full().child(story))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct MyPanel {
|
||||
focus_handle: gpui::FocusHandle,
|
||||
view: AnyView,
|
||||
_position: DockPosition,
|
||||
width: Option<Pixels>,
|
||||
height: Option<Pixels>,
|
||||
}
|
||||
|
||||
impl MyPanel {
|
||||
fn new(view: AnyView, position: DockPosition, size: Pixels, cx: &mut WindowContext) -> Self {
|
||||
let mut this = Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
view,
|
||||
_position: position,
|
||||
width: None,
|
||||
height: None,
|
||||
};
|
||||
this.update_size(size);
|
||||
this
|
||||
}
|
||||
|
||||
fn update_size(&mut self, size: Pixels) {
|
||||
match self._position {
|
||||
DockPosition::Bottom => self.height = Some(size),
|
||||
DockPosition::Left | DockPosition::Right => self.width = Some(size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Panel for MyPanel {
|
||||
fn persistent_name() -> &'static str {
|
||||
"my-panel"
|
||||
}
|
||||
|
||||
fn can_position(&self, position: DockPosition) -> bool {
|
||||
match self._position {
|
||||
DockPosition::Bottom => matches!(position, DockPosition::Bottom),
|
||||
DockPosition::Left | DockPosition::Right => {
|
||||
matches!(position, DockPosition::Left | DockPosition::Right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn position(&self, _cx: &WindowContext) -> DockPosition {
|
||||
self._position
|
||||
}
|
||||
|
||||
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
|
||||
self._position = position;
|
||||
cx.notify()
|
||||
}
|
||||
|
||||
fn size(&self, _cx: &WindowContext) -> gpui::Pixels {
|
||||
match self._position {
|
||||
DockPosition::Bottom => self.height.unwrap_or(px(100.)),
|
||||
DockPosition::Left | DockPosition::Right => self.width.unwrap_or(px(100.)),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: Option<gpui::Pixels>, cx: &mut ViewContext<Self>) {
|
||||
dbg!("================ set_size", size);
|
||||
if let Some(size) = size {
|
||||
self.update_size(size)
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<PanelEvent> for MyPanel {}
|
||||
impl FocusableView for MyPanel {
|
||||
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
impl Render for MyPanel {
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.id("my-panel")
|
||||
.track_focus(&self.focus_handle)
|
||||
.size_full()
|
||||
.child(self.view.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ use core::time;
|
|||
|
||||
use fake::Fake;
|
||||
use gpui::{
|
||||
actions, div, prelude::FluentBuilder as _, px, ElementId, InteractiveElement, IntoElement,
|
||||
ParentElement, Render, RenderOnce, Styled, Timer, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
actions, div, prelude::FluentBuilder as _, px, ElementId, FocusHandle, FocusableView,
|
||||
InteractiveElement, IntoElement, ParentElement, Render, RenderOnce, Styled, Timer, View,
|
||||
ViewContext, VisualContext, WindowContext,
|
||||
};
|
||||
|
||||
use ui::{
|
||||
|
|
@ -175,22 +175,11 @@ impl CompanyListDelegate {
|
|||
}
|
||||
|
||||
pub struct ListStory {
|
||||
focus_handle: FocusHandle,
|
||||
company_list: View<Picker<CompanyListDelegate>>,
|
||||
selected_company: Option<Company>,
|
||||
}
|
||||
|
||||
fn random_company() -> Company {
|
||||
let last_done = (0.0..999.0).fake::<f64>();
|
||||
let prev_close = last_done * (-0.1..0.1).fake::<f64>();
|
||||
Company {
|
||||
name: fake::faker::company::en::CompanyName().fake(),
|
||||
industry: fake::faker::company::en::Industry().fake(),
|
||||
description: fake::faker::lorem::en::Paragraph(3..5).fake(),
|
||||
last_done,
|
||||
prev_close,
|
||||
}
|
||||
}
|
||||
|
||||
impl ListStory {
|
||||
pub fn view(cx: &mut WindowContext) -> View<Self> {
|
||||
cx.new_view(|cx| Self::new(cx))
|
||||
|
|
@ -235,6 +224,7 @@ impl ListStory {
|
|||
.detach();
|
||||
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
company_list,
|
||||
selected_company: None,
|
||||
}
|
||||
|
|
@ -248,48 +238,68 @@ impl ListStory {
|
|||
}
|
||||
}
|
||||
|
||||
fn random_company() -> Company {
|
||||
let last_done = (0.0..999.0).fake::<f64>();
|
||||
let prev_close = last_done * (-0.1..0.1).fake::<f64>();
|
||||
Company {
|
||||
name: fake::faker::company::en::CompanyName().fake(),
|
||||
industry: fake::faker::company::en::Industry().fake(),
|
||||
description: fake::faker::lorem::en::Paragraph(3..5).fake(),
|
||||
last_done,
|
||||
prev_close,
|
||||
}
|
||||
}
|
||||
|
||||
impl FocusableView for ListStory {
|
||||
fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ListStory {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
h_flex()
|
||||
.size_full()
|
||||
.track_focus(&self.focus_handle)
|
||||
.on_action(cx.listener(Self::selected_company))
|
||||
.size_full()
|
||||
.gap_4()
|
||||
.mb_4()
|
||||
.child(
|
||||
v_flex()
|
||||
.h_full()
|
||||
.w_full()
|
||||
.border_1()
|
||||
.border_color(cx.theme().border)
|
||||
.rounded_md()
|
||||
.w(px(400.))
|
||||
.occlude()
|
||||
.child(self.company_list.clone()),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.size_full()
|
||||
.border_1()
|
||||
.border_color(cx.theme().border)
|
||||
.py_1()
|
||||
.px_4()
|
||||
.rounded_md()
|
||||
.when_some(self.selected_company.clone(), |this, company| {
|
||||
this.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.items_start()
|
||||
.justify_between()
|
||||
.child(div().text_3xl().mb_6().child(company.name.clone()))
|
||||
.child(format!("{:.2}", company.last_done)),
|
||||
)
|
||||
.child(company.industry.clone())
|
||||
.child(company.description.clone()),
|
||||
)
|
||||
}),
|
||||
)
|
||||
// .child(
|
||||
// div()
|
||||
// .invisible()
|
||||
// .flex_1()
|
||||
// .size_full()
|
||||
// .border_1()
|
||||
// .border_color(cx.theme().border)
|
||||
// .py_1()
|
||||
// .px_4()
|
||||
// .rounded_md()
|
||||
// .when_some(self.selected_company.clone(), |this, company| {
|
||||
// this.child(
|
||||
// div()
|
||||
// .flex_1()
|
||||
// .gap_2()
|
||||
// .child(
|
||||
// h_flex()
|
||||
// .items_start()
|
||||
// .justify_between()
|
||||
// .child(div().text_3xl().mb_6().child(company.name.clone()))
|
||||
// .child(format!("{:.2}", company.last_done)),
|
||||
// )
|
||||
// .child(company.industry.clone())
|
||||
// .child(company.description.clone()),
|
||||
// )
|
||||
// }),
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ impl Render for PopoverStory {
|
|||
let form = self.form.clone();
|
||||
|
||||
v_flex()
|
||||
.p_4()
|
||||
.size_full()
|
||||
.gap_6()
|
||||
.child(
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl TooltipStory {
|
|||
impl Render for TooltipStory {
|
||||
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement {
|
||||
v_flex()
|
||||
.w(px(360.))
|
||||
.p_4()
|
||||
.gap_5()
|
||||
.child(
|
||||
div()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::theme::ActiveTheme;
|
||||
use crate::theme::{hsl, ActiveTheme};
|
||||
use gpui::{hsla, point, px, BoxShadow, Styled, WindowContext};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
|
|
@ -76,6 +76,26 @@ pub trait StyledExt: Styled + Sized {
|
|||
fn elevation_3(self, cx: &WindowContext) -> Self {
|
||||
elevated(self, cx, ElevationIndex::ModalSurface)
|
||||
}
|
||||
|
||||
/// Render a border with a width of 1px, color blue
|
||||
fn debug_blue(self) -> Self {
|
||||
self.border_1().border_color(gpui::blue())
|
||||
}
|
||||
|
||||
/// Render a border with a width of 1px, color yellow
|
||||
fn debug_yellow(self) -> Self {
|
||||
self.border_1().border_color(gpui::yellow())
|
||||
}
|
||||
|
||||
/// Render a border with a width of 1px, color green
|
||||
fn debug_green(self) -> Self {
|
||||
self.border_1().border_color(gpui::green())
|
||||
}
|
||||
|
||||
/// Render a border with a width of 1px, color pink
|
||||
fn debug_pink(self) -> Self {
|
||||
self.border_1().border_color(hsl(300., 100., 47.))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Styled> StyledExt for E {}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub struct Tab {
|
|||
impl Tab {
|
||||
pub fn new(id: impl Into<ElementId>, label: impl Into<AnyElement>) -> Self {
|
||||
Self {
|
||||
base: div().id(id.into()),
|
||||
base: div().id(id.into()).gap_1().py_1().px_3(),
|
||||
label: label.into(),
|
||||
disabled: false,
|
||||
selected: false,
|
||||
|
|
@ -57,6 +57,12 @@ impl InteractiveElement for Tab {
|
|||
|
||||
impl StatefulInteractiveElement for Tab {}
|
||||
|
||||
impl Styled for Tab {
|
||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||
self.base.style()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Tab {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
let (text_color, bg_color) = match (self.selected, self.disabled) {
|
||||
|
|
@ -68,14 +74,10 @@ impl RenderOnce for Tab {
|
|||
self.base
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.h_full()
|
||||
.flex_shrink_0()
|
||||
.overflow_scroll()
|
||||
.cursor_pointer()
|
||||
.py_1()
|
||||
.px_3()
|
||||
.min_w_16()
|
||||
.text_color(text_color)
|
||||
.bg(bg_color)
|
||||
.when(self.selected, |this| this.rounded(px(6.)))
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use crate::stock::h_flex;
|
||||
use crate::theme::ActiveTheme;
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::InteractiveElement;
|
||||
use gpui::{
|
||||
div, AnyElement, Div, IntoElement, ParentElement, RenderOnce, ScrollHandle, SharedString,
|
||||
StatefulInteractiveElement as _, WindowContext,
|
||||
StatefulInteractiveElement as _, Styled, WindowContext,
|
||||
};
|
||||
use gpui::{InteractiveElement, Styled as _};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
|
|
@ -19,7 +19,7 @@ pub struct TabBar {
|
|||
impl TabBar {
|
||||
pub fn new(id: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
base: div(),
|
||||
base: div().h_10().p_1(),
|
||||
id: id.into(),
|
||||
children: SmallVec::new(),
|
||||
scroll_handle: None,
|
||||
|
|
@ -39,6 +39,12 @@ impl ParentElement for TabBar {
|
|||
}
|
||||
}
|
||||
|
||||
impl Styled for TabBar {
|
||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||
self.base.style()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for TabBar {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
|
|
@ -50,8 +56,6 @@ impl RenderOnce for TabBar {
|
|||
.flex_none()
|
||||
.items_center()
|
||||
.w_full()
|
||||
.h_10()
|
||||
.p_1()
|
||||
.bg(theme.muted)
|
||||
.text_color(theme.muted_foreground)
|
||||
.relative()
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ impl Colors {
|
|||
ring: hsl(240.0, 5.9, 10.0),
|
||||
selection: hsl(211.0, 97.0, 85.0),
|
||||
scrollbar: Hsla::transparent_black(),
|
||||
scrollbar_thumb: hsl(240.0, 5.9, 90.0),
|
||||
scrollbar_thumb: hsl(240.0, 5.9, 90.0).opacity(0.7),
|
||||
panel: hsl(0.0, 0.0, 100.0),
|
||||
drop_target: hsl(240.0, 65., 44.0).opacity(0.15),
|
||||
}
|
||||
|
|
@ -229,9 +229,9 @@ impl Colors {
|
|||
ring: hsl(240.0, 4.9, 83.9),
|
||||
selection: hsl(211.0, 97.0, 85.0),
|
||||
scrollbar: Hsla::transparent_black(),
|
||||
scrollbar_thumb: hsl(240.0, 3.7, 15.9),
|
||||
scrollbar_thumb: hsl(240.0, 3.7, 15.9).opacity(0.7),
|
||||
panel: hsl(299.0, 2., 9.),
|
||||
drop_target: hsl(240.0, 65., 29.0).opacity(0.3),
|
||||
drop_target: hsl(240.0, 89., 67.0).opacity(0.8),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,15 +45,17 @@ pub trait Panel: FocusableView + EventEmitter<PanelEvent> {
|
|||
/// Return the position of the panel.
|
||||
fn position(&self, cx: &WindowContext) -> DockPosition;
|
||||
/// Return true if the panel can be positioned at the given position.
|
||||
fn can_position(&self, position: DockPosition, cx: &WindowContext) -> bool;
|
||||
fn can_position(&self, position: DockPosition) -> bool {
|
||||
true
|
||||
}
|
||||
/// Set the position of the panel.
|
||||
fn set_position(&mut self, position: DockPosition, cx: &mut WindowContext);
|
||||
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {}
|
||||
/// Return the size of the panel.
|
||||
fn size(&self, cx: &WindowContext) -> Pixels;
|
||||
/// Set the size of the panel.
|
||||
fn set_size(&mut self, size: Option<Pixels>, cx: &mut WindowContext);
|
||||
fn set_size(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>) {}
|
||||
/// Set the active state of the panel.
|
||||
fn set_active(&mut self, active: bool, cx: &mut WindowContext);
|
||||
fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {}
|
||||
fn icon(&self, _cx: &WindowContext) -> Option<IconName> {
|
||||
None
|
||||
}
|
||||
|
|
@ -62,7 +64,7 @@ pub trait Panel: FocusableView + EventEmitter<PanelEvent> {
|
|||
}
|
||||
fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {}
|
||||
fn starts_open(&self, _cx: &WindowContext) -> bool {
|
||||
false
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +101,7 @@ where
|
|||
}
|
||||
|
||||
fn can_position(&self, position: DockPosition, cx: &WindowContext) -> bool {
|
||||
self.read(cx).can_position(position, cx)
|
||||
self.read(cx).can_position(position)
|
||||
}
|
||||
|
||||
fn set_position(&self, position: DockPosition, cx: &mut WindowContext) {
|
||||
|
|
|
|||
|
|
@ -183,6 +183,13 @@ impl Pane {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn set_should_display_tab_bar<F>(&mut self, f: F)
|
||||
where
|
||||
F: 'static + Fn(&ViewContext<Pane>) -> bool,
|
||||
{
|
||||
self.should_display_tab_bar = Rc::new(f);
|
||||
}
|
||||
|
||||
pub fn set_custom_drop_handle<F>(&mut self, cx: &mut ViewContext<Self>, handle: F)
|
||||
where
|
||||
F: 'static + Fn(&mut Pane, &dyn Any, &mut ViewContext<Pane>) -> ControlFlow<(), ()>,
|
||||
|
|
@ -651,24 +658,30 @@ impl Pane {
|
|||
},
|
||||
cx,
|
||||
);
|
||||
// let indicator = render_item_indicator(item.boxed_clone(), cx);
|
||||
|
||||
let item_id = item.item_id();
|
||||
let _is_first_item = ix == 0;
|
||||
let _is_last_item = ix == self.items.len() - 1;
|
||||
let _position_relative_to_active_item = ix.cmp(&self.active_item_index);
|
||||
|
||||
Tab::new(ix, label)
|
||||
.group("tab")
|
||||
.px(px(5.))
|
||||
.prefix(div().size_2().into_any_element())
|
||||
.gap_0p5()
|
||||
.suffix(
|
||||
div()
|
||||
.id("close-tab")
|
||||
.p(px(1.5))
|
||||
.rounded_md()
|
||||
.child(Icon::new(IconName::Close).size_3())
|
||||
.p(px(0.5))
|
||||
.rounded_lg()
|
||||
.invisible()
|
||||
.child(Icon::new(IconName::Close).size_2())
|
||||
.hover(|this| this.bg(cx.theme().accent.darken(0.1)))
|
||||
.active(|this| this.bg(cx.theme().accent.darken(0.2)))
|
||||
.on_click(cx.listener(move |pane, _, cx| {
|
||||
pane.close_item_by_id(item_id, cx).detach_and_log_err(cx);
|
||||
}))
|
||||
.group_hover("tab", |this| this.visible())
|
||||
.into_any(),
|
||||
)
|
||||
.selected(is_active)
|
||||
|
|
@ -685,7 +698,11 @@ impl Pane {
|
|||
},
|
||||
|tab, cx| cx.new_view(|_| tab.clone()),
|
||||
)
|
||||
.drag_over::<DraggedTab>(|tab, _, cx| tab.bg(cx.theme().drop_target))
|
||||
.drag_over::<DraggedTab>(|tab, _, cx| {
|
||||
tab.border_l_3()
|
||||
.rounded_l_none()
|
||||
.border_color(cx.theme().drop_target)
|
||||
})
|
||||
.drag_over::<DraggedSelection>(|tab, _, cx| tab.bg(cx.theme().drop_target))
|
||||
.when_some(self.can_drop_predicate.clone(), |this, p| {
|
||||
this.can_drop(move |a, cx| p(a, cx))
|
||||
|
|
@ -722,6 +739,7 @@ impl Pane {
|
|||
|
||||
TabBar::new("tab-bar")
|
||||
.track_scroll(self.tab_bar_scroll_handle.clone())
|
||||
.gap(px(2.))
|
||||
.when(self.has_focus(cx), |tab_bar| {
|
||||
tab_bar.child({
|
||||
let render_tab_buttons = self.render_tab_bar_buttons.clone();
|
||||
|
|
@ -739,8 +757,6 @@ impl Pane {
|
|||
div()
|
||||
.id("tab-bar-drop-target")
|
||||
.min_w_6()
|
||||
// HACK: This empty child is currently necessary to force the drop target to appear
|
||||
// despite us setting a min width above.
|
||||
.child("")
|
||||
.h_full()
|
||||
.flex_grow()
|
||||
|
|
|
|||
Loading…
Reference in a new issue