dock: Add visible method to Panel trait to control panel visible. (#503)
Closes #502 #499 ## Added - Add `visible` method to `Panel` trait, if return false this panel will disappear. ## Break changes - Renamed `closeable` to `closable`. - The `title_style`, `closable`, `zoomable` in `Panel` trait have been change `cx: &WindowContext` to `cx: &AppContext`.
This commit is contained in:
parent
4b211bfe21
commit
1a4ee348f8
10 changed files with 256 additions and 87 deletions
|
|
@ -50,7 +50,7 @@ impl super::Story for ButtonStory {
|
|||
"Displays a button or a component that looks like a button."
|
||||
}
|
||||
|
||||
fn closeable() -> bool {
|
||||
fn closable() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ impl super::Story for InputStory {
|
|||
"Input"
|
||||
}
|
||||
|
||||
fn closeable() -> bool {
|
||||
fn closable() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,9 +43,10 @@ pub use tooltip_story::TooltipStory;
|
|||
pub use webview_story::WebViewStory;
|
||||
|
||||
use gpui::{
|
||||
actions, div, prelude::FluentBuilder as _, px, AnyElement, AnyView, AppContext, Div,
|
||||
EventEmitter, FocusableView, Hsla, InteractiveElement, IntoElement, ParentElement, Render,
|
||||
SharedString, Styled as _, View, ViewContext, VisualContext, WindowContext,
|
||||
actions, div, prelude::FluentBuilder as _, px, AnyElement, AnyView, AppContext, Context as _,
|
||||
Div, EventEmitter, FocusableView, Global, Hsla, InteractiveElement, IntoElement, Model,
|
||||
ParentElement, Render, SharedString, Styled as _, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
};
|
||||
|
||||
use ui::{
|
||||
|
|
@ -62,7 +63,30 @@ use ui::{
|
|||
|
||||
const PANEL_NAME: &str = "StoryContainer";
|
||||
|
||||
pub struct AppState {
|
||||
pub invisible_panels: Model<Vec<SharedString>>,
|
||||
}
|
||||
impl AppState {
|
||||
fn init(cx: &mut AppContext) {
|
||||
let state = Self {
|
||||
invisible_panels: cx.new_model(|_| Vec::new()),
|
||||
};
|
||||
cx.set_global::<AppState>(state);
|
||||
}
|
||||
|
||||
pub fn global(cx: &AppContext) -> &Self {
|
||||
cx.global::<Self>()
|
||||
}
|
||||
|
||||
pub fn global_mut(cx: &mut AppContext) -> &mut Self {
|
||||
cx.global_mut::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
impl Global for AppState {}
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
AppState::init(cx);
|
||||
input_story::init(cx);
|
||||
dropdown_story::init(cx);
|
||||
popup_story::init(cx);
|
||||
|
|
@ -76,7 +100,7 @@ pub fn init(cx: &mut AppContext) {
|
|||
};
|
||||
|
||||
let view = cx.new_view(|cx| {
|
||||
let (title, description, closeable, zoomable, story) = story_state.to_story(cx);
|
||||
let (title, description, closable, zoomable, story) = story_state.to_story(cx);
|
||||
let mut container = StoryContainer::new(cx).story(story, story_state.story_klass);
|
||||
|
||||
cx.on_focus_in(&container.focus_handle, |this: &mut StoryContainer, _| {
|
||||
|
|
@ -86,7 +110,7 @@ pub fn init(cx: &mut AppContext) {
|
|||
|
||||
container.name = title.into();
|
||||
container.description = description.into();
|
||||
container.closeable = closeable;
|
||||
container.closable = closable;
|
||||
container.zoomable = zoomable;
|
||||
container
|
||||
});
|
||||
|
|
@ -122,7 +146,7 @@ pub struct StoryContainer {
|
|||
height: Option<gpui::Pixels>,
|
||||
story: Option<AnyView>,
|
||||
story_klass: Option<SharedString>,
|
||||
closeable: bool,
|
||||
closable: bool,
|
||||
zoomable: bool,
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +164,7 @@ pub trait Story: FocusableView {
|
|||
fn description() -> &'static str {
|
||||
""
|
||||
}
|
||||
fn closeable() -> bool {
|
||||
fn closable() -> bool {
|
||||
true
|
||||
}
|
||||
fn zoomable() -> bool {
|
||||
|
|
@ -167,7 +191,7 @@ impl StoryContainer {
|
|||
height: None,
|
||||
story: None,
|
||||
story_klass: None,
|
||||
closeable: true,
|
||||
closable: true,
|
||||
zoomable: true,
|
||||
}
|
||||
}
|
||||
|
|
@ -182,7 +206,7 @@ impl StoryContainer {
|
|||
let view = cx.new_view(|cx| {
|
||||
let mut story = Self::new(cx).story(story.into(), story_klass);
|
||||
story.focus_handle = focus_handle;
|
||||
story.closeable = S::closeable();
|
||||
story.closable = S::closable();
|
||||
story.zoomable = S::zoomable();
|
||||
story.name = name.into();
|
||||
story.description = description.into();
|
||||
|
|
@ -242,7 +266,7 @@ impl StoryState {
|
|||
(
|
||||
$klass::title(),
|
||||
$klass::description(),
|
||||
$klass::closeable(),
|
||||
$klass::closable(),
|
||||
$klass::zoomable(),
|
||||
$klass::view(cx).into(),
|
||||
)
|
||||
|
|
@ -285,7 +309,7 @@ impl Panel for StoryContainer {
|
|||
self.name.clone().into_any_element()
|
||||
}
|
||||
|
||||
fn title_style(&self, cx: &WindowContext) -> Option<TitleStyle> {
|
||||
fn title_style(&self, cx: &AppContext) -> Option<TitleStyle> {
|
||||
if let Some(bg) = self.title_bg {
|
||||
Some(TitleStyle {
|
||||
background: bg,
|
||||
|
|
@ -296,14 +320,21 @@ impl Panel for StoryContainer {
|
|||
}
|
||||
}
|
||||
|
||||
fn closeable(&self, _cx: &WindowContext) -> bool {
|
||||
self.closeable
|
||||
fn closable(&self, _cx: &AppContext) -> bool {
|
||||
self.closable
|
||||
}
|
||||
|
||||
fn zoomable(&self, _cx: &WindowContext) -> bool {
|
||||
fn zoomable(&self, _cx: &AppContext) -> bool {
|
||||
self.zoomable
|
||||
}
|
||||
|
||||
fn visible(&self, cx: &AppContext) -> bool {
|
||||
!AppState::global(cx)
|
||||
.invisible_panels
|
||||
.read(cx)
|
||||
.contains(&self.name)
|
||||
}
|
||||
|
||||
fn set_zoomed(&self, zoomed: bool, _cx: &ViewContext<Self>) {
|
||||
println!("panel: {} zoomed: {}", self.name, zoomed);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ use prelude::FluentBuilder as _;
|
|||
use serde::Deserialize;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use story::{
|
||||
AccordionStory, Assets, ButtonStory, CalendarStory, DropdownStory, IconStory, ImageStory,
|
||||
InputStory, ListStory, ModalStory, PopupStory, ProgressStory, ResizableStory, ScrollableStory,
|
||||
SidebarStory, StoryContainer, SwitchStory, TableStory, TextStory, TooltipStory,
|
||||
AccordionStory, AppState, Assets, ButtonStory, CalendarStory, DropdownStory, IconStory,
|
||||
ImageStory, InputStory, ListStory, ModalStory, PopupStory, ProgressStory, ResizableStory,
|
||||
ScrollableStory, SidebarStory, StoryContainer, SwitchStory, TableStory, TextStory,
|
||||
TooltipStory,
|
||||
};
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants as _},
|
||||
|
|
@ -36,9 +37,18 @@ struct SelectFont(usize);
|
|||
#[derive(Clone, PartialEq, Eq, Deserialize)]
|
||||
struct AddPanel(DockPlacement);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Deserialize)]
|
||||
struct TogglePanelVisible(SharedString);
|
||||
|
||||
impl_actions!(
|
||||
story,
|
||||
[SelectLocale, SelectFont, AddPanel, SelectScrollbarShow]
|
||||
[
|
||||
SelectLocale,
|
||||
SelectFont,
|
||||
AddPanel,
|
||||
SelectScrollbarShow,
|
||||
TogglePanelVisible
|
||||
]
|
||||
);
|
||||
|
||||
actions!(main_menu, [Quit]);
|
||||
|
|
@ -395,6 +405,24 @@ impl StoryWorkspace {
|
|||
dock_area.add_panel(panel, action.0, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn on_action_toggle_panel_visible(
|
||||
&mut self,
|
||||
action: &TogglePanelVisible,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let panel_name = action.0.clone();
|
||||
let invisible_panels = AppState::global(cx).invisible_panels.clone();
|
||||
invisible_panels.update(cx, |names, cx| {
|
||||
if names.contains(&panel_name) {
|
||||
names.retain(|id| id != &panel_name);
|
||||
} else {
|
||||
names.push(panel_name);
|
||||
}
|
||||
cx.notify();
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_new(
|
||||
|
|
@ -417,10 +445,12 @@ impl Render for StoryWorkspace {
|
|||
let modal_layer = Root::render_modal_layer(cx);
|
||||
let notification_layer = Root::render_notification_layer(cx);
|
||||
let notifications_count = cx.notifications().len();
|
||||
let invisible_panels = AppState::global(cx).invisible_panels.clone();
|
||||
|
||||
div()
|
||||
.id("story-workspace")
|
||||
.on_action(cx.listener(Self::on_action_add_panel))
|
||||
.on_action(cx.listener(Self::on_action_toggle_panel_visible))
|
||||
.relative()
|
||||
.size_full()
|
||||
.flex()
|
||||
|
|
@ -442,7 +472,7 @@ impl Render for StoryWorkspace {
|
|||
.icon(IconName::LayoutDashboard)
|
||||
.small()
|
||||
.ghost()
|
||||
.popup_menu(|menu, _| {
|
||||
.popup_menu(move |menu, cx| {
|
||||
menu.menu(
|
||||
"Add Panel to Center",
|
||||
Box::new(AddPanel(DockPlacement::Center)),
|
||||
|
|
@ -460,6 +490,43 @@ impl Render for StoryWorkspace {
|
|||
"Add Panel to Bottom",
|
||||
Box::new(AddPanel(DockPlacement::Bottom)),
|
||||
)
|
||||
.separator()
|
||||
.menu_with_check(
|
||||
"Sidebar",
|
||||
!invisible_panels
|
||||
.read(cx)
|
||||
.contains(&SharedString::from("Sidebar")),
|
||||
Box::new(TogglePanelVisible(SharedString::from(
|
||||
"Sidebar",
|
||||
))),
|
||||
)
|
||||
.menu_with_check(
|
||||
"Modal",
|
||||
!invisible_panels
|
||||
.read(cx)
|
||||
.contains(&SharedString::from("SidebModalar")),
|
||||
Box::new(TogglePanelVisible(SharedString::from(
|
||||
"Modal",
|
||||
))),
|
||||
)
|
||||
.menu_with_check(
|
||||
"Accordion",
|
||||
!invisible_panels
|
||||
.read(cx)
|
||||
.contains(&SharedString::from("Accordion")),
|
||||
Box::new(TogglePanelVisible(SharedString::from(
|
||||
"Accordion",
|
||||
))),
|
||||
)
|
||||
.menu_with_check(
|
||||
"List",
|
||||
!invisible_panels
|
||||
.read(cx)
|
||||
.contains(&SharedString::from("List")),
|
||||
Box::new(TogglePanelVisible(SharedString::from(
|
||||
"List",
|
||||
))),
|
||||
)
|
||||
})
|
||||
.anchor(Corner::TopRight),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ impl super::Story for TableStory {
|
|||
Self::view(cx)
|
||||
}
|
||||
|
||||
fn closeable() -> bool {
|
||||
fn closable() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ impl Dock {
|
|||
) -> Self {
|
||||
let panel = cx.new_view(|cx| {
|
||||
let mut tab = TabPanel::new(None, dock_area.clone(), cx);
|
||||
tab.closeable = false;
|
||||
tab.closable = false;
|
||||
tab
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -45,17 +45,28 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
|
|||
}
|
||||
|
||||
/// The theme of the panel title, default is `None`.
|
||||
fn title_style(&self, cx: &WindowContext) -> Option<TitleStyle> {
|
||||
fn title_style(&self, cx: &AppContext) -> Option<TitleStyle> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Whether the panel can be closed, default is `true`.
|
||||
fn closeable(&self, cx: &WindowContext) -> bool {
|
||||
///
|
||||
/// This method called in Panel render, we should make sure it is fast.
|
||||
fn closable(&self, cx: &AppContext) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Return true if the panel is zoomable, default is `false`.
|
||||
fn zoomable(&self, cx: &WindowContext) -> bool {
|
||||
///
|
||||
/// This method called in Panel render, we should make sure it is fast.
|
||||
fn zoomable(&self, cx: &AppContext) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Return false to hide panel, true to show panel, default is `true`.
|
||||
///
|
||||
/// This method called in Panel render, we should make sure it is fast.
|
||||
fn visible(&self, cx: &AppContext) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -95,9 +106,10 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
|
|||
pub trait PanelView: 'static + Send + Sync {
|
||||
fn panel_name(&self, cx: &AppContext) -> &'static str;
|
||||
fn title(&self, cx: &WindowContext) -> AnyElement;
|
||||
fn title_style(&self, cx: &WindowContext) -> Option<TitleStyle>;
|
||||
fn closeable(&self, cx: &WindowContext) -> bool;
|
||||
fn zoomable(&self, cx: &WindowContext) -> bool;
|
||||
fn title_style(&self, cx: &AppContext) -> Option<TitleStyle>;
|
||||
fn closable(&self, cx: &AppContext) -> bool;
|
||||
fn zoomable(&self, cx: &AppContext) -> bool;
|
||||
fn visible(&self, cx: &AppContext) -> bool;
|
||||
fn set_active(&self, active: bool, cx: &mut WindowContext);
|
||||
fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext);
|
||||
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu;
|
||||
|
|
@ -116,18 +128,22 @@ impl<T: Panel> PanelView for View<T> {
|
|||
self.read(cx).title(cx)
|
||||
}
|
||||
|
||||
fn title_style(&self, cx: &WindowContext) -> Option<TitleStyle> {
|
||||
fn title_style(&self, cx: &AppContext) -> Option<TitleStyle> {
|
||||
self.read(cx).title_style(cx)
|
||||
}
|
||||
|
||||
fn closeable(&self, cx: &WindowContext) -> bool {
|
||||
self.read(cx).closeable(cx)
|
||||
fn closable(&self, cx: &AppContext) -> bool {
|
||||
self.read(cx).closable(cx)
|
||||
}
|
||||
|
||||
fn zoomable(&self, cx: &WindowContext) -> bool {
|
||||
fn zoomable(&self, cx: &AppContext) -> bool {
|
||||
self.read(cx).zoomable(cx)
|
||||
}
|
||||
|
||||
fn visible(&self, cx: &AppContext) -> bool {
|
||||
self.read(cx).visible(cx)
|
||||
}
|
||||
|
||||
fn set_active(&self, active: bool, cx: &mut WindowContext) {
|
||||
self.update(cx, |this, cx| {
|
||||
this.set_active(active, cx);
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ impl StackPanel {
|
|||
fn new_resizable_panel(panel: Arc<dyn PanelView>, size: Option<Pixels>) -> ResizablePanel {
|
||||
resizable_panel()
|
||||
.content_view(panel.view())
|
||||
.content_visible(move |cx| panel.visible(cx))
|
||||
.when_some(size, |this, size| this.size(size))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use super::{
|
|||
|
||||
#[derive(Clone, Copy)]
|
||||
struct TabState {
|
||||
closeable: bool,
|
||||
closable: bool,
|
||||
zoomable: bool,
|
||||
draggable: bool,
|
||||
droppable: bool,
|
||||
|
|
@ -71,9 +71,9 @@ pub struct TabPanel {
|
|||
stack_panel: Option<WeakView<StackPanel>>,
|
||||
pub(crate) panels: Vec<Arc<dyn PanelView>>,
|
||||
pub(crate) active_ix: usize,
|
||||
/// If this is true, the Panel closeable will follow the active panel's closeable,
|
||||
/// If this is true, the Panel closable will follow the active panel's closable,
|
||||
/// otherwise this TabPanel will not able to close
|
||||
pub(crate) closeable: bool,
|
||||
pub(crate) closable: bool,
|
||||
|
||||
tab_bar_scroll_handle: ScrollHandle,
|
||||
is_zoomed: bool,
|
||||
|
|
@ -88,29 +88,33 @@ impl Panel for TabPanel {
|
|||
}
|
||||
|
||||
fn title(&self, cx: &WindowContext) -> gpui::AnyElement {
|
||||
self.active_panel()
|
||||
self.active_panel(cx)
|
||||
.map(|panel| panel.title(cx))
|
||||
.unwrap_or("Empty Tab".into_any_element())
|
||||
}
|
||||
|
||||
fn closeable(&self, cx: &WindowContext) -> bool {
|
||||
if !self.closeable {
|
||||
fn closable(&self, cx: &AppContext) -> bool {
|
||||
if !self.closable {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.active_panel()
|
||||
.map(|panel| panel.closeable(cx))
|
||||
self.active_panel(cx)
|
||||
.map(|panel| panel.closable(cx))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn zoomable(&self, cx: &WindowContext) -> bool {
|
||||
self.active_panel()
|
||||
fn zoomable(&self, cx: &AppContext) -> bool {
|
||||
self.active_panel(cx)
|
||||
.map(|panel| panel.zoomable(cx))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn visible(&self, cx: &AppContext) -> bool {
|
||||
self.visible_panels(cx).next().is_some()
|
||||
}
|
||||
|
||||
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu {
|
||||
if let Some(panel) = self.active_panel() {
|
||||
if let Some(panel) = self.active_panel(cx) {
|
||||
panel.popup_menu(menu, cx)
|
||||
} else {
|
||||
menu
|
||||
|
|
@ -118,7 +122,7 @@ impl Panel for TabPanel {
|
|||
}
|
||||
|
||||
fn toolbar_buttons(&self, cx: &WindowContext) -> Vec<Button> {
|
||||
if let Some(panel) = self.active_panel() {
|
||||
if let Some(panel) = self.active_panel(cx) {
|
||||
panel.toolbar_buttons(cx)
|
||||
} else {
|
||||
vec![]
|
||||
|
|
@ -151,7 +155,7 @@ impl TabPanel {
|
|||
will_split_placement: None,
|
||||
is_zoomed: false,
|
||||
is_collapsed: false,
|
||||
closeable: true,
|
||||
closable: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,8 +164,19 @@ impl TabPanel {
|
|||
}
|
||||
|
||||
/// Return current active_panel View
|
||||
pub fn active_panel(&self) -> Option<Arc<dyn PanelView>> {
|
||||
self.panels.get(self.active_ix).cloned()
|
||||
pub fn active_panel(&self, cx: &AppContext) -> Option<Arc<dyn PanelView>> {
|
||||
let panel = self.panels.get(self.active_ix);
|
||||
|
||||
if let Some(panel) = panel {
|
||||
if panel.visible(cx) {
|
||||
Some(panel.clone())
|
||||
} else {
|
||||
// Return the first visible panel
|
||||
self.visible_panels(cx).next()
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn set_active_ix(&mut self, ix: usize, cx: &mut ViewContext<Self>) {
|
||||
|
|
@ -335,6 +350,20 @@ impl TabPanel {
|
|||
self.panels.len() <= 1
|
||||
}
|
||||
|
||||
/// Return all visible panels
|
||||
fn visible_panels<'a>(
|
||||
&'a self,
|
||||
cx: &'a AppContext,
|
||||
) -> impl Iterator<Item = Arc<dyn PanelView>> + 'a {
|
||||
self.panels.iter().filter_map(|panel| {
|
||||
if panel.visible(cx) {
|
||||
Some(panel.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Return true if the tab panel is draggable.
|
||||
///
|
||||
/// E.g. if the parent and self only have one panel, it is not draggable.
|
||||
|
|
@ -392,7 +421,7 @@ impl TabPanel {
|
|||
};
|
||||
this.separator().menu(name, Box::new(ToggleZoom))
|
||||
})
|
||||
.when(state.closeable, |this| {
|
||||
.when(state.closable, |this| {
|
||||
this.separator()
|
||||
.menu(t!("Dock.Close"), Box::new(ClosePanel))
|
||||
})
|
||||
|
|
@ -496,6 +525,11 @@ impl TabPanel {
|
|||
|
||||
if self.panels.len() == 1 && panel_style == PanelStyle::Default {
|
||||
let panel = self.panels.get(0).unwrap();
|
||||
|
||||
if !panel.visible(cx) {
|
||||
return div().into_any_element();
|
||||
}
|
||||
|
||||
let title_style = panel.title_style(cx);
|
||||
|
||||
return h_flex()
|
||||
|
|
@ -580,47 +614,53 @@ impl TabPanel {
|
|||
)
|
||||
},
|
||||
)
|
||||
.children(self.panels.iter().enumerate().map(|(ix, panel)| {
|
||||
.children(self.panels.iter().enumerate().filter_map(|(ix, panel)| {
|
||||
let mut active = ix == self.active_ix;
|
||||
let disabled = self.is_collapsed;
|
||||
|
||||
if !panel.visible(cx) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Always not show active tab style, if the panel is collapsed
|
||||
if self.is_collapsed {
|
||||
active = false;
|
||||
}
|
||||
|
||||
Tab::new(("tab", ix), panel.title(cx))
|
||||
.py_2()
|
||||
.selected(active)
|
||||
.disabled(disabled)
|
||||
.when(!disabled, |this| {
|
||||
this.on_click(cx.listener(move |view, _, cx| {
|
||||
view.set_active_ix(ix, cx);
|
||||
}))
|
||||
.when(state.draggable, |this| {
|
||||
this.on_drag(
|
||||
DragPanel::new(panel.clone(), view.clone()),
|
||||
|drag, _, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.new_view(|_| drag.clone())
|
||||
},
|
||||
)
|
||||
})
|
||||
.when(state.droppable, |this| {
|
||||
this.drag_over::<DragPanel>(|this, _, cx| {
|
||||
this.rounded_l_none()
|
||||
.border_l_2()
|
||||
.border_r_0()
|
||||
.border_color(cx.theme().drag_border)
|
||||
Some(
|
||||
Tab::new(("tab", ix), panel.title(cx))
|
||||
.py_2()
|
||||
.selected(active)
|
||||
.disabled(disabled)
|
||||
.when(!disabled, |this| {
|
||||
this.on_click(cx.listener(move |view, _, cx| {
|
||||
view.set_active_ix(ix, cx);
|
||||
}))
|
||||
.when(state.draggable, |this| {
|
||||
this.on_drag(
|
||||
DragPanel::new(panel.clone(), view.clone()),
|
||||
|drag, _, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.new_view(|_| drag.clone())
|
||||
},
|
||||
)
|
||||
})
|
||||
.on_drop(cx.listener(
|
||||
move |this, drag: &DragPanel, cx| {
|
||||
this.will_split_placement = None;
|
||||
this.on_drop(drag, Some(ix), true, cx)
|
||||
},
|
||||
))
|
||||
})
|
||||
})
|
||||
.when(state.droppable, |this| {
|
||||
this.drag_over::<DragPanel>(|this, _, cx| {
|
||||
this.rounded_l_none()
|
||||
.border_l_2()
|
||||
.border_r_0()
|
||||
.border_color(cx.theme().drag_border)
|
||||
})
|
||||
.on_drop(cx.listener(
|
||||
move |this, drag: &DragPanel, cx| {
|
||||
this.will_split_placement = None;
|
||||
this.on_drop(drag, Some(ix), true, cx)
|
||||
},
|
||||
))
|
||||
})
|
||||
}),
|
||||
)
|
||||
}))
|
||||
.child(
|
||||
// empty space to allow move to last tab right
|
||||
|
|
@ -667,7 +707,7 @@ impl TabPanel {
|
|||
return Empty {}.into_any_element();
|
||||
}
|
||||
|
||||
self.active_panel()
|
||||
self.active_panel(cx)
|
||||
.map(|panel| {
|
||||
div()
|
||||
.id("tab-content")
|
||||
|
|
@ -885,7 +925,7 @@ impl TabPanel {
|
|||
}
|
||||
|
||||
fn focus_active_panel(&self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(active_panel) = self.active_panel() {
|
||||
if let Some(active_panel) = self.active_panel(cx) {
|
||||
active_panel.focus_handle(cx).focus(cx);
|
||||
}
|
||||
}
|
||||
|
|
@ -916,7 +956,7 @@ impl TabPanel {
|
|||
}
|
||||
|
||||
fn on_action_close_panel(&mut self, _: &ClosePanel, cx: &mut ViewContext<Self>) {
|
||||
if let Some(panel) = self.active_panel() {
|
||||
if let Some(panel) = self.active_panel(cx) {
|
||||
self.remove_panel(panel, cx);
|
||||
}
|
||||
}
|
||||
|
|
@ -924,7 +964,7 @@ impl TabPanel {
|
|||
|
||||
impl FocusableView for TabPanel {
|
||||
fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
|
||||
if let Some(active_panel) = self.active_panel() {
|
||||
if let Some(active_panel) = self.active_panel(cx) {
|
||||
active_panel.focus_handle(cx)
|
||||
} else {
|
||||
self.focus_handle.clone()
|
||||
|
|
@ -937,13 +977,13 @@ impl Render for TabPanel {
|
|||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl gpui::IntoElement {
|
||||
let focus_handle = self.focus_handle(cx);
|
||||
let mut state = TabState {
|
||||
closeable: self.closeable(cx),
|
||||
closable: self.closable(cx),
|
||||
draggable: self.draggable(cx),
|
||||
droppable: self.droppable(cx),
|
||||
zoomable: self.zoomable(cx),
|
||||
};
|
||||
if !state.draggable {
|
||||
state.closeable = false;
|
||||
state.closable = false;
|
||||
}
|
||||
|
||||
v_flex()
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ pub struct ResizablePanel {
|
|||
axis: Axis,
|
||||
content_builder: Option<Rc<dyn Fn(&mut WindowContext) -> AnyElement>>,
|
||||
content_view: Option<AnyView>,
|
||||
content_visible: Rc<Box<dyn Fn(&WindowContext) -> bool>>,
|
||||
/// The bounds of the resizable panel, when render the bounds will be updated.
|
||||
bounds: Bounds<Pixels>,
|
||||
resize_handle: Option<AnyElement>,
|
||||
|
|
@ -300,6 +301,7 @@ impl ResizablePanel {
|
|||
axis: Axis::Horizontal,
|
||||
content_builder: None,
|
||||
content_view: None,
|
||||
content_visible: Rc::new(Box::new(|_| true)),
|
||||
bounds: Bounds::default(),
|
||||
resize_handle: None,
|
||||
}
|
||||
|
|
@ -313,6 +315,14 @@ impl ResizablePanel {
|
|||
self
|
||||
}
|
||||
|
||||
pub(crate) fn content_visible<F>(mut self, content_visible: F) -> Self
|
||||
where
|
||||
F: Fn(&WindowContext) -> bool + 'static,
|
||||
{
|
||||
self.content_visible = Rc::new(Box::new(content_visible));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn content_view(mut self, content: AnyView) -> Self {
|
||||
self.content_view = Some(content);
|
||||
self
|
||||
|
|
@ -350,6 +360,10 @@ impl FluentBuilder for ResizablePanel {}
|
|||
|
||||
impl Render for ResizablePanel {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
if !(self.content_visible)(cx) {
|
||||
return div();
|
||||
}
|
||||
|
||||
let view = cx.view().clone();
|
||||
let total_size = self
|
||||
.group
|
||||
|
|
|
|||
Loading…
Reference in a new issue