From e0cb95b3344439527ff2610b18ba5b9dfb8353dd Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 00:51:47 -0400 Subject: [PATCH 01/30] Add From<&str> for Hsla --- crates/gpui2/src/color.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/gpui2/src/color.rs b/crates/gpui2/src/color.rs index db07259476..44b084e65f 100644 --- a/crates/gpui2/src/color.rs +++ b/crates/gpui2/src/color.rs @@ -233,6 +233,12 @@ impl Hsla { } } +impl From<&str> for Hsla { + fn from(s: &str) -> Self { + Rgba::try_from(s).unwrap().into() + } +} + // impl From for Rgba { // fn from(value: Hsla) -> Self { // let h = value.h; From 0efd69c60fe18f2c74669cbebe3cc07ea4744cdb Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 00:51:57 -0400 Subject: [PATCH 02/30] Refine default colors --- crates/theme2/src/default_colors.rs | 203 ++++++++++++++++------------ crates/theme2/src/theme2.rs | 12 ++ crates/ui2/src/elements/label.rs | 8 +- 3 files changed, 131 insertions(+), 92 deletions(-) diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 5ef93d036f..8b7a7ac5cf 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -1,4 +1,4 @@ -use gpui2::{hsla, Rgba}; +use gpui2::{hsla, Hsla, Rgba}; use crate::{ colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors}, @@ -6,6 +6,10 @@ use crate::{ syntax::SyntaxTheme, }; +fn neutral() -> DefaultColorScaleSet { + slate() +} + impl Default for SystemColors { fn default() -> Self { Self { @@ -20,17 +24,17 @@ impl Default for SystemColors { impl Default for StatusColors { fn default() -> Self { Self { - conflict: gpui2::black(), - created: gpui2::black(), - deleted: gpui2::black(), - error: gpui2::black(), - hidden: gpui2::black(), - ignored: gpui2::black(), - info: gpui2::black(), - modified: gpui2::black(), - renamed: gpui2::black(), - success: gpui2::black(), - warning: gpui2::black(), + conflict: red().dark(11).into(), + created: green().dark(11).into(), + deleted: red().dark(11).into(), + error: red().dark(11).into(), + hidden: neutral().dark(11).into(), + ignored: neutral().dark(11).into(), + info: blue().dark(11).into(), + modified: yellow().dark(11).into(), + renamed: blue().dark(11).into(), + success: green().dark(11).into(), + warning: yellow().dark(11).into(), } } } @@ -38,12 +42,12 @@ impl Default for StatusColors { impl Default for GitStatusColors { fn default() -> Self { Self { - conflict: gpui2::rgba(0xdec184ff).into(), - created: gpui2::rgba(0xa1c181ff).into(), - deleted: gpui2::rgba(0xd07277ff).into(), - ignored: gpui2::rgba(0x555a63ff).into(), - modified: gpui2::rgba(0x74ade8ff).into(), - renamed: gpui2::rgba(0xdec184ff).into(), + conflict: orange().dark(11), + created: green().dark(11), + deleted: red().dark(11), + ignored: green().dark(11), + modified: yellow().dark(11), + renamed: blue().dark(11), } } } @@ -189,82 +193,86 @@ impl SyntaxTheme { impl ThemeColors { pub fn default_light() -> Self { + let system = SystemColors::default(); + Self { - border: gpui2::white(), - border_variant: gpui2::white(), - border_focused: gpui2::white(), - border_transparent: gpui2::white(), - elevated_surface: gpui2::white(), - surface: gpui2::white(), - background: gpui2::white(), - element: gpui2::white(), - element_hover: gpui2::white(), - element_active: gpui2::white(), - element_selected: gpui2::white(), - element_disabled: gpui2::white(), - element_placeholder: gpui2::white(), - ghost_element: gpui2::white(), - ghost_element_hover: gpui2::white(), - ghost_element_active: gpui2::white(), - ghost_element_selected: gpui2::white(), - ghost_element_disabled: gpui2::white(), - text: gpui2::white(), - text_muted: gpui2::white(), - text_placeholder: gpui2::white(), - text_disabled: gpui2::white(), - text_accent: gpui2::white(), - icon: gpui2::white(), - icon_muted: gpui2::white(), - icon_disabled: gpui2::white(), - icon_placeholder: gpui2::white(), - icon_accent: gpui2::white(), - status_bar: gpui2::white(), - title_bar: gpui2::white(), - toolbar: gpui2::white(), - tab_bar: gpui2::white(), - editor: gpui2::white(), - editor_subheader: gpui2::white(), - editor_active_line: gpui2::white(), + border: neutral().light(6).into(), + border_variant: neutral().light(5).into(), + border_focused: blue().light(5).into(), + border_transparent: system.transparent, + elevated_surface: neutral().light(2).into(), + surface: neutral().light(2).into(), + background: neutral().light(1).into(), + element: neutral().light(3).into(), + element_hover: neutral().light(4).into(), + element_active: neutral().light(5).into(), + element_selected: neutral().light(5).into(), + element_disabled: neutral().light_alpha(3).into(), + element_placeholder: neutral().light(11).into(), + ghost_element: system.transparent, + ghost_element_hover: neutral().light(4).into(), + ghost_element_active: neutral().light(5).into(), + ghost_element_selected: neutral().light(5).into(), + ghost_element_disabled: neutral().light_alpha(3).into(), + text: neutral().light(12).into(), + text_muted: neutral().light(11).into(), + text_placeholder: neutral().light(11).into(), + text_disabled: neutral().light(10).into(), + text_accent: blue().light(12).into(), + icon: neutral().light(12).into(), + icon_muted: neutral().light(11).into(), + icon_disabled: neutral().light(10).into(), + icon_placeholder: neutral().light(11).into(), + icon_accent: blue().light(12).into(), + status_bar: neutral().light(2).into(), + title_bar: neutral().light(2).into(), + toolbar: neutral().light(2).into(), + tab_bar: neutral().light(2).into(), + editor: neutral().light(1).into(), + editor_subheader: neutral().light(2).into(), + editor_active_line: neutral().light_alpha(3).into(), } } pub fn default_dark() -> Self { + let system = SystemColors::default(); + Self { - border: gpui2::rgba(0x464b57ff).into(), - border_variant: gpui2::rgba(0x464b57ff).into(), - border_focused: gpui2::rgba(0x293b5bff).into(), - border_transparent: gpui2::rgba(0x00000000).into(), - elevated_surface: gpui2::rgba(0x3b414dff).into(), - surface: gpui2::rgba(0x2f343eff).into(), - background: gpui2::rgba(0x3b414dff).into(), - element: gpui2::rgba(0x3b414dff).into(), - element_hover: gpui2::rgba(0xffffff1e).into(), - element_active: gpui2::rgba(0xffffff28).into(), - element_selected: gpui2::rgba(0x18243dff).into(), - element_disabled: gpui2::rgba(0x00000000).into(), - element_placeholder: gpui2::black(), - ghost_element: gpui2::rgba(0x00000000).into(), - ghost_element_hover: gpui2::rgba(0xffffff14).into(), - ghost_element_active: gpui2::rgba(0xffffff1e).into(), - ghost_element_selected: gpui2::rgba(0x18243dff).into(), - ghost_element_disabled: gpui2::rgba(0x00000000).into(), - text: gpui2::rgba(0xc8ccd4ff).into(), - text_muted: gpui2::rgba(0x838994ff).into(), - text_placeholder: gpui2::rgba(0xd07277ff).into(), - text_disabled: gpui2::rgba(0x555a63ff).into(), - text_accent: gpui2::rgba(0x74ade8ff).into(), - icon: gpui2::black(), - icon_muted: gpui2::rgba(0x838994ff).into(), - icon_disabled: gpui2::black(), - icon_placeholder: gpui2::black(), - icon_accent: gpui2::black(), - status_bar: gpui2::rgba(0x3b414dff).into(), - title_bar: gpui2::rgba(0x3b414dff).into(), - toolbar: gpui2::rgba(0x282c33ff).into(), - tab_bar: gpui2::rgba(0x2f343eff).into(), - editor: gpui2::rgba(0x282c33ff).into(), - editor_subheader: gpui2::rgba(0x2f343eff).into(), - editor_active_line: gpui2::rgba(0x2f343eff).into(), + border: neutral().dark(6).into(), + border_variant: neutral().dark(5).into(), + border_focused: blue().dark(5).into(), + border_transparent: system.transparent, + elevated_surface: neutral().dark(2).into(), + surface: neutral().dark(2).into(), + background: neutral().dark(1).into(), + element: neutral().dark(3).into(), + element_hover: neutral().dark(4).into(), + element_active: neutral().dark(5).into(), + element_selected: neutral().dark(5).into(), + element_disabled: neutral().dark_alpha(3).into(), + element_placeholder: neutral().dark(11).into(), + ghost_element: system.transparent, + ghost_element_hover: neutral().dark(4).into(), + ghost_element_active: neutral().dark(5).into(), + ghost_element_selected: neutral().dark(5).into(), + ghost_element_disabled: neutral().dark_alpha(3).into(), + text: neutral().dark(12).into(), + text_muted: neutral().dark(11).into(), + text_placeholder: neutral().dark(11).into(), + text_disabled: neutral().dark(10).into(), + text_accent: blue().dark(12).into(), + icon: neutral().dark(12).into(), + icon_muted: neutral().dark(11).into(), + icon_disabled: neutral().dark(10).into(), + icon_placeholder: neutral().dark(11).into(), + icon_accent: blue().dark(12).into(), + status_bar: neutral().dark(2).into(), + title_bar: neutral().dark(2).into(), + toolbar: neutral().dark(2).into(), + tab_bar: neutral().dark(2).into(), + editor: neutral().dark(1).into(), + editor_subheader: neutral().dark(2).into(), + editor_active_line: neutral().dark_alpha(3).into(), } } } @@ -277,6 +285,25 @@ struct DefaultColorScaleSet { dark_alpha: [&'static str; 12], } +// See [ColorScaleSet] for why we use index-1. +impl DefaultColorScaleSet { + pub fn light(&self, index: usize) -> Hsla { + self.light[index - 1].into() + } + + pub fn light_alpha(&self, index: usize) -> Hsla { + self.light_alpha[index - 1].into() + } + + pub fn dark(&self, index: usize) -> Hsla { + self.dark[index - 1].into() + } + + pub fn dark_alpha(&self, index: usize) -> Hsla { + self.dark_alpha[index - 1].into() + } +} + impl From for ColorScaleSet { fn from(default: DefaultColorScaleSet) -> Self { Self::new( diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 34727eaf89..88dcbd1286 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -70,6 +70,18 @@ impl ThemeVariant { &self.styles.syntax } + /// Returns the [`StatusColors`] for the theme. + #[inline(always)] + pub fn status(&self) -> &StatusColors { + &self.styles.status + } + + /// Returns the [`GitStatusColors`] for the theme. + #[inline(always)] + pub fn git(&self) -> &GitStatusColors { + &self.styles.git + } + /// Returns the color for the syntax node with the given name. #[inline(always)] pub fn syntax_color(&self, name: &str) -> Hsla { diff --git a/crates/ui2/src/elements/label.rs b/crates/ui2/src/elements/label.rs index ee8ac9a636..7051565e17 100644 --- a/crates/ui2/src/elements/label.rs +++ b/crates/ui2/src/elements/label.rs @@ -21,11 +21,11 @@ impl LabelColor { match self { Self::Default => cx.theme().colors().text, Self::Muted => cx.theme().colors().text_muted, - Self::Created => gpui2::red(), - Self::Modified => gpui2::red(), - Self::Deleted => gpui2::red(), + Self::Created => cx.theme().status().created, + Self::Modified => cx.theme().status().modified, + Self::Deleted => cx.theme().status().deleted, Self::Disabled => cx.theme().colors().text_disabled, - Self::Hidden => gpui2::red(), + Self::Hidden => cx.theme().status().hidden, Self::Placeholder => cx.theme().colors().text_placeholder, Self::Accent => cx.theme().colors().text_accent, } From bfb1f5ecf08e5d75e1ef194f8f1e5ac441186a2a Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 01:41:33 -0400 Subject: [PATCH 03/30] Continue refining theme, update tabs & tab bar --- crates/storybook2/src/storybook2.rs | 2 +- crates/theme2/src/colors.rs | 3 ++ crates/theme2/src/default_colors.rs | 42 ++++++++++++++++------------ crates/ui2/src/components/tab.rs | 12 ++++---- crates/ui2/src/components/tab_bar.rs | 8 ++++++ crates/ui2/src/elements/icon.rs | 20 ++++++------- crates/ui2/src/elements/label.rs | 3 +- 7 files changed, 52 insertions(+), 38 deletions(-) diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 411fe18071..02284e433d 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -77,7 +77,7 @@ fn main() { WindowOptions { bounds: WindowBounds::Fixed(Bounds { origin: Default::default(), - size: size(px(1700.), px(980.)).into(), + size: size(px(1500.), px(780.)).into(), }), ..Default::default() }, diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs index 02c93a2e98..ee69eed612 100644 --- a/crates/theme2/src/colors.rs +++ b/crates/theme2/src/colors.rs @@ -64,6 +64,7 @@ pub struct ThemeColors { pub element_selected: Hsla, pub element_disabled: Hsla, pub element_placeholder: Hsla, + pub element_drop_target: Hsla, pub ghost_element: Hsla, pub ghost_element_hover: Hsla, pub ghost_element_active: Hsla, @@ -83,6 +84,8 @@ pub struct ThemeColors { pub title_bar: Hsla, pub toolbar: Hsla, pub tab_bar: Hsla, + pub tab_inactive: Hsla, + pub tab_active: Hsla, pub editor: Hsla, pub editor_subheader: Hsla, pub editor_active_line: Hsla, diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 8b7a7ac5cf..904e275c84 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -25,7 +25,7 @@ impl Default for StatusColors { fn default() -> Self { Self { conflict: red().dark(11).into(), - created: green().dark(11).into(), + created: grass().dark(11).into(), deleted: red().dark(11).into(), error: red().dark(11).into(), hidden: neutral().dark(11).into(), @@ -33,7 +33,7 @@ impl Default for StatusColors { info: blue().dark(11).into(), modified: yellow().dark(11).into(), renamed: blue().dark(11).into(), - success: green().dark(11).into(), + success: grass().dark(11).into(), warning: yellow().dark(11).into(), } } @@ -43,9 +43,9 @@ impl Default for GitStatusColors { fn default() -> Self { Self { conflict: orange().dark(11), - created: green().dark(11), + created: grass().dark(11), deleted: red().dark(11), - ignored: green().dark(11), + ignored: neutral().dark(11), modified: yellow().dark(11), renamed: blue().dark(11), } @@ -209,6 +209,7 @@ impl ThemeColors { element_selected: neutral().light(5).into(), element_disabled: neutral().light_alpha(3).into(), element_placeholder: neutral().light(11).into(), + element_drop_target: blue().light_alpha(2).into(), ghost_element: system.transparent, ghost_element_hover: neutral().light(4).into(), ghost_element_active: neutral().light(5).into(), @@ -218,16 +219,18 @@ impl ThemeColors { text_muted: neutral().light(11).into(), text_placeholder: neutral().light(11).into(), text_disabled: neutral().light(10).into(), - text_accent: blue().light(12).into(), - icon: neutral().light(12).into(), - icon_muted: neutral().light(11).into(), - icon_disabled: neutral().light(10).into(), - icon_placeholder: neutral().light(11).into(), - icon_accent: blue().light(12).into(), + text_accent: blue().light(11).into(), + icon: neutral().light(11).into(), + icon_muted: neutral().light(10).into(), + icon_disabled: neutral().light(9).into(), + icon_placeholder: neutral().light(10).into(), + icon_accent: blue().light(11).into(), status_bar: neutral().light(2).into(), title_bar: neutral().light(2).into(), - toolbar: neutral().light(2).into(), + toolbar: neutral().light(1).into(), tab_bar: neutral().light(2).into(), + tab_active: neutral().light(1).into(), + tab_inactive: neutral().light(2).into(), editor: neutral().light(1).into(), editor_subheader: neutral().light(2).into(), editor_active_line: neutral().light_alpha(3).into(), @@ -251,6 +254,7 @@ impl ThemeColors { element_selected: neutral().dark(5).into(), element_disabled: neutral().dark_alpha(3).into(), element_placeholder: neutral().dark(11).into(), + element_drop_target: blue().dark_alpha(2).into(), ghost_element: system.transparent, ghost_element_hover: neutral().dark(4).into(), ghost_element_active: neutral().dark(5).into(), @@ -260,16 +264,18 @@ impl ThemeColors { text_muted: neutral().dark(11).into(), text_placeholder: neutral().dark(11).into(), text_disabled: neutral().dark(10).into(), - text_accent: blue().dark(12).into(), - icon: neutral().dark(12).into(), - icon_muted: neutral().dark(11).into(), - icon_disabled: neutral().dark(10).into(), - icon_placeholder: neutral().dark(11).into(), - icon_accent: blue().dark(12).into(), + text_accent: blue().dark(11).into(), + icon: neutral().dark(11).into(), + icon_muted: neutral().dark(10).into(), + icon_disabled: neutral().dark(9).into(), + icon_placeholder: neutral().dark(10).into(), + icon_accent: blue().dark(11).into(), status_bar: neutral().dark(2).into(), title_bar: neutral().dark(2).into(), - toolbar: neutral().dark(2).into(), + toolbar: neutral().dark(1).into(), tab_bar: neutral().dark(2).into(), + tab_active: neutral().dark(1).into(), + tab_inactive: neutral().dark(2).into(), editor: neutral().dark(1).into(), editor_subheader: neutral().dark(2).into(), editor_active_line: neutral().dark_alpha(3).into(), diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index 5f20af0955..fddd82b064 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -108,13 +108,13 @@ impl Tab { let close_icon = || IconElement::new(Icon::Close).color(IconColor::Muted); let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current { - true => ( - cx.theme().colors().ghost_element, + false => ( + cx.theme().colors().tab_inactive, cx.theme().colors().ghost_element_hover, cx.theme().colors().ghost_element_active, ), - false => ( - cx.theme().colors().element, + true => ( + cx.theme().colors().tab_active, cx.theme().colors().element_hover, cx.theme().colors().element_active, ), @@ -127,7 +127,7 @@ impl Tab { div() .id(self.id.clone()) .on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone())) - .drag_over::(|d| d.bg(black())) + .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) .on_drop(|_view, state: View, cx| { dbg!(state.read(cx)); }) @@ -144,7 +144,7 @@ impl Tab { .px_1() .flex() .items_center() - .gap_1() + .gap_1p5() .children(has_fs_conflict.then(|| { IconElement::new(Icon::ExclamationTriangle) .size(crate::IconSize::Small) diff --git a/crates/ui2/src/components/tab_bar.rs b/crates/ui2/src/components/tab_bar.rs index 550105b98e..bb7fca1153 100644 --- a/crates/ui2/src/components/tab_bar.rs +++ b/crates/ui2/src/components/tab_bar.rs @@ -27,6 +27,7 @@ impl TabBar { let (can_navigate_back, can_navigate_forward) = self.can_navigate; div() + .group("tab_bar") .id(self.id.clone()) .w_full() .flex() @@ -34,6 +35,7 @@ impl TabBar { // Left Side .child( div() + .relative() .px_1() .flex() .flex_none() @@ -41,6 +43,7 @@ impl TabBar { // Nav Buttons .child( div() + .right_0() .flex() .items_center() .gap_px() @@ -67,10 +70,15 @@ impl TabBar { // Right Side .child( div() + // We only use absolute here since we don't + // have opacity or `hidden()` yet + .absolute() + .neg_top_7() .px_1() .flex() .flex_none() .gap_2() + .group_hover("tab_bar", |this| this.top_0()) // Nav Buttons .child( div() diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index 6c1b3a4f08..f3d612562f 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -26,18 +26,16 @@ pub enum IconColor { impl IconColor { pub fn color(self, cx: &WindowContext) -> Hsla { - let theme_colors = cx.theme().colors(); - match self { - IconColor::Default => theme_colors.icon, - IconColor::Muted => theme_colors.icon_muted, - IconColor::Disabled => theme_colors.icon_disabled, - IconColor::Placeholder => theme_colors.icon_placeholder, - IconColor::Accent => theme_colors.icon_accent, - IconColor::Error => gpui2::red(), - IconColor::Warning => gpui2::red(), - IconColor::Success => gpui2::red(), - IconColor::Info => gpui2::red(), + IconColor::Default => cx.theme().colors().icon, + IconColor::Muted => cx.theme().colors().icon_muted, + IconColor::Disabled => cx.theme().colors().icon_disabled, + IconColor::Placeholder => cx.theme().colors().icon_placeholder, + IconColor::Accent => cx.theme().colors().icon_accent, + IconColor::Error => cx.theme().status().error, + IconColor::Warning => cx.theme().status().warning, + IconColor::Success => cx.theme().status().success, + IconColor::Info => cx.theme().status().info, } } } diff --git a/crates/ui2/src/elements/label.rs b/crates/ui2/src/elements/label.rs index 7051565e17..360d003a8b 100644 --- a/crates/ui2/src/elements/label.rs +++ b/crates/ui2/src/elements/label.rs @@ -79,8 +79,7 @@ impl Label { this.relative().child( div() .absolute() - .top_px() - .my_auto() + .top_1_2() .w_full() .h_px() .bg(LabelColor::Hidden.hsla(cx)), From 3bcc2fa17be05441f149691aba5f663eacc4b4c6 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 02:10:23 -0400 Subject: [PATCH 04/30] Update notifications panel --- crates/ui2/src/components/list.rs | 71 ++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index 1668592a38..ebc442afdc 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -1,4 +1,4 @@ -use gpui2::{div, relative, Div}; +use gpui2::{div, px, relative, Div}; use crate::settings::user_settings; use crate::{ @@ -473,42 +473,63 @@ impl ListDetailsEntry { fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { let settings = user_settings(cx); - let (item_bg, item_bg_hover, item_bg_active) = match self.seen { - true => ( - cx.theme().colors().ghost_element, - cx.theme().colors().ghost_element_hover, - cx.theme().colors().ghost_element_active, - ), - false => ( - cx.theme().colors().element, - cx.theme().colors().element_hover, - cx.theme().colors().element_active, - ), - }; + let (item_bg, item_bg_hover, item_bg_active) = ( + cx.theme().colors().ghost_element, + cx.theme().colors().ghost_element_hover, + cx.theme().colors().ghost_element_active, + ); let label_color = match self.seen { true => LabelColor::Muted, false => LabelColor::Default, }; - v_stack() + div() .relative() .group("") .bg(item_bg) - .px_1() - .py_1_5() + .px_2() + .py_1p5() .w_full() - .line_height(relative(1.2)) - .child(Label::new(self.label.clone()).color(label_color)) - .children( - self.meta - .map(|meta| Label::new(meta).color(LabelColor::Muted)), - ) + .z_index(1) + .when(!self.seen, |this| { + this.child( + div() + .absolute() + .left(px(3.0)) + .top_3() + .rounded_full() + .border_2() + .border_color(cx.theme().colors().surface) + .w(px(9.0)) + .h(px(9.0)) + .z_index(2) + .bg(cx.theme().status().info), + ) + }) .child( - h_stack() + v_stack() + .w_full() + .line_height(relative(1.2)) .gap_1() - .justify_end() - .children(self.actions.unwrap_or_default()), + .child( + div() + .w_5() + .h_5() + .rounded_full() + .bg(cx.theme().colors().icon_accent), + ) + .child(Label::new(self.label.clone()).color(label_color)) + .children( + self.meta + .map(|meta| Label::new(meta).color(LabelColor::Muted)), + ) + .child( + h_stack() + .gap_1() + .justify_end() + .children(self.actions.unwrap_or_default()), + ), ) } } From b8547e926de412d78962801dbf1f344bbf944a25 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 10:31:03 -0400 Subject: [PATCH 05/30] Remove blanket `From<&str>` impl for `Hsla` Since this is a fallible operation we don't want to have a blanket infallible conversion from any arbitrary `&str` to an `Hsla`. --- crates/gpui2/src/color.rs | 6 ------ crates/theme2/src/default_colors.rs | 18 +++++++++--------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/gpui2/src/color.rs b/crates/gpui2/src/color.rs index 44b084e65f..db07259476 100644 --- a/crates/gpui2/src/color.rs +++ b/crates/gpui2/src/color.rs @@ -233,12 +233,6 @@ impl Hsla { } } -impl From<&str> for Hsla { - fn from(s: &str) -> Self { - Rgba::try_from(s).unwrap().into() - } -} - // impl From for Rgba { // fn from(value: Hsla) -> Self { // let h = value.h; diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 904e275c84..8bb1111727 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -4,6 +4,7 @@ use crate::{ colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors}, scale::{ColorScaleSet, ColorScales}, syntax::SyntaxTheme, + ColorScaleStep, }; fn neutral() -> DefaultColorScaleSet { @@ -291,22 +292,21 @@ struct DefaultColorScaleSet { dark_alpha: [&'static str; 12], } -// See [ColorScaleSet] for why we use index-1. impl DefaultColorScaleSet { - pub fn light(&self, index: usize) -> Hsla { - self.light[index - 1].into() + pub fn light(&self, index: ColorScaleStep) -> Hsla { + Rgba::try_from(self.light[index - 1]).unwrap().into() } - pub fn light_alpha(&self, index: usize) -> Hsla { - self.light_alpha[index - 1].into() + pub fn light_alpha(&self, index: ColorScaleStep) -> Hsla { + Rgba::try_from(self.light_alpha[index - 1]).unwrap().into() } - pub fn dark(&self, index: usize) -> Hsla { - self.dark[index - 1].into() + pub fn dark(&self, index: ColorScaleStep) -> Hsla { + Rgba::try_from(self.dark[index - 1]).unwrap().into() } - pub fn dark_alpha(&self, index: usize) -> Hsla { - self.dark_alpha[index - 1].into() + pub fn dark_alpha(&self, index: ColorScaleStep) -> Hsla { + Rgba::try_from(self.dark_alpha[index - 1]).unwrap().into() } } From d2a8f972f3de4d27343238e548e0d988e3da2039 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 10:48:23 -0400 Subject: [PATCH 06/30] Fix return type --- crates/theme2/src/default_colors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 7e2d808a53..15c09e212b 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -7,7 +7,7 @@ use crate::{ ColorScaleStep, }; -fn neutral() -> DefaultColorScaleSet { +fn neutral() -> ColorScaleSet { slate() } From 5d660759bf6d9d1a93d442541ce1086512542952 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 10:50:00 -0400 Subject: [PATCH 07/30] Continue refining default syntax theme --- crates/theme2/src/default_colors.rs | 174 +++++++++++++--------------- 1 file changed, 78 insertions(+), 96 deletions(-) diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 15c09e212b..cb2b406d9b 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -84,54 +84,45 @@ impl SyntaxTheme { pub fn default_light() -> Self { Self { highlights: vec![ - ( - "string.special.symbol".into(), - gpui2::rgba(0xad6e26ff).into(), - ), - ("hint".into(), gpui2::rgba(0x9294beff).into()), - ("link_uri".into(), gpui2::rgba(0x3882b7ff).into()), - ("type".into(), gpui2::rgba(0x3882b7ff).into()), - ("string.regex".into(), gpui2::rgba(0xad6e26ff).into()), - ("constant".into(), gpui2::rgba(0x669f59ff).into()), - ("function".into(), gpui2::rgba(0x5b79e3ff).into()), - ("string.special".into(), gpui2::rgba(0xad6e26ff).into()), - ("punctuation.bracket".into(), gpui2::rgba(0x4d4f52ff).into()), - ("variable".into(), gpui2::rgba(0x383a41ff).into()), - ("punctuation".into(), gpui2::rgba(0x383a41ff).into()), - ("property".into(), gpui2::rgba(0xd3604fff).into()), - ("string".into(), gpui2::rgba(0x649f57ff).into()), - ("predictive".into(), gpui2::rgba(0x9b9ec6ff).into()), - ("attribute".into(), gpui2::rgba(0x5c78e2ff).into()), - ("number".into(), gpui2::rgba(0xad6e25ff).into()), - ("constructor".into(), gpui2::rgba(0x5c78e2ff).into()), - ("embedded".into(), gpui2::rgba(0x383a41ff).into()), - ("title".into(), gpui2::rgba(0xd3604fff).into()), - ("tag".into(), gpui2::rgba(0x5c78e2ff).into()), - ("boolean".into(), gpui2::rgba(0xad6e25ff).into()), - ( - "punctuation.list_marker".into(), - gpui2::rgba(0xd3604fff).into(), - ), - ("variant".into(), gpui2::rgba(0x5b79e3ff).into()), - ("emphasis".into(), gpui2::rgba(0x5c78e2ff).into()), - ("link_text".into(), gpui2::rgba(0x5b79e3ff).into()), - ("comment".into(), gpui2::rgba(0xa2a3a7ff).into()), - ("punctuation.special".into(), gpui2::rgba(0xb92b46ff).into()), - ("emphasis.strong".into(), gpui2::rgba(0xad6e25ff).into()), - ("primary".into(), gpui2::rgba(0x383a41ff).into()), - ( - "punctuation.delimiter".into(), - gpui2::rgba(0x4d4f52ff).into(), - ), - ("label".into(), gpui2::rgba(0x5c78e2ff).into()), - ("keyword".into(), gpui2::rgba(0xa449abff).into()), - ("string.escape".into(), gpui2::rgba(0x7c7e86ff).into()), - ("text.literal".into(), gpui2::rgba(0x649f57ff).into()), - ("variable.special".into(), gpui2::rgba(0xad6e25ff).into()), - ("comment.doc".into(), gpui2::rgba(0x7c7e86ff).into()), - ("enum".into(), gpui2::rgba(0xd3604fff).into()), - ("operator".into(), gpui2::rgba(0x3882b7ff).into()), - ("preproc".into(), gpui2::rgba(0x383a41ff).into()), + ("attribute".into(), cyan().light(11).into()), + ("boolean".into(), tomato().light(11).into()), + ("comment".into(), neutral().light(11).into()), + ("comment.doc".into(), iris().light(12).into()), + ("constant".into(), red().light(7).into()), + ("constructor".into(), red().light(7).into()), + ("embedded".into(), red().light(7).into()), + ("emphasis".into(), red().light(7).into()), + ("emphasis.strong".into(), red().light(7).into()), + ("enum".into(), red().light(7).into()), + ("function".into(), red().light(7).into()), + ("hint".into(), red().light(7).into()), + ("keyword".into(), orange().light(11).into()), + ("label".into(), red().light(7).into()), + ("link_text".into(), red().light(7).into()), + ("link_uri".into(), red().light(7).into()), + ("number".into(), red().light(7).into()), + ("operator".into(), red().light(7).into()), + ("predictive".into(), red().light(7).into()), + ("preproc".into(), red().light(7).into()), + ("primary".into(), red().light(7).into()), + ("property".into(), red().light(7).into()), + ("punctuation".into(), neutral().light(11).into()), + ("punctuation.bracket".into(), neutral().light(11).into()), + ("punctuation.delimiter".into(), neutral().light(11).into()), + ("punctuation.list_marker".into(), blue().light(11).into()), + ("punctuation.special".into(), red().light(7).into()), + ("string".into(), jade().light(11).into()), + ("string.escape".into(), red().light(7).into()), + ("string.regex".into(), tomato().light(11).into()), + ("string.special".into(), red().light(7).into()), + ("string.special.symbol".into(), red().light(7).into()), + ("tag".into(), red().light(7).into()), + ("text.literal".into(), red().light(7).into()), + ("title".into(), red().light(7).into()), + ("type".into(), red().light(7).into()), + ("variable".into(), red().light(7).into()), + ("variable.special".into(), red().light(7).into()), + ("variant".into(), red().light(7).into()), ], } } @@ -139,54 +130,45 @@ impl SyntaxTheme { pub fn default_dark() -> Self { Self { highlights: vec![ - ("keyword".into(), gpui2::rgba(0xb477cfff).into()), - ("comment.doc".into(), gpui2::rgba(0x878e98ff).into()), - ("variant".into(), gpui2::rgba(0x73ade9ff).into()), - ("property".into(), gpui2::rgba(0xd07277ff).into()), - ("function".into(), gpui2::rgba(0x73ade9ff).into()), - ("type".into(), gpui2::rgba(0x6eb4bfff).into()), - ("tag".into(), gpui2::rgba(0x74ade8ff).into()), - ("string.escape".into(), gpui2::rgba(0x878e98ff).into()), - ("punctuation.bracket".into(), gpui2::rgba(0xb2b9c6ff).into()), - ("hint".into(), gpui2::rgba(0x5a6f89ff).into()), - ("punctuation".into(), gpui2::rgba(0xacb2beff).into()), - ("comment".into(), gpui2::rgba(0x5d636fff).into()), - ("emphasis".into(), gpui2::rgba(0x74ade8ff).into()), - ("punctuation.special".into(), gpui2::rgba(0xb1574bff).into()), - ("link_uri".into(), gpui2::rgba(0x6eb4bfff).into()), - ("string.regex".into(), gpui2::rgba(0xbf956aff).into()), - ("constructor".into(), gpui2::rgba(0x73ade9ff).into()), - ("operator".into(), gpui2::rgba(0x6eb4bfff).into()), - ("constant".into(), gpui2::rgba(0xdfc184ff).into()), - ("string.special".into(), gpui2::rgba(0xbf956aff).into()), - ("emphasis.strong".into(), gpui2::rgba(0xbf956aff).into()), - ( - "string.special.symbol".into(), - gpui2::rgba(0xbf956aff).into(), - ), - ("primary".into(), gpui2::rgba(0xacb2beff).into()), - ("preproc".into(), gpui2::rgba(0xc8ccd4ff).into()), - ("string".into(), gpui2::rgba(0xa1c181ff).into()), - ( - "punctuation.delimiter".into(), - gpui2::rgba(0xb2b9c6ff).into(), - ), - ("embedded".into(), gpui2::rgba(0xc8ccd4ff).into()), - ("enum".into(), gpui2::rgba(0xd07277ff).into()), - ("variable.special".into(), gpui2::rgba(0xbf956aff).into()), - ("text.literal".into(), gpui2::rgba(0xa1c181ff).into()), - ("attribute".into(), gpui2::rgba(0x74ade8ff).into()), - ("link_text".into(), gpui2::rgba(0x73ade9ff).into()), - ("title".into(), gpui2::rgba(0xd07277ff).into()), - ("predictive".into(), gpui2::rgba(0x5a6a87ff).into()), - ("number".into(), gpui2::rgba(0xbf956aff).into()), - ("label".into(), gpui2::rgba(0x74ade8ff).into()), - ("variable".into(), gpui2::rgba(0xc8ccd4ff).into()), - ("boolean".into(), gpui2::rgba(0xbf956aff).into()), - ( - "punctuation.list_marker".into(), - gpui2::rgba(0xd07277ff).into(), - ), + ("attribute".into(), cyan().dark(11).into()), + ("boolean".into(), tomato().dark(11).into()), + ("comment".into(), neutral().dark(11).into()), + ("comment.doc".into(), iris().dark(12).into()), + ("constant".into(), red().dark(7).into()), + ("constructor".into(), red().dark(7).into()), + ("embedded".into(), red().dark(7).into()), + ("emphasis".into(), red().dark(7).into()), + ("emphasis.strong".into(), red().dark(7).into()), + ("enum".into(), red().dark(7).into()), + ("function".into(), red().dark(7).into()), + ("hint".into(), red().dark(7).into()), + ("keyword".into(), orange().dark(11).into()), + ("label".into(), red().dark(7).into()), + ("link_text".into(), red().dark(7).into()), + ("link_uri".into(), red().dark(7).into()), + ("number".into(), red().dark(7).into()), + ("operator".into(), red().dark(7).into()), + ("predictive".into(), red().dark(7).into()), + ("preproc".into(), red().dark(7).into()), + ("primary".into(), red().dark(7).into()), + ("property".into(), red().dark(7).into()), + ("punctuation".into(), neutral().dark(11).into()), + ("punctuation.bracket".into(), neutral().dark(11).into()), + ("punctuation.delimiter".into(), neutral().dark(11).into()), + ("punctuation.list_marker".into(), blue().dark(11).into()), + ("punctuation.special".into(), red().dark(7).into()), + ("string".into(), jade().dark(11).into()), + ("string.escape".into(), red().dark(7).into()), + ("string.regex".into(), tomato().dark(11).into()), + ("string.special".into(), red().dark(7).into()), + ("string.special.symbol".into(), red().dark(7).into()), + ("tag".into(), red().dark(7).into()), + ("text.literal".into(), red().dark(7).into()), + ("title".into(), red().dark(7).into()), + ("type".into(), red().dark(7).into()), + ("variable".into(), red().dark(7).into()), + ("variable.special".into(), red().dark(7).into()), + ("variant".into(), red().dark(7).into()), ], } } From 3189cd779eb0149485c11d5b428ac4705d79d585 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 10:52:25 -0400 Subject: [PATCH 08/30] Remove unused impl --- crates/theme2/src/default_colors.rs | 21 +-------------------- crates/ui2/src/components/tab.rs | 2 +- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 15c09e212b..cad75f9935 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -1,10 +1,9 @@ -use gpui2::{hsla, Hsla, Rgba}; +use gpui2::{hsla, Rgba}; use crate::{ colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors}, scale::{ColorScaleSet, ColorScales}, syntax::SyntaxTheme, - ColorScaleStep, }; fn neutral() -> ColorScaleSet { @@ -292,24 +291,6 @@ struct DefaultColorScaleSet { dark_alpha: [&'static str; 12], } -impl DefaultColorScaleSet { - pub fn light(&self, index: ColorScaleStep) -> Hsla { - Rgba::try_from(self.light[index - 1]).unwrap().into() - } - - pub fn light_alpha(&self, index: ColorScaleStep) -> Hsla { - Rgba::try_from(self.light_alpha[index - 1]).unwrap().into() - } - - pub fn dark(&self, index: ColorScaleStep) -> Hsla { - Rgba::try_from(self.dark[index - 1]).unwrap().into() - } - - pub fn dark_alpha(&self, index: ColorScaleStep) -> Hsla { - Rgba::try_from(self.dark_alpha[index - 1]).unwrap().into() - } -} - impl From for ColorScaleSet { fn from(default: DefaultColorScaleSet) -> Self { Self::new( diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index fddd82b064..d7b52c7ab3 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -1,6 +1,6 @@ use crate::prelude::*; use crate::{Icon, IconColor, IconElement, Label, LabelColor}; -use gpui2::{black, red, Div, ElementId, Render, View, VisualContext}; +use gpui2::{red, Div, ElementId, Render, View, VisualContext}; #[derive(Component, Clone)] pub struct Tab { From 8dafd5f1f31771f1b816c2027c76d0deafc62767 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 12:43:25 -0400 Subject: [PATCH 09/30] Allow ListHeader to take a `meta` --- assets/icons/at-sign.svg | 1 + assets/icons/bell-off.svg | 1 + assets/icons/bell-ring.svg | 1 + assets/icons/bell.svg | 9 +- assets/icons/mail-open.svg | 1 + crates/ui2/src/components/list.rs | 84 +++++++++++-------- .../ui2/src/components/notifications_panel.rs | 20 +++-- crates/ui2/src/elements/icon.rs | 13 ++- crates/ui2/src/static_data.rs | 49 ++++++----- 9 files changed, 105 insertions(+), 74 deletions(-) create mode 100644 assets/icons/at-sign.svg create mode 100644 assets/icons/bell-off.svg create mode 100644 assets/icons/bell-ring.svg create mode 100644 assets/icons/mail-open.svg diff --git a/assets/icons/at-sign.svg b/assets/icons/at-sign.svg new file mode 100644 index 0000000000..5adac38f62 --- /dev/null +++ b/assets/icons/at-sign.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/bell-off.svg b/assets/icons/bell-off.svg new file mode 100644 index 0000000000..db1021f2d3 --- /dev/null +++ b/assets/icons/bell-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/bell-ring.svg b/assets/icons/bell-ring.svg new file mode 100644 index 0000000000..da51fdc5be --- /dev/null +++ b/assets/icons/bell-ring.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/bell.svg b/assets/icons/bell.svg index ea1c6dd42e..4c7d5472db 100644 --- a/assets/icons/bell.svg +++ b/assets/icons/bell.svg @@ -1,8 +1 @@ - - - + \ No newline at end of file diff --git a/assets/icons/mail-open.svg b/assets/icons/mail-open.svg new file mode 100644 index 0000000000..b63915bd73 --- /dev/null +++ b/assets/icons/mail-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index ebc442afdc..ad7ec2214f 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -15,12 +15,20 @@ pub enum ListItemVariant { Inset, } +pub enum ListHeaderMeta { + // TODO: These should be IconButtons + Tools(Vec), + // TODO: This should be a button + Button(Label), + Text(Label), +} + #[derive(Component)] pub struct ListHeader { label: SharedString, left_icon: Option, + meta: Option, variant: ListItemVariant, - state: InteractionState, toggleable: Toggleable, } @@ -29,8 +37,8 @@ impl ListHeader { Self { label: label.into(), left_icon: None, + meta: None, variant: ListItemVariant::default(), - state: InteractionState::default(), toggleable: Toggleable::Toggleable(ToggleState::Toggled), } } @@ -50,8 +58,8 @@ impl ListHeader { self } - pub fn state(mut self, state: InteractionState) -> Self { - self.state = state; + pub fn meta(mut self, meta: Option) -> Self { + self.meta = meta; self } @@ -74,34 +82,37 @@ impl ListHeader { } } - fn label_color(&self) -> LabelColor { - match self.state { - InteractionState::Disabled => LabelColor::Disabled, - _ => Default::default(), - } - } - - fn icon_color(&self) -> IconColor { - match self.state { - InteractionState::Disabled => IconColor::Disabled, - _ => Default::default(), - } - } - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { let is_toggleable = self.toggleable != Toggleable::NotToggleable; let is_toggled = self.toggleable.is_toggled(); let disclosure_control = self.disclosure_control(); + let meta = match self.meta { + Some(ListHeaderMeta::Tools(icons)) => div().child( + h_stack() + .gap_2() + .items_center() + .children(icons.into_iter().map(|i| { + IconElement::new(i) + .color(IconColor::Muted) + .size(IconSize::Small) + })), + ), + Some(ListHeaderMeta::Button(label)) => div().child(label), + Some(ListHeaderMeta::Text(label)) => div().child(label), + None => div(), + }; + h_stack() .flex_1() .w_full() .bg(cx.theme().colors().surface) - .when(self.state == InteractionState::Focused, |this| { - this.border() - .border_color(cx.theme().colors().border_focused) - }) + // TODO: Add focus state + // .when(self.state == InteractionState::Focused, |this| { + // this.border() + // .border_color(cx.theme().colors().border_focused) + // }) .relative() .child( div() @@ -109,22 +120,28 @@ impl ListHeader { .when(self.variant == ListItemVariant::Inset, |this| this.px_2()) .flex() .flex_1() + .items_center() + .justify_between() .w_full() .gap_1() - .items_center() .child( - div() - .flex() + h_stack() .gap_1() - .items_center() - .children(self.left_icon.map(|i| { - IconElement::new(i) - .color(IconColor::Muted) - .size(IconSize::Small) - })) - .child(Label::new(self.label.clone()).color(LabelColor::Muted)), + .child( + div() + .flex() + .gap_1() + .items_center() + .children(self.left_icon.map(|i| { + IconElement::new(i) + .color(IconColor::Muted) + .size(IconSize::Small) + })) + .child(Label::new(self.label.clone()).color(LabelColor::Muted)), + ) + .child(disclosure_control), ) - .child(disclosure_control), + .child(meta), ) } } @@ -593,6 +610,7 @@ impl List { }; v_stack() + .w_full() .py_1() .children(self.header.map(|header| header.toggleable(self.toggleable))) .child(list_content) diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs index 10b0e07af6..c102a2cf57 100644 --- a/crates/ui2/src/components/notifications_panel.rs +++ b/crates/ui2/src/components/notifications_panel.rs @@ -1,4 +1,4 @@ -use crate::{prelude::*, static_new_notification_items, static_read_notification_items}; +use crate::{prelude::*, static_new_notification_items, Icon, ListHeaderMeta}; use crate::{List, ListHeader}; #[derive(Component)] @@ -28,14 +28,16 @@ impl NotificationsPanel { .overflow_y_scroll() .child( List::new(static_new_notification_items()) - .header(ListHeader::new("NEW").toggle(ToggleState::Toggled)) - .toggle(ToggleState::Toggled), - ) - .child( - List::new(static_read_notification_items()) - .header(ListHeader::new("EARLIER").toggle(ToggleState::Toggled)) - .empty_message("No new notifications") - .toggle(ToggleState::Toggled), + .toggle(ToggleState::Toggled) + .header( + ListHeader::new("Notifications") + .toggle(ToggleState::Toggled) + .meta(Some(ListHeaderMeta::Tools(vec![ + Icon::AtSign, + Icon::BellOff, + Icon::MailOpen, + ]))), + ), ), ) } diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index f3d612562f..eef80cb5ad 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -40,7 +40,7 @@ impl IconColor { } } -#[derive(Debug, Default, PartialEq, Copy, Clone, EnumIter)] +#[derive(Debug, PartialEq, Copy, Clone, EnumIter)] pub enum Icon { Ai, ArrowLeft, @@ -67,7 +67,6 @@ pub enum Icon { Folder, FolderOpen, FolderX, - #[default] Hash, InlayHint, MagicWand, @@ -89,6 +88,11 @@ pub enum Icon { XCircle, Copilot, Envelope, + Bell, + BellOff, + BellRing, + MailOpen, + AtSign, } impl Icon { @@ -140,6 +144,11 @@ impl Icon { Icon::XCircle => "icons/error.svg", Icon::Copilot => "icons/copilot.svg", Icon::Envelope => "icons/feedback.svg", + Icon::Bell => "icons/bell.svg", + Icon::BellOff => "icons/bell-off.svg", + Icon::BellRing => "icons/bell-ring.svg", + Icon::MailOpen => "icons/mail-open.svg", + Icon::AtSign => "icons/at-sign.svg", } } } diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 7062c81954..ebbc89832c 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -7,9 +7,10 @@ use theme2::ActiveTheme; use crate::{ Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus, - HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListItem, - Livestream, MicStatus, ModifierKeys, PaletteItem, Player, PlayerCallStatus, - PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState, VideoStatus, + HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListHeaderMeta, + ListItem, ListSubHeader, Livestream, MicStatus, ModifierKeys, PaletteItem, Player, + PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState, + VideoStatus, }; use crate::{HighlightedText, ListDetailsEntry}; @@ -327,25 +328,29 @@ pub fn static_players_with_call_status() -> Vec { pub fn static_new_notification_items() -> Vec> { vec![ - ListDetailsEntry::new("maxdeviant invited you to join a stream in #design.") - .meta("4 people in stream."), - ListDetailsEntry::new("nathansobo accepted your contact request."), - ] - .into_iter() - .map(From::from) - .collect() -} - -pub fn static_read_notification_items() -> Vec> { - vec![ - ListDetailsEntry::new("mikaylamaki added you as a contact.").actions(vec![ - Button::new("Decline"), - Button::new("Accept").variant(crate::ButtonVariant::Filled), - ]), - ListDetailsEntry::new("maxdeviant invited you to a stream in #design.") - .seen(true) - .meta("This stream has ended."), - ListDetailsEntry::new("as-cii accepted your contact request."), + ListItem::Header(ListSubHeader::new("New")), + ListItem::Details( + ListDetailsEntry::new("maxdeviant invited you to join a stream in #design.") + .meta("4 people in stream."), + ), + ListItem::Details(ListDetailsEntry::new( + "nathansobo accepted your contact request.", + )), + ListItem::Header(ListSubHeader::new("Earlier")), + ListItem::Details( + ListDetailsEntry::new("mikaylamaki added you as a contact.").actions(vec![ + Button::new("Decline"), + Button::new("Accept").variant(crate::ButtonVariant::Filled), + ]), + ), + ListItem::Details( + ListDetailsEntry::new("maxdeviant invited you to a stream in #design.") + .seen(true) + .meta("This stream has ended."), + ), + ListItem::Details(ListDetailsEntry::new( + "as-cii accepted your contact request.", + )), ] .into_iter() .map(From::from) From 51ecf8e6b46af8c3a0ebaa32da01d3b73bf6edb7 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Wed, 1 Nov 2023 14:50:29 -0400 Subject: [PATCH 10/30] collab 0.28.0 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 755f4440d9..2b70dcd3cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1603,7 +1603,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.27.0" +version = "0.28.0" dependencies = [ "anyhow", "async-trait", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 987c295407..dea6e09245 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.27.0" +version = "0.28.0" publish = false [[bin]] From 3f74f75dee94158de57a59d2b1f72a87b8091e4f Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 16:19:49 -0400 Subject: [PATCH 11/30] WIP --- crates/theme2/src/default_colors.rs | 8 +- crates/ui2/src/components/list.rs | 9 +- .../ui2/src/components/notifications_panel.rs | 376 +++++++++++++++++- crates/ui2/src/elements.rs | 2 + crates/ui2/src/elements/indicator.rs | 22 + crates/ui2/src/static_data.rs | 53 ++- 6 files changed, 440 insertions(+), 30 deletions(-) create mode 100644 crates/ui2/src/elements/indicator.rs diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 3f2f0503cd..2e62b68fae 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -199,8 +199,8 @@ impl ThemeColors { ghost_element_disabled: neutral().light_alpha(3).into(), text: neutral().light(12).into(), text_muted: neutral().light(11).into(), - text_placeholder: neutral().light(11).into(), - text_disabled: neutral().light(10).into(), + text_placeholder: neutral().light(10).into(), + text_disabled: neutral().light(9).into(), text_accent: blue().light(11).into(), icon: neutral().light(11).into(), icon_muted: neutral().light(10).into(), @@ -244,8 +244,8 @@ impl ThemeColors { ghost_element_disabled: neutral().dark_alpha(3).into(), text: neutral().dark(12).into(), text_muted: neutral().dark(11).into(), - text_placeholder: neutral().dark(11).into(), - text_disabled: neutral().dark(10).into(), + text_placeholder: neutral().dark(10).into(), + text_disabled: neutral().dark(9).into(), text_accent: blue().dark(11).into(), icon: neutral().dark(11).into(), icon_muted: neutral().dark(10).into(), diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index ad7ec2214f..50a86ff256 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -39,7 +39,7 @@ impl ListHeader { left_icon: None, meta: None, variant: ListItemVariant::default(), - toggleable: Toggleable::Toggleable(ToggleState::Toggled), + toggleable: Toggleable::NotToggleable, } } @@ -105,7 +105,6 @@ impl ListHeader { }; h_stack() - .flex_1() .w_full() .bg(cx.theme().colors().surface) // TODO: Add focus state @@ -560,7 +559,7 @@ impl ListSeparator { } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - div().h_px().w_full().bg(cx.theme().colors().border) + div().h_px().w_full().bg(cx.theme().colors().border_variant) } } @@ -602,9 +601,9 @@ impl List { let is_toggled = Toggleable::is_toggled(&self.toggleable); let list_content = match (self.items.is_empty(), is_toggled) { - (_, false) => div(), (false, _) => div().children(self.items), - (true, _) => { + (true, false) => div(), + (true, true) => { div().child(Label::new(self.empty_message.clone()).color(LabelColor::Muted)) } }; diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs index c102a2cf57..367e0d0ba6 100644 --- a/crates/ui2/src/components/notifications_panel.rs +++ b/crates/ui2/src/components/notifications_panel.rs @@ -1,5 +1,9 @@ -use crate::{prelude::*, static_new_notification_items, Icon, ListHeaderMeta}; -use crate::{List, ListHeader}; +use crate::{ + h_stack, prelude::*, static_new_notification_items, v_stack, Avatar, Button, Icon, IconButton, + IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator, Stack, + UnreadIndicator, +}; +use crate::{ClickHandler, ListHeader}; #[derive(Component)] pub struct NotificationsPanel { @@ -16,33 +20,367 @@ impl NotificationsPanel { .id(self.id.clone()) .flex() .flex_col() - .w_full() - .h_full() + .size_full() .bg(cx.theme().colors().surface) .child( - div() - .id("header") - .w_full() - .flex() - .flex_col() + ListHeader::new("Notifications").meta(Some(ListHeaderMeta::Tools(vec![ + Icon::AtSign, + Icon::BellOff, + Icon::MailOpen, + ]))), + ) + .child(ListSeparator::new()) + .child( + v_stack() + .id("notifications-panel-scroll-view") + .py_1() .overflow_y_scroll() + .flex_1() .child( - List::new(static_new_notification_items()) - .toggle(ToggleState::Toggled) - .header( - ListHeader::new("Notifications") - .toggle(ToggleState::Toggled) - .meta(Some(ListHeaderMeta::Tools(vec![ - Icon::AtSign, - Icon::BellOff, - Icon::MailOpen, - ]))), + div() + .mx_2() + .p_1() + // TODO: Add cursor style + // .cursor(Cursor::IBeam) + .bg(cx.theme().colors().element) + .border() + .border_color(cx.theme().colors().border_variant) + .child( + Label::new("Search...") + .color(LabelColor::Placeholder) + .line_height_style(LineHeightStyle::UILabel), + ), + ) + .children(static_new_notification_items()), + ) + } +} + +pub enum NotificationItem { + Message(Notification), + // WithEdgeHeader(Notification), + WithRequiredActions(NotificationWithActions), +} + +pub enum ButtonOrIconButton { + Button(Button), + IconButton(IconButton), +} + +pub struct NotificationAction { + button: ButtonOrIconButton, + tooltip: SharedString, + /// Shows after action is chosen + /// + /// For example, if the action is "Accept" the taken message could be: + /// + /// - `(None,"Accepted")` - "Accepted" + /// + /// - `(Some(Icon::Check),"Accepted")` - ✓ "Accepted" + taken_message: (Option, SharedString), +} + +pub struct NotificationWithActions { + notification: Notification, + actions: [NotificationAction; 2], +} + +/// Represents a person with a Zed account's public profile. +/// All data in this struct should be considered public. +pub struct PublicActor { + username: SharedString, + avatar: SharedString, + is_contact: bool, +} + +pub enum ActorOrIcon { + Actor(PublicActor), + Icon(Icon), +} + +pub struct NotificationMeta { + items: Vec<(Option, SharedString, Option>)>, +} + +struct NotificationHandlers { + click: Option>, +} + +impl Default for NotificationHandlers { + fn default() -> Self { + Self { click: None } + } +} + +#[derive(Component)] +pub struct Notification { + id: ElementId, + slot: ActorOrIcon, + message: SharedString, + date_received: NaiveDateTime, + meta: Option>, + actions: Option<[NotificationAction; 2]>, + unread: bool, + new: bool, + action_taken: Option>, + handlers: NotificationHandlers, +} + +impl Notification { + fn new( + id: ElementId, + message: SharedString, + slot: ActorOrIcon, + click_action: Option>, + ) -> Self { + let handlers = if click_action.is_some() { + NotificationHandlers { + click: click_action, + } + } else { + NotificationHandlers::default() + }; + + Self { + id, + date_received: DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z") + .unwrap() + .naive_local(), + message, + meta: None, + slot, + actions: None, + unread: true, + new: false, + action_taken: None, + handlers, + } + } + + /// Creates a new notification with an actor slot. + /// + /// Requires a click action. + pub fn new_actor_message( + id: impl Into, + message: SharedString, + actor: PublicActor, + click_action: ClickHandler, + ) -> Self { + Self::new( + id.into(), + message, + ActorOrIcon::Actor(actor), + Some(click_action), + ) + } + + /// Creates a new notification with an icon slot. + /// + /// Requires a click action. + pub fn new_icon_message( + id: impl Into, + message: SharedString, + icon: Icon, + click_action: ClickHandler, + ) -> Self { + Self::new( + id.into(), + message, + ActorOrIcon::Icon(icon), + Some(click_action), + ) + } + + /// Creates a new notification with an actor slot + /// and a Call To Action row. + /// + /// Cannot take a click action due to required actions. + pub fn new_actor_with_actions( + id: impl Into, + message: SharedString, + actor: PublicActor, + click_action: ClickHandler, + actions: [NotificationAction; 2], + ) -> Self { + Self::new(id.into(), message, ActorOrIcon::Actor(actor), None).actions(actions) + } + + /// Creates a new notification with an icon slot + /// and a Call To Action row. + /// + /// Cannot take a click action due to required actions. + pub fn new_icon_with_actions( + id: impl Into, + message: SharedString, + icon: Icon, + click_action: ClickHandler, + actions: [NotificationAction; 2], + ) -> Self { + Self::new(id.into(), message, ActorOrIcon::Icon(icon), None).actions(actions) + } + + fn on_click(mut self, handler: ClickHandler) -> Self { + self.handlers.click = Some(handler); + self + } + + pub fn actions(mut self, actions: [NotificationAction; 2]) -> Self { + self.actions = Some(actions); + self + } + + pub fn meta(mut self, meta: NotificationMeta) -> Self { + self.meta = Some(meta); + self + } + + fn render_meta_items(&self, cx: &mut ViewContext) -> impl Component { + if let Some(meta) = &self.meta { + h_stack().children( + meta.items + .iter() + .map(|(icon, text, _)| { + let mut meta_el = div(); + if let Some(icon) = icon { + meta_el = meta_el.child(IconElement::new(icon.clone())); + } + meta_el.child(Label::new(text.clone()).color(LabelColor::Muted)) + }) + .collect::>(), + ) + } else { + div() + } + } + + fn render_actions(&self, cx: &mut ViewContext) -> impl Component { + // match (&self.actions, &self.action_taken) { + // // Show nothing + // (None, _) => div(), + // // Show the taken_message + // (Some(_), Some(action_taken)) => h_stack() + // .children( + // action_taken + // .taken_message + // .0 + // .map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)), + // ) + // .child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)), + // // Show the actions + // (Some(actions), None) => h_stack() + // .children(actions.iter().map(actiona.ction.tton Component::render(button),Component::render(icon_button))), + // })) + // .collect::>(), + } + + // if let Some(actions) = &self.actions { + // let action_children = actions + // .iter() + // .map(|action| match &action.button { + // ButtonOrIconButton::Button(button) => { + // div().class("action_button").child(button.label.clone()) + // } + // ButtonOrIconButton::IconButton(icon_button) => div() + // .class("action_icon_button") + // .child(icon_button.icon.to_string()), + // }) + // .collect::>(); + + // el = el.child(h_stack().children(action_children)); + // } else { + // el = el.child(h_stack().child(div())); + // } + } + + fn render_slot(&self, cx: &mut ViewContext) -> impl Component { + match &self.slot { + ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render(), + ActorOrIcon::Icon(icon) => IconElement::new(icon.clone()).render(), + } + } + + fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + div() + .relative() + .id(self.id.clone()) + .children( + Some( + div() + .absolute() + .left(px(3.0)) + .top_3() + .child(UnreadIndicator::new()), + ) + .filter(|_| self.unread), + ) + .child( + v_stack() + .gap_1() + .child( + h_stack() + .gap_2() + .child(self.render_slot(cx)) + .child(div().flex_1().child(Label::new(self.message.clone()))), + ) + .child( + h_stack() + .justify_between() + .child( + h_stack() + .gap_1() + .child( + Label::new( + self.date_received.format("%m/%d/%Y").to_string(), + ) + .color(LabelColor::Muted), + ) + .child(self.render_meta_items(cx)), + ) + .child( + + match (self.actions, self.action_taken) { + // Show nothing + (None, _) => div(), + // Show the taken_message + (Some(_), Some(action_taken)) => h_stack() + .children( + action_taken + .taken_message + .0 + .map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)), + ) + .child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)), + // Show the actions + (Some(actions), None) => h_stack() + + } + + // match (&self.actions, &self.action_taken) { + // // Show nothing + // (None, _) => div(), + // // Show the taken_message + // (Some(_), Some(action_taken)) => h_stack() + // .children( + // action_taken + // .taken_message + // .0 + // .map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)), + // ) + // .child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)), + // // Show the actions + // (Some(actions), None) => h_stack() + // .children(actions.iter().map(actiona.ction.tton Component::render(button),Component::render(icon_button))), + // })) + // .collect::>(), + ), ), ) } } +use chrono::{DateTime, NaiveDateTime}; +use gpui2::{px, Styled}; #[cfg(feature = "stories")] pub use stories::*; diff --git a/crates/ui2/src/elements.rs b/crates/ui2/src/elements.rs index c60902ae98..dfff2761a7 100644 --- a/crates/ui2/src/elements.rs +++ b/crates/ui2/src/elements.rs @@ -2,6 +2,7 @@ mod avatar; mod button; mod details; mod icon; +mod indicator; mod input; mod label; mod player; @@ -12,6 +13,7 @@ pub use avatar::*; pub use button::*; pub use details::*; pub use icon::*; +pub use indicator::*; pub use input::*; pub use label::*; pub use player::*; diff --git a/crates/ui2/src/elements/indicator.rs b/crates/ui2/src/elements/indicator.rs new file mode 100644 index 0000000000..86c83a1bf1 --- /dev/null +++ b/crates/ui2/src/elements/indicator.rs @@ -0,0 +1,22 @@ +use gpui2::px; + +use crate::prelude::*; + +#[derive(Component)] +pub struct UnreadIndicator; + +impl UnreadIndicator { + pub fn new() -> Self { + Self + } + + fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + div() + .border_2() + .border_color(cx.theme().colors().surface) + .w(px(9.0)) + .h(px(9.0)) + .z_index(2) + .bg(cx.theme().status().info) + } +} diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index ebbc89832c..dc724ec5c4 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -8,8 +8,8 @@ use theme2::ActiveTheme; use crate::{ Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus, HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListHeaderMeta, - ListItem, ListSubHeader, Livestream, MicStatus, ModifierKeys, PaletteItem, Player, - PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState, + ListItem, ListSubHeader, Livestream, MicStatus, ModifierKeys, NotificationItem, PaletteItem, + Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState, VideoStatus, }; use crate::{HighlightedText, ListDetailsEntry}; @@ -326,6 +326,9 @@ pub fn static_players_with_call_status() -> Vec { ] } +pub fn static_new_notification_items_2() -> Vec> { + vec![] +} pub fn static_new_notification_items() -> Vec> { vec![ ListItem::Header(ListSubHeader::new("New")), @@ -351,6 +354,52 @@ pub fn static_new_notification_items() -> Vec> { ListItem::Details(ListDetailsEntry::new( "as-cii accepted your contact request.", )), + ListItem::Details( + ListDetailsEntry::new("You were added as an admin on the #gpui2 channel.").seen(true), + ), + ListItem::Details(ListDetailsEntry::new( + "osiewicz accepted your contact request.", + )), + ListItem::Details(ListDetailsEntry::new( + "ConradIrwin accepted your contact request.", + )), + ListItem::Details( + ListDetailsEntry::new("nathansobo invited you to a stream in #gpui2.") + .seen(true) + .meta("This stream has ended."), + ), + ListItem::Details(ListDetailsEntry::new( + "nathansobo accepted your contact request.", + )), + ListItem::Header(ListSubHeader::new("Earlier")), + ListItem::Details( + ListDetailsEntry::new("mikaylamaki added you as a contact.").actions(vec![ + Button::new("Decline"), + Button::new("Accept").variant(crate::ButtonVariant::Filled), + ]), + ), + ListItem::Details( + ListDetailsEntry::new("maxdeviant invited you to a stream in #design.") + .seen(true) + .meta("This stream has ended."), + ), + ListItem::Details(ListDetailsEntry::new( + "as-cii accepted your contact request.", + )), + ListItem::Details( + ListDetailsEntry::new("You were added as an admin on the #gpui2 channel.").seen(true), + ), + ListItem::Details(ListDetailsEntry::new( + "osiewicz accepted your contact request.", + )), + ListItem::Details(ListDetailsEntry::new( + "ConradIrwin accepted your contact request.", + )), + ListItem::Details( + ListDetailsEntry::new("nathansobo invited you to a stream in #gpui2.") + .seen(true) + .meta("This stream has ended."), + ), ] .into_iter() .map(From::from) From be3cc6458c27d341e284af183239db4075f49262 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 16:34:42 -0400 Subject: [PATCH 12/30] Implement Notifications Co-Authored-By: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com> --- .../ui2/src/components/notifications_panel.rs | 153 +++++++----------- crates/ui2/src/elements/icon.rs | 2 + crates/ui2/src/prelude.rs | 18 +++ crates/ui2/src/static_data.rs | 37 ++++- 4 files changed, 110 insertions(+), 100 deletions(-) diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs index 367e0d0ba6..6bcc9574c9 100644 --- a/crates/ui2/src/components/notifications_panel.rs +++ b/crates/ui2/src/components/notifications_panel.rs @@ -1,6 +1,6 @@ use crate::{ h_stack, prelude::*, static_new_notification_items, v_stack, Avatar, Button, Icon, IconButton, - IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator, Stack, + IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator, UnreadIndicator, }; use crate::{ClickHandler, ListHeader}; @@ -67,6 +67,18 @@ pub enum ButtonOrIconButton { IconButton(IconButton), } +impl From> for ButtonOrIconButton { + fn from(value: Button) -> Self { + Self::Button(value) + } +} + +impl From> for ButtonOrIconButton { + fn from(value: IconButton) -> Self { + Self::IconButton(value) + } +} + pub struct NotificationAction { button: ButtonOrIconButton, tooltip: SharedString, @@ -80,19 +92,25 @@ pub struct NotificationAction { taken_message: (Option, SharedString), } +impl NotificationAction { + pub fn new( + button: impl Into>, + tooltip: impl Into, + (icon, taken_message): (Option, impl Into), + ) -> Self { + Self { + button: button.into(), + tooltip: tooltip.into(), + taken_message: (icon, taken_message.into()), + } + } +} + pub struct NotificationWithActions { notification: Notification, actions: [NotificationAction; 2], } -/// Represents a person with a Zed account's public profile. -/// All data in this struct should be considered public. -pub struct PublicActor { - username: SharedString, - avatar: SharedString, - is_contact: bool, -} - pub enum ActorOrIcon { Actor(PublicActor), Icon(Icon), @@ -162,13 +180,13 @@ impl Notification { /// Requires a click action. pub fn new_actor_message( id: impl Into, - message: SharedString, + message: impl Into, actor: PublicActor, click_action: ClickHandler, ) -> Self { Self::new( id.into(), - message, + message.into(), ActorOrIcon::Actor(actor), Some(click_action), ) @@ -179,13 +197,13 @@ impl Notification { /// Requires a click action. pub fn new_icon_message( id: impl Into, - message: SharedString, + message: impl Into, icon: Icon, click_action: ClickHandler, ) -> Self { Self::new( id.into(), - message, + message.into(), ActorOrIcon::Icon(icon), Some(click_action), ) @@ -197,12 +215,11 @@ impl Notification { /// Cannot take a click action due to required actions. pub fn new_actor_with_actions( id: impl Into, - message: SharedString, + message: impl Into, actor: PublicActor, - click_action: ClickHandler, actions: [NotificationAction; 2], ) -> Self { - Self::new(id.into(), message, ActorOrIcon::Actor(actor), None).actions(actions) + Self::new(id.into(), message.into(), ActorOrIcon::Actor(actor), None).actions(actions) } /// Creates a new notification with an icon slot @@ -211,12 +228,11 @@ impl Notification { /// Cannot take a click action due to required actions. pub fn new_icon_with_actions( id: impl Into, - message: SharedString, + message: impl Into, icon: Icon, - click_action: ClickHandler, actions: [NotificationAction; 2], ) -> Self { - Self::new(id.into(), message, ActorOrIcon::Icon(icon), None).actions(actions) + Self::new(id.into(), message.into(), ActorOrIcon::Icon(icon), None).actions(actions) } fn on_click(mut self, handler: ClickHandler) -> Self { @@ -253,45 +269,6 @@ impl Notification { } } - fn render_actions(&self, cx: &mut ViewContext) -> impl Component { - // match (&self.actions, &self.action_taken) { - // // Show nothing - // (None, _) => div(), - // // Show the taken_message - // (Some(_), Some(action_taken)) => h_stack() - // .children( - // action_taken - // .taken_message - // .0 - // .map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)), - // ) - // .child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)), - // // Show the actions - // (Some(actions), None) => h_stack() - // .children(actions.iter().map(actiona.ction.tton Component::render(button),Component::render(icon_button))), - // })) - // .collect::>(), - } - - // if let Some(actions) = &self.actions { - // let action_children = actions - // .iter() - // .map(|action| match &action.button { - // ButtonOrIconButton::Button(button) => { - // div().class("action_button").child(button.label.clone()) - // } - // ButtonOrIconButton::IconButton(icon_button) => div() - // .class("action_icon_button") - // .child(icon_button.icon.to_string()), - // }) - // .collect::>(); - - // el = el.child(h_stack().children(action_children)); - // } else { - // el = el.child(h_stack().child(div())); - // } - } - fn render_slot(&self, cx: &mut ViewContext) -> impl Component { match &self.slot { ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render(), @@ -336,44 +313,30 @@ impl Notification { ) .child(self.render_meta_items(cx)), ) - .child( - - match (self.actions, self.action_taken) { - // Show nothing - (None, _) => div(), - // Show the taken_message - (Some(_), Some(action_taken)) => h_stack() - .children( - action_taken - .taken_message - .0 - .map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)), - ) - .child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)), - // Show the actions - (Some(actions), None) => h_stack() - + .child(match (self.actions, self.action_taken) { + // Show nothing + (None, _) => div(), + // Show the taken_message + (Some(_), Some(action_taken)) => h_stack() + .children(action_taken.taken_message.0.map(|icon| { + IconElement::new(icon).color(crate::IconColor::Muted) + })) + .child( + Label::new(action_taken.taken_message.1.clone()) + .color(LabelColor::Muted), + ), + // Show the actions + (Some(actions), None) => { + h_stack().children(actions.map(|action| match action.button { + ButtonOrIconButton::Button(button) => { + Component::render(button) + } + ButtonOrIconButton::IconButton(icon_button) => { + Component::render(icon_button) + } + })) } - - // match (&self.actions, &self.action_taken) { - // // Show nothing - // (None, _) => div(), - // // Show the taken_message - // (Some(_), Some(action_taken)) => h_stack() - // .children( - // action_taken - // .taken_message - // .0 - // .map(|icon| IconElement::new(icon).color(crate::IconColor::Muted)), - // ) - // .child(Label::new(action_taken.taken_message.1.clone()).color(LabelColor::Muted)), - // // Show the actions - // (Some(actions), None) => h_stack() - // .children(actions.iter().map(actiona.ction.tton Component::render(button),Component::render(icon_button))), - // })) - // .collect::>(), - - ), + }), ), ) } diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index eef80cb5ad..2038e5a000 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -49,6 +49,7 @@ pub enum Icon { AudioOff, AudioOn, Bolt, + Check, ChevronDown, ChevronLeft, ChevronRight, @@ -105,6 +106,7 @@ impl Icon { Icon::AudioOff => "icons/speaker-off.svg", Icon::AudioOn => "icons/speaker-loud.svg", Icon::Bolt => "icons/bolt.svg", + Icon::Check => "icons/check.svg", Icon::ChevronDown => "icons/chevron_down.svg", Icon::ChevronLeft => "icons/chevron_left.svg", Icon::ChevronRight => "icons/chevron_right.svg", diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index b424ce6123..53adc8c0f9 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -19,6 +19,24 @@ pub fn ui_size(cx: &mut WindowContext, size: f32) -> Rems { rems(*settings.ui_scale * UI_SCALE_RATIO * size) } +/// Represents a person with a Zed account's public profile. +/// All data in this struct should be considered public. +pub struct PublicActor { + pub username: SharedString, + pub avatar: SharedString, + pub is_contact: bool, +} + +impl PublicActor { + pub fn new(username: impl Into, avatar: impl Into) -> Self { + Self { + username: username.into(), + avatar: avatar.into(), + is_contact: false, + } + } +} + #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] pub enum FileSystemStatus { #[default] diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index dc724ec5c4..7e206f0cf6 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -1,5 +1,6 @@ use std::path::PathBuf; use std::str::FromStr; +use std::sync::Arc; use gpui2::{AppContext, ViewContext}; use rand::Rng; @@ -7,12 +8,13 @@ use theme2::ActiveTheme; use crate::{ Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus, - HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListHeaderMeta, - ListItem, ListSubHeader, Livestream, MicStatus, ModifierKeys, NotificationItem, PaletteItem, - Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState, - VideoStatus, + HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListSubHeader, + Livestream, MicStatus, ModifierKeys, Notification, NotificationItem, PaletteItem, Player, + PlayerCallStatus, PlayerWithCallStatus, PublicActor, ScreenShareStatus, Symbol, Tab, + ToggleState, VideoStatus, }; use crate::{HighlightedText, ListDetailsEntry}; +use crate::{ListItem, NotificationAction}; pub fn static_tabs_example() -> Vec { vec![ @@ -327,8 +329,33 @@ pub fn static_players_with_call_status() -> Vec { } pub fn static_new_notification_items_2() -> Vec> { - vec![] + vec![ + NotificationItem::Message(Notification::new_icon_message( + "notif-1", + "You were mentioned in a note.", + Icon::AtSign, + Arc::new(|_, _| {}), + )), + NotificationItem::Message(Notification::new_actor_with_actions( + "notif-2", + "as-cii sent you a contact request.", + PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + [ + NotificationAction::new( + Button::new("Decline"), + "Decline Request", + (Some(Icon::XCircle), "Declined"), + ), + NotificationAction::new( + Button::new("Accept").variant(crate::ButtonVariant::Filled), + "Accept Request", + (Some(Icon::Check), "Accepted"), + ), + ], + )), + ] } + pub fn static_new_notification_items() -> Vec> { vec![ ListItem::Header(ListSubHeader::new("New")), From 229ba0744e2d736ca0c6012178d7743a47327181 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 1 Nov 2023 16:50:59 -0400 Subject: [PATCH 13/30] Add additional notifications and style tweaks Co-Authored-By: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com> --- .../ui2/src/components/notifications_panel.rs | 25 ++-- crates/ui2/src/elements/indicator.rs | 1 + crates/ui2/src/static_data.rs | 107 ++++++++++++++++-- 3 files changed, 111 insertions(+), 22 deletions(-) diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs index 6bcc9574c9..c53b60f213 100644 --- a/crates/ui2/src/components/notifications_panel.rs +++ b/crates/ui2/src/components/notifications_panel.rs @@ -1,6 +1,6 @@ use crate::{ - h_stack, prelude::*, static_new_notification_items, v_stack, Avatar, Button, Icon, IconButton, - IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator, + h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, Button, Icon, + IconButton, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator, UnreadIndicator, }; use crate::{ClickHandler, ListHeader}; @@ -51,17 +51,11 @@ impl NotificationsPanel { .line_height_style(LineHeightStyle::UILabel), ), ) - .children(static_new_notification_items()), + .child(v_stack().px_1().children(static_new_notification_items_2())), ) } } -pub enum NotificationItem { - Message(Notification), - // WithEdgeHeader(Notification), - WithRequiredActions(NotificationWithActions), -} - pub enum ButtonOrIconButton { Button(Button), IconButton(IconButton), @@ -106,11 +100,6 @@ impl NotificationAction { } } -pub struct NotificationWithActions { - notification: Notification, - actions: [NotificationAction; 2], -} - pub enum ActorOrIcon { Actor(PublicActor), Icon(Icon), @@ -280,21 +269,29 @@ impl Notification { div() .relative() .id(self.id.clone()) + .p_1() + .flex() + .flex_col() + .w_full() .children( Some( div() .absolute() .left(px(3.0)) .top_3() + .z_index(2) .child(UnreadIndicator::new()), ) .filter(|_| self.unread), ) .child( v_stack() + .z_index(1) .gap_1() + .w_full() .child( h_stack() + .w_full() .gap_2() .child(self.render_slot(cx)) .child(div().flex_1().child(Label::new(self.message.clone()))), diff --git a/crates/ui2/src/elements/indicator.rs b/crates/ui2/src/elements/indicator.rs index 86c83a1bf1..1f6e00e621 100644 --- a/crates/ui2/src/elements/indicator.rs +++ b/crates/ui2/src/elements/indicator.rs @@ -12,6 +12,7 @@ impl UnreadIndicator { fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { div() + .rounded_full() .border_2() .border_color(cx.theme().colors().surface) .w(px(9.0)) diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 7e206f0cf6..45e4dfa423 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -9,9 +9,8 @@ use theme2::ActiveTheme; use crate::{ Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus, HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListSubHeader, - Livestream, MicStatus, ModifierKeys, Notification, NotificationItem, PaletteItem, Player, - PlayerCallStatus, PlayerWithCallStatus, PublicActor, ScreenShareStatus, Symbol, Tab, - ToggleState, VideoStatus, + Livestream, MicStatus, ModifierKeys, Notification, PaletteItem, Player, PlayerCallStatus, + PlayerWithCallStatus, PublicActor, ScreenShareStatus, Symbol, Tab, ToggleState, VideoStatus, }; use crate::{HighlightedText, ListDetailsEntry}; use crate::{ListItem, NotificationAction}; @@ -328,15 +327,15 @@ pub fn static_players_with_call_status() -> Vec { ] } -pub fn static_new_notification_items_2() -> Vec> { +pub fn static_new_notification_items_2() -> Vec> { vec![ - NotificationItem::Message(Notification::new_icon_message( + Notification::new_icon_message( "notif-1", "You were mentioned in a note.", Icon::AtSign, Arc::new(|_, _| {}), - )), - NotificationItem::Message(Notification::new_actor_with_actions( + ), + Notification::new_actor_with_actions( "notif-2", "as-cii sent you a contact request.", PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), @@ -352,7 +351,99 @@ pub fn static_new_notification_items_2() -> Vec> (Some(Icon::Check), "Accepted"), ), ], - )), + ), + Notification::new_icon_message( + "notif-3", + "You were mentioned #design.", + Icon::MessageBubbles, + Arc::new(|_, _| {}), + ), + Notification::new_actor_with_actions( + "notif-4", + "as-cii sent you a contact request.", + PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + [ + NotificationAction::new( + Button::new("Decline"), + "Decline Request", + (Some(Icon::XCircle), "Declined"), + ), + NotificationAction::new( + Button::new("Accept").variant(crate::ButtonVariant::Filled), + "Accept Request", + (Some(Icon::Check), "Accepted"), + ), + ], + ), + Notification::new_icon_message( + "notif-5", + "You were mentioned in a note.", + Icon::AtSign, + Arc::new(|_, _| {}), + ), + Notification::new_actor_with_actions( + "notif-6", + "as-cii sent you a contact request.", + PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + [ + NotificationAction::new( + Button::new("Decline"), + "Decline Request", + (Some(Icon::XCircle), "Declined"), + ), + NotificationAction::new( + Button::new("Accept").variant(crate::ButtonVariant::Filled), + "Accept Request", + (Some(Icon::Check), "Accepted"), + ), + ], + ), + Notification::new_icon_message( + "notif-7", + "You were mentioned in a note.", + Icon::AtSign, + Arc::new(|_, _| {}), + ), + Notification::new_actor_with_actions( + "notif-8", + "as-cii sent you a contact request.", + PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + [ + NotificationAction::new( + Button::new("Decline"), + "Decline Request", + (Some(Icon::XCircle), "Declined"), + ), + NotificationAction::new( + Button::new("Accept").variant(crate::ButtonVariant::Filled), + "Accept Request", + (Some(Icon::Check), "Accepted"), + ), + ], + ), + Notification::new_icon_message( + "notif-9", + "You were mentioned in a note.", + Icon::AtSign, + Arc::new(|_, _| {}), + ), + Notification::new_actor_with_actions( + "notif-10", + "as-cii sent you a contact request.", + PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + [ + NotificationAction::new( + Button::new("Decline"), + "Decline Request", + (Some(Icon::XCircle), "Declined"), + ), + NotificationAction::new( + Button::new("Accept").variant(crate::ButtonVariant::Filled), + "Accept Request", + (Some(Icon::Check), "Accepted"), + ), + ], + ), ] } From 220228c183d7a2f5dcf161586f004196f306f895 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 21:15:06 -0400 Subject: [PATCH 14/30] Fix underflow when indexing into `ColorScale`s --- crates/theme2/src/scale.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/theme2/src/scale.rs b/crates/theme2/src/scale.rs index a22036df8d..c02f078c89 100644 --- a/crates/theme2/src/scale.rs +++ b/crates/theme2/src/scale.rs @@ -132,19 +132,19 @@ impl ColorScaleSet { } pub fn light(&self, step: ColorScaleStep) -> Hsla { - self.light[step - 1] + self.light[step] } pub fn light_alpha(&self, step: ColorScaleStep) -> Hsla { - self.light_alpha[step - 1] + self.light_alpha[step] } pub fn dark(&self, step: ColorScaleStep) -> Hsla { - self.dark[step - 1] + self.dark[step] } pub fn dark_alpha(&self, step: ColorScaleStep) -> Hsla { - self.dark_alpha[step - 1] + self.dark_alpha[step] } pub fn step(&self, cx: &AppContext, step: ColorScaleStep) -> Hsla { From b9ac1e43cdae9f3c19bd62281e61c281c612c630 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 22:00:16 -0400 Subject: [PATCH 15/30] Update scale accessors --- crates/theme2/src/default_colors.rs | 355 +++++++++++++++------------- 1 file changed, 188 insertions(+), 167 deletions(-) diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 6ef71009c5..15f4d6548e 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -27,17 +27,17 @@ impl Default for SystemColors { impl Default for StatusColors { fn default() -> Self { Self { - conflict: red().dark(11).into(), - created: grass().dark(11).into(), - deleted: red().dark(11).into(), - error: red().dark(11).into(), - hidden: neutral().dark(11).into(), - ignored: neutral().dark(11).into(), - info: blue().dark(11).into(), - modified: yellow().dark(11).into(), - renamed: blue().dark(11).into(), - success: grass().dark(11).into(), - warning: yellow().dark(11).into(), + conflict: red().dark().step_11().into(), + created: grass().dark().step_11().into(), + deleted: red().dark().step_11().into(), + error: red().dark().step_11().into(), + hidden: neutral().dark().step_11().into(), + ignored: neutral().dark().step_11().into(), + info: blue().dark().step_11().into(), + modified: yellow().dark().step_11().into(), + renamed: blue().dark().step_11().into(), + success: grass().dark().step_11().into(), + warning: yellow().dark().step_11().into(), } } } @@ -45,12 +45,12 @@ impl Default for StatusColors { impl Default for GitStatusColors { fn default() -> Self { Self { - conflict: orange().dark(11), - created: grass().dark(11), - deleted: red().dark(11), - ignored: neutral().dark(11), - modified: yellow().dark(11), - renamed: blue().dark(11), + conflict: orange().dark().step_11(), + created: grass().dark().step_11(), + deleted: red().dark().step_11(), + ignored: neutral().dark().step_11(), + modified: yellow().dark().step_11(), + renamed: blue().dark().step_11(), } } } @@ -86,45 +86,57 @@ impl SyntaxTheme { pub fn default_light() -> Self { Self { highlights: vec![ - ("attribute".into(), cyan().light(11).into()), - ("boolean".into(), tomato().light(11).into()), - ("comment".into(), neutral().light(11).into()), - ("comment.doc".into(), iris().light(12).into()), - ("constant".into(), red().light(7).into()), - ("constructor".into(), red().light(7).into()), - ("embedded".into(), red().light(7).into()), - ("emphasis".into(), red().light(7).into()), - ("emphasis.strong".into(), red().light(7).into()), - ("enum".into(), red().light(7).into()), - ("function".into(), red().light(7).into()), - ("hint".into(), red().light(7).into()), - ("keyword".into(), orange().light(11).into()), - ("label".into(), red().light(7).into()), - ("link_text".into(), red().light(7).into()), - ("link_uri".into(), red().light(7).into()), - ("number".into(), red().light(7).into()), - ("operator".into(), red().light(7).into()), - ("predictive".into(), red().light(7).into()), - ("preproc".into(), red().light(7).into()), - ("primary".into(), red().light(7).into()), - ("property".into(), red().light(7).into()), - ("punctuation".into(), neutral().light(11).into()), - ("punctuation.bracket".into(), neutral().light(11).into()), - ("punctuation.delimiter".into(), neutral().light(11).into()), - ("punctuation.list_marker".into(), blue().light(11).into()), - ("punctuation.special".into(), red().light(7).into()), - ("string".into(), jade().light(11).into()), - ("string.escape".into(), red().light(7).into()), - ("string.regex".into(), tomato().light(11).into()), - ("string.special".into(), red().light(7).into()), - ("string.special.symbol".into(), red().light(7).into()), - ("tag".into(), red().light(7).into()), - ("text.literal".into(), red().light(7).into()), - ("title".into(), red().light(7).into()), - ("type".into(), red().light(7).into()), - ("variable".into(), red().light(7).into()), - ("variable.special".into(), red().light(7).into()), - ("variant".into(), red().light(7).into()), + ("attribute".into(), cyan().light().step_11().into()), + ("boolean".into(), tomato().light().step_11().into()), + ("comment".into(), neutral().light().step_11().into()), + ("comment.doc".into(), iris().light().step_12().into()), + ("constant".into(), red().light().step_7().into()), + ("constructor".into(), red().light().step_7().into()), + ("embedded".into(), red().light().step_7().into()), + ("emphasis".into(), red().light().step_7().into()), + ("emphasis.strong".into(), red().light().step_7().into()), + ("enum".into(), red().light().step_7().into()), + ("function".into(), red().light().step_7().into()), + ("hint".into(), red().light().step_7().into()), + ("keyword".into(), orange().light().step_11().into()), + ("label".into(), red().light().step_7().into()), + ("link_text".into(), red().light().step_7().into()), + ("link_uri".into(), red().light().step_7().into()), + ("number".into(), red().light().step_7().into()), + ("operator".into(), red().light().step_7().into()), + ("predictive".into(), red().light().step_7().into()), + ("preproc".into(), red().light().step_7().into()), + ("primary".into(), red().light().step_7().into()), + ("property".into(), red().light().step_7().into()), + ("punctuation".into(), neutral().light().step_11().into()), + ( + "punctuation.bracket".into(), + neutral().light().step_11().into(), + ), + ( + "punctuation.delimiter".into(), + neutral().light().step_11().into(), + ), + ( + "punctuation.list_marker".into(), + blue().light().step_11().into(), + ), + ("punctuation.special".into(), red().light().step_7().into()), + ("string".into(), jade().light().step_11().into()), + ("string.escape".into(), red().light().step_7().into()), + ("string.regex".into(), tomato().light().step_11().into()), + ("string.special".into(), red().light().step_7().into()), + ( + "string.special.symbol".into(), + red().light().step_7().into(), + ), + ("tag".into(), red().light().step_7().into()), + ("text.literal".into(), red().light().step_7().into()), + ("title".into(), red().light().step_7().into()), + ("type".into(), red().light().step_7().into()), + ("variable".into(), red().light().step_7().into()), + ("variable.special".into(), red().light().step_7().into()), + ("variant".into(), red().light().step_7().into()), ], } } @@ -132,45 +144,54 @@ impl SyntaxTheme { pub fn default_dark() -> Self { Self { highlights: vec![ - ("attribute".into(), cyan().dark(11).into()), - ("boolean".into(), tomato().dark(11).into()), - ("comment".into(), neutral().dark(11).into()), - ("comment.doc".into(), iris().dark(12).into()), - ("constant".into(), red().dark(7).into()), - ("constructor".into(), red().dark(7).into()), - ("embedded".into(), red().dark(7).into()), - ("emphasis".into(), red().dark(7).into()), - ("emphasis.strong".into(), red().dark(7).into()), - ("enum".into(), red().dark(7).into()), - ("function".into(), red().dark(7).into()), - ("hint".into(), red().dark(7).into()), - ("keyword".into(), orange().dark(11).into()), - ("label".into(), red().dark(7).into()), - ("link_text".into(), red().dark(7).into()), - ("link_uri".into(), red().dark(7).into()), - ("number".into(), red().dark(7).into()), - ("operator".into(), red().dark(7).into()), - ("predictive".into(), red().dark(7).into()), - ("preproc".into(), red().dark(7).into()), - ("primary".into(), red().dark(7).into()), - ("property".into(), red().dark(7).into()), - ("punctuation".into(), neutral().dark(11).into()), - ("punctuation.bracket".into(), neutral().dark(11).into()), - ("punctuation.delimiter".into(), neutral().dark(11).into()), - ("punctuation.list_marker".into(), blue().dark(11).into()), - ("punctuation.special".into(), red().dark(7).into()), - ("string".into(), jade().dark(11).into()), - ("string.escape".into(), red().dark(7).into()), - ("string.regex".into(), tomato().dark(11).into()), - ("string.special".into(), red().dark(7).into()), - ("string.special.symbol".into(), red().dark(7).into()), - ("tag".into(), red().dark(7).into()), - ("text.literal".into(), red().dark(7).into()), - ("title".into(), red().dark(7).into()), - ("type".into(), red().dark(7).into()), - ("variable".into(), red().dark(7).into()), - ("variable.special".into(), red().dark(7).into()), - ("variant".into(), red().dark(7).into()), + ("attribute".into(), cyan().dark().step_11().into()), + ("boolean".into(), tomato().dark().step_11().into()), + ("comment".into(), neutral().dark().step_11().into()), + ("comment.doc".into(), iris().dark().step_12().into()), + ("constant".into(), red().dark().step_7().into()), + ("constructor".into(), red().dark().step_7().into()), + ("embedded".into(), red().dark().step_7().into()), + ("emphasis".into(), red().dark().step_7().into()), + ("emphasis.strong".into(), red().dark().step_7().into()), + ("enum".into(), red().dark().step_7().into()), + ("function".into(), red().dark().step_7().into()), + ("hint".into(), red().dark().step_7().into()), + ("keyword".into(), orange().dark().step_11().into()), + ("label".into(), red().dark().step_7().into()), + ("link_text".into(), red().dark().step_7().into()), + ("link_uri".into(), red().dark().step_7().into()), + ("number".into(), red().dark().step_7().into()), + ("operator".into(), red().dark().step_7().into()), + ("predictive".into(), red().dark().step_7().into()), + ("preproc".into(), red().dark().step_7().into()), + ("primary".into(), red().dark().step_7().into()), + ("property".into(), red().dark().step_7().into()), + ("punctuation".into(), neutral().dark().step_11().into()), + ( + "punctuation.bracket".into(), + neutral().dark().step_11().into(), + ), + ( + "punctuation.delimiter".into(), + neutral().dark().step_11().into(), + ), + ( + "punctuation.list_marker".into(), + blue().dark().step_11().into(), + ), + ("punctuation.special".into(), red().dark().step_7().into()), + ("string".into(), jade().dark().step_11().into()), + ("string.escape".into(), red().dark().step_7().into()), + ("string.regex".into(), tomato().dark().step_11().into()), + ("string.special".into(), red().dark().step_7().into()), + ("string.special.symbol".into(), red().dark().step_7().into()), + ("tag".into(), red().dark().step_7().into()), + ("text.literal".into(), red().dark().step_7().into()), + ("title".into(), red().dark().step_7().into()), + ("type".into(), red().dark().step_7().into()), + ("variable".into(), red().dark().step_7().into()), + ("variable.special".into(), red().dark().step_7().into()), + ("variant".into(), red().dark().step_7().into()), ], } } @@ -181,44 +202,44 @@ impl ThemeColors { let system = SystemColors::default(); Self { - border: neutral().light(6).into(), - border_variant: neutral().light(5).into(), - border_focused: blue().light(5).into(), + border: neutral().light().step_6().into(), + border_variant: neutral().light().step_5().into(), + border_focused: blue().light().step_5().into(), border_transparent: system.transparent, - elevated_surface: neutral().light(2).into(), - surface: neutral().light(2).into(), - background: neutral().light(1).into(), - element: neutral().light(3).into(), - element_hover: neutral().light(4).into(), - element_active: neutral().light(5).into(), - element_selected: neutral().light(5).into(), - element_disabled: neutral().light_alpha(3).into(), - element_placeholder: neutral().light(11).into(), - element_drop_target: blue().light_alpha(2).into(), + elevated_surface: neutral().light().step_2().into(), + surface: neutral().light().step_2().into(), + background: neutral().light().step_1().into(), + element: neutral().light().step_3().into(), + element_hover: neutral().light().step_4().into(), + element_active: neutral().light().step_5().into(), + element_selected: neutral().light().step_5().into(), + element_disabled: neutral().light_alpha().step_3().into(), + element_placeholder: neutral().light().step_11().into(), + element_drop_target: blue().light_alpha().step_2().into(), ghost_element: system.transparent, - ghost_element_hover: neutral().light(4).into(), - ghost_element_active: neutral().light(5).into(), - ghost_element_selected: neutral().light(5).into(), - ghost_element_disabled: neutral().light_alpha(3).into(), - text: neutral().light(12).into(), - text_muted: neutral().light(11).into(), - text_placeholder: neutral().light(10).into(), - text_disabled: neutral().light(9).into(), - text_accent: blue().light(11).into(), - icon: neutral().light(11).into(), - icon_muted: neutral().light(10).into(), - icon_disabled: neutral().light(9).into(), - icon_placeholder: neutral().light(10).into(), - icon_accent: blue().light(11).into(), - status_bar: neutral().light(2).into(), - title_bar: neutral().light(2).into(), - toolbar: neutral().light(1).into(), - tab_bar: neutral().light(2).into(), - tab_active: neutral().light(1).into(), - tab_inactive: neutral().light(2).into(), - editor: neutral().light(1).into(), - editor_subheader: neutral().light(2).into(), - editor_active_line: neutral().light_alpha(3).into(), + ghost_element_hover: neutral().light().step_4().into(), + ghost_element_active: neutral().light().step_5().into(), + ghost_element_selected: neutral().light().step_5().into(), + ghost_element_disabled: neutral().light_alpha().step_3().into(), + text: neutral().light().step_12().into(), + text_muted: neutral().light().step_11().into(), + text_placeholder: neutral().light().step_10().into(), + text_disabled: neutral().light().step_9().into(), + text_accent: blue().light().step_11().into(), + icon: neutral().light().step_11().into(), + icon_muted: neutral().light().step_10().into(), + icon_disabled: neutral().light().step_9().into(), + icon_placeholder: neutral().light().step_10().into(), + icon_accent: blue().light().step_11().into(), + status_bar: neutral().light().step_2().into(), + title_bar: neutral().light().step_2().into(), + toolbar: neutral().light().step_1().into(), + tab_bar: neutral().light().step_2().into(), + tab_active: neutral().light().step_1().into(), + tab_inactive: neutral().light().step_2().into(), + editor: neutral().light().step_1().into(), + editor_subheader: neutral().light().step_2().into(), + editor_active_line: neutral().light_alpha().step_3().into(), } } @@ -226,44 +247,44 @@ impl ThemeColors { let system = SystemColors::default(); Self { - border: neutral().dark(6).into(), - border_variant: neutral().dark(5).into(), - border_focused: blue().dark(5).into(), + border: neutral().dark().step_6().into(), + border_variant: neutral().dark().step_5().into(), + border_focused: blue().dark().step_5().into(), border_transparent: system.transparent, - elevated_surface: neutral().dark(2).into(), - surface: neutral().dark(2).into(), - background: neutral().dark(1).into(), - element: neutral().dark(3).into(), - element_hover: neutral().dark(4).into(), - element_active: neutral().dark(5).into(), - element_selected: neutral().dark(5).into(), - element_disabled: neutral().dark_alpha(3).into(), - element_placeholder: neutral().dark(11).into(), - element_drop_target: blue().dark_alpha(2).into(), + elevated_surface: neutral().dark().step_2().into(), + surface: neutral().dark().step_2().into(), + background: neutral().dark().step_1().into(), + element: neutral().dark().step_3().into(), + element_hover: neutral().dark().step_4().into(), + element_active: neutral().dark().step_5().into(), + element_selected: neutral().dark().step_5().into(), + element_disabled: neutral().dark_alpha().step_3().into(), + element_placeholder: neutral().dark().step_11().into(), + element_drop_target: blue().dark_alpha().step_2().into(), ghost_element: system.transparent, - ghost_element_hover: neutral().dark(4).into(), - ghost_element_active: neutral().dark(5).into(), - ghost_element_selected: neutral().dark(5).into(), - ghost_element_disabled: neutral().dark_alpha(3).into(), - text: neutral().dark(12).into(), - text_muted: neutral().dark(11).into(), - text_placeholder: neutral().dark(10).into(), - text_disabled: neutral().dark(9).into(), - text_accent: blue().dark(11).into(), - icon: neutral().dark(11).into(), - icon_muted: neutral().dark(10).into(), - icon_disabled: neutral().dark(9).into(), - icon_placeholder: neutral().dark(10).into(), - icon_accent: blue().dark(11).into(), - status_bar: neutral().dark(2).into(), - title_bar: neutral().dark(2).into(), - toolbar: neutral().dark(1).into(), - tab_bar: neutral().dark(2).into(), - tab_active: neutral().dark(1).into(), - tab_inactive: neutral().dark(2).into(), - editor: neutral().dark(1).into(), - editor_subheader: neutral().dark(2).into(), - editor_active_line: neutral().dark_alpha(3).into(), + ghost_element_hover: neutral().dark().step_4().into(), + ghost_element_active: neutral().dark().step_5().into(), + ghost_element_selected: neutral().dark().step_5().into(), + ghost_element_disabled: neutral().dark_alpha().step_3().into(), + text: neutral().dark().step_12().into(), + text_muted: neutral().dark().step_11().into(), + text_placeholder: neutral().dark().step_10().into(), + text_disabled: neutral().dark().step_9().into(), + text_accent: blue().dark().step_11().into(), + icon: neutral().dark().step_11().into(), + icon_muted: neutral().dark().step_10().into(), + icon_disabled: neutral().dark().step_9().into(), + icon_placeholder: neutral().dark().step_10().into(), + icon_accent: blue().dark().step_11().into(), + status_bar: neutral().dark().step_2().into(), + title_bar: neutral().dark().step_2().into(), + toolbar: neutral().dark().step_1().into(), + tab_bar: neutral().dark().step_2().into(), + tab_active: neutral().dark().step_1().into(), + tab_inactive: neutral().dark().step_2().into(), + editor: neutral().dark().step_1().into(), + editor_subheader: neutral().dark().step_2().into(), + editor_active_line: neutral().dark_alpha().step_3().into(), } } } From aa14552feec49059729337630330f5a44c7cae45 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 1 Nov 2023 22:01:59 -0400 Subject: [PATCH 16/30] Remove unneeded `.into`s --- crates/theme2/src/default_colors.rs | 166 ++++++++++++++-------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 15f4d6548e..8b7137683c 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -27,17 +27,17 @@ impl Default for SystemColors { impl Default for StatusColors { fn default() -> Self { Self { - conflict: red().dark().step_11().into(), - created: grass().dark().step_11().into(), - deleted: red().dark().step_11().into(), - error: red().dark().step_11().into(), - hidden: neutral().dark().step_11().into(), - ignored: neutral().dark().step_11().into(), - info: blue().dark().step_11().into(), - modified: yellow().dark().step_11().into(), - renamed: blue().dark().step_11().into(), - success: grass().dark().step_11().into(), - warning: yellow().dark().step_11().into(), + conflict: red().dark().step_11(), + created: grass().dark().step_11(), + deleted: red().dark().step_11(), + error: red().dark().step_11(), + hidden: neutral().dark().step_11(), + ignored: neutral().dark().step_11(), + info: blue().dark().step_11(), + modified: yellow().dark().step_11(), + renamed: blue().dark().step_11(), + success: grass().dark().step_11(), + warning: yellow().dark().step_11(), } } } @@ -202,44 +202,44 @@ impl ThemeColors { let system = SystemColors::default(); Self { - border: neutral().light().step_6().into(), - border_variant: neutral().light().step_5().into(), - border_focused: blue().light().step_5().into(), + border: neutral().light().step_6(), + border_variant: neutral().light().step_5(), + border_focused: blue().light().step_5(), border_transparent: system.transparent, - elevated_surface: neutral().light().step_2().into(), - surface: neutral().light().step_2().into(), - background: neutral().light().step_1().into(), - element: neutral().light().step_3().into(), - element_hover: neutral().light().step_4().into(), - element_active: neutral().light().step_5().into(), - element_selected: neutral().light().step_5().into(), - element_disabled: neutral().light_alpha().step_3().into(), - element_placeholder: neutral().light().step_11().into(), - element_drop_target: blue().light_alpha().step_2().into(), + elevated_surface: neutral().light().step_2(), + surface: neutral().light().step_2(), + background: neutral().light().step_1(), + element: neutral().light().step_3(), + element_hover: neutral().light().step_4(), + element_active: neutral().light().step_5(), + element_selected: neutral().light().step_5(), + element_disabled: neutral().light_alpha().step_3(), + element_placeholder: neutral().light().step_11(), + element_drop_target: blue().light_alpha().step_2(), ghost_element: system.transparent, - ghost_element_hover: neutral().light().step_4().into(), - ghost_element_active: neutral().light().step_5().into(), - ghost_element_selected: neutral().light().step_5().into(), - ghost_element_disabled: neutral().light_alpha().step_3().into(), - text: neutral().light().step_12().into(), - text_muted: neutral().light().step_11().into(), - text_placeholder: neutral().light().step_10().into(), - text_disabled: neutral().light().step_9().into(), - text_accent: blue().light().step_11().into(), - icon: neutral().light().step_11().into(), - icon_muted: neutral().light().step_10().into(), - icon_disabled: neutral().light().step_9().into(), - icon_placeholder: neutral().light().step_10().into(), - icon_accent: blue().light().step_11().into(), - status_bar: neutral().light().step_2().into(), - title_bar: neutral().light().step_2().into(), - toolbar: neutral().light().step_1().into(), - tab_bar: neutral().light().step_2().into(), - tab_active: neutral().light().step_1().into(), - tab_inactive: neutral().light().step_2().into(), - editor: neutral().light().step_1().into(), - editor_subheader: neutral().light().step_2().into(), - editor_active_line: neutral().light_alpha().step_3().into(), + ghost_element_hover: neutral().light().step_4(), + ghost_element_active: neutral().light().step_5(), + ghost_element_selected: neutral().light().step_5(), + ghost_element_disabled: neutral().light_alpha().step_3(), + text: neutral().light().step_12(), + text_muted: neutral().light().step_11(), + text_placeholder: neutral().light().step_10(), + text_disabled: neutral().light().step_9(), + text_accent: blue().light().step_11(), + icon: neutral().light().step_11(), + icon_muted: neutral().light().step_10(), + icon_disabled: neutral().light().step_9(), + icon_placeholder: neutral().light().step_10(), + icon_accent: blue().light().step_11(), + status_bar: neutral().light().step_2(), + title_bar: neutral().light().step_2(), + toolbar: neutral().light().step_1(), + tab_bar: neutral().light().step_2(), + tab_active: neutral().light().step_1(), + tab_inactive: neutral().light().step_2(), + editor: neutral().light().step_1(), + editor_subheader: neutral().light().step_2(), + editor_active_line: neutral().light_alpha().step_3(), } } @@ -247,44 +247,44 @@ impl ThemeColors { let system = SystemColors::default(); Self { - border: neutral().dark().step_6().into(), - border_variant: neutral().dark().step_5().into(), - border_focused: blue().dark().step_5().into(), + border: neutral().dark().step_6(), + border_variant: neutral().dark().step_5(), + border_focused: blue().dark().step_5(), border_transparent: system.transparent, - elevated_surface: neutral().dark().step_2().into(), - surface: neutral().dark().step_2().into(), - background: neutral().dark().step_1().into(), - element: neutral().dark().step_3().into(), - element_hover: neutral().dark().step_4().into(), - element_active: neutral().dark().step_5().into(), - element_selected: neutral().dark().step_5().into(), - element_disabled: neutral().dark_alpha().step_3().into(), - element_placeholder: neutral().dark().step_11().into(), - element_drop_target: blue().dark_alpha().step_2().into(), + elevated_surface: neutral().dark().step_2(), + surface: neutral().dark().step_2(), + background: neutral().dark().step_1(), + element: neutral().dark().step_3(), + element_hover: neutral().dark().step_4(), + element_active: neutral().dark().step_5(), + element_selected: neutral().dark().step_5(), + element_disabled: neutral().dark_alpha().step_3(), + element_placeholder: neutral().dark().step_11(), + element_drop_target: blue().dark_alpha().step_2(), ghost_element: system.transparent, - ghost_element_hover: neutral().dark().step_4().into(), - ghost_element_active: neutral().dark().step_5().into(), - ghost_element_selected: neutral().dark().step_5().into(), - ghost_element_disabled: neutral().dark_alpha().step_3().into(), - text: neutral().dark().step_12().into(), - text_muted: neutral().dark().step_11().into(), - text_placeholder: neutral().dark().step_10().into(), - text_disabled: neutral().dark().step_9().into(), - text_accent: blue().dark().step_11().into(), - icon: neutral().dark().step_11().into(), - icon_muted: neutral().dark().step_10().into(), - icon_disabled: neutral().dark().step_9().into(), - icon_placeholder: neutral().dark().step_10().into(), - icon_accent: blue().dark().step_11().into(), - status_bar: neutral().dark().step_2().into(), - title_bar: neutral().dark().step_2().into(), - toolbar: neutral().dark().step_1().into(), - tab_bar: neutral().dark().step_2().into(), - tab_active: neutral().dark().step_1().into(), - tab_inactive: neutral().dark().step_2().into(), - editor: neutral().dark().step_1().into(), - editor_subheader: neutral().dark().step_2().into(), - editor_active_line: neutral().dark_alpha().step_3().into(), + ghost_element_hover: neutral().dark().step_4(), + ghost_element_active: neutral().dark().step_5(), + ghost_element_selected: neutral().dark().step_5(), + ghost_element_disabled: neutral().dark_alpha().step_3(), + text: neutral().dark().step_12(), + text_muted: neutral().dark().step_11(), + text_placeholder: neutral().dark().step_10(), + text_disabled: neutral().dark().step_9(), + text_accent: blue().dark().step_11(), + icon: neutral().dark().step_11(), + icon_muted: neutral().dark().step_10(), + icon_disabled: neutral().dark().step_9(), + icon_placeholder: neutral().dark().step_10(), + icon_accent: blue().dark().step_11(), + status_bar: neutral().dark().step_2(), + title_bar: neutral().dark().step_2(), + toolbar: neutral().dark().step_1(), + tab_bar: neutral().dark().step_2(), + tab_active: neutral().dark().step_1(), + tab_inactive: neutral().dark().step_2(), + editor: neutral().dark().step_1(), + editor_subheader: neutral().dark().step_2(), + editor_active_line: neutral().dark_alpha().step_3(), } } } From f724b6d032c8ff0fff237ae2b8ab0bbee63b64e9 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Thu, 2 Nov 2023 09:05:16 -0400 Subject: [PATCH 17/30] Cleanly truncate Discord release notes --- .github/workflows/release_actions.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release_actions.yml b/.github/workflows/release_actions.yml index 550eda882b..a72abf300d 100644 --- a/.github/workflows/release_actions.yml +++ b/.github/workflows/release_actions.yml @@ -16,7 +16,7 @@ jobs: fi echo "::set-output name=URL::$URL" - name: Get content - uses: 2428392/gh-truncate-string-action@v1.2.0 + uses: 2428392/gh-truncate-string-action@v1.3.0 id: get-content with: stringToTruncate: | @@ -24,6 +24,7 @@ jobs: ${{ github.event.release.body }} maxLength: 2000 + truncationSymbol: "..." - name: Discord Webhook Action uses: tsickert/discord-webhook@v5.3.0 with: From 971563fd489667624038c117ac8623b4e6d420fc Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Thu, 2 Nov 2023 09:05:29 -0400 Subject: [PATCH 18/30] Format YAML --- .github/workflows/release_actions.yml | 46 +++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/release_actions.yml b/.github/workflows/release_actions.yml index a72abf300d..c1d2457ed4 100644 --- a/.github/workflows/release_actions.yml +++ b/.github/workflows/release_actions.yml @@ -6,27 +6,27 @@ jobs: discord_release: runs-on: ubuntu-latest steps: - - name: Get release URL - id: get-release-url - run: | - if [ "${{ github.event.release.prerelease }}" == "true" ]; then - URL="https://zed.dev/releases/preview/latest" - else - URL="https://zed.dev/releases/stable/latest" - fi - echo "::set-output name=URL::$URL" - - name: Get content - uses: 2428392/gh-truncate-string-action@v1.3.0 - id: get-content - with: - stringToTruncate: | - 📣 Zed [${{ github.event.release.tag_name }}](${{ steps.get-release-url.outputs.URL }}) was just released! + - name: Get release URL + id: get-release-url + run: | + if [ "${{ github.event.release.prerelease }}" == "true" ]; then + URL="https://zed.dev/releases/preview/latest" + else + URL="https://zed.dev/releases/stable/latest" + fi + echo "::set-output name=URL::$URL" + - name: Get content + uses: 2428392/gh-truncate-string-action@v1.3.0 + id: get-content + with: + stringToTruncate: | + 📣 Zed [${{ github.event.release.tag_name }}](${{ steps.get-release-url.outputs.URL }}) was just released! - ${{ github.event.release.body }} - maxLength: 2000 - truncationSymbol: "..." - - name: Discord Webhook Action - uses: tsickert/discord-webhook@v5.3.0 - with: - webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} - content: ${{ steps.get-content.outputs.string }} + ${{ github.event.release.body }} + maxLength: 2000 + truncationSymbol: "..." + - name: Discord Webhook Action + uses: tsickert/discord-webhook@v5.3.0 + with: + webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} + content: ${{ steps.get-content.outputs.string }} From b5fe0d72ee4328d9d7a6d9cf9ec0e371a7d0a3f1 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 2 Nov 2023 09:34:18 -0400 Subject: [PATCH 19/30] authenticate with completion provider on new inline assists --- crates/assistant/src/assistant_panel.rs | 15 +++++++++------ crates/assistant/src/codegen.rs | 14 ++++++++------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 03eb3c238f..022c228790 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -259,7 +259,13 @@ impl AssistantPanel { cx: &mut ViewContext, ) { let this = if let Some(this) = workspace.panel::(cx) { - if this.update(cx, |assistant, _| assistant.has_credentials()) { + if this.update(cx, |assistant, cx| { + if !assistant.has_credentials() { + assistant.load_credentials(cx); + }; + + assistant.has_credentials() + }) { this } else { workspace.focus_panel::(cx); @@ -320,13 +326,10 @@ impl AssistantPanel { }; let inline_assist_id = post_inc(&mut self.next_inline_assist_id); - let provider = Arc::new(OpenAICompletionProvider::new( - "gpt-4", - cx.background().clone(), - )); + let provider = self.completion_provider.clone(); // Retrieve Credentials Authenticates the Provider - // provider.retrieve_credentials(cx); + provider.retrieve_credentials(cx); let codegen = cx.add_model(|cx| { Codegen::new(editor.read(cx).buffer().clone(), codegen_kind, provider, cx) diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index f62c91fcb7..da7beda2dc 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -6,7 +6,7 @@ use futures::{channel::mpsc, SinkExt, Stream, StreamExt}; use gpui::{Entity, ModelContext, ModelHandle, Task}; use language::{Rope, TransactionId}; use multi_buffer; -use std::{cmp, future, ops::Range, sync::Arc}; +use std::{cmp, future, ops::Range}; pub enum Event { Finished, @@ -20,7 +20,7 @@ pub enum CodegenKind { } pub struct Codegen { - provider: Arc, + provider: Box, buffer: ModelHandle, snapshot: MultiBufferSnapshot, kind: CodegenKind, @@ -40,7 +40,7 @@ impl Codegen { pub fn new( buffer: ModelHandle, kind: CodegenKind, - provider: Arc, + provider: Box, cx: &mut ModelContext, ) -> Self { let snapshot = buffer.read(cx).snapshot(cx); @@ -367,6 +367,8 @@ fn strip_invalid_spans_from_codeblock( #[cfg(test)] mod tests { + use std::sync::Arc; + use super::*; use ai::test::FakeCompletionProvider; use futures::stream::{self}; @@ -412,7 +414,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(4, 5)) }); - let provider = Arc::new(FakeCompletionProvider::new()); + let provider = Box::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), @@ -478,7 +480,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 6)) }); - let provider = Arc::new(FakeCompletionProvider::new()); + let provider = Box::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), @@ -544,7 +546,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 2)) }); - let provider = Arc::new(FakeCompletionProvider::new()); + let provider = Box::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), From d5b6300fd7edb6441516be5004887a5090e5a599 Mon Sep 17 00:00:00 2001 From: KCaverly Date: Thu, 2 Nov 2023 10:08:47 -0400 Subject: [PATCH 20/30] moved from Boxes to Arcs for shared access of completion providers across the assistant panel and inline assistant --- crates/ai/src/test.rs | 11 ++++++++++- crates/assistant/src/assistant_panel.rs | 20 ++++++++++---------- crates/assistant/src/codegen.rs | 14 ++++++++------ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/crates/ai/src/test.rs b/crates/ai/src/test.rs index d4165f3cca..3f331da117 100644 --- a/crates/ai/src/test.rs +++ b/crates/ai/src/test.rs @@ -153,10 +153,17 @@ impl FakeCompletionProvider { pub fn send_completion(&self, completion: impl Into) { let mut tx = self.last_completion_tx.lock(); - tx.as_mut().unwrap().try_send(completion.into()).unwrap(); + + println!("COMPLETION TX: {:?}", &tx); + + let a = tx.as_mut().unwrap(); + a.try_send(completion.into()).unwrap(); + + // tx.as_mut().unwrap().try_send(completion.into()).unwrap(); } pub fn finish_completion(&self) { + println!("FINISHING COMPLETION"); self.last_completion_tx.lock().take().unwrap(); } } @@ -181,8 +188,10 @@ impl CompletionProvider for FakeCompletionProvider { &self, _prompt: Box, ) -> BoxFuture<'static, anyhow::Result>>> { + println!("COMPLETING"); let (tx, rx) = mpsc::channel(1); *self.last_completion_tx.lock() = Some(tx); + println!("TX: {:?}", *self.last_completion_tx.lock()); async move { Ok(rx.map(|rx| Ok(rx)).boxed()) }.boxed() } fn box_clone(&self) -> Box { diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 022c228790..6ab96093a7 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -142,7 +142,7 @@ pub struct AssistantPanel { zoomed: bool, has_focus: bool, toolbar: ViewHandle, - completion_provider: Box, + completion_provider: Arc, api_key_editor: Option>, languages: Arc, fs: Arc, @@ -204,7 +204,7 @@ impl AssistantPanel { let semantic_index = SemanticIndex::global(cx); // Defaulting currently to GPT4, allow for this to be set via config. - let completion_provider = Box::new(OpenAICompletionProvider::new( + let completion_provider = Arc::new(OpenAICompletionProvider::new( "gpt-4", cx.background().clone(), )); @@ -1442,7 +1442,7 @@ struct Conversation { pending_save: Task>, path: Option, _subscriptions: Vec, - completion_provider: Box, + completion_provider: Arc, } impl Entity for Conversation { @@ -1453,7 +1453,7 @@ impl Conversation { fn new( language_registry: Arc, cx: &mut ModelContext, - completion_provider: Box, + completion_provider: Arc, ) -> Self { let markdown = language_registry.language_for_name("Markdown"); let buffer = cx.add_model(|cx| { @@ -1547,7 +1547,7 @@ impl Conversation { None => Some(Uuid::new_v4().to_string()), }; let model = saved_conversation.model; - let completion_provider: Box = Box::new( + let completion_provider: Arc = Arc::new( OpenAICompletionProvider::new(model.full_name(), cx.background().clone()), ); completion_provider.retrieve_credentials(cx); @@ -2204,7 +2204,7 @@ struct ConversationEditor { impl ConversationEditor { fn new( - completion_provider: Box, + completion_provider: Arc, language_registry: Arc, fs: Arc, workspace: WeakViewHandle, @@ -3409,7 +3409,7 @@ mod tests { init(cx); let registry = Arc::new(LanguageRegistry::test()); - let completion_provider = Box::new(FakeCompletionProvider::new()); + let completion_provider = Arc::new(FakeCompletionProvider::new()); let conversation = cx.add_model(|cx| Conversation::new(registry, cx, completion_provider)); let buffer = conversation.read(cx).buffer.clone(); @@ -3538,7 +3538,7 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let completion_provider = Box::new(FakeCompletionProvider::new()); + let completion_provider = Arc::new(FakeCompletionProvider::new()); let conversation = cx.add_model(|cx| Conversation::new(registry, cx, completion_provider)); let buffer = conversation.read(cx).buffer.clone(); @@ -3636,7 +3636,7 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let completion_provider = Box::new(FakeCompletionProvider::new()); + let completion_provider = Arc::new(FakeCompletionProvider::new()); let conversation = cx.add_model(|cx| Conversation::new(registry, cx, completion_provider)); let buffer = conversation.read(cx).buffer.clone(); @@ -3719,7 +3719,7 @@ mod tests { cx.set_global(SettingsStore::test(cx)); init(cx); let registry = Arc::new(LanguageRegistry::test()); - let completion_provider = Box::new(FakeCompletionProvider::new()); + let completion_provider = Arc::new(FakeCompletionProvider::new()); let conversation = cx.add_model(|cx| Conversation::new(registry.clone(), cx, completion_provider)); let buffer = conversation.read(cx).buffer.clone(); diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index da7beda2dc..25c9deef7f 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -6,7 +6,7 @@ use futures::{channel::mpsc, SinkExt, Stream, StreamExt}; use gpui::{Entity, ModelContext, ModelHandle, Task}; use language::{Rope, TransactionId}; use multi_buffer; -use std::{cmp, future, ops::Range}; +use std::{cmp, future, ops::Range, sync::Arc}; pub enum Event { Finished, @@ -20,7 +20,7 @@ pub enum CodegenKind { } pub struct Codegen { - provider: Box, + provider: Arc, buffer: ModelHandle, snapshot: MultiBufferSnapshot, kind: CodegenKind, @@ -40,7 +40,7 @@ impl Codegen { pub fn new( buffer: ModelHandle, kind: CodegenKind, - provider: Box, + provider: Arc, cx: &mut ModelContext, ) -> Self { let snapshot = buffer.read(cx).snapshot(cx); @@ -414,7 +414,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(4, 5)) }); - let provider = Box::new(FakeCompletionProvider::new()); + let provider = Arc::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), @@ -439,6 +439,7 @@ mod tests { let max_len = cmp::min(new_text.len(), 10); let len = rng.gen_range(1..=max_len); let (chunk, suffix) = new_text.split_at(len); + println!("CHUNK: {:?}", &chunk); provider.send_completion(chunk); new_text = suffix; deterministic.run_until_parked(); @@ -480,7 +481,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 6)) }); - let provider = Box::new(FakeCompletionProvider::new()); + let provider = Arc::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), @@ -546,7 +547,7 @@ mod tests { let snapshot = buffer.snapshot(cx); snapshot.anchor_before(Point::new(1, 2)) }); - let provider = Box::new(FakeCompletionProvider::new()); + let provider = Arc::new(FakeCompletionProvider::new()); let codegen = cx.add_model(|cx| { Codegen::new( buffer.clone(), @@ -571,6 +572,7 @@ mod tests { let max_len = cmp::min(new_text.len(), 10); let len = rng.gen_range(1..=max_len); let (chunk, suffix) = new_text.split_at(len); + println!("{:?}", &chunk); provider.send_completion(chunk); new_text = suffix; deterministic.run_until_parked(); From cde7b244bc471abcd6a040338f9f7c7c35df744e Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 2 Nov 2023 10:11:25 -0400 Subject: [PATCH 21/30] Checkpoint - impl format_distance --- crates/ui2/src/lib.rs | 1 + crates/ui2/src/utils.rs | 3 + crates/ui2/src/utils/format_distance.rs | 166 ++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 crates/ui2/src/utils.rs create mode 100644 crates/ui2/src/utils/format_distance.rs diff --git a/crates/ui2/src/lib.rs b/crates/ui2/src/lib.rs index c1da5e410d..5d0a57c6d9 100644 --- a/crates/ui2/src/lib.rs +++ b/crates/ui2/src/lib.rs @@ -23,6 +23,7 @@ mod elevation; pub mod prelude; pub mod settings; mod static_data; +pub mod utils; pub use components::*; pub use elements::*; diff --git a/crates/ui2/src/utils.rs b/crates/ui2/src/utils.rs new file mode 100644 index 0000000000..573a1333ef --- /dev/null +++ b/crates/ui2/src/utils.rs @@ -0,0 +1,3 @@ +mod format_distance; + +pub use format_distance::*; diff --git a/crates/ui2/src/utils/format_distance.rs b/crates/ui2/src/utils/format_distance.rs new file mode 100644 index 0000000000..6835e530d2 --- /dev/null +++ b/crates/ui2/src/utils/format_distance.rs @@ -0,0 +1,166 @@ +use chrono::NaiveDateTime; + +fn distance_in_seconds(date: NaiveDateTime, base_date: NaiveDateTime) -> i64 { + let duration = date.signed_duration_since(base_date); + duration.num_seconds() +} + +fn distance_string(distance: i64, include_seconds: bool, add_suffix: bool) -> String { + let suffix = if distance < 0 { " from now" } else { " ago" }; + + let d = distance.abs(); + + let minutes = d / 60; + let hours = d / 3600; + let days = d / 86400; + let months = d / 2592000; + let years = d / 31536000; + + let string = if d < 5 && include_seconds { + "less than 5 seconds".to_string() + } else if d < 10 && include_seconds { + "less than 10 seconds".to_string() + } else if d < 20 && include_seconds { + "less than 20 seconds".to_string() + } else if d < 40 && include_seconds { + "half a minute".to_string() + } else if d < 60 && include_seconds { + "less than a minute".to_string() + } else if d < 90 && include_seconds { + "1 minute".to_string() + } else if d < 30 { + "less than a minute".to_string() + } else if d < 90 { + "1 minute".to_string() + } else if d < 2700 { + format!("{} minutes", minutes) + } else if d < 5400 { + "about 1 hour".to_string() + } else if d < 86400 { + format!("about {} hours", hours) + } else if d < 172800 { + "1 day".to_string() + } else if d < 2592000 { + format!("{} days", days) + } else if d < 5184000 { + "about 1 month".to_string() + } else if d < 7776000 { + "about 2 months".to_string() + } else if d < 31536000 { + format!("{} months", months) + } else if d < 39408000 { + "about 1 year".to_string() + } else if d < 47318400 { + "over 1 year".to_string() + } else if d < 63072000 { + "almost 2 years".to_string() + } else { + format!("{} years", years) + }; + + if add_suffix { + return format!("{}{}", string, suffix); + } else { + string + } +} + +pub fn naive_format_distance( + date: NaiveDateTime, + base_date: NaiveDateTime, + include_seconds: bool, + add_suffix: bool, +) -> String { + let distance = distance_in_seconds(date, base_date); + + distance_string(distance, include_seconds, add_suffix) +} + +pub fn naive_format_distance_from_now( + datetime: NaiveDateTime, + include_seconds: bool, + add_suffix: bool, +) -> String { + let now = chrono::offset::Local::now().naive_local(); + + naive_format_distance(datetime, now, include_seconds, add_suffix) +} + +#[cfg(test)] +mod tests { + use super::*; + use chrono::NaiveDateTime; + + // #[test] + // fn test_naive_format_distance() { + // let date = NaiveDateTime::from_timestamp_opt(0, 0); + // let base_date = NaiveDateTime::from_timestamp_opt(9400, 0); + + // assert_eq!( + // "about 2 hours ago", + // naive_format_distance( + // date.expect("invalid datetime"), + // base_date.expect("invalid datetime"), + // false, + // true + // ) + // ); + // } + + // #[test] + // fn test_naive_format_distance_with_seconds() { + // let date = NaiveDateTime::from_timestamp_opt(0, 0); + // let base_date = NaiveDateTime::from_timestamp_opt(10, 0); + + // assert_eq!( + // "less than 30 seconds ago", + // naive_format_distance( + // date.expect("invalid datetime"), + // base_date.expect("invalid datetime"), + // true, + // true + // ) + // ); + // } + + // #[test] + // fn test_naive_format_distance_with_future_date() { + // let date = NaiveDateTime::from_timestamp_opt(3400, 0); + // let base_date = NaiveDateTime::from_timestamp_opt(00, 0); + + // assert_eq!( + // "about 56 from now", + // naive_format_distance( + // date.expect("invalid datetime"), + // base_date.expect("invalid datetime"), + // false, + // true + // ) + // ); + // } + + #[test] + fn test_naive_format_distance_string() { + assert_eq!(distance_string(3, false, false), "less than a minute"); + assert_eq!(distance_string(7, false, false), "less than a minute"); + assert_eq!(distance_string(13, false, false), "less than a minute"); + assert_eq!(distance_string(21, false, false), "less than a minute"); + assert_eq!(distance_string(45, false, false), "1 minute"); + assert_eq!(distance_string(61, false, false), "1 minute"); + assert_eq!(distance_string(1920, false, false), "32 minutes"); + assert_eq!(distance_string(3902, false, false), "about 1 hour"); + assert_eq!(distance_string(18002, false, false), "about 5 hours"); + assert_eq!(distance_string(86470, false, false), "1 day"); + assert_eq!(distance_string(345880, false, false), "4 days"); + } + + #[test] + fn test_naive_format_distance_string_include_seconds() { + assert_eq!(distance_string(3, true, false), "less than 5 seconds"); + assert_eq!(distance_string(7, true, false), "less than 10 seconds"); + assert_eq!(distance_string(13, true, false), "less than 20 seconds"); + assert_eq!(distance_string(21, true, false), "half a minute"); + assert_eq!(distance_string(45, true, false), "less than a minute"); + assert_eq!(distance_string(61, true, false), "1 minute"); + } +} From b76c1179794d3b1e6d0ade20676a0f68e319e72e Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 2 Nov 2023 10:37:33 -0400 Subject: [PATCH 22/30] Update naive_format_distance and tests --- crates/ui2/src/utils/format_distance.rs | 103 +++++++++++++----------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/crates/ui2/src/utils/format_distance.rs b/crates/ui2/src/utils/format_distance.rs index 6835e530d2..8a33619910 100644 --- a/crates/ui2/src/utils/format_distance.rs +++ b/crates/ui2/src/utils/format_distance.rs @@ -2,7 +2,7 @@ use chrono::NaiveDateTime; fn distance_in_seconds(date: NaiveDateTime, base_date: NaiveDateTime) -> i64 { let duration = date.signed_duration_since(base_date); - duration.num_seconds() + -duration.num_seconds() } fn distance_string(distance: i64, include_seconds: bool, add_suffix: bool) -> String { @@ -46,16 +46,25 @@ fn distance_string(distance: i64, include_seconds: bool, add_suffix: bool) -> St "about 1 month".to_string() } else if d < 7776000 { "about 2 months".to_string() - } else if d < 31536000 { + } else if d < 31540000 { format!("{} months", months) - } else if d < 39408000 { + } else if d < 39425000 { "about 1 year".to_string() - } else if d < 47318400 { + } else if d < 55195000 { "over 1 year".to_string() - } else if d < 63072000 { + } else if d < 63080000 { "almost 2 years".to_string() } else { - format!("{} years", years) + let years = d / 31536000; + let remaining_months = (d % 31536000) / 2592000; + + if remaining_months < 3 { + format!("about {} years", years) + } else if remaining_months < 9 { + format!("over {} years", years) + } else { + format!("almost {} years", years + 1) + } }; if add_suffix { @@ -91,53 +100,42 @@ mod tests { use super::*; use chrono::NaiveDateTime; - // #[test] - // fn test_naive_format_distance() { - // let date = NaiveDateTime::from_timestamp_opt(0, 0); - // let base_date = NaiveDateTime::from_timestamp_opt(9400, 0); + #[test] + fn test_naive_format_distance() { + let date = + NaiveDateTime::from_timestamp_opt(9600, 0).expect("Invalid NaiveDateTime for date"); + let base_date = + NaiveDateTime::from_timestamp_opt(0, 0).expect("Invalid NaiveDateTime for base_date"); - // assert_eq!( - // "about 2 hours ago", - // naive_format_distance( - // date.expect("invalid datetime"), - // base_date.expect("invalid datetime"), - // false, - // true - // ) - // ); - // } + assert_eq!( + "about 2 hours", + naive_format_distance(date, base_date, false, false) + ); + } - // #[test] - // fn test_naive_format_distance_with_seconds() { - // let date = NaiveDateTime::from_timestamp_opt(0, 0); - // let base_date = NaiveDateTime::from_timestamp_opt(10, 0); + #[test] + fn test_naive_format_distance_with_suffix() { + let date = + NaiveDateTime::from_timestamp_opt(9600, 0).expect("Invalid NaiveDateTime for date"); + let base_date = + NaiveDateTime::from_timestamp_opt(0, 0).expect("Invalid NaiveDateTime for base_date"); - // assert_eq!( - // "less than 30 seconds ago", - // naive_format_distance( - // date.expect("invalid datetime"), - // base_date.expect("invalid datetime"), - // true, - // true - // ) - // ); - // } + assert_eq!( + "about 2 hours from now", + naive_format_distance(date, base_date, false, true) + ); + } - // #[test] - // fn test_naive_format_distance_with_future_date() { - // let date = NaiveDateTime::from_timestamp_opt(3400, 0); - // let base_date = NaiveDateTime::from_timestamp_opt(00, 0); + #[test] + fn test_naive_format_distance_from_now() { + let date = NaiveDateTime::parse_from_str("1969-07-20T00:00:00Z", "%Y-%m-%dT%H:%M:%SZ") + .expect("Invalid NaiveDateTime for date"); - // assert_eq!( - // "about 56 from now", - // naive_format_distance( - // date.expect("invalid datetime"), - // base_date.expect("invalid datetime"), - // false, - // true - // ) - // ); - // } + assert_eq!( + "over 54 years ago", + naive_format_distance_from_now(date, false, true) + ); + } #[test] fn test_naive_format_distance_string() { @@ -152,6 +150,15 @@ mod tests { assert_eq!(distance_string(18002, false, false), "about 5 hours"); assert_eq!(distance_string(86470, false, false), "1 day"); assert_eq!(distance_string(345880, false, false), "4 days"); + assert_eq!(distance_string(2764800, false, false), "about 1 month"); + assert_eq!(distance_string(5184000, false, false), "about 2 months"); + assert_eq!(distance_string(10368000, false, false), "4 months"); + assert_eq!(distance_string(34694000, false, false), "about 1 year"); + assert_eq!(distance_string(47310000, false, false), "over 1 year"); + assert_eq!(distance_string(61503000, false, false), "almost 2 years"); + assert_eq!(distance_string(160854000, false, false), "about 5 years"); + assert_eq!(distance_string(236550000, false, false), "over 7 years"); + assert_eq!(distance_string(249166000, false, false), "almost 8 years"); } #[test] From 46e81da4e1453d8caf355a402123420e0683203f Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 2 Nov 2023 11:00:47 -0400 Subject: [PATCH 23/30] Uncomment db2 tests --- crates/db2/src/db2.rs | 226 +++++++++++++++++++++--------------------- crates/db2/src/kvp.rs | 46 ++++----- 2 files changed, 138 insertions(+), 134 deletions(-) diff --git a/crates/db2/src/db2.rs b/crates/db2/src/db2.rs index fe79dfbb0c..e052d59d12 100644 --- a/crates/db2/src/db2.rs +++ b/crates/db2/src/db2.rs @@ -190,138 +190,142 @@ where .detach() } -// #[cfg(test)] -// mod tests { -// use std::thread; +#[cfg(test)] +mod tests { + use std::thread; -// use sqlez::domain::Domain; -// use sqlez_macros::sql; -// use tempdir::TempDir; + use sqlez::domain::Domain; + use sqlez_macros::sql; + use tempdir::TempDir; -// use crate::open_db; + use crate::open_db; -// // Test bad migration panics -// #[gpui::test] -// #[should_panic] -// async fn test_bad_migration_panics() { -// enum BadDB {} + // Test bad migration panics + #[gpui2::test] + #[should_panic] + async fn test_bad_migration_panics() { + enum BadDB {} -// impl Domain for BadDB { -// fn name() -> &'static str { -// "db_tests" -// } + impl Domain for BadDB { + fn name() -> &'static str { + "db_tests" + } -// fn migrations() -> &'static [&'static str] { -// &[ -// sql!(CREATE TABLE test(value);), -// // failure because test already exists -// sql!(CREATE TABLE test(value);), -// ] -// } -// } + fn migrations() -> &'static [&'static str] { + &[ + sql!(CREATE TABLE test(value);), + // failure because test already exists + sql!(CREATE TABLE test(value);), + ] + } + } -// let tempdir = TempDir::new("DbTests").unwrap(); -// let _bad_db = open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; -// } + let tempdir = TempDir::new("DbTests").unwrap(); + let _bad_db = open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; + } -// /// Test that DB exists but corrupted (causing recreate) -// #[gpui::test] -// async fn test_db_corruption() { -// enum CorruptedDB {} + /// Test that DB exists but corrupted (causing recreate) + #[gpui2::test] + async fn test_db_corruption(cx: &mut gpui2::TestAppContext) { + cx.executor().allow_parking(); -// impl Domain for CorruptedDB { -// fn name() -> &'static str { -// "db_tests" -// } + enum CorruptedDB {} -// fn migrations() -> &'static [&'static str] { -// &[sql!(CREATE TABLE test(value);)] -// } -// } + impl Domain for CorruptedDB { + fn name() -> &'static str { + "db_tests" + } -// enum GoodDB {} + fn migrations() -> &'static [&'static str] { + &[sql!(CREATE TABLE test(value);)] + } + } -// impl Domain for GoodDB { -// fn name() -> &'static str { -// "db_tests" //Notice same name -// } + enum GoodDB {} -// fn migrations() -> &'static [&'static str] { -// &[sql!(CREATE TABLE test2(value);)] //But different migration -// } -// } + impl Domain for GoodDB { + fn name() -> &'static str { + "db_tests" //Notice same name + } -// let tempdir = TempDir::new("DbTests").unwrap(); -// { -// let corrupt_db = -// open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; -// assert!(corrupt_db.persistent()); -// } + fn migrations() -> &'static [&'static str] { + &[sql!(CREATE TABLE test2(value);)] //But different migration + } + } -// let good_db = open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; -// assert!( -// good_db.select_row::("SELECT * FROM test2").unwrap()() -// .unwrap() -// .is_none() -// ); -// } + let tempdir = TempDir::new("DbTests").unwrap(); + { + let corrupt_db = + open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; + assert!(corrupt_db.persistent()); + } -// /// Test that DB exists but corrupted (causing recreate) -// #[gpui::test(iterations = 30)] -// async fn test_simultaneous_db_corruption() { -// enum CorruptedDB {} + let good_db = open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; + assert!( + good_db.select_row::("SELECT * FROM test2").unwrap()() + .unwrap() + .is_none() + ); + } -// impl Domain for CorruptedDB { -// fn name() -> &'static str { -// "db_tests" -// } + /// Test that DB exists but corrupted (causing recreate) + #[gpui2::test(iterations = 30)] + async fn test_simultaneous_db_corruption(cx: &mut gpui2::TestAppContext) { + cx.executor().allow_parking(); -// fn migrations() -> &'static [&'static str] { -// &[sql!(CREATE TABLE test(value);)] -// } -// } + enum CorruptedDB {} -// enum GoodDB {} + impl Domain for CorruptedDB { + fn name() -> &'static str { + "db_tests" + } -// impl Domain for GoodDB { -// fn name() -> &'static str { -// "db_tests" //Notice same name -// } + fn migrations() -> &'static [&'static str] { + &[sql!(CREATE TABLE test(value);)] + } + } -// fn migrations() -> &'static [&'static str] { -// &[sql!(CREATE TABLE test2(value);)] //But different migration -// } -// } + enum GoodDB {} -// let tempdir = TempDir::new("DbTests").unwrap(); -// { -// // Setup the bad database -// let corrupt_db = -// open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; -// assert!(corrupt_db.persistent()); -// } + impl Domain for GoodDB { + fn name() -> &'static str { + "db_tests" //Notice same name + } -// // Try to connect to it a bunch of times at once -// let mut guards = vec![]; -// for _ in 0..10 { -// let tmp_path = tempdir.path().to_path_buf(); -// let guard = thread::spawn(move || { -// let good_db = smol::block_on(open_db::( -// tmp_path.as_path(), -// &util::channel::ReleaseChannel::Dev, -// )); -// assert!( -// good_db.select_row::("SELECT * FROM test2").unwrap()() -// .unwrap() -// .is_none() -// ); -// }); + fn migrations() -> &'static [&'static str] { + &[sql!(CREATE TABLE test2(value);)] //But different migration + } + } -// guards.push(guard); -// } + let tempdir = TempDir::new("DbTests").unwrap(); + { + // Setup the bad database + let corrupt_db = + open_db::(tempdir.path(), &util::channel::ReleaseChannel::Dev).await; + assert!(corrupt_db.persistent()); + } -// for guard in guards.into_iter() { -// assert!(guard.join().is_ok()); -// } -// } -// } + // Try to connect to it a bunch of times at once + let mut guards = vec![]; + for _ in 0..10 { + let tmp_path = tempdir.path().to_path_buf(); + let guard = thread::spawn(move || { + let good_db = smol::block_on(open_db::( + tmp_path.as_path(), + &util::channel::ReleaseChannel::Dev, + )); + assert!( + good_db.select_row::("SELECT * FROM test2").unwrap()() + .unwrap() + .is_none() + ); + }); + + guards.push(guard); + } + + for guard in guards.into_iter() { + assert!(guard.join().is_ok()); + } + } +} diff --git a/crates/db2/src/kvp.rs b/crates/db2/src/kvp.rs index 254d91689d..b4445e3586 100644 --- a/crates/db2/src/kvp.rs +++ b/crates/db2/src/kvp.rs @@ -31,32 +31,32 @@ impl KeyValueStore { } } -// #[cfg(test)] -// mod tests { -// use crate::kvp::KeyValueStore; +#[cfg(test)] +mod tests { + use crate::kvp::KeyValueStore; -// #[gpui::test] -// async fn test_kvp() { -// let db = KeyValueStore(crate::open_test_db("test_kvp").await); + #[gpui2::test] + async fn test_kvp() { + let db = KeyValueStore(crate::open_test_db("test_kvp").await); -// assert_eq!(db.read_kvp("key-1").unwrap(), None); + assert_eq!(db.read_kvp("key-1").unwrap(), None); -// db.write_kvp("key-1".to_string(), "one".to_string()) -// .await -// .unwrap(); -// assert_eq!(db.read_kvp("key-1").unwrap(), Some("one".to_string())); + db.write_kvp("key-1".to_string(), "one".to_string()) + .await + .unwrap(); + assert_eq!(db.read_kvp("key-1").unwrap(), Some("one".to_string())); -// db.write_kvp("key-1".to_string(), "one-2".to_string()) -// .await -// .unwrap(); -// assert_eq!(db.read_kvp("key-1").unwrap(), Some("one-2".to_string())); + db.write_kvp("key-1".to_string(), "one-2".to_string()) + .await + .unwrap(); + assert_eq!(db.read_kvp("key-1").unwrap(), Some("one-2".to_string())); -// db.write_kvp("key-2".to_string(), "two".to_string()) -// .await -// .unwrap(); -// assert_eq!(db.read_kvp("key-2").unwrap(), Some("two".to_string())); + db.write_kvp("key-2".to_string(), "two".to_string()) + .await + .unwrap(); + assert_eq!(db.read_kvp("key-2").unwrap(), Some("two".to_string())); -// db.delete_kvp("key-1".to_string()).await.unwrap(); -// assert_eq!(db.read_kvp("key-1").unwrap(), None); -// } -// } + db.delete_kvp("key-1".to_string()).await.unwrap(); + assert_eq!(db.read_kvp("key-1").unwrap(), None); + } +} From 6d562aaa6f111736408a3edb55e7e79bac57dccf Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 2 Nov 2023 11:02:17 -0400 Subject: [PATCH 24/30] Use naive_format_distance_from_now in Notifications --- .../ui2/src/components/notifications_panel.rs | 38 +++++++++++++++---- crates/ui2/src/static_data.rs | 31 +++++++++++++++ 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs index c53b60f213..697b4de63b 100644 --- a/crates/ui2/src/components/notifications_panel.rs +++ b/crates/ui2/src/components/notifications_panel.rs @@ -1,3 +1,4 @@ +use crate::utils::naive_format_distance_from_now; use crate::{ h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, Button, Icon, IconButton, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator, @@ -137,6 +138,7 @@ impl Notification { fn new( id: ElementId, message: SharedString, + date_received: NaiveDateTime, slot: ActorOrIcon, click_action: Option>, ) -> Self { @@ -150,9 +152,7 @@ impl Notification { Self { id, - date_received: DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z") - .unwrap() - .naive_local(), + date_received, message, meta: None, slot, @@ -170,12 +170,14 @@ impl Notification { pub fn new_actor_message( id: impl Into, message: impl Into, + date_received: NaiveDateTime, actor: PublicActor, click_action: ClickHandler, ) -> Self { Self::new( id.into(), message.into(), + date_received, ActorOrIcon::Actor(actor), Some(click_action), ) @@ -187,12 +189,14 @@ impl Notification { pub fn new_icon_message( id: impl Into, message: impl Into, + date_received: NaiveDateTime, icon: Icon, click_action: ClickHandler, ) -> Self { Self::new( id.into(), message.into(), + date_received, ActorOrIcon::Icon(icon), Some(click_action), ) @@ -205,10 +209,18 @@ impl Notification { pub fn new_actor_with_actions( id: impl Into, message: impl Into, + date_received: NaiveDateTime, actor: PublicActor, actions: [NotificationAction; 2], ) -> Self { - Self::new(id.into(), message.into(), ActorOrIcon::Actor(actor), None).actions(actions) + Self::new( + id.into(), + message.into(), + date_received, + ActorOrIcon::Actor(actor), + None, + ) + .actions(actions) } /// Creates a new notification with an icon slot @@ -218,10 +230,18 @@ impl Notification { pub fn new_icon_with_actions( id: impl Into, message: impl Into, + date_received: NaiveDateTime, icon: Icon, actions: [NotificationAction; 2], ) -> Self { - Self::new(id.into(), message.into(), ActorOrIcon::Icon(icon), None).actions(actions) + Self::new( + id.into(), + message.into(), + date_received, + ActorOrIcon::Icon(icon), + None, + ) + .actions(actions) } fn on_click(mut self, handler: ClickHandler) -> Self { @@ -303,9 +323,11 @@ impl Notification { h_stack() .gap_1() .child( - Label::new( - self.date_received.format("%m/%d/%Y").to_string(), - ) + Label::new(naive_format_distance_from_now( + self.date_received, + true, + true, + )) .color(LabelColor::Muted), ) .child(self.render_meta_items(cx)), diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 45e4dfa423..3964873055 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; +use chrono::{DateTime, NaiveDateTime}; use gpui2::{AppContext, ViewContext}; use rand::Rng; use theme2::ActiveTheme; @@ -332,12 +333,18 @@ pub fn static_new_notification_items_2() -> Vec> { Notification::new_icon_message( "notif-1", "You were mentioned in a note.", + DateTime::parse_from_rfc3339("2023-11-02T11:59:57Z") + .unwrap() + .naive_local(), Icon::AtSign, Arc::new(|_, _| {}), ), Notification::new_actor_with_actions( "notif-2", "as-cii sent you a contact request.", + DateTime::parse_from_rfc3339("2023-11-02T12:09:07Z") + .unwrap() + .naive_local(), PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( @@ -355,12 +362,18 @@ pub fn static_new_notification_items_2() -> Vec> { Notification::new_icon_message( "notif-3", "You were mentioned #design.", + DateTime::parse_from_rfc3339("2023-11-02T12:09:07Z") + .unwrap() + .naive_local(), Icon::MessageBubbles, Arc::new(|_, _| {}), ), Notification::new_actor_with_actions( "notif-4", "as-cii sent you a contact request.", + DateTime::parse_from_rfc3339("2023-11-01T12:09:07Z") + .unwrap() + .naive_local(), PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( @@ -378,12 +391,18 @@ pub fn static_new_notification_items_2() -> Vec> { Notification::new_icon_message( "notif-5", "You were mentioned in a note.", + DateTime::parse_from_rfc3339("2023-10-28T12:09:07Z") + .unwrap() + .naive_local(), Icon::AtSign, Arc::new(|_, _| {}), ), Notification::new_actor_with_actions( "notif-6", "as-cii sent you a contact request.", + DateTime::parse_from_rfc3339("2022-10-25T12:09:07Z") + .unwrap() + .naive_local(), PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( @@ -401,12 +420,18 @@ pub fn static_new_notification_items_2() -> Vec> { Notification::new_icon_message( "notif-7", "You were mentioned in a note.", + DateTime::parse_from_rfc3339("2022-10-14T12:09:07Z") + .unwrap() + .naive_local(), Icon::AtSign, Arc::new(|_, _| {}), ), Notification::new_actor_with_actions( "notif-8", "as-cii sent you a contact request.", + DateTime::parse_from_rfc3339("2021-10-12T12:09:07Z") + .unwrap() + .naive_local(), PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( @@ -424,12 +449,18 @@ pub fn static_new_notification_items_2() -> Vec> { Notification::new_icon_message( "notif-9", "You were mentioned in a note.", + DateTime::parse_from_rfc3339("2021-02-02T12:09:07Z") + .unwrap() + .naive_local(), Icon::AtSign, Arc::new(|_, _| {}), ), Notification::new_actor_with_actions( "notif-10", "as-cii sent you a contact request.", + DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z") + .unwrap() + .naive_local(), PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( From d48ec7d58e49ce837055cab693d3d3f5ec86f567 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 2 Nov 2023 11:02:46 -0400 Subject: [PATCH 25/30] Remove unused import --- crates/ui2/src/components/notifications_panel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs index 697b4de63b..74f015ac06 100644 --- a/crates/ui2/src/components/notifications_panel.rs +++ b/crates/ui2/src/components/notifications_panel.rs @@ -361,7 +361,7 @@ impl Notification { } } -use chrono::{DateTime, NaiveDateTime}; +use chrono::NaiveDateTime; use gpui2::{px, Styled}; #[cfg(feature = "stories")] pub use stories::*; From d4db7a14b3f9f59c1b602f3710fb05e2ee54207e Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 2 Nov 2023 11:04:04 -0400 Subject: [PATCH 26/30] Remove unused import --- crates/ui2/src/static_data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 3964873055..68f625c75d 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; -use chrono::{DateTime, NaiveDateTime}; +use chrono::DateTime; use gpui2::{AppContext, ViewContext}; use rand::Rng; use theme2::ActiveTheme; From de80974a1d5bb3beb877aae19d22638206386b2a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 2 Nov 2023 16:11:52 +0100 Subject: [PATCH 27/30] Add `ViewContext::observe_fullscreen` Co-Authored-By: Conrad Irwin --- crates/gpui2/src/window.rs | 44 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 0202b7521e..667ef6c486 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -6,8 +6,9 @@ use crate::{ Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, - SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, - Underline, UnderlineStyle, View, VisualContext, WeakView, WindowOptions, SUBPIXEL_VARIANTS, + SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription, + TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView, + WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Result}; use collections::HashMap; @@ -64,6 +65,7 @@ type AnyKeyListener = Box< + 'static, >; type AnyFocusListener = Box; +type AnyFullScreenListener = Box bool + 'static>; slotmap::new_key_type! { pub struct FocusId; } @@ -180,10 +182,12 @@ pub struct Window { focus_stack: Vec, focus_parents_by_child: HashMap, pub(crate) focus_listeners: Vec, + fullscreen_listeners: SubscriberSet<(), AnyFullScreenListener>, pub(crate) focus_handles: Arc>>, default_prevented: bool, mouse_position: Point, scale_factor: f32, + fullscreen: bool, pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, pub(crate) last_blur: Option>, @@ -202,6 +206,7 @@ impl Window { let mouse_position = platform_window.mouse_position(); let content_size = platform_window.content_size(); let scale_factor = platform_window.scale_factor(); + platform_window.on_resize(Box::new({ let mut cx = cx.to_async(); move |content_size, scale_factor| { @@ -216,6 +221,21 @@ impl Window { .log_err(); } })); + platform_window.on_fullscreen(Box::new({ + let mut cx = cx.to_async(); + move |fullscreen| { + handle + .update(&mut cx, |_, cx| { + cx.window.fullscreen = fullscreen; + cx.window + .fullscreen_listeners + .clone() + .retain(&(), |callback| callback(fullscreen, cx)); + cx.window.dirty = true; + }) + .log_err(); + } + })); platform_window.on_input({ let mut cx = cx.to_async(); @@ -250,10 +270,12 @@ impl Window { focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), focus_listeners: Vec::new(), + fullscreen_listeners: SubscriberSet::new(), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), default_prevented: true, mouse_position, scale_factor, + fullscreen: false, scene_builder: SceneBuilder::new(), dirty: true, last_blur: None, @@ -1710,6 +1732,24 @@ impl<'a, V: 'static> ViewContext<'a, V> { }); } + pub fn observe_fullscreen( + &mut self, + mut callback: impl FnMut(&mut V, bool, &mut ViewContext) + 'static, + ) -> Subscription { + let view = self.view.downgrade(); + self.window.fullscreen_listeners.insert( + (), + Box::new(move |fullscreen, cx| { + view.update(cx, |view, cx| callback(view, fullscreen, cx)) + .is_ok() + }), + ) + } + + // fn observe_window_activation(&mut self) -> Subscription {} + + // fn observe_window_bounds(&mut self) -> Subscription {} + pub fn on_focus_changed( &mut self, listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext) + 'static, From 3a824e468f8852204265f74a649b8fa652bdf4c5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 2 Nov 2023 16:31:27 +0100 Subject: [PATCH 28/30] Subsume `observe_fullscreen` into `observe_window_bounds` Co-Authored-By: Conrad Irwin --- crates/gpui2/src/window.rs | 68 +++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 667ef6c486..8d3afc9ae0 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -8,7 +8,7 @@ use crate::{ PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView, - WindowOptions, SUBPIXEL_VARIANTS, + WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Result}; use collections::HashMap; @@ -54,6 +54,7 @@ pub enum DispatchPhase { Capture, } +type AnyObserver = Box bool + 'static>; type AnyListener = Box; type AnyKeyListener = Box< dyn Fn( @@ -65,7 +66,6 @@ type AnyKeyListener = Box< + 'static, >; type AnyFocusListener = Box; -type AnyFullScreenListener = Box bool + 'static>; slotmap::new_key_type! { pub struct FocusId; } @@ -182,12 +182,12 @@ pub struct Window { focus_stack: Vec, focus_parents_by_child: HashMap, pub(crate) focus_listeners: Vec, - fullscreen_listeners: SubscriberSet<(), AnyFullScreenListener>, pub(crate) focus_handles: Arc>>, default_prevented: bool, mouse_position: Point, scale_factor: f32, - fullscreen: bool, + bounds: WindowBounds, + bounds_observers: SubscriberSet<(), AnyObserver>, pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, pub(crate) last_blur: Option>, @@ -206,33 +206,21 @@ impl Window { let mouse_position = platform_window.mouse_position(); let content_size = platform_window.content_size(); let scale_factor = platform_window.scale_factor(); + let bounds = platform_window.bounds(); platform_window.on_resize(Box::new({ let mut cx = cx.to_async(); - move |content_size, scale_factor| { + move |_, _| { handle - .update(&mut cx, |_, cx| { - cx.window.scale_factor = scale_factor; - cx.window.scene_builder = SceneBuilder::new(); - cx.window.content_size = content_size; - cx.window.display_id = cx.window.platform_window.display().id(); - cx.window.dirty = true; - }) + .update(&mut cx, |_, cx| cx.window_bounds_changed()) .log_err(); } })); - platform_window.on_fullscreen(Box::new({ + platform_window.on_moved(Box::new({ let mut cx = cx.to_async(); - move |fullscreen| { + move || { handle - .update(&mut cx, |_, cx| { - cx.window.fullscreen = fullscreen; - cx.window - .fullscreen_listeners - .clone() - .retain(&(), |callback| callback(fullscreen, cx)); - cx.window.dirty = true; - }) + .update(&mut cx, |_, cx| cx.window_bounds_changed()) .log_err(); } })); @@ -270,12 +258,12 @@ impl Window { focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), focus_listeners: Vec::new(), - fullscreen_listeners: SubscriberSet::new(), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), default_prevented: true, mouse_position, scale_factor, - fullscreen: false, + bounds, + bounds_observers: SubscriberSet::new(), scene_builder: SceneBuilder::new(), dirty: true, last_blur: None, @@ -540,6 +528,23 @@ impl<'a> WindowContext<'a> { bounds } + fn window_bounds_changed(&mut self) { + self.window.scale_factor = self.window.platform_window.scale_factor(); + self.window.content_size = self.window.platform_window.content_size(); + self.window.bounds = self.window.platform_window.bounds(); + self.window.display_id = self.window.platform_window.display().id(); + self.window.dirty = true; + + self.window + .bounds_observers + .clone() + .retain(&(), |callback| callback(self)); + } + + pub fn window_bounds(&self) -> WindowBounds { + self.window.bounds + } + /// The scale factor of the display associated with the window. For example, it could /// return 2.0 for a "retina" display, indicating that each logical pixel should actually /// be rendered as two pixels on screen. @@ -1732,23 +1737,18 @@ impl<'a, V: 'static> ViewContext<'a, V> { }); } - pub fn observe_fullscreen( + pub fn observe_window_bounds( &mut self, - mut callback: impl FnMut(&mut V, bool, &mut ViewContext) + 'static, + mut callback: impl FnMut(&mut V, &mut ViewContext) + 'static, ) -> Subscription { let view = self.view.downgrade(); - self.window.fullscreen_listeners.insert( + self.window.bounds_observers.insert( (), - Box::new(move |fullscreen, cx| { - view.update(cx, |view, cx| callback(view, fullscreen, cx)) - .is_ok() - }), + Box::new(move |cx| view.update(cx, |view, cx| callback(view, cx)).is_ok()), ) } - // fn observe_window_activation(&mut self) -> Subscription {} - - // fn observe_window_bounds(&mut self) -> Subscription {} + fn observe_window_activation(&mut self) -> Subscription {} pub fn on_focus_changed( &mut self, From ec4f0d7bca73cc55c71882ab73ab96ebe6517a9c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 2 Nov 2023 16:37:22 +0100 Subject: [PATCH 29/30] Implement `ViewContext::observe_window_activation` Co-Authored-By: Conrad Irwin --- crates/gpui2/src/window.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 8d3afc9ae0..f1335b3cff 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -188,6 +188,8 @@ pub struct Window { scale_factor: f32, bounds: WindowBounds, bounds_observers: SubscriberSet<(), AnyObserver>, + active: bool, + activation_observers: SubscriberSet<(), AnyObserver>, pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, pub(crate) last_blur: Option>, @@ -224,6 +226,20 @@ impl Window { .log_err(); } })); + platform_window.on_active_status_change(Box::new({ + let mut cx = cx.to_async(); + move |active| { + handle + .update(&mut cx, |_, cx| { + cx.window.active = active; + cx.window + .activation_observers + .clone() + .retain(&(), |callback| callback(cx)); + }) + .log_err(); + } + })); platform_window.on_input({ let mut cx = cx.to_async(); @@ -264,6 +280,8 @@ impl Window { scale_factor, bounds, bounds_observers: SubscriberSet::new(), + active: false, + activation_observers: SubscriberSet::new(), scene_builder: SceneBuilder::new(), dirty: true, last_blur: None, @@ -1748,7 +1766,16 @@ impl<'a, V: 'static> ViewContext<'a, V> { ) } - fn observe_window_activation(&mut self) -> Subscription {} + pub fn observe_window_activation( + &mut self, + mut callback: impl FnMut(&mut V, &mut ViewContext) + 'static, + ) -> Subscription { + let view = self.view.downgrade(); + self.window.activation_observers.insert( + (), + Box::new(move |cx| view.update(cx, |view, cx| callback(view, cx)).is_ok()), + ) + } pub fn on_focus_changed( &mut self, From ec0cff0e1a251d7bf3ab9b92c8bd49de375518c5 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 2 Nov 2023 16:39:40 +0100 Subject: [PATCH 30/30] Add `map` method to `Component`s (#3210) This PR adds a `map` method to the `Component` trait. `map` is a fully-generalized form of `when`, as `when` can be expressed in terms of `map`: ```rs div().map(|this| if condition { then(this) } else { this }) ``` This allows us to take advantage of Rust's pattern matching when building up conditions: ```rs // Before div() .when(self.current_side == PanelSide::Left, |this| this.border_r()) .when(self.current_side == PanelSide::Right, |this| { this.border_l() }) .when(self.current_side == PanelSide::Bottom, |this| { this.border_b().w_full().h(current_size) }) // After div() .map(|this| match self.current_side { PanelSide::Left => this.border_r(), PanelSide::Right => this.border_l(), PanelSide::Bottom => this.border_b().w_full().h(current_size), }) ``` Release Notes: - N/A --- crates/gpui2/src/element.rs | 15 ++++++++++----- crates/ui2/src/components/panel.rs | 16 +++++++--------- crates/ui2/src/elements/input.rs | 15 +++++++-------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index 4890b79a9a..a92dbd6ff9 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -198,14 +198,19 @@ impl AnyElement { pub trait Component { fn render(self) -> AnyElement; - fn when(mut self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self + fn map(self, f: impl FnOnce(Self) -> U) -> U + where + Self: Sized, + U: Component, + { + f(self) + } + + fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self where Self: Sized, { - if condition { - self = then(self); - } - self + self.map(|this| if condition { then(this) } else { this }) } } diff --git a/crates/ui2/src/components/panel.rs b/crates/ui2/src/components/panel.rs index c4a9ac5111..5d941eb50e 100644 --- a/crates/ui2/src/components/panel.rs +++ b/crates/ui2/src/components/panel.rs @@ -98,16 +98,14 @@ impl Panel { v_stack() .id(self.id.clone()) .flex_initial() - .when( - self.current_side == PanelSide::Left || self.current_side == PanelSide::Right, - |this| this.h_full().w(current_size), - ) - .when(self.current_side == PanelSide::Left, |this| this.border_r()) - .when(self.current_side == PanelSide::Right, |this| { - this.border_l() + .map(|this| match self.current_side { + PanelSide::Left | PanelSide::Right => this.h_full().w(current_size), + PanelSide::Bottom => this, }) - .when(self.current_side == PanelSide::Bottom, |this| { - this.border_b().w_full().h(current_size) + .map(|this| match self.current_side { + PanelSide::Left => this.border_r(), + PanelSide::Right => this.border_l(), + PanelSide::Bottom => this.border_b().w_full().h(current_size), }) .bg(cx.theme().colors().surface) .border_color(cx.theme().colors().border) diff --git a/crates/ui2/src/elements/input.rs b/crates/ui2/src/elements/input.rs index 3f82512b84..2884470ce2 100644 --- a/crates/ui2/src/elements/input.rs +++ b/crates/ui2/src/elements/input.rs @@ -94,14 +94,13 @@ impl Input { .active(|style| style.bg(input_active_bg)) .flex() .items_center() - .child( - div() - .flex() - .items_center() - .text_sm() - .when(self.value.is_empty(), |this| this.child(placeholder_label)) - .when(!self.value.is_empty(), |this| this.child(label)), - ) + .child(div().flex().items_center().text_sm().map(|this| { + if self.value.is_empty() { + this.child(placeholder_label) + } else { + this.child(label) + } + })) } }