Add an actual context menu into terminal-2

(click the text, not the pane!)

Co-Authored-By: Piotr <piotr@zed.dev>
This commit is contained in:
Kirill Bulatov 2023-11-16 15:13:04 +02:00
parent f638d4ce1d
commit ab0a3f19ab
12 changed files with 112 additions and 646 deletions

View file

@ -3,17 +3,29 @@ use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHea
pub enum ContextMenuItem {
Header(SharedString),
Entry(Label),
Entry(Label, Box<dyn gpui::Action>),
Separator,
}
impl Clone for ContextMenuItem {
fn clone(&self) -> Self {
match self {
ContextMenuItem::Header(name) => ContextMenuItem::Header(name.clone()),
ContextMenuItem::Entry(label, action) => {
ContextMenuItem::Entry(label.clone(), action.boxed_clone())
}
ContextMenuItem::Separator => ContextMenuItem::Separator,
}
}
}
impl ContextMenuItem {
fn to_list_item<V: 'static>(self) -> ListItem {
match self {
ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
ContextMenuItem::Entry(label) => {
ListEntry::new(label).variant(ListItemVariant::Inset).into()
}
ContextMenuItem::Entry(label, action) => ListEntry::new(label)
.variant(ListItemVariant::Inset)
.on_click(action)
.into(),
ContextMenuItem::Separator => ListSeparator::new().into(),
}
}
@ -26,12 +38,12 @@ impl ContextMenuItem {
Self::Separator
}
pub fn entry(label: Label) -> Self {
Self::Entry(label)
pub fn entry(label: Label, action: impl Action) -> Self {
Self::Entry(label, Box::new(action))
}
}
#[derive(Component)]
#[derive(Component, Clone)]
pub struct ContextMenu {
items: Vec<ContextMenuItem>,
}
@ -42,7 +54,12 @@ impl ContextMenu {
items: items.into_iter().collect(),
}
}
// todo!()
// cx.add_action(ContextMenu::select_first);
// cx.add_action(ContextMenu::select_last);
// cx.add_action(ContextMenu::select_next);
// cx.add_action(ContextMenu::select_prev);
// cx.add_action(ContextMenu::confirm);
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
v_stack()
.flex()
@ -55,9 +72,11 @@ impl ContextMenu {
.map(ContextMenuItem::to_list_item::<V>)
.collect(),
))
.on_mouse_down_out(|_, _, cx| cx.dispatch_action(Box::new(menu::Cancel)))
}
}
use gpui::Action;
#[cfg(feature = "stories")]
pub use stories::*;
@ -65,7 +84,7 @@ pub use stories::*;
mod stories {
use super::*;
use crate::story::Story;
use gpui::{Div, Render};
use gpui::{action, Div, Render};
pub struct ContextMenuStory;
@ -73,14 +92,22 @@ mod stories {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
#[action]
struct PrintCurrentDate {}
Story::container(cx)
.child(Story::title_for::<_, ContextMenu>(cx))
.child(Story::label(cx, "Default"))
.child(ContextMenu::new([
ContextMenuItem::header("Section header"),
ContextMenuItem::Separator,
ContextMenuItem::entry(Label::new("Some entry")),
ContextMenuItem::entry(Label::new("Print current time"), PrintCurrentDate {}),
]))
.on_action(|_, _: &PrintCurrentDate, _| {
if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
println!("Current Unix time is {:?}", unix_time.as_secs());
}
})
}
}
}

View file

@ -60,7 +60,7 @@ pub enum LineHeightStyle {
UILabel,
}
#[derive(Component)]
#[derive(Clone, Component)]
pub struct Label {
label: SharedString,
size: LabelSize,

View file

@ -1,11 +1,10 @@
use gpui::div;
use gpui::{div, Action};
use crate::prelude::*;
use crate::settings::user_settings;
use crate::{
disclosure_control, h_stack, v_stack, Avatar, GraphicSlot, Icon, IconElement, IconSize, Label,
TextColor, Toggle,
disclosure_control, h_stack, v_stack, Avatar, Icon, IconElement, IconSize, Label, Toggle,
};
use crate::{prelude::*, GraphicSlot};
#[derive(Clone, Copy, Default, Debug, PartialEq)]
pub enum ListItemVariant {
@ -232,6 +231,7 @@ pub struct ListEntry {
size: ListEntrySize,
toggle: Toggle,
variant: ListItemVariant,
on_click: Option<Box<dyn Action>>,
}
impl ListEntry {
@ -245,9 +245,15 @@ impl ListEntry {
size: ListEntrySize::default(),
toggle: Toggle::NotToggleable,
variant: ListItemVariant::default(),
on_click: Default::default(),
}
}
pub fn on_click(mut self, action: impl Into<Box<dyn Action>>) -> Self {
self.on_click = Some(action.into());
self
}
pub fn variant(mut self, variant: ListItemVariant) -> Self {
self.variant = variant;
self
@ -303,9 +309,21 @@ impl ListEntry {
ListEntrySize::Small => div().h_6(),
ListEntrySize::Medium => div().h_7(),
};
div()
.relative()
.hover(|mut style| {
style.background = Some(cx.theme().colors().editor_background.into());
style
})
.on_mouse_down(gpui::MouseButton::Left, {
let action = self.on_click.map(|action| action.boxed_clone());
move |entry: &mut V, event, cx| {
if let Some(action) = action.as_ref() {
cx.dispatch_action(action.boxed_clone());
}
}
})
.group("")
.bg(cx.theme().colors().surface_background)
// TODO: Add focus state
@ -401,7 +419,7 @@ impl List {
v_stack()
.w_full()
.py_1()
.children(self.header)
.children(self.header.map(|header| header))
.child(list_content)
}
}