Detailed changes
@@ -4224,7 +4224,7 @@ impl<T> Hash for WeakViewHandle<T> {
}
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct AnyWeakViewHandle {
window_id: usize,
view_id: usize,
@@ -13,7 +13,7 @@ use gpui::{
keymap_matcher::KeymapContext,
platform::{CursorStyle, MouseButton, PromptLevel},
AnyElement, AppContext, ClipboardItem, Element, Entity, ModelHandle, Task, View, ViewContext,
- ViewHandle, WeakViewHandle,
+ ViewHandle, WeakViewHandle, WindowContext,
};
use menu::{Confirm, SelectNext, SelectPrev};
use project::{Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
@@ -48,6 +48,7 @@ pub struct ProjectPanel {
context_menu: ViewHandle<ContextMenu>,
dragged_entry_destination: Option<Arc<Path>>,
workspace: WeakViewHandle<Workspace>,
+ has_focus: bool,
}
#[derive(Copy, Clone)]
@@ -139,6 +140,7 @@ pub enum Event {
focus_opened_item: bool,
},
DockPositionChanged,
+ Focus,
}
impl ProjectPanel {
@@ -214,6 +216,7 @@ impl ProjectPanel {
context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)),
dragged_entry_destination: None,
workspace: workspace.weak_handle(),
+ has_focus: false,
};
this.update_visible_entries(None, cx);
@@ -259,7 +262,7 @@ impl ProjectPanel {
}
}
}
- Event::DockPositionChanged => {}
+ _ => {}
}
})
.detach();
@@ -1338,6 +1341,17 @@ impl View for ProjectPanel {
Self::reset_to_default_keymap_context(keymap);
keymap.add_identifier("menu");
}
+
+ fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if !self.has_focus {
+ self.has_focus = true;
+ cx.emit(Event::Focus);
+ }
+ }
+
+ fn focus_out(&mut self, _: gpui::AnyViewHandle, _: &mut ViewContext<Self>) {
+ self.has_focus = false;
+ }
}
impl Entity for ProjectPanel {
@@ -1345,7 +1359,7 @@ impl Entity for ProjectPanel {
}
impl workspace::dock::Panel for ProjectPanel {
- fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
+ fn position(&self, cx: &WindowContext) -> DockPosition {
let settings = cx.global::<Settings>();
match settings.project_panel.dock {
settings::ProjectPanelDockPosition::Left => DockPosition::Left,
@@ -1369,7 +1383,7 @@ impl workspace::dock::Panel for ProjectPanel {
})
}
- fn default_size(&self, cx: &gpui::WindowContext) -> f32 {
+ fn default_size(&self, cx: &WindowContext) -> f32 {
cx.global::<Settings>().project_panel.default_width
}
@@ -1395,13 +1409,21 @@ impl workspace::dock::Panel for ProjectPanel {
matches!(event, Event::DockPositionChanged)
}
- fn should_activate_on_event(&self, _: &Self::Event, _: &AppContext) -> bool {
+ fn should_activate_on_event(_: &Self::Event) -> bool {
false
}
- fn should_close_on_event(&self, _: &Self::Event, _: &AppContext) -> bool {
+ fn should_close_on_event(_: &Self::Event) -> bool {
false
}
+
+ fn has_focus(&self, _: &WindowContext) -> bool {
+ self.has_focus
+ }
+
+ fn is_focus_event(event: &Self::Event) -> bool {
+ matches!(event, Event::Focus)
+ }
}
impl ClipboardEntry {
@@ -20,6 +20,7 @@ pub enum Event {
DockPositionChanged,
ZoomIn,
ZoomOut,
+ Focus,
}
pub struct TerminalPanel {
@@ -100,6 +101,7 @@ impl TerminalPanel {
pane::Event::Remove => cx.emit(Event::Close),
pane::Event::ZoomIn => cx.emit(Event::ZoomIn),
pane::Event::ZoomOut => cx.emit(Event::ZoomOut),
+ pane::Event::Focus => cx.emit(Event::Focus),
_ => {}
}
}
@@ -149,6 +151,10 @@ impl View for TerminalPanel {
if self.pane.read(cx).items_len() == 0 {
self.add_terminal(&Default::default(), cx)
}
+
+ if cx.is_self_focused() {
+ cx.focus(&self.pane);
+ }
}
}
@@ -211,7 +217,7 @@ impl Panel for TerminalPanel {
"Terminals".to_string()
}
- fn icon_label(&self, cx: &AppContext) -> Option<String> {
+ fn icon_label(&self, cx: &WindowContext) -> Option<String> {
let count = self.pane.read(cx).items_len();
if count == 0 {
None
@@ -224,11 +230,19 @@ impl Panel for TerminalPanel {
matches!(event, Event::DockPositionChanged)
}
- fn should_activate_on_event(&self, _: &Self::Event, _: &AppContext) -> bool {
+ fn should_activate_on_event(_: &Self::Event) -> bool {
false
}
- fn should_close_on_event(&self, event: &Event, _: &AppContext) -> bool {
+ fn should_close_on_event(event: &Event) -> bool {
matches!(event, Event::Close)
}
+
+ fn has_focus(&self, cx: &WindowContext) -> bool {
+ self.pane.read(cx).has_focus()
+ }
+
+ fn is_focus_event(event: &Self::Event) -> bool {
+ matches!(event, Event::Focus)
+ }
}
@@ -1,9 +1,8 @@
use crate::{StatusItemView, Workspace};
use context_menu::{ContextMenu, ContextMenuItem};
use gpui::{
- elements::*, impl_actions, platform::CursorStyle, platform::MouseButton, AnyViewHandle,
- AppContext, Axis, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
- WindowContext,
+ elements::*, impl_actions, platform::CursorStyle, platform::MouseButton, AnyViewHandle, Axis,
+ Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
};
use serde::Deserialize;
use settings::Settings;
@@ -16,15 +15,17 @@ pub trait Panel: View {
fn default_size(&self, cx: &WindowContext) -> f32;
fn icon_path(&self) -> &'static str;
fn icon_tooltip(&self) -> String;
- fn icon_label(&self, _: &AppContext) -> Option<String> {
+ fn icon_label(&self, _: &WindowContext) -> Option<String> {
None
}
fn should_change_position_on_event(_: &Self::Event) -> bool;
fn should_zoom_in_on_event(_: &Self::Event) -> bool;
fn should_zoom_out_on_event(_: &Self::Event) -> bool;
fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>);
- fn should_activate_on_event(&self, _: &Self::Event, _: &AppContext) -> bool;
- fn should_close_on_event(&self, _: &Self::Event, _: &AppContext) -> bool;
+ fn should_activate_on_event(_: &Self::Event) -> bool;
+ fn should_close_on_event(_: &Self::Event) -> bool;
+ fn has_focus(&self, cx: &WindowContext) -> bool;
+ fn is_focus_event(_: &Self::Event) -> bool;
}
pub trait PanelHandle {
@@ -37,7 +38,7 @@ pub trait PanelHandle {
fn icon_path(&self, cx: &WindowContext) -> &'static str;
fn icon_tooltip(&self, cx: &WindowContext) -> String;
fn icon_label(&self, cx: &WindowContext) -> Option<String>;
- fn is_focused(&self, cx: &WindowContext) -> bool;
+ fn has_focus(&self, cx: &WindowContext) -> bool;
fn as_any(&self) -> &AnyViewHandle;
}
@@ -81,8 +82,8 @@ where
self.read(cx).icon_label(cx)
}
- fn is_focused(&self, cx: &WindowContext) -> bool {
- ViewHandle::is_focused(self, cx)
+ fn has_focus(&self, cx: &WindowContext) -> bool {
+ self.read(cx).has_focus(cx)
}
fn as_any(&self) -> &AnyViewHandle {
@@ -170,6 +171,11 @@ impl Dock {
self.is_open
}
+ pub fn has_focus(&self, cx: &WindowContext) -> bool {
+ self.active_panel()
+ .map_or(false, |panel| panel.has_focus(cx))
+ }
+
pub fn active_panel_index(&self) -> usize {
self.active_panel_index
}
@@ -220,7 +226,7 @@ impl Dock {
let subscriptions = [
cx.observe(&panel, |_, _, cx| cx.notify()),
cx.subscribe(&panel, |this, panel, event, cx| {
- if panel.read(cx).should_activate_on_event(event, cx) {
+ if T::should_activate_on_event(event) {
if let Some(ix) = this
.panel_entries
.iter()
@@ -230,7 +236,7 @@ impl Dock {
this.activate_panel(ix, cx);
cx.focus(&panel);
}
- } else if panel.read(cx).should_close_on_event(event, cx)
+ } else if T::should_close_on_event(event)
&& this.active_panel().map_or(false, |p| p.id() == panel.id())
{
this.set_open(false, cx);
@@ -302,10 +308,10 @@ impl Dock {
}
}
- pub fn zoomed_panel(&self) -> Option<AnyViewHandle> {
+ pub fn zoomed_panel(&self) -> Option<Rc<dyn PanelHandle>> {
let entry = self.active_entry()?;
if entry.zoomed {
- Some(entry.panel.as_any().clone())
+ Some(entry.panel.clone())
} else {
None
}
@@ -344,6 +350,24 @@ impl Dock {
cx.notify();
}
}
+
+ pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement<Workspace> {
+ if let Some(active_entry) = self.active_entry() {
+ let style = &cx.global::<Settings>().theme.workspace.dock;
+ Empty::new()
+ .into_any()
+ .contained()
+ .with_style(style.container)
+ .resizable(
+ self.position.to_resize_handle_side(),
+ active_entry.size,
+ |_, _, _| {},
+ )
+ .into_any()
+ } else {
+ Empty::new().into_any()
+ }
+ }
}
impl Entity for Dock {
@@ -357,21 +381,16 @@ impl View for Dock {
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
if let Some(active_entry) = self.active_entry() {
- if active_entry.zoomed {
- Empty::new().into_any()
- } else {
- let size = self.active_panel_size().unwrap();
- let style = &cx.global::<Settings>().theme.workspace.dock;
- ChildView::new(active_entry.panel.as_any(), cx)
- .contained()
- .with_style(style.container)
- .resizable(
- self.position.to_resize_handle_side(),
- size,
- |dock: &mut Self, size, cx| dock.resize_active_panel(size, cx),
- )
- .into_any()
- }
+ let style = &cx.global::<Settings>().theme.workspace.dock;
+ ChildView::new(active_entry.panel.as_any(), cx)
+ .contained()
+ .with_style(style.container)
+ .resizable(
+ self.position.to_resize_handle_side(),
+ active_entry.size,
+ |dock: &mut Self, size, cx| dock.resize_active_panel(size, cx),
+ )
+ .into_any()
} else {
Empty::new().into_any()
}
@@ -604,12 +623,20 @@ pub(crate) mod test {
false
}
- fn should_activate_on_event(&self, event: &Self::Event, _: &gpui::AppContext) -> bool {
+ fn should_activate_on_event(event: &Self::Event) -> bool {
matches!(event, TestPanelEvent::Activated)
}
- fn should_close_on_event(&self, event: &Self::Event, _: &gpui::AppContext) -> bool {
+ fn should_close_on_event(event: &Self::Event) -> bool {
matches!(event, TestPanelEvent::Closed)
}
+
+ fn has_focus(&self, _cx: &WindowContext) -> bool {
+ unimplemented!()
+ }
+
+ fn is_focus_event(_: &Self::Event) -> bool {
+ unimplemented!()
+ }
}
}
@@ -1699,7 +1699,11 @@ impl View for Pane {
}
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
- self.has_focus = true;
+ if !self.has_focus {
+ self.has_focus = true;
+ cx.emit(Event::Focus);
+ }
+
self.toolbar.update(cx, |toolbar, cx| {
toolbar.pane_focus_update(true, cx);
});
@@ -1725,8 +1729,6 @@ impl View for Pane {
.insert(active_item.id(), focused.downgrade());
}
}
-
- cx.emit(Event::Focus);
}
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
@@ -7,7 +7,7 @@ use gpui::{
elements::*,
geometry::{rect::RectF, vector::Vector2F},
platform::{CursorStyle, MouseButton},
- Axis, Border, ModelHandle, ViewContext, ViewHandle,
+ AnyViewHandle, Axis, Border, ModelHandle, ViewContext, ViewHandle,
};
use project::Project;
use serde::Deserialize;
@@ -72,6 +72,7 @@ impl PaneGroup {
follower_states: &FollowerStatesByLeader,
active_call: Option<&ModelHandle<ActiveCall>>,
active_pane: &ViewHandle<Pane>,
+ zoomed: Option<&AnyViewHandle>,
app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>,
) -> AnyElement<Workspace> {
@@ -81,6 +82,7 @@ impl PaneGroup {
follower_states,
active_call,
active_pane,
+ zoomed,
app_state,
cx,
)
@@ -135,6 +137,7 @@ impl Member {
follower_states: &FollowerStatesByLeader,
active_call: Option<&ModelHandle<ActiveCall>>,
active_pane: &ViewHandle<Pane>,
+ zoomed: Option<&AnyViewHandle>,
app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>,
) -> AnyElement<Workspace> {
@@ -142,7 +145,7 @@ impl Member {
match self {
Member::Pane(pane) => {
- let pane_element = if pane.read(cx).is_zoomed() {
+ let pane_element = if Some(&**pane) == zoomed {
Empty::new().into_any()
} else {
ChildView::new(pane, cx).into_any()
@@ -274,6 +277,7 @@ impl Member {
follower_states,
active_call,
active_pane,
+ zoomed,
app_state,
cx,
),
@@ -378,6 +382,7 @@ impl PaneAxis {
follower_state: &FollowerStatesByLeader,
active_call: Option<&ModelHandle<ActiveCall>>,
active_pane: &ViewHandle<Pane>,
+ zoomed: Option<&AnyViewHandle>,
app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>,
) -> AnyElement<Workspace> {
@@ -395,6 +400,7 @@ impl PaneAxis {
follower_state,
active_call,
active_pane,
+ zoomed,
app_state,
cx,
);
@@ -893,6 +893,8 @@ impl Workspace {
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
} else if T::should_zoom_out_on_event(event) {
this.zoom_out(cx);
+ } else if T::is_focus_event(event) {
+ cx.notify();
}
}
})
@@ -1309,16 +1311,42 @@ impl Workspace {
}
}
- fn zoomed(&self, cx: &AppContext) -> Option<AnyViewHandle> {
- self.left_dock
- .read(cx)
- .zoomed_panel()
- .or(self.bottom_dock.read(cx).zoomed_panel())
- .or(self.right_dock.read(cx).zoomed_panel())
- .or_else(|| {
- let pane = self.panes.iter().find(|pane| pane.read(cx).is_zoomed())?;
- Some(pane.clone().into_any())
- })
+ fn zoomed(&self, cx: &WindowContext) -> Option<AnyViewHandle> {
+ self.zoomed_panel_for_dock(DockPosition::Left, cx)
+ .or_else(|| self.zoomed_panel_for_dock(DockPosition::Bottom, cx))
+ .or_else(|| self.zoomed_panel_for_dock(DockPosition::Right, cx))
+ .or_else(|| self.zoomed_pane(cx))
+ }
+
+ fn zoomed_panel_for_dock(
+ &self,
+ position: DockPosition,
+ cx: &WindowContext,
+ ) -> Option<AnyViewHandle> {
+ let (dock, other_docks) = match position {
+ DockPosition::Left => (&self.left_dock, [&self.bottom_dock, &self.right_dock]),
+ DockPosition::Bottom => (&self.bottom_dock, [&self.left_dock, &self.right_dock]),
+ DockPosition::Right => (&self.right_dock, [&self.left_dock, &self.bottom_dock]),
+ };
+
+ let zoomed_panel = dock.read(&cx).zoomed_panel()?;
+ if other_docks.iter().all(|dock| !dock.read(cx).has_focus(cx))
+ && !self.active_pane.read(cx).has_focus()
+ {
+ Some(zoomed_panel.as_any().clone())
+ } else {
+ None
+ }
+ }
+
+ fn zoomed_pane(&self, cx: &WindowContext) -> Option<AnyViewHandle> {
+ let active_pane = self.active_pane.read(cx);
+ let docks = [&self.left_dock, &self.bottom_dock, &self.right_dock];
+ if active_pane.is_zoomed() && docks.iter().all(|dock| !dock.read(cx).has_focus(cx)) {
+ Some(self.active_pane.clone().into_any())
+ } else {
+ None
+ }
}
pub fn items<'a>(
@@ -1433,7 +1461,7 @@ impl Workspace {
});
if let Some(active_item) = active_item {
- if active_item.is_focused(cx) {
+ if active_item.has_focus(cx) {
cx.focus_self();
} else {
cx.focus(active_item.as_any());
@@ -1464,7 +1492,7 @@ impl Workspace {
dock.active_panel().cloned()
});
if let Some(active_item) = active_item {
- if active_item.is_focused(cx) {
+ if active_item.has_focus(cx) {
cx.focus_self();
} else {
cx.focus(active_item.as_any());
@@ -1663,7 +1691,6 @@ impl Workspace {
});
self.active_item_path_changed(cx);
self.last_active_center_pane = Some(pane.downgrade());
- cx.notify();
}
self.update_followers(
@@ -1677,6 +1704,8 @@ impl Workspace {
}),
cx,
);
+
+ cx.notify();
}
fn handle_pane_event(
@@ -1716,9 +1745,11 @@ impl Workspace {
self.handle_pane_focused(pane.clone(), cx);
}
pane::Event::ZoomIn => {
- self.zoom_out(cx);
- pane.update(cx, |pane, cx| pane.set_zoomed(true, cx));
- cx.notify();
+ if pane == self.active_pane {
+ self.zoom_out(cx);
+ pane.update(cx, |pane, cx| pane.set_zoomed(true, cx));
+ cx.notify();
+ }
}
pane::Event::ZoomOut => self.zoom_out(cx),
}
@@ -2646,6 +2677,33 @@ impl Workspace {
});
Self::new(None, 0, project, app_state, cx)
}
+
+ fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> {
+ let dock = match position {
+ DockPosition::Left => &self.left_dock,
+ DockPosition::Right => &self.right_dock,
+ DockPosition::Bottom => &self.bottom_dock,
+ };
+ let active_panel = dock.read(cx).active_panel()?;
+ let element = if Some(active_panel.as_any()) == self.zoomed(cx).as_ref() {
+ dock.read(cx).render_placeholder(cx)
+ } else {
+ ChildView::new(dock, cx).into_any()
+ };
+
+ Some(
+ element
+ .constrained()
+ .dynamically(move |constraint, _, cx| match position {
+ DockPosition::Left | DockPosition::Right => SizeConstraint::new(
+ Vector2F::new(20., constraint.min.y()),
+ Vector2F::new(cx.window_size().x() * 0.8, constraint.max.y()),
+ ),
+ _ => constraint,
+ })
+ .into_any(),
+ )
+ }
}
fn notify_if_database_failed(workspace: &WeakViewHandle<Workspace>, cx: &mut AsyncAppContext) {
@@ -2702,25 +2760,7 @@ impl View for Workspace {
.with_child({
let project = self.project.clone();
Flex::row()
- .with_children(
- if self.left_dock.read(cx).active_panel().is_some() {
- Some(
- ChildView::new(&self.left_dock, cx)
- .constrained()
- .dynamically(|constraint, _, cx| {
- SizeConstraint::new(
- Vector2F::new(20., constraint.min.y()),
- Vector2F::new(
- cx.window_size().x() * 0.8,
- constraint.max.y(),
- ),
- )
- }),
- )
- } else {
- None
- },
- )
+ .with_children(self.render_dock(DockPosition::Left, cx))
.with_child(
Flex::column()
.with_child(
@@ -2730,44 +2770,18 @@ impl View for Workspace {
&self.follower_states_by_leader,
self.active_call(),
self.active_pane(),
+ self.zoomed(cx).as_ref(),
&self.app_state,
cx,
))
.flex(1., true),
)
.with_children(
- if self
- .bottom_dock
- .read(cx)
- .active_panel()
- .is_some()
- {
- Some(ChildView::new(&self.bottom_dock, cx))
- } else {
- None
- },
+ self.render_dock(DockPosition::Bottom, cx),
)
.flex(1., true),
)
- .with_children(
- if self.right_dock.read(cx).active_panel().is_some() {
- Some(
- ChildView::new(&self.right_dock, cx)
- .constrained()
- .dynamically(|constraint, _, cx| {
- SizeConstraint::new(
- Vector2F::new(20., constraint.min.y()),
- Vector2F::new(
- cx.window_size().x() * 0.8,
- constraint.max.y(),
- ),
- )
- }),
- )
- } else {
- None
- },
- )
+ .with_children(self.render_dock(DockPosition::Right, cx))
})
.with_child(Overlay::new(
Stack::new()
@@ -2810,6 +2824,7 @@ impl View for Workspace {
if cx.is_self_focused() {
cx.focus(&self.active_pane);
}
+ cx.notify();
}
}