Merge branch 'main' into search2

This commit is contained in:
Piotr Osiewicz 2023-11-16 17:16:15 +01:00
commit 6b6a30c3da
85 changed files with 15481 additions and 3134 deletions

View file

@ -61,7 +61,7 @@ impl ButtonVariant {
}
}
pub type ClickHandler<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) + Send + Sync>;
pub type ClickHandler<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>)>;
struct ButtonHandlers<V: 'static> {
click: Option<ClickHandler<V>>,

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

@ -25,6 +25,7 @@ pub enum Icon {
ChevronRight,
ChevronUp,
Close,
Collab,
Dash,
Exit,
ExclamationTriangle,
@ -85,6 +86,7 @@ impl Icon {
Icon::ChevronRight => "icons/chevron_right.svg",
Icon::ChevronUp => "icons/chevron_up.svg",
Icon::Close => "icons/x.svg",
Icon::Collab => "icons/user_group_16.svg",
Icon::Dash => "icons/dash.svg",
Icon::Exit => "icons/exit.svg",
Icon::ExclamationTriangle => "icons/warning.svg",

View file

@ -1,5 +1,5 @@
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement, TextTooltip};
use gpui::{prelude::*, MouseButton, VisualContext};
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement};
use gpui::{prelude::*, AnyView, MouseButton};
use std::sync::Arc;
struct IconButtonHandlers<V: 'static> {
@ -19,7 +19,7 @@ pub struct IconButton<V: 'static> {
color: TextColor,
variant: ButtonVariant,
state: InteractionState,
tooltip: Option<SharedString>,
tooltip: Option<Box<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>>,
handlers: IconButtonHandlers<V>,
}
@ -56,22 +56,23 @@ impl<V: 'static> IconButton<V> {
self
}
pub fn tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
self.tooltip = Some(tooltip.into());
pub fn tooltip(
mut self,
tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static,
) -> Self {
self.tooltip = Some(Box::new(tooltip));
self
}
pub fn on_click(
mut self,
handler: impl 'static + Fn(&mut V, &mut ViewContext<V>) + Send + Sync,
) -> Self {
pub fn on_click(mut self, handler: impl 'static + Fn(&mut V, &mut ViewContext<V>)) -> Self {
self.handlers.click = Some(Arc::new(handler));
self
}
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
fn render(mut self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
let icon_color = match (self.state, self.color) {
(InteractionState::Disabled, _) => TextColor::Disabled,
(InteractionState::Active, _) => TextColor::Error,
_ => self.color,
};
@ -99,15 +100,16 @@ impl<V: 'static> IconButton<V> {
.child(IconElement::new(self.icon).color(icon_color));
if let Some(click_handler) = self.handlers.click.clone() {
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
cx.stop_propagation();
click_handler(state, cx);
});
button = button
.on_mouse_down(MouseButton::Left, move |state, event, cx| {
cx.stop_propagation();
click_handler(state, cx);
})
.cursor_pointer();
}
if let Some(tooltip) = self.tooltip.clone() {
button =
button.tooltip(move |_, cx| cx.build_view(|cx| TextTooltip::new(tooltip.clone())));
if let Some(tooltip) = self.tooltip.take() {
button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
}
button

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)
}
}

View file

@ -1,17 +1,53 @@
use gpui::{Div, Render};
use gpui::{overlay, Action, AnyView, Overlay, Render, VisualContext};
use settings2::Settings;
use theme2::{ActiveTheme, ThemeSettings};
use crate::prelude::*;
use crate::{h_stack, v_stack, KeyBinding, Label, LabelSize, StyledExt, TextColor};
pub struct TextTooltip {
pub struct Tooltip {
title: SharedString,
meta: Option<SharedString>,
key_binding: Option<KeyBinding>,
}
impl TextTooltip {
impl Tooltip {
pub fn text(title: impl Into<SharedString>, cx: &mut WindowContext) -> AnyView {
cx.build_view(|cx| Self {
title: title.into(),
meta: None,
key_binding: None,
})
.into()
}
pub fn for_action(
title: impl Into<SharedString>,
action: &dyn Action,
cx: &mut WindowContext,
) -> AnyView {
cx.build_view(|cx| Self {
title: title.into(),
meta: None,
key_binding: KeyBinding::for_action(action, cx),
})
.into()
}
pub fn with_meta(
title: impl Into<SharedString>,
action: Option<&dyn Action>,
meta: impl Into<SharedString>,
cx: &mut WindowContext,
) -> AnyView {
cx.build_view(|cx| Self {
title: title.into(),
meta: Some(meta.into()),
key_binding: action.and_then(|action| KeyBinding::for_action(action, cx)),
})
.into()
}
pub fn new(title: impl Into<SharedString>) -> Self {
Self {
title: title.into(),
@ -31,31 +67,36 @@ impl TextTooltip {
}
}
impl Render for TextTooltip {
type Element = Div<Self>;
impl Render for Tooltip {
type Element = Overlay<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
v_stack()
.elevation_2(cx)
.font(ui_font)
.text_ui_sm()
.text_color(cx.theme().colors().text)
.py_1()
.px_2()
.child(
h_stack()
.child(self.title.clone())
.when_some(self.key_binding.clone(), |this, key_binding| {
this.justify_between().child(key_binding)
overlay().child(
// padding to avoid mouse cursor
div().pl_2().pt_2p5().child(
v_stack()
.elevation_2(cx)
.font(ui_font)
.text_ui_sm()
.text_color(cx.theme().colors().text)
.py_1()
.px_2()
.child(
h_stack()
.child(self.title.clone())
.when_some(self.key_binding.clone(), |this, key_binding| {
this.justify_between().child(key_binding)
}),
)
.when_some(self.meta.clone(), |this, meta| {
this.child(
Label::new(meta)
.size(LabelSize::Small)
.color(TextColor::Muted),
)
}),
)
.when_some(self.meta.clone(), |this, meta| {
this.child(
Label::new(meta)
.size(LabelSize::Small)
.color(TextColor::Muted),
)
})
),
)
}
}