crates/auto_update2/src/update_notification.rs 🔗
@@ -51,6 +51,6 @@ impl UpdateNotification {
}
pub fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
- cx.emit(DismissEvent::Dismiss);
+ cx.emit(DismissEvent);
}
}
Max Brunsfeld created
This PR adds a context menu to the project panel in Zed2.
* [x] Allow the context menu to extend outside of the project panel's
bounds
* [x] Add keyboard shortcuts to the context menu
* [x] Dismiss the context menu
* [x] when running an action
* [x] when changing selection in the project panel
Release Notes:
NA
crates/auto_update2/src/update_notification.rs | 2
crates/collab_ui2/src/collab_panel/contact_finder.rs | 2
crates/command_palette2/src/command_palette.rs | 2
crates/file_finder2/src/file_finder.rs | 6
crates/go_to_line2/src/go_to_line.rs | 6
crates/gpui2/src/app/async_context.rs | 5
crates/gpui2/src/app/test_context.rs | 2
crates/gpui2/src/window.rs | 20
crates/project_panel2/src/project_panel.rs | 181 +++++++-----
crates/terminal_view2/src/terminal_view.rs | 9
crates/theme_selector2/src/theme_selector.rs | 2
crates/ui2/src/components/context_menu.rs | 187 ++++++++++---
crates/ui2/src/components/list.rs | 12
crates/ui2/src/components/stories/context_menu.rs | 4
crates/welcome2/src/base_keymap_picker.rs | 2
crates/workspace2/src/dock.rs | 2
crates/workspace2/src/notifications.rs | 8
17 files changed, 277 insertions(+), 175 deletions(-)
@@ -51,6 +51,6 @@ impl UpdateNotification {
}
pub fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
- cx.emit(DismissEvent::Dismiss);
+ cx.emit(DismissEvent);
}
}
@@ -161,7 +161,7 @@ impl PickerDelegate for ContactFinderDelegate {
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
//cx.emit(PickerEvent::Dismiss);
self.parent
- .update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
+ .update(cx, |_, cx| cx.emit(DismissEvent))
.log_err();
}
@@ -269,7 +269,7 @@ impl PickerDelegate for CommandPaletteDelegate {
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
self.command_palette
- .update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
+ .update(cx, |_, cx| cx.emit(DismissEvent))
.log_err();
}
@@ -687,9 +687,7 @@ impl PickerDelegate for FileFinderDelegate {
.log_err();
}
}
- finder
- .update(&mut cx, |_, cx| cx.emit(DismissEvent::Dismiss))
- .ok()?;
+ finder.update(&mut cx, |_, cx| cx.emit(DismissEvent)).ok()?;
Some(())
})
@@ -700,7 +698,7 @@ impl PickerDelegate for FileFinderDelegate {
fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
self.file_finder
- .update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
+ .update(cx, |_, cx| cx.emit(DismissEvent))
.log_err();
}
@@ -90,7 +90,7 @@ impl GoToLine {
) {
match event {
// todo!() this isn't working...
- editor::EditorEvent::Blurred => cx.emit(DismissEvent::Dismiss),
+ editor::EditorEvent::Blurred => cx.emit(DismissEvent),
editor::EditorEvent::BufferEdited { .. } => self.highlight_current_line(cx),
_ => {}
}
@@ -125,7 +125,7 @@ impl GoToLine {
}
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
- cx.emit(DismissEvent::Dismiss);
+ cx.emit(DismissEvent);
}
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
@@ -142,7 +142,7 @@ impl GoToLine {
self.prev_scroll_position.take();
}
- cx.emit(DismissEvent::Dismiss);
+ cx.emit(DismissEvent);
}
}
@@ -325,8 +325,7 @@ impl VisualContext for AsyncWindowContext {
where
V: crate::ManagedView,
{
- self.window.update(self, |_, cx| {
- view.update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
- })
+ self.window
+ .update(self, |_, cx| view.update(cx, |_, cx| cx.emit(DismissEvent)))
}
}
@@ -611,7 +611,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
{
self.window
.update(self.cx, |_, cx| {
- view.update(cx, |_, cx| cx.emit(crate::DismissEvent::Dismiss))
+ view.update(cx, |_, cx| cx.emit(crate::DismissEvent))
})
.unwrap()
}
@@ -197,9 +197,7 @@ pub trait ManagedView: FocusableView + EventEmitter<DismissEvent> {}
impl<M: FocusableView + EventEmitter<DismissEvent>> ManagedView for M {}
-pub enum DismissEvent {
- Dismiss,
-}
+pub struct DismissEvent;
// Holds the state for a specific window.
pub struct Window {
@@ -1482,13 +1480,15 @@ impl<'a> WindowContext<'a> {
}
}
- pub fn constructor_for<V: Render, R>(
+ pub fn handler_for<V: Render>(
&self,
view: &View<V>,
- f: impl Fn(&mut V, &mut ViewContext<V>) -> R + 'static,
- ) -> impl Fn(&mut WindowContext) -> R + 'static {
- let view = view.clone();
- move |cx: &mut WindowContext| view.update(cx, |view, cx| f(view, cx))
+ f: impl Fn(&mut V, &mut ViewContext<V>) + 'static,
+ ) -> impl Fn(&mut WindowContext) {
+ let view = view.downgrade();
+ move |cx: &mut WindowContext| {
+ view.update(cx, |view, cx| f(view, cx)).ok();
+ }
}
//========== ELEMENT RELATED FUNCTIONS ===========
@@ -1699,7 +1699,7 @@ impl VisualContext for WindowContext<'_> {
where
V: ManagedView,
{
- self.update_view(view, |_, cx| cx.emit(DismissEvent::Dismiss))
+ self.update_view(view, |_, cx| cx.emit(DismissEvent))
}
}
@@ -2386,7 +2386,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
where
V: ManagedView,
{
- self.defer(|_, cx| cx.emit(DismissEvent::Dismiss))
+ self.defer(|_, cx| cx.emit(DismissEvent))
}
pub fn listener<E>(
@@ -8,10 +8,11 @@ use file_associations::FileAssociations;
use anyhow::{anyhow, Result};
use gpui::{
- actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
- ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement,
- Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, Stateful, Styled,
- Task, UniformListScrollHandle, View, ViewContext, VisualContext as _, WeakView, WindowContext,
+ actions, div, overlay, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
+ ClipboardItem, DismissEvent, Div, EventEmitter, FocusHandle, Focusable, FocusableView,
+ InteractiveElement, Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point,
+ PromptLevel, Render, Stateful, Styled, Subscription, Task, UniformListScrollHandle, View,
+ ViewContext, VisualContext as _, WeakView, WindowContext,
};
use menu::{Confirm, SelectNext, SelectPrev};
use project::{
@@ -29,7 +30,7 @@ use std::{
sync::Arc,
};
use theme::ActiveTheme as _;
-use ui::{v_stack, IconElement, Label, ListItem};
+use ui::{v_stack, ContextMenu, IconElement, Label, ListItem};
use unicase::UniCase;
use util::{maybe, ResultExt, TryFutureExt};
use workspace::{
@@ -49,6 +50,7 @@ pub struct ProjectPanel {
last_worktree_root_id: Option<ProjectEntryId>,
expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
selection: Option<Selection>,
+ context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
edit_state: Option<EditState>,
filename_editor: View<Editor>,
clipboard_entry: Option<ClipboardEntry>,
@@ -231,6 +233,7 @@ impl ProjectPanel {
expanded_dir_ids: Default::default(),
selection: None,
edit_state: None,
+ context_menu: None,
filename_editor,
clipboard_entry: None,
// context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)),
@@ -366,80 +369,93 @@ impl ProjectPanel {
fn deploy_context_menu(
&mut self,
- _position: Point<Pixels>,
- _entry_id: ProjectEntryId,
- _cx: &mut ViewContext<Self>,
+ position: Point<Pixels>,
+ entry_id: ProjectEntryId,
+ cx: &mut ViewContext<Self>,
) {
- // todo!()
- // let project = self.project.read(cx);
+ let this = cx.view().clone();
+ let project = self.project.read(cx);
- // let worktree_id = if let Some(id) = project.worktree_id_for_entry(entry_id, cx) {
- // id
- // } else {
- // return;
- // };
+ let worktree_id = if let Some(id) = project.worktree_id_for_entry(entry_id, cx) {
+ id
+ } else {
+ return;
+ };
- // self.selection = Some(Selection {
- // worktree_id,
- // entry_id,
- // });
-
- // let mut menu_entries = Vec::new();
- // if let Some((worktree, entry)) = self.selected_entry(cx) {
- // let is_root = Some(entry) == worktree.root_entry();
- // if !project.is_remote() {
- // menu_entries.push(ContextMenuItem::action(
- // "Add Folder to Project",
- // workspace::AddFolderToProject,
- // ));
- // if is_root {
- // let project = self.project.clone();
- // menu_entries.push(ContextMenuItem::handler("Remove from Project", move |cx| {
- // project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
- // }));
- // }
- // }
- // menu_entries.push(ContextMenuItem::action("New File", NewFile));
- // menu_entries.push(ContextMenuItem::action("New Folder", NewDirectory));
- // menu_entries.push(ContextMenuItem::Separator);
- // menu_entries.push(ContextMenuItem::action("Cut", Cut));
- // menu_entries.push(ContextMenuItem::action("Copy", Copy));
- // if let Some(clipboard_entry) = self.clipboard_entry {
- // if clipboard_entry.worktree_id() == worktree.id() {
- // menu_entries.push(ContextMenuItem::action("Paste", Paste));
- // }
- // }
- // menu_entries.push(ContextMenuItem::Separator);
- // menu_entries.push(ContextMenuItem::action("Copy Path", CopyPath));
- // menu_entries.push(ContextMenuItem::action(
- // "Copy Relative Path",
- // CopyRelativePath,
- // ));
-
- // if entry.is_dir() {
- // menu_entries.push(ContextMenuItem::Separator);
- // }
- // menu_entries.push(ContextMenuItem::action("Reveal in Finder", RevealInFinder));
- // if entry.is_dir() {
- // menu_entries.push(ContextMenuItem::action("Open in Terminal", OpenInTerminal));
- // menu_entries.push(ContextMenuItem::action(
- // "Search Inside",
- // NewSearchInDirectory,
- // ));
- // }
-
- // menu_entries.push(ContextMenuItem::Separator);
- // menu_entries.push(ContextMenuItem::action("Rename", Rename));
- // if !is_root {
- // menu_entries.push(ContextMenuItem::action("Delete", Delete));
- // }
- // }
-
- // // self.context_menu.update(cx, |menu, cx| {
- // // menu.show(position, AnchorCorner::TopLeft, menu_entries, cx);
- // // });
-
- // cx.notify();
+ self.selection = Some(Selection {
+ worktree_id,
+ entry_id,
+ });
+
+ if let Some((worktree, entry)) = self.selected_entry(cx) {
+ let is_root = Some(entry) == worktree.root_entry();
+ let is_dir = entry.is_dir();
+ let worktree_id = worktree.id();
+ let is_local = project.is_local();
+
+ let context_menu = ContextMenu::build(cx, |mut menu, cx| {
+ if is_local {
+ menu = menu.action(
+ "Add Folder to Project",
+ Box::new(workspace::AddFolderToProject),
+ cx,
+ );
+ if is_root {
+ menu = menu.entry(
+ "Remove from Project",
+ cx.handler_for(&this, move |this, cx| {
+ this.project.update(cx, |project, cx| {
+ project.remove_worktree(worktree_id, cx)
+ });
+ }),
+ );
+ }
+ }
+
+ menu = menu
+ .action("New File", Box::new(NewFile), cx)
+ .action("New Folder", Box::new(NewDirectory), cx)
+ .separator()
+ .action("Cut", Box::new(Cut), cx)
+ .action("Copy", Box::new(Copy), cx);
+
+ if let Some(clipboard_entry) = self.clipboard_entry {
+ if clipboard_entry.worktree_id() == worktree_id {
+ menu = menu.action("Paste", Box::new(Paste), cx);
+ }
+ }
+
+ menu = menu
+ .separator()
+ .action("Copy Path", Box::new(CopyPath), cx)
+ .action("Copy Relative Path", Box::new(CopyRelativePath), cx)
+ .separator()
+ .action("Reveal in Finder", Box::new(RevealInFinder), cx);
+
+ if is_dir {
+ menu = menu
+ .action("Open in Terminal", Box::new(OpenInTerminal), cx)
+ .action("Search Inside", Box::new(NewSearchInDirectory), cx)
+ }
+
+ menu = menu.separator().action("Rename", Box::new(Rename), cx);
+
+ if !is_root {
+ menu = menu.action("Delete", Box::new(Delete), cx);
+ }
+
+ menu
+ });
+
+ cx.focus_view(&context_menu);
+ let subscription = cx.subscribe(&context_menu, |this, _, _: &DismissEvent, cx| {
+ this.context_menu.take();
+ cx.notify();
+ });
+ self.context_menu = Some((context_menu, position, subscription));
+ }
+
+ cx.notify();
}
fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) {
@@ -643,7 +659,6 @@ impl ProjectPanel {
}
fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
- dbg!("odd");
self.edit_state = None;
self.update_visible_entries(None, cx);
cx.focus(&self.focus_handle);
@@ -1370,7 +1385,7 @@ impl ProjectPanel {
})
.child(
if let (Some(editor), true) = (Some(&self.filename_editor), show_editor) {
- div().w_full().child(editor.clone())
+ div().h_full().w_full().child(editor.clone())
} else {
div()
.text_color(filename_text_color)
@@ -1379,6 +1394,9 @@ impl ProjectPanel {
.ml_1(),
)
.on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
+ if event.down.button == MouseButton::Right {
+ return;
+ }
if !show_editor {
if kind.is_dir() {
this.toggle_expanded(entry_id, cx);
@@ -1415,6 +1433,7 @@ impl Render for ProjectPanel {
div()
.id("project-panel")
.size_full()
+ .relative()
.key_context("ProjectPanel")
.on_action(cx.listener(Self::select_next))
.on_action(cx.listener(Self::select_prev))
@@ -1458,6 +1477,12 @@ impl Render for ProjectPanel {
.size_full()
.track_scroll(self.list.clone()),
)
+ .children(self.context_menu.as_ref().map(|(menu, position, _)| {
+ overlay()
+ .position(*position)
+ .anchor(gpui::AnchorCorner::BottomLeft)
+ .child(menu.clone())
+ }))
} else {
v_stack()
.id("empty-project_panel")
@@ -298,9 +298,12 @@ impl TerminalView {
position: gpui::Point<Pixels>,
cx: &mut ViewContext<Self>,
) {
- self.context_menu = Some(ContextMenu::build(cx, |menu, _| {
- menu.action("Clear", Box::new(Clear))
- .action("Close", Box::new(CloseActiveItem { save_intent: None }))
+ self.context_menu = Some(ContextMenu::build(cx, |menu, cx| {
+ menu.action("Clear", Box::new(Clear), cx).action(
+ "Close",
+ Box::new(CloseActiveItem { save_intent: None }),
+ cx,
+ )
}));
dbg!(&position);
// todo!()
@@ -177,7 +177,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
self.view
.update(cx, |_, cx| {
- cx.emit(DismissEvent::Dismiss);
+ cx.emit(DismissEvent);
})
.ok();
}
@@ -1,23 +1,28 @@
-use std::cell::RefCell;
-use std::rc::Rc;
-
-use crate::{prelude::*, v_stack, Label, List};
-use crate::{ListItem, ListSeparator, ListSubHeader};
+use crate::{
+ h_stack, prelude::*, v_stack, KeyBinding, Label, List, ListItem, ListSeparator, ListSubHeader,
+};
use gpui::{
- overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DismissEvent,
- DispatchPhase, Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId,
- ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View, VisualContext,
+ overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, DismissEvent, DispatchPhase,
+ Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId, ManagedView, MouseButton,
+ MouseDownEvent, Pixels, Point, Render, View, VisualContext,
};
+use menu::{SelectFirst, SelectLast, SelectNext, SelectPrev};
+use std::{cell::RefCell, rc::Rc};
pub enum ContextMenuItem {
Separator,
Header(SharedString),
- Entry(SharedString, Rc<dyn Fn(&ClickEvent, &mut WindowContext)>),
+ Entry {
+ label: SharedString,
+ handler: Rc<dyn Fn(&mut WindowContext)>,
+ key_binding: Option<KeyBinding>,
+ },
}
pub struct ContextMenu {
items: Vec<ContextMenuItem>,
focus_handle: FocusHandle,
+ selected_index: Option<usize>,
}
impl FocusableView for ContextMenu {
@@ -39,6 +44,7 @@ impl ContextMenu {
Self {
items: Default::default(),
focus_handle: cx.focus_handle(),
+ selected_index: None,
},
cx,
)
@@ -58,27 +64,90 @@ impl ContextMenu {
pub fn entry(
mut self,
label: impl Into<SharedString>,
- on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+ on_click: impl Fn(&mut WindowContext) + 'static,
) -> Self {
- self.items
- .push(ContextMenuItem::Entry(label.into(), Rc::new(on_click)));
+ self.items.push(ContextMenuItem::Entry {
+ label: label.into(),
+ handler: Rc::new(on_click),
+ key_binding: None,
+ });
self
}
- pub fn action(self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self {
- // todo: add the keybindings to the list entry
- self.entry(label.into(), move |_, cx| {
- cx.dispatch_action(action.boxed_clone())
- })
+ pub fn action(
+ mut self,
+ label: impl Into<SharedString>,
+ action: Box<dyn Action>,
+ cx: &mut WindowContext,
+ ) -> Self {
+ self.items.push(ContextMenuItem::Entry {
+ label: label.into(),
+ key_binding: KeyBinding::for_action(&*action, cx),
+ handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())),
+ });
+ self
}
pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
- // todo!()
- cx.emit(DismissEvent::Dismiss);
+ if let Some(ContextMenuItem::Entry { handler, .. }) =
+ self.selected_index.and_then(|ix| self.items.get(ix))
+ {
+ (handler)(cx)
+ }
+ cx.emit(DismissEvent);
}
pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
- cx.emit(DismissEvent::Dismiss);
+ cx.emit(DismissEvent);
+ }
+
+ fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
+ self.selected_index = self.items.iter().position(|item| item.is_selectable());
+ cx.notify();
+ }
+
+ fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
+ for (ix, item) in self.items.iter().enumerate().rev() {
+ if item.is_selectable() {
+ self.selected_index = Some(ix);
+ cx.notify();
+ break;
+ }
+ }
+ }
+
+ fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
+ if let Some(ix) = self.selected_index {
+ for (ix, item) in self.items.iter().enumerate().skip(ix + 1) {
+ if item.is_selectable() {
+ self.selected_index = Some(ix);
+ cx.notify();
+ break;
+ }
+ }
+ } else {
+ self.select_first(&Default::default(), cx);
+ }
+ }
+
+ pub fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
+ if let Some(ix) = self.selected_index {
+ for (ix, item) in self.items.iter().enumerate().take(ix).rev() {
+ if item.is_selectable() {
+ self.selected_index = Some(ix);
+ cx.notify();
+ break;
+ }
+ }
+ } else {
+ self.select_last(&Default::default(), cx);
+ }
+ }
+}
+
+impl ContextMenuItem {
+ fn is_selectable(&self) -> bool {
+ matches!(self, Self::Entry { .. })
}
}
@@ -90,38 +159,51 @@ impl Render for ContextMenu {
v_stack()
.min_w(px(200.))
.track_focus(&self.focus_handle)
- .on_mouse_down_out(
- cx.listener(|this: &mut Self, _, cx| this.cancel(&Default::default(), cx)),
- )
- // .on_action(ContextMenu::select_first)
- // .on_action(ContextMenu::select_last)
- // .on_action(ContextMenu::select_next)
- // .on_action(ContextMenu::select_prev)
+ .on_mouse_down_out(cx.listener(|this, _, cx| this.cancel(&Default::default(), cx)))
+ .key_context("menu")
+ .on_action(cx.listener(ContextMenu::select_first))
+ .on_action(cx.listener(ContextMenu::select_last))
+ .on_action(cx.listener(ContextMenu::select_next))
+ .on_action(cx.listener(ContextMenu::select_prev))
.on_action(cx.listener(ContextMenu::confirm))
.on_action(cx.listener(ContextMenu::cancel))
.flex_none()
- // .bg(cx.theme().colors().elevated_surface_background)
- // .border()
- // .border_color(cx.theme().colors().border)
.child(
- List::new().children(self.items.iter().map(|item| match item {
- ContextMenuItem::Separator => ListSeparator::new().into_any_element(),
- ContextMenuItem::Header(header) => {
- ListSubHeader::new(header.clone()).into_any_element()
- }
- ContextMenuItem::Entry(entry, callback) => {
- let callback = callback.clone();
- let dismiss = cx.listener(|_, _, cx| cx.emit(DismissEvent::Dismiss));
-
- ListItem::new(entry.clone())
- .child(Label::new(entry.clone()))
- .on_click(move |event, cx| {
- callback(event, cx);
- dismiss(event, cx)
- })
- .into_any_element()
- }
- })),
+ List::new().children(self.items.iter().enumerate().map(
+ |(ix, item)| match item {
+ ContextMenuItem::Separator => ListSeparator::new().into_any_element(),
+ ContextMenuItem::Header(header) => {
+ ListSubHeader::new(header.clone()).into_any_element()
+ }
+ ContextMenuItem::Entry {
+ label: entry,
+ handler: callback,
+ key_binding,
+ } => {
+ let callback = callback.clone();
+ let dismiss = cx.listener(|_, _, cx| cx.emit(DismissEvent));
+
+ ListItem::new(entry.clone())
+ .child(
+ h_stack()
+ .w_full()
+ .justify_between()
+ .child(Label::new(entry.clone()))
+ .children(
+ key_binding
+ .clone()
+ .map(|binding| div().ml_1().child(binding)),
+ ),
+ )
+ .selected(Some(ix) == self.selected_index)
+ .on_click(move |event, cx| {
+ callback(cx);
+ dismiss(event, cx)
+ })
+ .into_any_element()
+ }
+ },
+ )),
),
)
}
@@ -177,6 +259,7 @@ pub struct MenuHandleState<M> {
child_element: Option<AnyElement>,
menu_element: Option<AnyElement>,
}
+
impl<M: ManagedView> Element for MenuHandle<M> {
type State = MenuHandleState<M>;
@@ -264,11 +347,9 @@ impl<M: ManagedView> Element for MenuHandle<M> {
let new_menu = (builder)(cx);
let menu2 = menu.clone();
- cx.subscribe(&new_menu, move |_modal, e, cx| match e {
- &DismissEvent::Dismiss => {
- *menu2.borrow_mut() = None;
- cx.notify();
- }
+ cx.subscribe(&new_menu, move |_modal, _: &DismissEvent, cx| {
+ *menu2.borrow_mut() = None;
+ cx.notify();
})
.detach();
cx.focus_view(&new_menu);
@@ -374,17 +374,15 @@ impl RenderOnce for List {
type Rendered = Div;
fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
- let list_content = match (self.children.is_empty(), self.toggle) {
- (false, _) => div().children(self.children),
- (true, Toggle::Toggled(false)) => div(),
- (true, _) => div().child(Label::new(self.empty_message.clone()).color(Color::Muted)),
- };
-
v_stack()
.w_full()
.py_1()
.children(self.header.map(|header| header))
- .child(list_content)
+ .map(|this| match (self.children.is_empty(), self.toggle) {
+ (false, _) => this.children(self.children),
+ (true, Toggle::Toggled(false)) => this,
+ (true, _) => this.child(Label::new(self.empty_message.clone()).color(Color::Muted)),
+ })
}
}
@@ -10,11 +10,11 @@ fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<C
ContextMenu::build(cx, |menu, _| {
menu.header(header)
.separator()
- .entry("Print current time", |_event, cx| {
+ .entry("Print current time", |cx| {
println!("dispatching PrintCurrentTime action");
cx.dispatch_action(PrintCurrentDate.boxed_clone())
})
- .entry("Print best foot", |_event, cx| {
+ .entry("Print best foot", |cx| {
cx.dispatch_action(PrintBestFood.boxed_clone())
})
})
@@ -180,7 +180,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
self.view
.update(cx, |_, cx| {
- cx.emit(DismissEvent::Dismiss);
+ cx.emit(DismissEvent);
})
.ok();
}
@@ -721,7 +721,7 @@ impl Render for PanelButtons {
&& panel.position_is_valid(position, cx)
{
let panel = panel.clone();
- menu = menu.entry(position.to_label(), move |_, cx| {
+ menu = menu.entry(position.to_label(), move |cx| {
panel.set_position(position, cx);
})
}
@@ -106,10 +106,8 @@ impl Workspace {
let notification = build_notification(cx);
cx.subscribe(
¬ification,
- move |this, handle, event: &DismissEvent, cx| match event {
- DismissEvent::Dismiss => {
- this.dismiss_notification_internal(type_id, id, cx);
- }
+ move |this, handle, event: &DismissEvent, cx| {
+ this.dismiss_notification_internal(type_id, id, cx);
},
)
.detach();
@@ -260,7 +258,7 @@ pub mod simple_message_notification {
}
pub fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
- cx.emit(DismissEvent::Dismiss);
+ cx.emit(DismissEvent);
}
}