Restore zoom buttons in pane tab bars, fix + button in the terminal one (#3780)

Kirill Bulatov created

Release Notes:

- N/A

Change summary

crates/terminal_view2/src/terminal_panel.rs |  72 +++-----
crates/workspace2/src/pane.rs               | 189 +++++++++-------------
2 files changed, 103 insertions(+), 158 deletions(-)

Detailed changes

crates/terminal_view2/src/terminal_panel.rs 🔗

@@ -4,13 +4,14 @@ use crate::TerminalView;
 use db::kvp::KEY_VALUE_STORE;
 use gpui::{
     actions, div, serde_json, AppContext, AsyncWindowContext, Div, Entity, EventEmitter,
-    ExternalPaths, FocusHandle, FocusableView, ParentElement, Render, Styled, Subscription, Task,
-    View, ViewContext, VisualContext, WeakView, WindowContext,
+    ExternalPaths, FocusHandle, FocusableView, IntoElement, ParentElement, Render, Styled,
+    Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
 use project::Fs;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings};
+use ui::{h_stack, ButtonCommon, Clickable, IconButton, IconSize, Selectable, Tooltip};
 use util::{ResultExt, TryFutureExt};
 use workspace::{
     dock::{DockPosition, Panel, PanelEvent},
@@ -51,9 +52,8 @@ pub struct TerminalPanel {
 
 impl TerminalPanel {
     fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
-        let _weak_self = cx.view().downgrade();
+        let terminal_panel = cx.view().clone();
         let pane = cx.build_view(|cx| {
-            let _window = cx.window_handle();
             let mut pane = Pane::new(
                 workspace.weak_handle(),
                 workspace.project().clone(),
@@ -74,45 +74,31 @@ impl TerminalPanel {
             );
             pane.set_can_split(false, cx);
             pane.set_can_navigate(false, cx);
-            // todo!()
-            // pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
-            //     let this = weak_self.clone();
-            //     Flex::row()
-            //         .with_child(Pane::render_tab_bar_button(
-            //             0,
-            //             "icons/plus.svg",
-            //             false,
-            //             Some(("New Terminal", Some(Box::new(workspace::NewTerminal)))),
-            //             cx,
-            //             move |_, cx| {
-            //                 let this = this.clone();
-            //                 cx.window_context().defer(move |cx| {
-            //                     if let Some(this) = this.upgrade() {
-            //                         this.update(cx, |this, cx| {
-            //                             this.add_terminal(None, cx);
-            //                         });
-            //                     }
-            //                 })
-            //             },
-            //             |_, _| {},
-            //             None,
-            //         ))
-            //         .with_child(Pane::render_tab_bar_button(
-            //             1,
-            //             if pane.is_zoomed() {
-            //                 "icons/minimize.svg"
-            //             } else {
-            //                 "icons/maximize.svg"
-            //             },
-            //             pane.is_zoomed(),
-            //             Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))),
-            //             cx,
-            //             move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
-            //             |_, _| {},
-            //             None,
-            //         ))
-            //         .into_any()
-            // });
+            pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
+                h_stack()
+                    .child(
+                        IconButton::new("plus", Icon::Plus)
+                            .icon_size(IconSize::Small)
+                            .on_click(cx.listener_for(&terminal_panel, |terminal_panel, _, cx| {
+                                terminal_panel.add_terminal(None, cx);
+                            }))
+                            .tooltip(|cx| Tooltip::text("New Terminal", cx)),
+                    )
+                    .child({
+                        let zoomed = pane.is_zoomed();
+                        IconButton::new("toggle_zoom", Icon::Maximize)
+                            .icon_size(IconSize::Small)
+                            .selected(zoomed)
+                            .selected_icon(Icon::Minimize)
+                            .on_click(cx.listener(|pane, _, cx| {
+                                pane.toggle_zoom(&workspace::ToggleZoom, cx);
+                            }))
+                            .tooltip(move |cx| {
+                                Tooltip::text(if zoomed { "Zoom Out" } else { "Zoom In" }, cx)
+                            })
+                    })
+                    .into_any_element()
+            });
             // let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
             // pane.toolbar()
             //     .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));

crates/workspace2/src/pane.rs 🔗

@@ -7,7 +7,7 @@ use crate::{
 use anyhow::Result;
 use collections::{HashMap, HashSet, VecDeque};
 use gpui::{
-    actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AppContext,
+    actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyElement, AppContext,
     AsyncWindowContext, DismissEvent, Div, DragMoveEvent, EntityId, EventEmitter, FocusHandle,
     Focusable, FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel,
     Render, ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakView,
@@ -21,6 +21,7 @@ use std::{
     any::Any,
     cmp, fmt, mem,
     path::{Path, PathBuf},
+    rc::Rc,
     sync::{
         atomic::{AtomicUsize, Ordering},
         Arc,
@@ -183,7 +184,7 @@ pub struct Pane {
     drag_split_direction: Option<SplitDirection>,
     can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>>,
     can_split: bool,
-    //     render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>,
+    render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement>,
     _subscriptions: Vec<Subscription>,
     tab_bar_scroll_handle: ScrollHandle,
 }
@@ -374,63 +375,66 @@ impl Pane {
             project,
             can_drop_predicate,
             can_split: true,
-            // render_tab_bar_buttons: Rc::new(move |pane, cx| {
-            //     Flex::row()
-            //         // New menu
-            //         .with_child(Self::render_tab_bar_button(
-            //             0,
-            //             "icons/plus.svg",
-            //             false,
-            //             Some(("New...".into(), None)),
-            //             cx,
-            //             |pane, cx| pane.deploy_new_menu(cx),
-            //             |pane, cx| {
-            //                 pane.tab_bar_context_menu
-            //                     .handle
-            //                     .update(cx, |menu, _| menu.delay_cancel())
-            //             },
-            //             pane.tab_bar_context_menu
-            //                 .handle_if_kind(TabBarContextMenuKind::New),
-            //         ))
-            //         .with_child(Self::render_tab_bar_button(
-            //             1,
-            //             "icons/split.svg",
-            //             false,
-            //             Some(("Split Pane".into(), None)),
-            //             cx,
-            //             |pane, cx| pane.deploy_split_menu(cx),
-            //             |pane, cx| {
-            //                 pane.tab_bar_context_menu
-            //                     .handle
-            //                     .update(cx, |menu, _| menu.delay_cancel())
-            //             },
-            //             pane.tab_bar_context_menu
-            //                 .handle_if_kind(TabBarContextMenuKind::Split),
-            //         ))
-            //         .with_child({
-            //             let icon_path;
-            //             let tooltip_label;
-            //             if pane.is_zoomed() {
-            //                 icon_path = "icons/minimize.svg";
-            //                 tooltip_label = "Zoom In";
-            //             } else {
-            //                 icon_path = "icons/maximize.svg";
-            //                 tooltip_label = "Zoom In";
-            //             }
-
-            //             Pane::render_tab_bar_button(
-            //                 2,
-            //                 icon_path,
-            //                 pane.is_zoomed(),
-            //                 Some((tooltip_label, Some(Box::new(ToggleZoom)))),
-            //                 cx,
-            //                 move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
-            //                 move |_, _| {},
-            //                 None,
-            //             )
-            //         })
-            //         .into_any()
-            // }),
+            render_tab_bar_buttons: Rc::new(move |pane, cx| {
+                h_stack()
+                    .child(
+                        IconButton::new("plus", Icon::Plus)
+                            .icon_size(IconSize::Small)
+                            .on_click(cx.listener(|pane, _, cx| {
+                                let menu = ContextMenu::build(cx, |menu, _| {
+                                    menu.action("New File", NewFile.boxed_clone())
+                                        .action("New Terminal", NewCenterTerminal.boxed_clone())
+                                        .action("New Search", NewSearch.boxed_clone())
+                                });
+                                cx.subscribe(&menu, |pane, _, _: &DismissEvent, cx| {
+                                    pane.focus(cx);
+                                    pane.new_item_menu = None;
+                                })
+                                .detach();
+                                pane.new_item_menu = Some(menu);
+                            }))
+                            .tooltip(|cx| Tooltip::text("New...", cx)),
+                    )
+                    .when_some(pane.new_item_menu.as_ref(), |el, new_item_menu| {
+                        el.child(Self::render_menu_overlay(new_item_menu))
+                    })
+                    .child(
+                        IconButton::new("split", Icon::Split)
+                            .icon_size(IconSize::Small)
+                            .on_click(cx.listener(|pane, _, cx| {
+                                let menu = ContextMenu::build(cx, |menu, _| {
+                                    menu.action("Split Right", SplitRight.boxed_clone())
+                                        .action("Split Left", SplitLeft.boxed_clone())
+                                        .action("Split Up", SplitUp.boxed_clone())
+                                        .action("Split Down", SplitDown.boxed_clone())
+                                });
+                                cx.subscribe(&menu, |pane, _, _: &DismissEvent, cx| {
+                                    pane.focus(cx);
+                                    pane.split_item_menu = None;
+                                })
+                                .detach();
+                                pane.split_item_menu = Some(menu);
+                            }))
+                            .tooltip(|cx| Tooltip::text("Split Pane", cx)),
+                    )
+                    .child({
+                        let zoomed = pane.is_zoomed();
+                        IconButton::new("toggle_zoom", Icon::Maximize)
+                            .icon_size(IconSize::Small)
+                            .selected(zoomed)
+                            .selected_icon(Icon::Minimize)
+                            .on_click(cx.listener(|pane, _, cx| {
+                                pane.toggle_zoom(&crate::ToggleZoom, cx);
+                            }))
+                            .tooltip(move |cx| {
+                                Tooltip::text(if zoomed { "Zoom Out" } else { "Zoom In" }, cx)
+                            })
+                    })
+                    .when_some(pane.split_item_menu.as_ref(), |el, split_item_menu| {
+                        el.child(Self::render_menu_overlay(split_item_menu))
+                    })
+                    .into_any_element()
+            }),
             _subscriptions: subscriptions,
         }
     }
@@ -510,13 +514,13 @@ impl Pane {
         cx.notify();
     }
 
-    //     pub fn set_render_tab_bar_buttons<F>(&mut self, cx: &mut ViewContext<Self>, render: F)
-    //     where
-    //         F: 'static + Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>,
-    //     {
-    //         self.render_tab_bar_buttons = Rc::new(render);
-    //         cx.notify();
-    //     }
+    pub fn set_render_tab_bar_buttons<F>(&mut self, cx: &mut ViewContext<Self>, render: F)
+    where
+        F: 'static + Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement,
+    {
+        self.render_tab_bar_buttons = Rc::new(render);
+        cx.notify();
+    }
 
     pub fn nav_history_for_item<T: Item>(&self, item: &View<T>) -> ItemNavHistory {
         ItemNavHistory {
@@ -1672,55 +1676,10 @@ impl Pane {
                     .disabled(!self.can_navigate_forward())
                     .tooltip(|cx| Tooltip::for_action("Go Forward", &GoForward, cx)),
             )
-            .end_child(
-                div()
-                    .child(
-                        IconButton::new("plus", Icon::Plus)
-                            .icon_size(IconSize::Small)
-                            .on_click(cx.listener(|this, _, cx| {
-                                let menu = ContextMenu::build(cx, |menu, _| {
-                                    menu.action("New File", NewFile.boxed_clone())
-                                        .action("New Terminal", NewCenterTerminal.boxed_clone())
-                                        .action("New Search", NewSearch.boxed_clone())
-                                });
-                                cx.subscribe(&menu, |this, _, _: &DismissEvent, cx| {
-                                    this.focus(cx);
-                                    this.new_item_menu = None;
-                                })
-                                .detach();
-                                this.new_item_menu = Some(menu);
-                            }))
-                            .tooltip(|cx| Tooltip::text("New...", cx)),
-                    )
-                    .when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
-                        el.child(Self::render_menu_overlay(new_item_menu))
-                    }),
-            )
-            .end_child(
-                div()
-                    .child(
-                        IconButton::new("split", Icon::Split)
-                            .icon_size(IconSize::Small)
-                            .on_click(cx.listener(|this, _, cx| {
-                                let menu = ContextMenu::build(cx, |menu, _| {
-                                    menu.action("Split Right", SplitRight.boxed_clone())
-                                        .action("Split Left", SplitLeft.boxed_clone())
-                                        .action("Split Up", SplitUp.boxed_clone())
-                                        .action("Split Down", SplitDown.boxed_clone())
-                                });
-                                cx.subscribe(&menu, |this, _, _: &DismissEvent, cx| {
-                                    this.focus(cx);
-                                    this.split_item_menu = None;
-                                })
-                                .detach();
-                                this.split_item_menu = Some(menu);
-                            }))
-                            .tooltip(|cx| Tooltip::text("Split Pane", cx)),
-                    )
-                    .when_some(self.split_item_menu.as_ref(), |el, split_item_menu| {
-                        el.child(Self::render_menu_overlay(split_item_menu))
-                    }),
-            )
+            .end_child({
+                let render_tab_buttons = self.render_tab_bar_buttons.clone();
+                render_tab_buttons(self, cx)
+            })
             .children(
                 self.items
                     .iter()