dock: Add zoomable support to Panel. (#269)
This commit is contained in:
parent
1e44b5659f
commit
2d6f478512
6 changed files with 85 additions and 9 deletions
|
|
@ -36,6 +36,10 @@ impl super::Story for IconStory {
|
||||||
fn new_view(cx: &mut WindowContext) -> View<impl gpui::FocusableView> {
|
fn new_view(cx: &mut WindowContext) -> View<impl gpui::FocusableView> {
|
||||||
Self::view(cx)
|
Self::view(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zoomable() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl gpui::FocusableView for IconStory {
|
impl gpui::FocusableView for IconStory {
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,12 @@ pub fn init(cx: &mut AppContext) {
|
||||||
};
|
};
|
||||||
|
|
||||||
let view = cx.new_view(|cx| {
|
let view = cx.new_view(|cx| {
|
||||||
let (title, description, story) = story_state.to_story(cx);
|
let (title, description, closeable, zoomable, story) = story_state.to_story(cx);
|
||||||
let mut container = StoryContainer::new(cx).story(story, story_state.story_klass);
|
let mut container = StoryContainer::new(cx).story(story, story_state.story_klass);
|
||||||
container.name = title.into();
|
container.name = title.into();
|
||||||
container.description = description.into();
|
container.description = description.into();
|
||||||
|
container.closeable = closeable;
|
||||||
|
container.zoomable = zoomable;
|
||||||
container
|
container
|
||||||
});
|
});
|
||||||
Box::new(view)
|
Box::new(view)
|
||||||
|
|
@ -106,6 +108,7 @@ pub struct StoryContainer {
|
||||||
story: Option<AnyView>,
|
story: Option<AnyView>,
|
||||||
story_klass: Option<SharedString>,
|
story_klass: Option<SharedString>,
|
||||||
closeable: bool,
|
closeable: bool,
|
||||||
|
zoomable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -125,6 +128,9 @@ pub trait Story: FocusableView {
|
||||||
fn closeable() -> bool {
|
fn closeable() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
fn zoomable() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
fn title_bg() -> Option<Hsla> {
|
fn title_bg() -> Option<Hsla> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -147,10 +153,10 @@ impl StoryContainer {
|
||||||
story: None,
|
story: None,
|
||||||
story_klass: None,
|
story_klass: None,
|
||||||
closeable: true,
|
closeable: true,
|
||||||
|
zoomable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn panel<S: Story>(cx: &mut WindowContext) -> View<Self> {
|
pub fn panel<S: Story>(cx: &mut WindowContext) -> View<Self> {
|
||||||
let name = S::title();
|
let name = S::title();
|
||||||
let description = S::description();
|
let description = S::description();
|
||||||
|
|
@ -162,6 +168,7 @@ impl StoryContainer {
|
||||||
let mut story = Self::new(cx).story(story.into(), story_klass);
|
let mut story = Self::new(cx).story(story.into(), story_klass);
|
||||||
story.focus_handle = focus_handle;
|
story.focus_handle = focus_handle;
|
||||||
story.closeable = S::closeable();
|
story.closeable = S::closeable();
|
||||||
|
story.zoomable = S::zoomable();
|
||||||
story.name = name.into();
|
story.name = name.into();
|
||||||
story.description = description.into();
|
story.description = description.into();
|
||||||
story.title_bg = S::title_bg();
|
story.title_bg = S::title_bg();
|
||||||
|
|
@ -211,12 +218,17 @@ impl StoryState {
|
||||||
serde_json::from_value(value).unwrap()
|
serde_json::from_value(value).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_story(&self, cx: &mut WindowContext) -> (&'static str, &'static str, AnyView) {
|
fn to_story(
|
||||||
|
&self,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> (&'static str, &'static str, bool, bool, AnyView) {
|
||||||
macro_rules! story {
|
macro_rules! story {
|
||||||
($klass:tt) => {
|
($klass:tt) => {
|
||||||
(
|
(
|
||||||
$klass::title(),
|
$klass::title(),
|
||||||
$klass::description(),
|
$klass::description(),
|
||||||
|
$klass::closeable(),
|
||||||
|
$klass::zoomable(),
|
||||||
$klass::view(cx).into(),
|
$klass::view(cx).into(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
@ -271,6 +283,10 @@ impl Panel for StoryContainer {
|
||||||
self.closeable
|
self.closeable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zoomable(&self, _cx: &WindowContext) -> bool {
|
||||||
|
self.zoomable
|
||||||
|
}
|
||||||
|
|
||||||
fn popup_menu(&self, menu: PopupMenu, _cx: &WindowContext) -> PopupMenu {
|
fn popup_menu(&self, menu: PopupMenu, _cx: &WindowContext) -> PopupMenu {
|
||||||
menu.track_focus(&self.focus_handle)
|
menu.track_focus(&self.focus_handle)
|
||||||
.menu("Info", Box::new(PanelInfo))
|
.menu("Info", Box::new(PanelInfo))
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,10 @@ impl super::Story for TableStory {
|
||||||
fn new_view(cx: &mut WindowContext) -> View<impl gpui::FocusableView> {
|
fn new_view(cx: &mut WindowContext) -> View<impl gpui::FocusableView> {
|
||||||
Self::view(cx)
|
Self::view(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn closeable() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl gpui::FocusableView for TableStory {
|
impl gpui::FocusableView for TableStory {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ impl super::Story for TooltipStory {
|
||||||
fn new_view(cx: &mut WindowContext) -> View<impl gpui::FocusableView> {
|
fn new_view(cx: &mut WindowContext) -> View<impl gpui::FocusableView> {
|
||||||
Self::view(cx)
|
Self::view(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zoomable() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl gpui::FocusableView for TooltipStory {
|
impl gpui::FocusableView for TooltipStory {
|
||||||
fn focus_handle(&self, _: &gpui::AppContext) -> gpui::FocusHandle {
|
fn focus_handle(&self, _: &gpui::AppContext) -> gpui::FocusHandle {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,16 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if the panel is zoomable, default is `false`.
|
||||||
|
fn zoomable(&self, _cx: &WindowContext) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true if the panel is collapsable, default is `false`.
|
||||||
|
fn collapsible(&self, _cx: &WindowContext) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// The addition popup menu of the panel, default is `None`.
|
/// The addition popup menu of the panel, default is `None`.
|
||||||
fn popup_menu(&self, this: PopupMenu, _cx: &WindowContext) -> PopupMenu {
|
fn popup_menu(&self, this: PopupMenu, _cx: &WindowContext) -> PopupMenu {
|
||||||
this
|
this
|
||||||
|
|
@ -56,11 +66,14 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PanelView: 'static + Send + Sync {
|
pub trait PanelView: 'static + Send + Sync {
|
||||||
|
fn panel_name(&self, _cx: &WindowContext) -> &'static str;
|
||||||
fn title(&self, _cx: &WindowContext) -> AnyElement;
|
fn title(&self, _cx: &WindowContext) -> AnyElement;
|
||||||
|
|
||||||
fn title_style(&self, _cx: &WindowContext) -> Option<TitleStyle>;
|
fn title_style(&self, _cx: &WindowContext) -> Option<TitleStyle>;
|
||||||
|
|
||||||
fn closeable(&self, cx: &WindowContext) -> bool;
|
fn closeable(&self, cx: &WindowContext) -> bool;
|
||||||
|
fn zoomable(&self, cx: &WindowContext) -> bool;
|
||||||
|
fn collapsible(&self, cx: &WindowContext) -> bool;
|
||||||
|
|
||||||
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu;
|
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu;
|
||||||
|
|
||||||
|
|
@ -72,6 +85,9 @@ pub trait PanelView: 'static + Send + Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Panel> PanelView for View<T> {
|
impl<T: Panel> PanelView for View<T> {
|
||||||
|
fn panel_name(&self, cx: &WindowContext) -> &'static str {
|
||||||
|
self.read(cx).panel_name()
|
||||||
|
}
|
||||||
fn title(&self, cx: &WindowContext) -> AnyElement {
|
fn title(&self, cx: &WindowContext) -> AnyElement {
|
||||||
self.read(cx).title(cx)
|
self.read(cx).title(cx)
|
||||||
}
|
}
|
||||||
|
|
@ -84,6 +100,14 @@ impl<T: Panel> PanelView for View<T> {
|
||||||
self.read(cx).closeable(cx)
|
self.read(cx).closeable(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zoomable(&self, cx: &WindowContext) -> bool {
|
||||||
|
self.read(cx).zoomable(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collapsible(&self, cx: &WindowContext) -> bool {
|
||||||
|
self.read(cx).collapsible(cx)
|
||||||
|
}
|
||||||
|
|
||||||
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu {
|
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu {
|
||||||
self.read(cx).popup_menu(menu, cx)
|
self.read(cx).popup_menu(menu, cx)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,18 @@ impl Panel for TabPanel {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zoomable(&self, cx: &WindowContext) -> bool {
|
||||||
|
self.active_panel()
|
||||||
|
.map(|panel| panel.zoomable(cx))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collapsible(&self, cx: &WindowContext) -> bool {
|
||||||
|
self.active_panel()
|
||||||
|
.map(|panel| panel.collapsible(cx))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu {
|
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu {
|
||||||
if let Some(panel) = self.active_panel() {
|
if let Some(panel) = self.active_panel() {
|
||||||
panel.popup_menu(menu, cx)
|
panel.popup_menu(menu, cx)
|
||||||
|
|
@ -140,6 +152,12 @@ impl TabPanel {
|
||||||
|
|
||||||
/// Add a panel to the end of the tabs
|
/// Add a panel to the end of the tabs
|
||||||
pub fn add_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
|
pub fn add_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
|
||||||
|
assert_ne!(
|
||||||
|
panel.panel_name(cx),
|
||||||
|
"StackPanel",
|
||||||
|
"can not allows add `StackPanel` to `TabPanel`"
|
||||||
|
);
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.panels
|
.panels
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -228,8 +246,10 @@ impl TabPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_menu_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render_menu_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let is_zoomed = self.is_zoomed;
|
|
||||||
let closeable = self.closeable(cx);
|
let closeable = self.closeable(cx);
|
||||||
|
let zoomable = self.zoomable(cx);
|
||||||
|
|
||||||
|
let is_zoomed = self.is_zoomed && zoomable;
|
||||||
let view = cx.view().clone();
|
let view = cx.view().clone();
|
||||||
let build_popup_menu = move |this, cx: &WindowContext| view.read(cx).popup_menu(this, cx);
|
let build_popup_menu = move |this, cx: &WindowContext| view.read(cx).popup_menu(this, cx);
|
||||||
|
|
||||||
|
|
@ -256,14 +276,14 @@ impl TabPanel {
|
||||||
.ghost()
|
.ghost()
|
||||||
.popup_menu(move |this, cx| {
|
.popup_menu(move |this, cx| {
|
||||||
build_popup_menu(this, cx)
|
build_popup_menu(this, cx)
|
||||||
.menu(
|
.when(zoomable, |this| {
|
||||||
if is_zoomed {
|
let name = if is_zoomed {
|
||||||
t!("Dock.Zoom Out")
|
t!("Dock.Zoom Out")
|
||||||
} else {
|
} else {
|
||||||
t!("Dock.Zoom In")
|
t!("Dock.Zoom In")
|
||||||
},
|
};
|
||||||
Box::new(ToggleZoom),
|
this.separator().menu(name, Box::new(ToggleZoom))
|
||||||
)
|
})
|
||||||
.when(closeable, |this| {
|
.when(closeable, |this| {
|
||||||
this.separator()
|
this.separator()
|
||||||
.menu(t!("Dock.Close"), Box::new(ClosePanel))
|
.menu(t!("Dock.Close"), Box::new(ClosePanel))
|
||||||
|
|
@ -581,6 +601,10 @@ impl TabPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_action_toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
|
fn on_action_toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
|
||||||
|
if !self.zoomable(cx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if !self.is_zoomed {
|
if !self.is_zoomed {
|
||||||
cx.emit(PanelEvent::ZoomIn)
|
cx.emit(PanelEvent::ZoomIn)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue