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