mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-20 19:08:00 +03:00
Pane context menus & focus shenanigans
Co-Authored-By: Conrad Irwin <conrad@zed.dev>
This commit is contained in:
parent
21dfe58ad9
commit
d516ae0d8a
@ -2,14 +2,15 @@ use crate::{
|
||||
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
|
||||
toolbar::Toolbar,
|
||||
workspace_settings::{AutosaveSetting, WorkspaceSettings},
|
||||
SplitDirection, Workspace,
|
||||
NewCenterTerminal, NewFile, NewSearch, SplitDirection, ToggleZoom, Workspace,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::{HashMap, HashSet, VecDeque};
|
||||
use gpui::{
|
||||
actions, prelude::*, Action, AnyWeakView, AppContext, AsyncWindowContext, Div, EntityId,
|
||||
EventEmitter, FocusHandle, Focusable, FocusableView, Model, Pixels, Point, PromptLevel, Render,
|
||||
Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext,
|
||||
AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
|
||||
FocusableView, Model, Pixels, Point, PromptLevel, Render, Task, View, ViewContext,
|
||||
VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use project2::{Project, ProjectEntryId, ProjectPath};
|
||||
@ -25,8 +26,8 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use ui::v_stack;
|
||||
use ui::{prelude::*, Color, Icon, IconButton, IconElement, Tooltip};
|
||||
use ui::{menu_handle, prelude::*, Color, Icon, IconButton, IconElement, Tooltip};
|
||||
use ui::{v_stack, ContextMenu};
|
||||
use util::truncate_and_remove_front;
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
||||
@ -50,7 +51,7 @@ pub enum SaveIntent {
|
||||
|
||||
//todo!("Do we need the default bound on actions? Decide soon")
|
||||
// #[register_action]
|
||||
#[derive(Clone, Deserialize, PartialEq, Debug)]
|
||||
#[derive(Action, Clone, Deserialize, PartialEq, Debug)]
|
||||
pub struct ActivateItem(pub usize);
|
||||
|
||||
// #[derive(Clone, PartialEq)]
|
||||
@ -158,7 +159,9 @@ pub struct Pane {
|
||||
autoscroll: bool,
|
||||
nav_history: NavHistory,
|
||||
toolbar: View<Toolbar>,
|
||||
// tab_bar_context_menu: TabBarContextMenu,
|
||||
tab_bar_focus_handle: FocusHandle,
|
||||
new_item_menu: Option<View<ContextMenu>>,
|
||||
split_item_menu: Option<View<ContextMenu>>,
|
||||
// tab_context_menu: ViewHandle<ContextMenu>,
|
||||
workspace: WeakView<Workspace>,
|
||||
project: Model<Project>,
|
||||
@ -323,6 +326,9 @@ impl Pane {
|
||||
next_timestamp,
|
||||
}))),
|
||||
toolbar: cx.build_view(|_| Toolbar::new()),
|
||||
tab_bar_focus_handle: cx.focus_handle(),
|
||||
new_item_menu: None,
|
||||
split_item_menu: None,
|
||||
// tab_bar_context_menu: TabBarContextMenu {
|
||||
// kind: TabBarContextMenuKind::New,
|
||||
// handle: context_menu,
|
||||
@ -397,6 +403,7 @@ impl Pane {
|
||||
}
|
||||
|
||||
pub fn has_focus(&self, cx: &WindowContext) -> bool {
|
||||
// todo!(); // inline this manually
|
||||
self.focus_handle.contains_focused(cx)
|
||||
}
|
||||
|
||||
@ -422,11 +429,11 @@ impl Pane {
|
||||
}
|
||||
|
||||
active_item.focus_handle(cx).focus(cx);
|
||||
// todo!() Do this once we have tab bar context menu
|
||||
// } else if !self.tab_bar_context_menu.handle.is_focused() {
|
||||
} else if let Some(focused) = cx.focused() {
|
||||
self.last_focused_view_by_item
|
||||
.insert(active_item.item_id(), focused);
|
||||
} else if !self.tab_bar_focus_handle.contains_focused(cx) {
|
||||
if let Some(focused) = cx.focused() {
|
||||
self.last_focused_view_by_item
|
||||
.insert(active_item.item_id(), focused);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -673,21 +680,16 @@ impl Pane {
|
||||
.position(|i| i.item_id() == item.item_id())
|
||||
}
|
||||
|
||||
// pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
|
||||
// // Potentially warn the user of the new keybinding
|
||||
// let workspace_handle = self.workspace().clone();
|
||||
// cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) })
|
||||
// .detach();
|
||||
|
||||
// if self.zoomed {
|
||||
// cx.emit(Event::ZoomOut);
|
||||
// } else if !self.items.is_empty() {
|
||||
// if !self.has_focus {
|
||||
// cx.focus_self();
|
||||
// }
|
||||
// cx.emit(Event::ZoomIn);
|
||||
// }
|
||||
// }
|
||||
pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
|
||||
if self.zoomed {
|
||||
cx.emit(Event::ZoomOut);
|
||||
} else if !self.items.is_empty() {
|
||||
if !self.focus_handle.contains_focused(cx) {
|
||||
cx.focus_self();
|
||||
}
|
||||
cx.emit(Event::ZoomIn);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn activate_item(
|
||||
&mut self,
|
||||
@ -1424,7 +1426,7 @@ impl Pane {
|
||||
let close_right = ItemSettings::get_global(cx).close_position.right();
|
||||
let is_active = ix == self.active_item_index;
|
||||
|
||||
div()
|
||||
let tab = div()
|
||||
.group("")
|
||||
.id(ix)
|
||||
.cursor_pointer()
|
||||
@ -1498,13 +1500,41 @@ impl Pane {
|
||||
.children((!close_right).then(|| close_icon()))
|
||||
.child(label)
|
||||
.children(close_right.then(|| close_icon())),
|
||||
)
|
||||
);
|
||||
|
||||
menu_handle(ix).child(|_| tab).menu(|cx| {
|
||||
ContextMenu::build(cx, |menu, cx| {
|
||||
menu.action(
|
||||
"Close Active Item",
|
||||
CloseActiveItem { save_intent: None }.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
.action("Close Inactive Items", CloseInactiveItems.boxed_clone(), cx)
|
||||
.action("Close Clean Items", CloseCleanItems.boxed_clone(), cx)
|
||||
.action(
|
||||
"Close Items To The Left",
|
||||
CloseItemsToTheLeft.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
.action(
|
||||
"Close Items To The Right",
|
||||
CloseItemsToTheRight.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
.action(
|
||||
"Close All Items",
|
||||
CloseAllItems { save_intent: None }.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
|
||||
div()
|
||||
.group("tab_bar")
|
||||
.id("tab_bar")
|
||||
.track_focus(&self.tab_bar_focus_handle)
|
||||
.w_full()
|
||||
.flex()
|
||||
.bg(cx.theme().colors().tab_bar_background)
|
||||
@ -1563,20 +1593,87 @@ impl Pane {
|
||||
.gap_px()
|
||||
.child(
|
||||
div()
|
||||
.bg(gpui::blue())
|
||||
.border()
|
||||
.border_color(gpui::red())
|
||||
.child(IconButton::new("plus", Icon::Plus)),
|
||||
.child(IconButton::new("plus", Icon::Plus).on_click(
|
||||
cx.listener(|this, _, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||
menu.action("New File", NewFile.boxed_clone(), cx)
|
||||
.action(
|
||||
"New Terminal",
|
||||
NewCenterTerminal.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
.action(
|
||||
"New Search",
|
||||
NewSearch.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.subscribe(
|
||||
&menu,
|
||||
|this, _, event: &DismissEvent, cx| {
|
||||
this.focus(cx);
|
||||
this.new_item_menu = None;
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
this.new_item_menu = Some(menu);
|
||||
}),
|
||||
))
|
||||
.when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
|
||||
el.child(Self::render_menu_overlay(new_item_menu))
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.border()
|
||||
.border_color(gpui::red())
|
||||
.child(IconButton::new("split", Icon::Split)),
|
||||
.child(IconButton::new("split", Icon::Split).on_click(
|
||||
cx.listener(|this, _, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||
menu.action(
|
||||
"Split Right",
|
||||
SplitRight.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
.action("Split Left", SplitLeft.boxed_clone(), cx)
|
||||
.action("Split Up", SplitUp.boxed_clone(), cx)
|
||||
.action("Split Down", SplitDown.boxed_clone(), cx)
|
||||
});
|
||||
cx.subscribe(
|
||||
&menu,
|
||||
|this, _, event: &DismissEvent, cx| {
|
||||
this.focus(cx);
|
||||
this.split_item_menu = None;
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
this.split_item_menu = Some(menu);
|
||||
}),
|
||||
))
|
||||
.when_some(
|
||||
self.split_item_menu.as_ref(),
|
||||
|el, split_item_menu| {
|
||||
el.child(Self::render_menu_overlay(split_item_menu))
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
|
||||
div()
|
||||
.absolute()
|
||||
.z_index(1)
|
||||
.bottom_0()
|
||||
.right_0()
|
||||
.size_0()
|
||||
.child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone()))
|
||||
}
|
||||
|
||||
// fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
|
||||
// let theme = theme::current(cx).clone();
|
||||
|
||||
@ -2004,25 +2101,53 @@ impl Render for Pane {
|
||||
.on_action(cx.listener(|pane: &mut Pane, _: &SplitDown, cx| {
|
||||
pane.split(SplitDirection::Down, cx)
|
||||
}))
|
||||
// cx.add_action(Pane::toggle_zoom);
|
||||
// cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
|
||||
// pane.activate_item(action.0, true, true, cx);
|
||||
// });
|
||||
// cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| {
|
||||
// pane.activate_item(pane.items.len() - 1, true, true, cx);
|
||||
// });
|
||||
// cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
|
||||
// pane.activate_prev_item(true, cx);
|
||||
// });
|
||||
// cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| {
|
||||
// pane.activate_next_item(true, cx);
|
||||
// });
|
||||
// cx.add_async_action(Pane::close_active_item);
|
||||
// cx.add_async_action(Pane::close_inactive_items);
|
||||
// cx.add_async_action(Pane::close_clean_items);
|
||||
// cx.add_async_action(Pane::close_items_to_the_left);
|
||||
// cx.add_async_action(Pane::close_items_to_the_right);
|
||||
// cx.add_async_action(Pane::close_all_items);
|
||||
.on_action(cx.listener(Pane::toggle_zoom))
|
||||
.on_action(cx.listener(|pane: &mut Pane, action: &ActivateItem, cx| {
|
||||
pane.activate_item(action.0, true, true, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|pane: &mut Pane, _: &ActivateLastItem, cx| {
|
||||
pane.activate_item(pane.items.len() - 1, true, true, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
|
||||
pane.activate_prev_item(true, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|pane: &mut Pane, _: &ActivateNextItem, cx| {
|
||||
pane.activate_next_item(true, cx);
|
||||
}))
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
|
||||
pane.close_active_item(action, cx)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
}),
|
||||
)
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Self, action: &CloseInactiveItems, cx| {
|
||||
pane.close_inactive_items(action, cx)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
}),
|
||||
)
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Self, action: &CloseCleanItems, cx| {
|
||||
pane.close_clean_items(action, cx)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
}),
|
||||
)
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Self, action: &CloseItemsToTheLeft, cx| {
|
||||
pane.close_items_to_the_left(action, cx)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
}),
|
||||
)
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Self, action: &CloseItemsToTheRight, cx| {
|
||||
pane.close_items_to_the_right(action, cx)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
}),
|
||||
)
|
||||
.on_action(cx.listener(|pane: &mut Self, action: &CloseAllItems, cx| {
|
||||
pane.close_all_items(action, cx)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
}))
|
||||
.size_full()
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
|
||||
|
@ -290,7 +290,6 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for View<T> {
|
||||
}
|
||||
|
||||
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
|
||||
println!("focus changed, pane_focused: {pane_focused}");
|
||||
self.update(cx, |this, cx| {
|
||||
this.pane_focus_update(pane_focused, cx);
|
||||
cx.notify();
|
||||
|
@ -3587,87 +3587,6 @@ fn open_items(
|
||||
})
|
||||
}
|
||||
|
||||
// todo!()
|
||||
// fn notify_of_new_dock(workspace: &WeakView<Workspace>, cx: &mut AsyncAppContext) {
|
||||
// const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system";
|
||||
// const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key";
|
||||
// const MESSAGE_ID: usize = 2;
|
||||
|
||||
// if workspace
|
||||
// .read_with(cx, |workspace, cx| {
|
||||
// workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
|
||||
// })
|
||||
// .unwrap_or(false)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if db::kvp::KEY_VALUE_STORE
|
||||
// .read_kvp(NEW_DOCK_HINT_KEY)
|
||||
// .ok()
|
||||
// .flatten()
|
||||
// .is_some()
|
||||
// {
|
||||
// if !workspace
|
||||
// .read_with(cx, |workspace, cx| {
|
||||
// workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
|
||||
// })
|
||||
// .unwrap_or(false)
|
||||
// {
|
||||
// cx.update(|cx| {
|
||||
// cx.update_global::<NotificationTracker, _, _>(|tracker, _| {
|
||||
// let entry = tracker
|
||||
// .entry(TypeId::of::<MessageNotification>())
|
||||
// .or_default();
|
||||
// if !entry.contains(&MESSAGE_ID) {
|
||||
// entry.push(MESSAGE_ID);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
// cx.spawn(|_| async move {
|
||||
// db::kvp::KEY_VALUE_STORE
|
||||
// .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string())
|
||||
// .await
|
||||
// .ok();
|
||||
// })
|
||||
// .detach();
|
||||
|
||||
// workspace
|
||||
// .update(cx, |workspace, cx| {
|
||||
// workspace.show_notification_once(2, cx, |cx| {
|
||||
// cx.build_view(|_| {
|
||||
// MessageNotification::new_element(|text, _| {
|
||||
// Text::new(
|
||||
// "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.",
|
||||
// text,
|
||||
// )
|
||||
// .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| {
|
||||
// let code_span_background_color = settings::get::<ThemeSettings>(cx)
|
||||
// .theme
|
||||
// .editor
|
||||
// .document_highlight_read_background;
|
||||
|
||||
// cx.scene().push_quad(gpui::Quad {
|
||||
// bounds,
|
||||
// background: Some(code_span_background_color),
|
||||
// border: Default::default(),
|
||||
// corner_radii: (2.0).into(),
|
||||
// })
|
||||
// })
|
||||
// .into_any()
|
||||
// })
|
||||
// .with_click_message("Read more about the new panel system")
|
||||
// .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST))
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
// .ok();
|
||||
|
||||
fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncAppContext) {
|
||||
const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user