Release Notes: - N/A
Unified · Split
| @@ -4,13 +4,14 @@ use crate::TerminalView; · 3 unmodified lines | ||
| 4 | 4 | use db::kvp::KEY_VALUE_STORE; |
| 5 | 5 | use gpui::{ |
| 6 | 6 | actions, div, serde_json, AppContext, AsyncWindowContext, Div, Entity, EventEmitter, |
| 7 | − ExternalPaths, FocusHandle, FocusableView, ParentElement, Render, Styled, Subscription, Task, | |
| 8 | − View, ViewContext, VisualContext, WeakView, WindowContext, | |
| 7 | + ExternalPaths, FocusHandle, FocusableView, IntoElement, ParentElement, Render, Styled, | |
| 8 | + Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext, | |
| 9 | 9 | }; |
| 10 | 10 | use project::Fs; |
| 11 | 11 | use serde::{Deserialize, Serialize}; |
| 12 | 12 | use settings::{Settings, SettingsStore}; |
| 13 | 13 | use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings}; |
| 14 | +use ui::{h_stack, ButtonCommon, Clickable, IconButton, IconSize, Selectable, Tooltip}; | |
| 14 | 15 | use util::{ResultExt, TryFutureExt}; |
| 15 | 16 | use workspace::{ |
| 16 | 17 | dock::{DockPosition, Panel, PanelEvent}, |
| @@ -51,9 +52,8 @@ pub struct TerminalPanel { · 34 unmodified lines | ||
| 51 | 52 | |
| 52 | 53 | impl TerminalPanel { |
| 53 | 54 | fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self { |
| 54 | − let _weak_self = cx.view().downgrade(); | |
| 55 | + let terminal_panel = cx.view().clone(); | |
| 55 | 56 | let pane = cx.build_view(|cx| { |
| 56 | − let _window = cx.window_handle(); | |
| 57 | 57 | let mut pane = Pane::new( |
| 58 | 58 | workspace.weak_handle(), |
| 59 | 59 | workspace.project().clone(), |
| @@ -74,45 +74,31 @@ impl TerminalPanel { · 14 unmodified lines | ||
| 74 | 74 | ); |
| 75 | 75 | pane.set_can_split(false, cx); |
| 76 | 76 | pane.set_can_navigate(false, cx); |
| 77 | − // todo!() | |
| 78 | − // pane.set_render_tab_bar_buttons(cx, move |pane, cx| { | |
| 79 | − // let this = weak_self.clone(); | |
| 80 | − // Flex::row() | |
| 81 | − // .with_child(Pane::render_tab_bar_button( | |
| 82 | − // 0, | |
| 83 | − // "icons/plus.svg", | |
| 84 | − // false, | |
| 85 | − // Some(("New Terminal", Some(Box::new(workspace::NewTerminal)))), | |
| 86 | − // cx, | |
| 87 | − // move |_, cx| { | |
| 88 | − // let this = this.clone(); | |
| 89 | − // cx.window_context().defer(move |cx| { | |
| 90 | − // if let Some(this) = this.upgrade() { | |
| 91 | − // this.update(cx, |this, cx| { | |
| 92 | − // this.add_terminal(None, cx); | |
| 93 | − // }); | |
| 94 | − // } | |
| 95 | − // }) | |
| 96 | − // }, | |
| 97 | − // |_, _| {}, | |
| 98 | − // None, | |
| 99 | − // )) | |
| 100 | − // .with_child(Pane::render_tab_bar_button( | |
| 101 | − // 1, | |
| 102 | − // if pane.is_zoomed() { | |
| 103 | − // "icons/minimize.svg" | |
| 104 | − // } else { | |
| 105 | − // "icons/maximize.svg" | |
| 106 | − // }, | |
| 107 | − // pane.is_zoomed(), | |
| 108 | − // Some(("Toggle Zoom".into(), Some(Box::new(workspace::ToggleZoom)))), | |
| 109 | − // cx, | |
| 110 | − // move |pane, cx| pane.toggle_zoom(&Default::default(), cx), | |
| 111 | − // |_, _| {}, | |
| 112 | − // None, | |
| 113 | − // )) | |
| 114 | − // .into_any() | |
| 115 | − // }); | |
| 77 | + pane.set_render_tab_bar_buttons(cx, move |pane, cx| { | |
| 78 | + h_stack() | |
| 79 | + .child( | |
| 80 | + IconButton::new("plus", Icon::Plus) | |
| 81 | + .icon_size(IconSize::Small) | |
| 82 | + .on_click(cx.listener_for(&terminal_panel, |terminal_panel, _, cx| { | |
| 83 | + terminal_panel.add_terminal(None, cx); | |
| 84 | + })) | |
| 85 | + .tooltip(|cx| Tooltip::text("New Terminal", cx)), | |
| 86 | + ) | |
| 87 | + .child({ | |
| 88 | + let zoomed = pane.is_zoomed(); | |
| 89 | + IconButton::new("toggle_zoom", Icon::Maximize) | |
| 90 | + .icon_size(IconSize::Small) | |
| 91 | + .selected(zoomed) | |
| 92 | + .selected_icon(Icon::Minimize) | |
| 93 | + .on_click(cx.listener(|pane, _, cx| { | |
| 94 | + pane.toggle_zoom(&workspace::ToggleZoom, cx); | |
| 95 | + })) | |
| 96 | + .tooltip(move |cx| { | |
| 97 | + Tooltip::text(if zoomed { "Zoom Out" } else { "Zoom In" }, cx) | |
| 98 | + }) | |
| 99 | + }) | |
| 100 | + .into_any_element() | |
| 101 | + }); | |
| 116 | 102 | // let buffer_search_bar = cx.build_view(search::BufferSearchBar::new); |
| 117 | 103 | // pane.toolbar() |
| 118 | 104 | // .update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx)); |
| @@ -7,7 +7,7 @@ use crate::{ · 6 unmodified lines | ||
| 7 | 7 | use anyhow::Result; |
| 8 | 8 | use collections::{HashMap, HashSet, VecDeque}; |
| 9 | 9 | use gpui::{ |
| 10 | − actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AppContext, | |
| 10 | + actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyElement, AppContext, | |
| 11 | 11 | AsyncWindowContext, DismissEvent, Div, DragMoveEvent, EntityId, EventEmitter, FocusHandle, |
| 12 | 12 | Focusable, FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel, |
| 13 | 13 | Render, ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakView, |
| @@ -21,6 +21,7 @@ use std::{ · 7 unmodified lines | ||
| 21 | 21 | any::Any, |
| 22 | 22 | cmp, fmt, mem, |
| 23 | 23 | path::{Path, PathBuf}, |
| 24 | + rc::Rc, | |
| 24 | 25 | sync::{ |
| 25 | 26 | atomic::{AtomicUsize, Ordering}, |
| 26 | 27 | Arc, |
| @@ -183,7 +184,7 @@ pub struct Pane { · 156 unmodified lines | ||
| 183 | 184 | drag_split_direction: Option<SplitDirection>, |
| 184 | 185 | can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>>, |
| 185 | 186 | can_split: bool, |
| 186 | − // render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>, | |
| 187 | + render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement>, | |
| 187 | 188 | _subscriptions: Vec<Subscription>, |
| 188 | 189 | tab_bar_scroll_handle: ScrollHandle, |
| 189 | 190 | } |
| @@ -374,63 +375,66 @@ impl Pane { · 184 unmodified lines | ||
| 374 | 375 | project, |
| 375 | 376 | can_drop_predicate, |
| 376 | 377 | can_split: true, |
| 377 | − // render_tab_bar_buttons: Rc::new(move |pane, cx| { | |
| 378 | − // Flex::row() | |
| 379 | − // // New menu | |
| 380 | − // .with_child(Self::render_tab_bar_button( | |
| 381 | − // 0, | |
| 382 | − // "icons/plus.svg", | |
| 383 | − // false, | |
| 384 | − // Some(("New...".into(), None)), | |
| 385 | − // cx, | |
| 386 | − // |pane, cx| pane.deploy_new_menu(cx), | |
| 387 | − // |pane, cx| { | |
| 388 | − // pane.tab_bar_context_menu | |
| 389 | − // .handle | |
| 390 | − // .update(cx, |menu, _| menu.delay_cancel()) | |
| 391 | − // }, | |
| 392 | − // pane.tab_bar_context_menu | |
| 393 | − // .handle_if_kind(TabBarContextMenuKind::New), | |
| 394 | − // )) | |
| 395 | − // .with_child(Self::render_tab_bar_button( | |
| 396 | − // 1, | |
| 397 | − // "icons/split.svg", | |
| 398 | − // false, | |
| 399 | − // Some(("Split Pane".into(), None)), | |
| 400 | − // cx, | |
| 401 | − // |pane, cx| pane.deploy_split_menu(cx), | |
| 402 | − // |pane, cx| { | |
| 403 | − // pane.tab_bar_context_menu | |
| 404 | − // .handle | |
| 405 | − // .update(cx, |menu, _| menu.delay_cancel()) | |
| 406 | − // }, | |
| 407 | − // pane.tab_bar_context_menu | |
| 408 | − // .handle_if_kind(TabBarContextMenuKind::Split), | |
| 409 | − // )) | |
| 410 | − // .with_child({ | |
| 411 | − // let icon_path; | |
| 412 | − // let tooltip_label; | |
| 413 | − // if pane.is_zoomed() { | |
| 414 | − // icon_path = "icons/minimize.svg"; | |
| 415 | − // tooltip_label = "Zoom In"; | |
| 416 | − // } else { | |
| 417 | − // icon_path = "icons/maximize.svg"; | |
| 418 | − // tooltip_label = "Zoom In"; | |
| 419 | − // } | |
| 420 | − | |
| 421 | − // Pane::render_tab_bar_button( | |
| 422 | − // 2, | |
| 423 | − // icon_path, | |
| 424 | − // pane.is_zoomed(), | |
| 425 | − // Some((tooltip_label, Some(Box::new(ToggleZoom)))), | |
| 426 | − // cx, | |
| 427 | − // move |pane, cx| pane.toggle_zoom(&Default::default(), cx), | |
| 428 | − // move |_, _| {}, | |
| 429 | − // None, | |
| 430 | − // ) | |
| 431 | − // }) | |
| 432 | − // .into_any() | |
| 433 | − // }), | |
| 378 | + render_tab_bar_buttons: Rc::new(move |pane, cx| { | |
| 379 | + h_stack() | |
| 380 | + .child( | |
| 381 | + IconButton::new("plus", Icon::Plus) | |
| 382 | + .icon_size(IconSize::Small) | |
| 383 | + .on_click(cx.listener(|pane, _, cx| { | |
| 384 | + let menu = ContextMenu::build(cx, |menu, _| { | |
| 385 | + menu.action("New File", NewFile.boxed_clone()) | |
| 386 | + .action("New Terminal", NewCenterTerminal.boxed_clone()) | |
| 387 | + .action("New Search", NewSearch.boxed_clone()) | |
| 388 | + }); | |
| 389 | + cx.subscribe(&menu, |pane, _, _: &DismissEvent, cx| { | |
| 390 | + pane.focus(cx); | |
| 391 | + pane.new_item_menu = None; | |
| 392 | + }) | |
| 393 | + .detach(); | |
| 394 | + pane.new_item_menu = Some(menu); | |
| 395 | + })) | |
| 396 | + .tooltip(|cx| Tooltip::text("New...", cx)), | |
| 397 | + ) | |
| 398 | + .when_some(pane.new_item_menu.as_ref(), |el, new_item_menu| { | |
| 399 | + el.child(Self::render_menu_overlay(new_item_menu)) | |
| 400 | + }) | |
| 401 | + .child( | |
| 402 | + IconButton::new("split", Icon::Split) | |
| 403 | + .icon_size(IconSize::Small) | |
| 404 | + .on_click(cx.listener(|pane, _, cx| { | |
| 405 | + let menu = ContextMenu::build(cx, |menu, _| { | |
| 406 | + menu.action("Split Right", SplitRight.boxed_clone()) | |
| 407 | + .action("Split Left", SplitLeft.boxed_clone()) | |
| 408 | + .action("Split Up", SplitUp.boxed_clone()) | |
| 409 | + .action("Split Down", SplitDown.boxed_clone()) | |
| 410 | + }); | |
| 411 | + cx.subscribe(&menu, |pane, _, _: &DismissEvent, cx| { | |
| 412 | + pane.focus(cx); | |
| 413 | + pane.split_item_menu = None; | |
| 414 | + }) | |
| 415 | + .detach(); | |
| 416 | + pane.split_item_menu = Some(menu); | |
| 417 | + })) | |
| 418 | + .tooltip(|cx| Tooltip::text("Split Pane", cx)), | |
| 419 | + ) | |
| 420 | + .child({ | |
| 421 | + let zoomed = pane.is_zoomed(); | |
| 422 | + IconButton::new("toggle_zoom", Icon::Maximize) | |
| 423 | + .icon_size(IconSize::Small) | |
| 424 | + .selected(zoomed) | |
| 425 | + .selected_icon(Icon::Minimize) | |
| 426 | + .on_click(cx.listener(|pane, _, cx| { | |
| 427 | + pane.toggle_zoom(&crate::ToggleZoom, cx); | |
| 428 | + })) | |
| 429 | + .tooltip(move |cx| { | |
| 430 | + Tooltip::text(if zoomed { "Zoom Out" } else { "Zoom In" }, cx) | |
| 431 | + }) | |
| 432 | + }) | |
| 433 | + .when_some(pane.split_item_menu.as_ref(), |el, split_item_menu| { | |
| 434 | + el.child(Self::render_menu_overlay(split_item_menu)) | |
| 435 | + }) | |
| 436 | + .into_any_element() | |
| 437 | + }), | |
| 434 | 438 | _subscriptions: subscriptions, |
| 435 | 439 | } |
| 436 | 440 | } |
| @@ -510,13 +514,13 @@ impl Pane { · 73 unmodified lines | ||
| 510 | 514 | cx.notify(); |
| 511 | 515 | } |
| 512 | 516 | |
| 513 | − // pub fn set_render_tab_bar_buttons<F>(&mut self, cx: &mut ViewContext<Self>, render: F) | |
| 514 | − // where | |
| 515 | − // F: 'static + Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>, | |
| 516 | − // { | |
| 517 | − // self.render_tab_bar_buttons = Rc::new(render); | |
| 518 | − // cx.notify(); | |
| 519 | − // } | |
| 517 | + pub fn set_render_tab_bar_buttons<F>(&mut self, cx: &mut ViewContext<Self>, render: F) | |
| 518 | + where | |
| 519 | + F: 'static + Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement, | |
| 520 | + { | |
| 521 | + self.render_tab_bar_buttons = Rc::new(render); | |
| 522 | + cx.notify(); | |
| 523 | + } | |
| 520 | 524 | |
| 521 | 525 | pub fn nav_history_for_item<T: Item>(&self, item: &View<T>) -> ItemNavHistory { |
| 522 | 526 | ItemNavHistory { |
| @@ -1672,55 +1676,10 @@ impl Pane { · 1149 unmodified lines | ||
| 1672 | 1676 | .disabled(!self.can_navigate_forward()) |
| 1673 | 1677 | .tooltip(|cx| Tooltip::for_action("Go Forward", &GoForward, cx)), |
| 1674 | 1678 | ) |
| 1675 | − .end_child( | |
| 1676 | − div() | |
| 1677 | − .child( | |
| 1678 | − IconButton::new("plus", Icon::Plus) | |
| 1679 | − .icon_size(IconSize::Small) | |
| 1680 | − .on_click(cx.listener(|this, _, cx| { | |
| 1681 | − let menu = ContextMenu::build(cx, |menu, _| { | |
| 1682 | − menu.action("New File", NewFile.boxed_clone()) | |
| 1683 | − .action("New Terminal", NewCenterTerminal.boxed_clone()) | |
| 1684 | − .action("New Search", NewSearch.boxed_clone()) | |
| 1685 | − }); | |
| 1686 | − cx.subscribe(&menu, |this, _, _: &DismissEvent, cx| { | |
| 1687 | − this.focus(cx); | |
| 1688 | − this.new_item_menu = None; | |
| 1689 | − }) | |
| 1690 | − .detach(); | |
| 1691 | − this.new_item_menu = Some(menu); | |
| 1692 | − })) | |
| 1693 | − .tooltip(|cx| Tooltip::text("New...", cx)), | |
| 1694 | − ) | |
| 1695 | − .when_some(self.new_item_menu.as_ref(), |el, new_item_menu| { | |
| 1696 | − el.child(Self::render_menu_overlay(new_item_menu)) | |
| 1697 | − }), | |
| 1698 | − ) | |
| 1699 | − .end_child( | |
| 1700 | − div() | |
| 1701 | − .child( | |
| 1702 | − IconButton::new("split", Icon::Split) | |
| 1703 | − .icon_size(IconSize::Small) | |
| 1704 | − .on_click(cx.listener(|this, _, cx| { | |
| 1705 | − let menu = ContextMenu::build(cx, |menu, _| { | |
| 1706 | − menu.action("Split Right", SplitRight.boxed_clone()) | |
| 1707 | − .action("Split Left", SplitLeft.boxed_clone()) | |
| 1708 | − .action("Split Up", SplitUp.boxed_clone()) | |
| 1709 | − .action("Split Down", SplitDown.boxed_clone()) | |
| 1710 | − }); | |
| 1711 | − cx.subscribe(&menu, |this, _, _: &DismissEvent, cx| { | |
| 1712 | − this.focus(cx); | |
| 1713 | − this.split_item_menu = None; | |
| 1714 | − }) | |
| 1715 | − .detach(); | |
| 1716 | − this.split_item_menu = Some(menu); | |
| 1717 | − })) | |
| 1718 | − .tooltip(|cx| Tooltip::text("Split Pane", cx)), | |
| 1719 | − ) | |
| 1720 | − .when_some(self.split_item_menu.as_ref(), |el, split_item_menu| { | |
| 1721 | − el.child(Self::render_menu_overlay(split_item_menu)) | |
| 1722 | − }), | |
| 1723 | − ) | |
| 1679 | + .end_child({ | |
| 1680 | + let render_tab_buttons = self.render_tab_bar_buttons.clone(); | |
| 1681 | + render_tab_buttons(self, cx) | |
| 1682 | + }) | |
| 1724 | 1683 | .children( |
| 1725 | 1684 | self.items |
| 1726 | 1685 | .iter() |