From b21c25826dc84dc68eea21033a9775d93412b48c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Nov 2023 15:51:33 +0100 Subject: [PATCH 01/32] Make tab bar visible --- crates/editor2/src/items.rs | 5 ++--- crates/workspace2/src/pane.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/editor2/src/items.rs b/crates/editor2/src/items.rs index c439adcc44..84263d4ed7 100644 --- a/crates/editor2/src/items.rs +++ b/crates/editor2/src/items.rs @@ -578,12 +578,12 @@ impl Item for Editor { fn tab_content(&self, detail: Option, cx: &AppContext) -> AnyElement { let theme = cx.theme(); + AnyElement::new( div() .flex() .flex_row() .items_center() - .bg(gpui::white()) .text_color(gpui::white()) .child(self.title(cx).to_string()) .children(detail.and_then(|detail| { @@ -625,8 +625,7 @@ impl Item for Editor { fn deactivated(&mut self, cx: &mut ViewContext) { let selection = self.selections.newest_anchor(); - todo!() - // self.push_to_nav_history(selection.head(), None, cx); + self.push_to_nav_history(selection.head(), None, cx); } fn workspace_deactivated(&mut self, cx: &mut ViewContext) { diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index acc41deba8..c56defed26 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1457,7 +1457,7 @@ impl Pane { ), ) .child( - div().w_0().flex_1().h_full().child( + div().flex_1().h_full().child( div().id("tabs").flex().overflow_x_scroll().children( self.items .iter() From 0edcec7c4eee800ebb93d24884d51678cd5ed301 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 09:12:12 -0600 Subject: [PATCH 02/32] Fix tab text colors --- crates/editor2/src/items.rs | 10 ++++++++-- crates/workspace2/src/pane.rs | 5 ++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/editor2/src/items.rs b/crates/editor2/src/items.rs index 84263d4ed7..a4d34ad36f 100644 --- a/crates/editor2/src/items.rs +++ b/crates/editor2/src/items.rs @@ -584,12 +584,18 @@ impl Item for Editor { .flex() .flex_row() .items_center() - .text_color(gpui::white()) + .gap_2() .child(self.title(cx).to_string()) .children(detail.and_then(|detail| { let path = path_for_buffer(&self.buffer, detail, false, cx)?; let description = path.to_string_lossy(); - Some(util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN)) + + Some( + div() + .text_color(theme.colors().text_muted) + .text_xs() + .child(util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN)), + ) })), ) } diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index c56defed26..eef829afa7 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1361,13 +1361,15 @@ impl Pane { let label = item.tab_content(Some(detail), cx); let close_icon = || IconElement::new(Icon::Close).color(IconColor::Muted); - let (tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index { + let (text_color, tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index { false => ( + cx.theme().colors().text_muted, cx.theme().colors().tab_inactive, cx.theme().colors().ghost_element_hover, cx.theme().colors().ghost_element_active, ), true => ( + cx.theme().colors().text, cx.theme().colors().tab_active, cx.theme().colors().element_hover, cx.theme().colors().element_active, @@ -1397,6 +1399,7 @@ impl Pane { .flex() .items_center() .gap_1p5() + .text_color(text_color) .children(if item.has_conflict(cx) { Some( IconElement::new(Icon::ExclamationTriangle) From b5c2cf371a5c405d122b9a85da8bace4bb7e7dab Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Nov 2023 16:13:23 +0100 Subject: [PATCH 03/32] Ensure panes cover the available space --- crates/workspace2/src/pane.rs | 7 ++++--- crates/workspace2/src/pane_group.rs | 2 +- crates/workspace2/src/status_bar.rs | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index eef829afa7..1bccaaea64 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1891,13 +1891,14 @@ impl Render for Pane { fn render(&mut self, cx: &mut ViewContext) -> Self::Element { v_stack() + .size_full() .child(self.render_tab_bar(cx)) - .child(div() /* toolbar */) + .child(div() /* todo!(toolbar) */) .child(if let Some(item) = self.active_item() { - item.to_any().render() + div().flex_1().child(item.to_any()) } else { // todo!() - div().child("Empty Pane").render() + div().child("Empty Pane") }) // enum MouseNavigationHandler {} diff --git a/crates/workspace2/src/pane_group.rs b/crates/workspace2/src/pane_group.rs index 441aef21f5..a9e95b8c29 100644 --- a/crates/workspace2/src/pane_group.rs +++ b/crates/workspace2/src/pane_group.rs @@ -201,7 +201,7 @@ impl Member { // Some(pane) // }; - div().child(pane.clone()).render() + div().size_full().child(pane.clone()).render() // Stack::new() // .with_child(pane_element.contained().with_border(leader_border)) diff --git a/crates/workspace2/src/status_bar.rs b/crates/workspace2/src/status_bar.rs index ca4ebcdb13..4f7ba02c2a 100644 --- a/crates/workspace2/src/status_bar.rs +++ b/crates/workspace2/src/status_bar.rs @@ -44,6 +44,7 @@ impl Render for StatusBar { .items_center() .justify_between() .w_full() + .h_8() .bg(cx.theme().colors().status_bar) .child(self.render_left_tools(cx)) .child(self.render_right_tools(cx)) From 920ea1bf0a64a679dfebe4cdc611a1b8cb3c008a Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 09:20:13 -0600 Subject: [PATCH 04/32] Make the close button close --- crates/workspace2/src/pane.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 1bccaaea64..4c4f26fe0e 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1359,7 +1359,16 @@ impl Pane { cx: &mut ViewContext<'_, Pane>, ) -> impl Component { let label = item.tab_content(Some(detail), cx); - let close_icon = || IconElement::new(Icon::Close).color(IconColor::Muted); + let close_icon = || { + let id = item.id(); + div() + .id(item.id()) + .child(IconElement::new(Icon::Close).color(IconColor::Muted)) + .on_click(move |pane: &mut Self, _, cx| { + pane.close_item_by_id(id, SaveIntent::Close, cx) + .detach_and_log_err(cx); + }) + }; let (text_color, tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index { false => ( From 580694dbda97cb851e9084290a011cd860ca362f Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 09:56:21 -0600 Subject: [PATCH 05/32] Fix bug when unsubscribe called after remove Co-Authored-By: Julia --- crates/gpui2/src/subscription.rs | 57 ++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/crates/gpui2/src/subscription.rs b/crates/gpui2/src/subscription.rs index 64fcd74dd2..744e83bbbd 100644 --- a/crates/gpui2/src/subscription.rs +++ b/crates/gpui2/src/subscription.rs @@ -14,7 +14,7 @@ impl Clone for SubscriberSet { } struct SubscriberSetState { - subscribers: BTreeMap>, + subscribers: BTreeMap>>, dropped_subscribers: BTreeSet<(EmitterKey, usize)>, next_subscriber_id: usize, } @@ -38,12 +38,18 @@ where lock.subscribers .entry(emitter_key.clone()) .or_default() + .insert(Default::default()) .insert(subscriber_id, callback); let this = self.0.clone(); Subscription { unsubscribe: Some(Box::new(move || { let mut lock = this.lock(); - if let Some(subscribers) = lock.subscribers.get_mut(&emitter_key) { + let Some(subscribers) = lock.subscribers.get_mut(&emitter_key) else { + // remove was called with this emitter_key + return; + }; + + if let Some(subscribers) = subscribers { subscribers.remove(&subscriber_id); if subscribers.is_empty() { lock.subscribers.remove(&emitter_key); @@ -62,34 +68,43 @@ where pub fn remove(&self, emitter: &EmitterKey) -> impl IntoIterator { let subscribers = self.0.lock().subscribers.remove(&emitter); - subscribers.unwrap_or_default().into_values() + subscribers + .unwrap_or_default() + .map(|s| s.into_values()) + .into_iter() + .flatten() } pub fn retain(&self, emitter: &EmitterKey, mut f: F) where F: FnMut(&mut Callback) -> bool, { - let entry = self.0.lock().subscribers.remove_entry(emitter); - if let Some((emitter, mut subscribers)) = entry { - subscribers.retain(|_, callback| f(callback)); - let mut lock = self.0.lock(); + let Some(mut subscribers) = self + .0 + .lock() + .subscribers + .get_mut(emitter) + .and_then(|s| s.take()) + else { + return; + }; - // Add any new subscribers that were added while invoking the callback. - if let Some(new_subscribers) = lock.subscribers.remove(&emitter) { - subscribers.extend(new_subscribers); - } + subscribers.retain(|_, callback| f(callback)); + let mut lock = self.0.lock(); - // Remove any dropped subscriptions that were dropped while invoking the callback. - for (dropped_emitter, dropped_subscription_id) in - mem::take(&mut lock.dropped_subscribers) - { - debug_assert_eq!(emitter, dropped_emitter); - subscribers.remove(&dropped_subscription_id); - } + // Add any new subscribers that were added while invoking the callback. + if let Some(Some(new_subscribers)) = lock.subscribers.remove(&emitter) { + subscribers.extend(new_subscribers); + } - if !subscribers.is_empty() { - lock.subscribers.insert(emitter, subscribers); - } + // Remove any dropped subscriptions that were dropped while invoking the callback. + for (dropped_emitter, dropped_subscription_id) in mem::take(&mut lock.dropped_subscribers) { + debug_assert_eq!(*emitter, dropped_emitter); + subscribers.remove(&dropped_subscription_id); + } + + if !subscribers.is_empty() { + lock.subscribers.insert(emitter.clone(), Some(subscribers)); } } } From d3b02c4de47c80bd3a7bb544e75c7e347c0f3839 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Nov 2023 17:14:11 +0100 Subject: [PATCH 06/32] WIP: start on editor element --- crates/editor2/src/editor.rs | 92 ++++++------ crates/editor2/src/element.rs | 177 +++++++++++++++++++----- crates/editor2/src/scroll.rs | 28 ++-- crates/editor2/src/scroll/autoscroll.rs | 6 +- crates/gpui2/src/geometry.rs | 6 + 5 files changed, 215 insertions(+), 94 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index ea747de5de..52628f61b5 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -37,8 +37,8 @@ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ div, AnyElement, AppContext, BackgroundExecutor, Context, Div, Element, EventEmitter, - FocusHandle, Hsla, Model, Pixels, Render, Subscription, Task, TextStyle, View, ViewContext, - VisualContext, WeakView, WindowContext, + FocusHandle, Hsla, Model, Pixels, Render, Styled, Subscription, Task, TextStyle, View, + ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -68,6 +68,7 @@ use scroll::{ use selections_collection::{MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; +use smallvec::SmallVec; use std::{ any::TypeId, borrow::Cow, @@ -8347,51 +8348,51 @@ impl Editor { // .text() // } - // pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> { - // let mut wrap_guides = smallvec::smallvec![]; + pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> { + let mut wrap_guides = smallvec::smallvec![]; - // if self.show_wrap_guides == Some(false) { - // return wrap_guides; - // } + if self.show_wrap_guides == Some(false) { + return wrap_guides; + } - // let settings = self.buffer.read(cx).settings_at(0, cx); - // if settings.show_wrap_guides { - // if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) { - // wrap_guides.push((soft_wrap as usize, true)); - // } - // wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false))) - // } + let settings = self.buffer.read(cx).settings_at(0, cx); + if settings.show_wrap_guides { + if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) { + wrap_guides.push((soft_wrap as usize, true)); + } + wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false))) + } - // wrap_guides - // } + wrap_guides + } - // pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap { - // let settings = self.buffer.read(cx).settings_at(0, cx); - // let mode = self - // .soft_wrap_mode_override - // .unwrap_or_else(|| settings.soft_wrap); - // match mode { - // language_settings::SoftWrap::None => SoftWrap::None, - // language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth, - // language_settings::SoftWrap::PreferredLineLength => { - // SoftWrap::Column(settings.preferred_line_length) - // } - // } - // } + pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap { + let settings = self.buffer.read(cx).settings_at(0, cx); + let mode = self + .soft_wrap_mode_override + .unwrap_or_else(|| settings.soft_wrap); + match mode { + language_settings::SoftWrap::None => SoftWrap::None, + language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth, + language_settings::SoftWrap::PreferredLineLength => { + SoftWrap::Column(settings.preferred_line_length) + } + } + } - // pub fn set_soft_wrap_mode( - // &mut self, - // mode: language_settings::SoftWrap, - // cx: &mut ViewContext, - // ) { - // self.soft_wrap_mode_override = Some(mode); - // cx.notify(); - // } + pub fn set_soft_wrap_mode( + &mut self, + mode: language_settings::SoftWrap, + cx: &mut ViewContext, + ) { + self.soft_wrap_mode_override = Some(mode); + cx.notify(); + } - // pub fn set_wrap_width(&self, width: Option, cx: &mut AppContext) -> bool { - // self.display_map - // .update(cx, |map, cx| map.set_wrap_width(width, cx)) - // } + pub fn set_wrap_width(&self, width: Option, cx: &mut AppContext) -> bool { + self.display_map + .update(cx, |map, cx| map.set_wrap_width(width, cx)) + } // pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext) { // if self.soft_wrap_mode_override.is_some() { @@ -9321,11 +9322,14 @@ impl EventEmitter for Editor { } impl Render for Editor { - type Element = Div; + type Element = EditorElement; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - // todo!() - div() + EditorElement::new(EditorStyle { + text: cx.text_style(), + line_height_scalar: 1., + theme_id: 0, + }) } } diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 645cdc7646..6420d1e6cd 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -3,17 +3,18 @@ use super::{ }; use crate::{ display_map::{BlockStyle, DisplaySnapshot}, - EditorStyle, + EditorMode, EditorStyle, SoftWrap, }; use anyhow::Result; use gpui::{ - black, px, relative, AnyElement, Bounds, Element, Hsla, Line, Pixels, Size, Style, TextRun, - TextSystem, + black, point, px, relative, size, AnyElement, Bounds, Element, Hsla, Line, Pixels, Size, Style, + TextRun, TextSystem, ViewContext, }; use language::{CursorShape, Selection}; use smallvec::SmallVec; -use std::{ops::Range, sync::Arc}; +use std::{cmp, ops::Range, sync::Arc}; use sum_tree::Bias; +use theme::ActiveTheme; enum FoldMarkers {} @@ -1321,29 +1322,31 @@ impl EditorElement { // } // } - // fn column_pixels(&self, column: usize, cx: &ViewContext) -> f32 { - // let style = &self.style; + fn column_pixels(&self, column: usize, cx: &ViewContext) -> Pixels { + let style = &self.style; + let font_size = style.text.font_size * cx.rem_size(); + let layout = cx + .text_system() + .layout_text( + " ".repeat(column).as_str(), + font_size, + &[TextRun { + len: column, + font: style.text.font(), + color: Hsla::default(), + underline: None, + }], + None, + ) + .unwrap(); - // cx.text_layout_cache() - // .layout_str( - // " ".repeat(column).as_str(), - // style.text.font_size, - // &[( - // column, - // RunStyle { - // font_id: style.text.font_id, - // color: Color::black(), - // underline: Default::default(), - // }, - // )], - // ) - // .width() - // } + layout[0].width + } - // fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext) -> f32 { - // let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1; - // self.column_pixels(digit_count, cx) - // } + fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext) -> Pixels { + let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1; + self.column_pixels(digit_count, cx) + } //Folds contained in a hunk are ignored apart from shrinking visual size //If a fold contains any hunks then that fold line is marked as modified @@ -2002,6 +2005,7 @@ impl Element for EditorElement { element_state: &mut Self::ElementState, cx: &mut gpui::ViewContext, ) -> gpui::LayoutId { + let rem_size = cx.rem_size(); let mut style = Style::default(); style.size.width = relative(1.).into(); style.size.height = relative(1.).into(); @@ -2011,18 +2015,125 @@ impl Element for EditorElement { fn paint( &mut self, bounds: Bounds, - view_state: &mut Editor, + editor: &mut Editor, element_state: &mut Self::ElementState, cx: &mut gpui::ViewContext, ) { - let text_style = cx.text_style(); + // let mut size = constraint.max; + // if size.x().is_infinite() { + // unimplemented!("we don't yet handle an infinite width constraint on buffer elements"); + // } - let layout_text = cx.text_system().layout_text( - "hello world", - text_style.font_size * cx.rem_size(), - &[text_style.to_run("hello world".len())], - None, - ); + let snapshot = editor.snapshot(cx); + let style = self.style.clone(); + let font_id = cx.text_system().font_id(&style.text.font()).unwrap(); + let font_size = style.text.font_size * cx.rem_size(); + let line_height = (font_size * style.line_height_scalar).round(); + let em_width = cx + .text_system() + .typographic_bounds(font_id, font_size, 'm') + .unwrap() + .size + .width; + let em_advance = cx + .text_system() + .advance(font_id, font_size, 'm') + .unwrap() + .width; + + let gutter_padding; + let gutter_width; + let gutter_margin; + if snapshot.show_gutter { + let descent = cx.text_system().descent(font_id, font_size).unwrap(); + + let gutter_padding_factor = 3.5; + gutter_padding = (em_width * gutter_padding_factor).round(); + gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0; + gutter_margin = -descent; + } else { + gutter_padding = px(0.0); + gutter_width = px(0.0); + gutter_margin = px(0.0); + }; + + let text_width = bounds.size.width - gutter_width; + let overscroll = point(em_width, px(0.)); + let snapshot = { + editor.set_visible_line_count((bounds.size.height / line_height).into(), cx); + + let editor_width = text_width - gutter_margin - overscroll.x - em_width; + let wrap_width = match editor.soft_wrap_mode(cx) { + SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance, + SoftWrap::EditorWidth => editor_width, + SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance), + }; + + if editor.set_wrap_width(Some(wrap_width), cx) { + editor.snapshot(cx) + } else { + snapshot + } + }; + + let wrap_guides = editor + .wrap_guides(cx) + .iter() + .map(|(guide, active)| (self.column_pixels(*guide, cx), *active)) + .collect::>(); + + let scroll_height = Pixels::from(snapshot.max_point().row() + 1) * line_height; + // todo!("this should happen during layout") + if let EditorMode::AutoHeight { max_lines } = snapshot.mode { + todo!() + // size.set_y( + // scroll_height + // .min(constraint.max_along(Axis::Vertical)) + // .max(constraint.min_along(Axis::Vertical)) + // .max(line_height) + // .min(line_height * max_lines as f32), + // ) + } else if let EditorMode::SingleLine = snapshot.mode { + todo!() + // size.set_y(line_height.max(constraint.min_along(Axis::Vertical))) + } + // todo!() + // else if size.y().is_infinite() { + // // size.set_y(scroll_height); + // } + // + let gutter_size = size(gutter_width, bounds.size.height); + let text_size = size(text_width, bounds.size.height); + + let autoscroll_horizontally = + editor.autoscroll_vertically(bounds.size.height, line_height, cx); + let mut snapshot = editor.snapshot(cx); + + let scroll_position = snapshot.scroll_position(); + // The scroll position is a fractional point, the whole number of which represents + // the top of the window in terms of display rows. + let start_row = scroll_position.y as u32; + let height_in_lines = f32::from(bounds.size.height / line_height); + let max_row = snapshot.max_point().row(); + + // Add 1 to ensure selections bleed off screen + let end_row = 1 + cmp::min((scroll_position.y + height_in_lines).ceil() as u32, max_row); + + dbg!(start_row..end_row); + // let text_style = cx.text_style(); + // let layout_text = cx.text_system().layout_text( + // "hello world", + // text_style.font_size * cx.rem_size(), + // &[text_style.to_run("hello world".len())], + // None, + // ); + // let line_height = text_style + // .line_height + // .to_pixels(text_style.font_size.into(), cx.rem_size()); + + // layout_text.unwrap()[0] + // .paint(bounds.origin, line_height, cx) + // .unwrap(); } } diff --git a/crates/editor2/src/scroll.rs b/crates/editor2/src/scroll.rs index 5e4b32265a..1876952ae2 100644 --- a/crates/editor2/src/scroll.rs +++ b/crates/editor2/src/scroll.rs @@ -303,20 +303,20 @@ impl Editor { self.scroll_manager.visible_line_count } - // pub(crate) fn set_visible_line_count(&mut self, lines: f32, cx: &mut ViewContext) { - // let opened_first_time = self.scroll_manager.visible_line_count.is_none(); - // self.scroll_manager.visible_line_count = Some(lines); - // if opened_first_time { - // cx.spawn(|editor, mut cx| async move { - // editor - // .update(&mut cx, |editor, cx| { - // editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx) - // }) - // .ok() - // }) - // .detach() - // } - // } + pub(crate) fn set_visible_line_count(&mut self, lines: f32, cx: &mut ViewContext) { + let opened_first_time = self.scroll_manager.visible_line_count.is_none(); + self.scroll_manager.visible_line_count = Some(lines); + if opened_first_time { + cx.spawn(|editor, mut cx| async move { + editor + .update(&mut cx, |editor, cx| { + editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx) + }) + .ok() + }) + .detach() + } + } pub fn set_scroll_position( &mut self, diff --git a/crates/editor2/src/scroll/autoscroll.rs b/crates/editor2/src/scroll/autoscroll.rs index a4c37a258e..9315d5c099 100644 --- a/crates/editor2/src/scroll/autoscroll.rs +++ b/crates/editor2/src/scroll/autoscroll.rs @@ -48,11 +48,11 @@ impl AutoscrollStrategy { impl Editor { pub fn autoscroll_vertically( &mut self, - viewport_height: f32, - line_height: f32, + viewport_height: Pixels, + line_height: Pixels, cx: &mut ViewContext, ) -> bool { - let visible_lines = viewport_height / line_height; + let visible_lines = f32::from(viewport_height / line_height); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut scroll_position = self.scroll_manager.scroll_position(&display_map); let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) { diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index b2fad4efda..081b11aae0 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -825,6 +825,12 @@ impl From for u32 { } } +impl From for Pixels { + fn from(pixels: u32) -> Self { + Pixels(pixels as f32) + } +} + impl From for usize { fn from(pixels: Pixels) -> Self { pixels.0 as usize From c604a2e34e8b26dd3780de0a250b0e5bf8bbba7b Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 10:54:26 -0600 Subject: [PATCH 07/32] Add hover behaviour to tabs Co-Authored-By: Marshall Co-Authored-By: Nathan --- crates/gpui2/src/element.rs | 13 +++++++++++++ crates/gpui2/src/elements/div.rs | 8 ++++++-- crates/gpui2/src/style.rs | 11 +++++++++++ crates/gpui2/src/styled.rs | 21 +++++++++++++++++++++ crates/ui2/src/elements/icon.rs | 13 +++++++++++-- crates/workspace2/src/pane.rs | 10 +++++++++- 6 files changed, 71 insertions(+), 5 deletions(-) diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index a92dbd6ff9..2a0f557272 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -212,6 +212,19 @@ pub trait Component { { self.map(|this| if condition { then(this) } else { this }) } + + fn when_some(self, option: Option, then: impl FnOnce(Self, T) -> Self) -> Self + where + Self: Sized, + { + self.map(|this| { + if let Some(value) = option { + then(this, value) + } else { + this + } + }) + } } impl Component for AnyElement { diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 56940efce4..bf28ec3bf5 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -3,7 +3,7 @@ use crate::{ ElementInteraction, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable, GlobalElementId, GroupBounds, InteractiveElementState, LayoutId, Overflow, ParentElement, Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction, - StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, + StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, Visibility, }; use refineable::Refineable; use smallvec::SmallVec; @@ -249,11 +249,15 @@ where cx: &mut ViewContext, ) { self.with_element_id(cx, |this, _global_id, cx| { + let style = this.compute_style(bounds, element_state, cx); + if style.visibility == Visibility::Hidden { + return; + } + if let Some(group) = this.group.clone() { GroupBounds::push(group, bounds, cx); } - let style = this.compute_style(bounds, element_state, cx); let z_index = style.z_index.unwrap_or(0); let mut child_min = point(Pixels::MAX, Pixels::MAX); diff --git a/crates/gpui2/src/style.rs b/crates/gpui2/src/style.rs index b30d4aa002..b3cd5c478b 100644 --- a/crates/gpui2/src/style.rs +++ b/crates/gpui2/src/style.rs @@ -19,6 +19,9 @@ pub struct Style { /// What layout strategy should be used? pub display: Display, + /// Should the element be painted on screen? + pub visibility: Visibility, + // Overflow properties /// How children overflowing their container should affect layout #[refineable] @@ -107,6 +110,13 @@ impl Styled for StyleRefinement { } } +#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)] +pub enum Visibility { + #[default] + Visible, + Hidden, +} + #[derive(Clone, Debug)] pub struct BoxShadow { pub color: Hsla, @@ -297,6 +307,7 @@ impl Default for Style { fn default() -> Self { Style { display: Display::Block, + visibility: Visibility::Visible, overflow: Point { x: Overflow::Visible, y: Overflow::Visible, diff --git a/crates/gpui2/src/styled.rs b/crates/gpui2/src/styled.rs index 1eed74f096..a88a8b4d6f 100644 --- a/crates/gpui2/src/styled.rs +++ b/crates/gpui2/src/styled.rs @@ -1,6 +1,7 @@ use crate::{ self as gpui2, hsla, point, px, relative, rems, AlignItems, DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString, StyleRefinement, + Visibility, }; use crate::{BoxShadow, TextStyleRefinement}; use smallvec::smallvec; @@ -60,6 +61,26 @@ pub trait Styled { self } + /// Sets the visibility of the element to `visible`. + /// [Docs](https://tailwindcss.com/docs/visibility) + fn visible(mut self) -> Self + where + Self: Sized, + { + self.style().visibility = Some(Visibility::Visible); + self + } + + /// Sets the visibility of the element to `hidden`. + /// [Docs](https://tailwindcss.com/docs/visibility) + fn invisible(mut self) -> Self + where + Self: Sized, + { + self.style().visibility = Some(Visibility::Hidden); + self + } + /// Sets the flex direction of the element to `column`. /// [Docs](https://tailwindcss.com/docs/flex-direction#column) fn flex_col(mut self) -> Self diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index 5885d76101..94e15ca0f1 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -159,6 +159,7 @@ impl Icon { pub struct IconElement { icon: Icon, color: IconColor, + hover_color: Option, size: IconSize, } @@ -167,6 +168,7 @@ impl IconElement { Self { icon, color: IconColor::default(), + hover_color: None, size: IconSize::default(), } } @@ -176,13 +178,17 @@ impl IconElement { self } + pub fn hover_color(mut self, hover_color: impl Into>) -> Self { + self.hover_color = hover_color.into(); + self + } + pub fn size(mut self, size: IconSize) -> Self { self.size = size; self } fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let fill = self.color.color(cx); let svg_size = match self.size { IconSize::Small => rems(0.75), IconSize::Medium => rems(0.9375), @@ -192,7 +198,10 @@ impl IconElement { .size(svg_size) .flex_none() .path(self.icon.path()) - .text_color(fill) + .text_color(self.color.color(cx)) + .when_some(self.hover_color, |this, hover_color| { + this.hover(|style| style.text_color(hover_color.color(cx))) + }) } } diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 4c4f26fe0e..f8e3c02178 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1361,9 +1361,16 @@ impl Pane { let label = item.tab_content(Some(detail), cx); let close_icon = || { let id = item.id(); + div() .id(item.id()) - .child(IconElement::new(Icon::Close).color(IconColor::Muted)) + .invisible() + .group_hover("", |style| style.visible()) + .child( + IconElement::new(Icon::Close) + .color(IconColor::Muted) + .hover_color(IconColor::Accent), + ) .on_click(move |pane: &mut Self, _, cx| { pane.close_item_by_id(id, SaveIntent::Close, cx) .detach_and_log_err(cx); @@ -1388,6 +1395,7 @@ impl Pane { let close_right = ItemSettings::get_global(cx).close_position.right(); div() + .group("") .id(item.id()) // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) From d73c54f6049aeef161e6673eeabeedea2918b830 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 11:30:15 -0600 Subject: [PATCH 08/32] Add PointingHand on tabs --- crates/gpui2/src/elements/div.rs | 7 +++++++ crates/gpui2/src/style.rs | 10 +++++++--- crates/gpui2/src/styled.rs | 34 +++++++++++++++++++++++++++++--- crates/gpui2/src/window.rs | 31 ++++++++++++++++++++--------- crates/workspace2/src/pane.rs | 6 ++++-- crates/zed2/src/main.rs | 1 - 6 files changed, 71 insertions(+), 18 deletions(-) diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index bf28ec3bf5..e011041bae 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -254,6 +254,13 @@ where return; } + if let Some(mouse_cursor) = style.mouse_cursor { + let hovered = bounds.contains_point(&cx.mouse_position()); + if hovered { + cx.set_cursor_style(mouse_cursor); + } + } + if let Some(group) = this.group.clone() { GroupBounds::push(group, bounds, cx); } diff --git a/crates/gpui2/src/style.rs b/crates/gpui2/src/style.rs index b3cd5c478b..d2571a3253 100644 --- a/crates/gpui2/src/style.rs +++ b/crates/gpui2/src/style.rs @@ -1,8 +1,8 @@ use crate::{ black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask, - Corners, CornersRefinement, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, - FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems, Result, Rgba, - SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext, WindowContext, + Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font, + FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems, + Result, Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext, WindowContext, }; use refineable::{Cascade, Refineable}; use smallvec::SmallVec; @@ -101,6 +101,9 @@ pub struct Style { /// TEXT pub text: TextStyleRefinement, + /// The mouse cursor style shown when the mouse pointer is over an element. + pub mouse_cursor: Option, + pub z_index: Option, } @@ -339,6 +342,7 @@ impl Default for Style { corner_radii: Corners::default(), box_shadow: Default::default(), text: TextStyleRefinement::default(), + mouse_cursor: None, z_index: None, } } diff --git a/crates/gpui2/src/styled.rs b/crates/gpui2/src/styled.rs index a88a8b4d6f..4a9a5d7ecf 100644 --- a/crates/gpui2/src/styled.rs +++ b/crates/gpui2/src/styled.rs @@ -1,7 +1,7 @@ use crate::{ - self as gpui2, hsla, point, px, relative, rems, AlignItems, DefiniteLength, Display, Fill, - FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString, StyleRefinement, - Visibility, + self as gpui2, hsla, point, px, relative, rems, AlignItems, CursorStyle, DefiniteLength, + Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString, + StyleRefinement, Visibility, }; use crate::{BoxShadow, TextStyleRefinement}; use smallvec::smallvec; @@ -81,6 +81,34 @@ pub trait Styled { self } + fn cursor(mut self, cursor: CursorStyle) -> Self + where + Self: Sized, + { + self.style().mouse_cursor = Some(cursor); + self + } + + /// Sets the cursor style when hovering an element to `default`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_default(mut self) -> Self + where + Self: Sized, + { + self.style().mouse_cursor = Some(CursorStyle::Arrow); + self + } + + /// Sets the cursor style when hovering an element to `pointer`. + /// [Docs](https://tailwindcss.com/docs/cursor) + fn cursor_pointer(mut self) -> Self + where + Self: Sized, + { + self.style().mouse_cursor = Some(CursorStyle::PointingHand); + self + } + /// Sets the flex direction of the element to `column`. /// [Docs](https://tailwindcss.com/docs/flex-direction#column) fn flex_col(mut self) -> Self diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 2284f3ccc2..e998766bbb 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1,14 +1,14 @@ use crate::{ px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace, - Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect, - Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, - Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId, - Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, - MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformWindow, Point, - PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, - RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, - Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, - WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, + Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DispatchContext, DisplayId, + Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, + GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, + KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, + PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, + RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, + Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, + VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Result}; use collections::HashMap; @@ -190,6 +190,7 @@ pub struct Window { pub(crate) focus_handles: Arc>>, default_prevented: bool, mouse_position: Point, + requested_cursor_style: Option, scale_factor: f32, bounds: WindowBounds, bounds_observers: SubscriberSet<(), AnyObserver>, @@ -283,6 +284,7 @@ impl Window { focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), default_prevented: true, mouse_position, + requested_cursor_style: None, scale_factor, bounds, bounds_observers: SubscriberSet::new(), @@ -669,6 +671,10 @@ impl<'a> WindowContext<'a> { self.window.mouse_position } + pub fn set_cursor_style(&mut self, style: CursorStyle) { + self.window.requested_cursor_style = Some(style) + } + /// Called during painting to invoke the given closure in a new stacking context. The given /// z-index is interpreted relative to the previous call to `stack`. pub fn stack(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { @@ -987,6 +993,13 @@ impl<'a> WindowContext<'a> { let scene = self.window.scene_builder.build(); self.window.platform_window.draw(scene); + let cursor_style = self + .window + .requested_cursor_style + .take() + .unwrap_or(CursorStyle::Arrow); + self.platform.set_cursor_style(cursor_style); + self.window.dirty = false; } diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index f8e3c02178..4d5b96ea6d 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -9,8 +9,9 @@ use crate::{ use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use gpui::{ - AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle, Model, - PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext, + AppContext, AsyncWindowContext, Component, CursorStyle, Div, EntityId, EventEmitter, + FocusHandle, Model, PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, + WindowContext, }; use parking_lot::Mutex; use project2::{Project, ProjectEntryId, ProjectPath}; @@ -1397,6 +1398,7 @@ impl Pane { div() .group("") .id(item.id()) + .cursor_pointer() // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) // .on_drop(|_view, state: View, cx| { diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 79ba132e4f..52eb512eb4 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -208,7 +208,6 @@ fn main() { if stdout_is_a_pty() { cx.activate(true); let urls = collect_url_args(); - dbg!(&urls); if !urls.is_empty() { listener.open_urls(urls) } From 26e64fb84385bc1d6f3f531a60fc1472b51989d4 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 12:51:38 -0600 Subject: [PATCH 09/32] gpui2: Add on_hover events --- crates/gpui2/src/interactive.rs | 60 +++++++++++++++++++++++++++++++++ crates/workspace2/src/pane.rs | 4 +++ 2 files changed, 64 insertions(+) diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index 020cb82cd2..7966b68e12 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -333,6 +333,37 @@ pub trait StatefulInteractive: StatelessInteractive { })); self } + + fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext)) -> Self + where + Self: Sized, + { + debug_assert!( + self.stateful_interaction().hover_listener.is_none(), + "calling on_hover more than once on the same element is not supported" + ); + self.stateful_interaction().hover_listener = Some(Box::new(listener)); + self + } + + fn tooltip( + mut self, + build_tooltip: impl Fn(&mut V, &mut ViewContext) -> View + 'static, + ) -> Self + where + Self: Sized, + W: 'static + Render, + { + debug_assert!( + self.stateful_interaction().tooltip_builder.is_none(), + "calling tooltip more than once on the same element is not supported" + ); + self.stateful_interaction().tooltip_builder = Some(Box::new(move |view_state, cx| { + build_tooltip(view_state, cx).into() + })); + + self + } } pub trait ElementInteraction: 'static { @@ -568,6 +599,24 @@ pub trait ElementInteraction: 'static { } } + if let Some(hover_listener) = stateful.hover_listener.take() { + let was_hovered = element_state.hover_state.clone(); + let has_mouse_down = element_state.pending_mouse_down.lock().is_some(); + cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { + if phase != DispatchPhase::Bubble { + return; + } + let is_hovered = bounds.contains_point(&event.position) && !has_mouse_down; + let mut was_hovered = was_hovered.lock(); + + if is_hovered != was_hovered.clone() { + *was_hovered = is_hovered; + drop(was_hovered); + hover_listener(view_state, is_hovered, cx); + } + }); + } + let active_state = element_state.active_state.clone(); if active_state.lock().is_none() { let active_group_bounds = stateful @@ -639,6 +688,8 @@ pub struct StatefulInteraction { active_style: StyleRefinement, group_active_style: Option, drag_listener: Option>, + hover_listener: Option>, + tooltip_builder: Option>, } impl ElementInteraction for StatefulInteraction { @@ -666,6 +717,8 @@ impl From for StatefulInteraction { stateless: StatelessInteraction::default(), click_listeners: SmallVec::new(), drag_listener: None, + hover_listener: None, + tooltip_builder: None, active_style: StyleRefinement::default(), group_active_style: None, } @@ -695,6 +748,8 @@ impl StatelessInteraction { stateless: self, click_listeners: SmallVec::new(), drag_listener: None, + hover_listener: None, + tooltip_builder: None, active_style: StyleRefinement::default(), group_active_style: None, } @@ -746,6 +801,7 @@ impl ActiveState { #[derive(Default)] pub struct InteractiveElementState { active_state: Arc>, + hover_state: Arc>, pending_mouse_down: Arc>>, scroll_offset: Option>>>, } @@ -1097,6 +1153,10 @@ pub type ClickListener = Box) pub(crate) type DragListener = Box, &mut ViewContext) -> AnyDrag + 'static>; +pub(crate) type HoverListener = Box) + 'static>; + +pub(crate) type TooltipBuilder = Box) -> AnyView + 'static>; + pub type KeyListener = Box< dyn Fn( &mut V, diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 4d5b96ea6d..acda04c5a4 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1399,6 +1399,10 @@ impl Pane { .group("") .id(item.id()) .cursor_pointer() + .on_hover(|_, hovered, _| { + dbg!(hovered); + }) + // .tooltip(|pane, cx| cx.create_view( tooltip.child("Hovering the tab")) // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) // .on_drop(|_view, state: View, cx| { From 33245d119e24063ed2732d138e58c296cd5518ca Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 14:02:46 -0600 Subject: [PATCH 10/32] Tooltip on tabs Co-Authored-By: Julia --- crates/gpui2/src/app.rs | 2 ++ crates/gpui2/src/interactive.rs | 24 +++++++++++++++++-- crates/gpui2/src/window.rs | 8 +++++++ crates/ui2/src/components.rs | 2 ++ crates/ui2/src/components/tooltip.rs | 36 ++++++++++++++++++++++++++++ crates/workspace2/src/pane.rs | 12 ++++++---- 6 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 crates/ui2/src/components/tooltip.rs diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 9ec76a86d4..974395e897 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -157,6 +157,7 @@ pub struct AppContext { flushing_effects: bool, pending_updates: usize, pub(crate) active_drag: Option, + pub(crate) active_tooltip: Option, pub(crate) next_frame_callbacks: HashMap>, pub(crate) frame_consumers: HashMap>, pub(crate) background_executor: BackgroundExecutor, @@ -215,6 +216,7 @@ impl AppContext { flushing_effects: false, pending_updates: 0, active_drag: None, + active_tooltip: None, next_frame_callbacks: HashMap::default(), frame_consumers: HashMap::default(), background_executor: executor, diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index 7966b68e12..def7315a7c 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -358,7 +358,7 @@ pub trait StatefulInteractive: StatelessInteractive { self.stateful_interaction().tooltip_builder.is_none(), "calling tooltip more than once on the same element is not supported" ); - self.stateful_interaction().tooltip_builder = Some(Box::new(move |view_state, cx| { + self.stateful_interaction().tooltip_builder = Some(Arc::new(move |view_state, cx| { build_tooltip(view_state, cx).into() })); @@ -602,6 +602,10 @@ pub trait ElementInteraction: 'static { if let Some(hover_listener) = stateful.hover_listener.take() { let was_hovered = element_state.hover_state.clone(); let has_mouse_down = element_state.pending_mouse_down.lock().is_some(); + + let active_tooltip = element_state.active_tooltip.clone(); + let tooltip_builder = stateful.tooltip_builder.clone(); + cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -612,11 +616,26 @@ pub trait ElementInteraction: 'static { if is_hovered != was_hovered.clone() { *was_hovered = is_hovered; drop(was_hovered); + if let Some(tooltip_builder) = &tooltip_builder { + let mut active_tooltip = active_tooltip.lock(); + if is_hovered && active_tooltip.is_none() { + *active_tooltip = Some(tooltip_builder(view_state, cx)); + } else if !is_hovered { + active_tooltip.take(); + } + } + hover_listener(view_state, is_hovered, cx); } }); } + if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() { + if *element_state.hover_state.lock() { + cx.active_tooltip = Some(active_tooltip.clone()); + } + } + let active_state = element_state.active_state.clone(); if active_state.lock().is_none() { let active_group_bounds = stateful @@ -804,6 +823,7 @@ pub struct InteractiveElementState { hover_state: Arc>, pending_mouse_down: Arc>>, scroll_offset: Option>>>, + active_tooltip: Arc>>, } impl InteractiveElementState { @@ -1155,7 +1175,7 @@ pub(crate) type DragListener = pub(crate) type HoverListener = Box) + 'static>; -pub(crate) type TooltipBuilder = Box) -> AnyView + 'static>; +pub(crate) type TooltipBuilder = Arc) -> AnyView + 'static>; pub type KeyListener = Box< dyn Fn( diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index e998766bbb..46ac30592b 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -987,6 +987,14 @@ impl<'a> WindowContext<'a> { cx.active_drag = Some(active_drag); }); }); + } else if let Some(active_tooltip) = self.app.active_tooltip.take() { + self.stack(1, |cx| { + cx.with_element_offset(Some(cx.mouse_position()), |cx| { + let available_space = + size(AvailableSpace::MinContent, AvailableSpace::MinContent); + active_tooltip.draw(available_space, cx); + }); + }); } self.window.root_view = Some(root_view); diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 7a9cac01ca..f10f8cee8d 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -31,6 +31,7 @@ mod theme_selector; mod title_bar; mod toast; mod toolbar; +mod tooltip; mod traffic_lights; mod workspace; @@ -67,5 +68,6 @@ pub use theme_selector::*; pub use title_bar::*; pub use toast::*; pub use toolbar::*; +pub use tooltip::*; pub use traffic_lights::*; pub use workspace::*; diff --git a/crates/ui2/src/components/tooltip.rs b/crates/ui2/src/components/tooltip.rs new file mode 100644 index 0000000000..eb53b506eb --- /dev/null +++ b/crates/ui2/src/components/tooltip.rs @@ -0,0 +1,36 @@ +use gpui2::{ + div, px, Div, ParentElement, Render, SharedString, Styled, View, ViewContext, VisualContext, +}; +use theme2::ActiveTheme; + +#[derive(Clone, Debug)] +pub struct TextTooltip { + title: SharedString, +} + +impl TextTooltip { + pub fn new(str: SharedString) -> Self { + Self { title: str } + } + + pub fn build_view(str: SharedString, cx: &mut C) -> C::Result> { + cx.build_view(|cx| TextTooltip::new(str)) + } +} + +impl Render for TextTooltip { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + let theme = cx.theme(); + div() + .bg(theme.colors().background) + .rounded(px(8.)) + .border() + .border_color(theme.colors().border) + .text_color(theme.colors().text) + .pl_2() + .pr_2() + .child(self.title.clone()) + } +} diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index acda04c5a4..a1ec168488 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -9,9 +9,8 @@ use crate::{ use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use gpui::{ - AppContext, AsyncWindowContext, Component, CursorStyle, Div, EntityId, EventEmitter, - FocusHandle, Model, PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, - WindowContext, + AnyView, AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle, + Model, PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext, }; use parking_lot::Mutex; use project2::{Project, ProjectEntryId, ProjectPath}; @@ -27,7 +26,7 @@ use std::{ }, }; use ui::v_stack; -use ui::{prelude::*, Icon, IconButton, IconColor, IconElement}; +use ui::{prelude::*, Icon, IconButton, IconColor, IconElement, TextTooltip}; use util::truncate_and_remove_front; #[derive(PartialEq, Clone, Copy, Deserialize, Debug)] @@ -1402,7 +1401,10 @@ impl Pane { .on_hover(|_, hovered, _| { dbg!(hovered); }) - // .tooltip(|pane, cx| cx.create_view( tooltip.child("Hovering the tab")) + .when_some(item.tab_tooltip_text(cx), |div, text| { + div.tooltip(move |_, cx| TextTooltip::build_view(text.clone(), cx)) + }) + // .tooltip(|pane, cx| cx.build_view(|cx| div().child(title))) // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) // .on_drop(|_view, state: View, cx| { From f97046b86fae67d9cbf8ccf4e7b86781f9d2f7a9 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 14:15:32 -0600 Subject: [PATCH 11/32] MOAR TOOLTIPS --- crates/gpui2/src/interactive.rs | 51 ++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index def7315a7c..4a6185f737 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -17,6 +17,7 @@ use std::{ ops::Deref, path::PathBuf, sync::Arc, + time::{Duration, Instant}, }; const DRAG_THRESHOLD: f64 = 2.; @@ -603,9 +604,6 @@ pub trait ElementInteraction: 'static { let was_hovered = element_state.hover_state.clone(); let has_mouse_down = element_state.pending_mouse_down.lock().is_some(); - let active_tooltip = element_state.active_tooltip.clone(); - let tooltip_builder = stateful.tooltip_builder.clone(); - cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; @@ -616,23 +614,39 @@ pub trait ElementInteraction: 'static { if is_hovered != was_hovered.clone() { *was_hovered = is_hovered; drop(was_hovered); - if let Some(tooltip_builder) = &tooltip_builder { - let mut active_tooltip = active_tooltip.lock(); - if is_hovered && active_tooltip.is_none() { - *active_tooltip = Some(tooltip_builder(view_state, cx)); - } else if !is_hovered { - active_tooltip.take(); - } - } hover_listener(view_state, is_hovered, cx); } }); } - if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() { - if *element_state.hover_state.lock() { - cx.active_tooltip = Some(active_tooltip.clone()); + // if we're hovered: + // if no timer, start timer + // if timer hits 1s, call tooltip_builder() + // + + if let Some(tooltip_builder) = &stateful.tooltip_builder { + let mut active_tooltip = element_state.active_tooltip.lock(); + let is_hovered = bounds.contains_point(&cx.mouse_position()) + && !element_state.pending_mouse_down.lock().is_some(); + + if is_hovered { + if let Some(active_tooltip) = active_tooltip { + active_tooltip.view = Some(tooltip_builder(cx)) + } else { + *active_tooltip = Some(ActiveTooltip { + hover_start: Instant::now(), + view: None, + }); + } + } else { + active_tooltip.take(); + } + + if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() { + if *element_state.hover_state.lock() { + cx.active_tooltip = Some(active_tooltip.clone()); + } } } @@ -823,7 +837,12 @@ pub struct InteractiveElementState { hover_state: Arc>, pending_mouse_down: Arc>>, scroll_offset: Option>>>, - active_tooltip: Arc>>, + active_tooltip: Arc>>, +} + +pub struct ActiveTooltip { + hover_start: Instant, + view: Option, } impl InteractiveElementState { @@ -1175,7 +1194,7 @@ pub(crate) type DragListener = pub(crate) type HoverListener = Box) + 'static>; -pub(crate) type TooltipBuilder = Arc) -> AnyView + 'static>; +pub(crate) type TooltipBuilder = Arc) -> AnyView + 'static>; pub type KeyListener = Box< dyn Fn( From 1361b617082bf730dfbd4aaa57bf8bbe4226e08f Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Fri, 3 Nov 2023 16:36:02 -0400 Subject: [PATCH 12/32] Use an `IconButton` for the tab close button --- crates/ui2/src/elements/icon.rs | 10 ---------- crates/workspace2/src/pane.rs | 19 ++++++++----------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs index 94e15ca0f1..9c056d68d1 100644 --- a/crates/ui2/src/elements/icon.rs +++ b/crates/ui2/src/elements/icon.rs @@ -159,7 +159,6 @@ impl Icon { pub struct IconElement { icon: Icon, color: IconColor, - hover_color: Option, size: IconSize, } @@ -168,7 +167,6 @@ impl IconElement { Self { icon, color: IconColor::default(), - hover_color: None, size: IconSize::default(), } } @@ -178,11 +176,6 @@ impl IconElement { self } - pub fn hover_color(mut self, hover_color: impl Into>) -> Self { - self.hover_color = hover_color.into(); - self - } - pub fn size(mut self, size: IconSize) -> Self { self.size = size; self @@ -199,9 +192,6 @@ impl IconElement { .flex_none() .path(self.icon.path()) .text_color(self.color.color(cx)) - .when_some(self.hover_color, |this, hover_color| { - this.hover(|style| style.text_color(hover_color.color(cx))) - }) } } diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index a1ec168488..0c2ee5f46e 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -9,8 +9,8 @@ use crate::{ use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use gpui::{ - AnyView, AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle, - Model, PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext, + AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle, Model, + PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext, }; use parking_lot::Mutex; use project2::{Project, ProjectEntryId, ProjectPath}; @@ -1366,15 +1366,12 @@ impl Pane { .id(item.id()) .invisible() .group_hover("", |style| style.visible()) - .child( - IconElement::new(Icon::Close) - .color(IconColor::Muted) - .hover_color(IconColor::Accent), - ) - .on_click(move |pane: &mut Self, _, cx| { - pane.close_item_by_id(id, SaveIntent::Close, cx) - .detach_and_log_err(cx); - }) + .child(IconButton::::new("close_tab", Icon::Close).on_click( + move |pane: &mut Self, cx| { + pane.close_item_by_id(id, SaveIntent::Close, cx) + .detach_and_log_err(cx); + }, + )) }; let (text_color, tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index { From b5224bdafd5417619f5652458bcc0b38d896c0d5 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Fri, 3 Nov 2023 16:40:46 -0400 Subject: [PATCH 13/32] Remove unneeded type qualification --- crates/workspace2/src/pane.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 0c2ee5f46e..3cc9a8b5f2 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1366,7 +1366,7 @@ impl Pane { .id(item.id()) .invisible() .group_hover("", |style| style.visible()) - .child(IconButton::::new("close_tab", Icon::Close).on_click( + .child(IconButton::new("close_tab", Icon::Close).on_click( move |pane: &mut Self, cx| { pane.close_item_by_id(id, SaveIntent::Close, cx) .detach_and_log_err(cx); From 3834e26f7150215502754eae999f7fc35ca11008 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 3 Nov 2023 18:02:58 -0400 Subject: [PATCH 14/32] Tooltips in mouse event handler & fix executor timer Co-Authored-By: Conrad Irwin --- crates/gpui2/build.rs | 1 + crates/gpui2/src/interactive.rs | 56 +++++++++------------ crates/gpui2/src/platform/mac/dispatcher.rs | 14 +----- crates/gpui2/src/window.rs | 13 +++-- crates/ui2/src/components/tooltip.rs | 34 +++++++++++-- crates/workspace2/src/pane.rs | 8 +-- 6 files changed, 67 insertions(+), 59 deletions(-) diff --git a/crates/gpui2/build.rs b/crates/gpui2/build.rs index c9abfaa6bb..6e8a0868b9 100644 --- a/crates/gpui2/build.rs +++ b/crates/gpui2/build.rs @@ -20,6 +20,7 @@ fn generate_dispatch_bindings() { .header("src/platform/mac/dispatch.h") .allowlist_var("_dispatch_main_q") .allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT") + .allowlist_var("DISPATCH_TIME_NOW") .allowlist_function("dispatch_get_global_queue") .allowlist_function("dispatch_async_f") .allowlist_function("dispatch_after_f") diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index 4a6185f737..f7a8f033c4 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -17,7 +17,6 @@ use std::{ ops::Deref, path::PathBuf, sync::Arc, - time::{Duration, Instant}, }; const DRAG_THRESHOLD: f64 = 2.; @@ -602,13 +601,14 @@ pub trait ElementInteraction: 'static { if let Some(hover_listener) = stateful.hover_listener.take() { let was_hovered = element_state.hover_state.clone(); - let has_mouse_down = element_state.pending_mouse_down.lock().is_some(); + let has_mouse_down = element_state.pending_mouse_down.clone(); cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; } - let is_hovered = bounds.contains_point(&event.position) && !has_mouse_down; + let is_hovered = + bounds.contains_point(&event.position) && has_mouse_down.lock().is_none(); let mut was_hovered = was_hovered.lock(); if is_hovered != was_hovered.clone() { @@ -620,33 +620,30 @@ pub trait ElementInteraction: 'static { }); } - // if we're hovered: - // if no timer, start timer - // if timer hits 1s, call tooltip_builder() - // + if let Some(tooltip_builder) = stateful.tooltip_builder.take() { + let tooltip_view = element_state.tooltip_view.clone(); + let pending_mouse_down = element_state.pending_mouse_down.clone(); - if let Some(tooltip_builder) = &stateful.tooltip_builder { - let mut active_tooltip = element_state.active_tooltip.lock(); - let is_hovered = bounds.contains_point(&cx.mouse_position()) - && !element_state.pending_mouse_down.lock().is_some(); + cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { + if phase != DispatchPhase::Bubble { + return; + } - if is_hovered { - if let Some(active_tooltip) = active_tooltip { - active_tooltip.view = Some(tooltip_builder(cx)) + let is_hovered = bounds.contains_point(&event.position) + && pending_mouse_down.lock().is_none(); + let mut tooltip_view = tooltip_view.lock(); + + if is_hovered { + if tooltip_view.is_none() { + *tooltip_view = Some(tooltip_builder(view_state, cx)); + } } else { - *active_tooltip = Some(ActiveTooltip { - hover_start: Instant::now(), - view: None, - }); + tooltip_view.take(); } - } else { - active_tooltip.take(); - } + }); - if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() { - if *element_state.hover_state.lock() { - cx.active_tooltip = Some(active_tooltip.clone()); - } + if let Some(active_tooltip) = element_state.tooltip_view.lock().as_ref() { + cx.active_tooltip = Some(active_tooltip.clone()); } } @@ -837,12 +834,7 @@ pub struct InteractiveElementState { hover_state: Arc>, pending_mouse_down: Arc>>, scroll_offset: Option>>>, - active_tooltip: Arc>>, -} - -pub struct ActiveTooltip { - hover_start: Instant, - view: Option, + tooltip_view: Arc>>, } impl InteractiveElementState { @@ -1194,7 +1186,7 @@ pub(crate) type DragListener = pub(crate) type HoverListener = Box) + 'static>; -pub(crate) type TooltipBuilder = Arc) -> AnyView + 'static>; +pub(crate) type TooltipBuilder = Arc) -> AnyView + 'static>; pub type KeyListener = Box< dyn Fn( diff --git a/crates/gpui2/src/platform/mac/dispatcher.rs b/crates/gpui2/src/platform/mac/dispatcher.rs index f5334912c6..68c0e3b4f5 100644 --- a/crates/gpui2/src/platform/mac/dispatcher.rs +++ b/crates/gpui2/src/platform/mac/dispatcher.rs @@ -11,11 +11,7 @@ use objc::{ }; use parking::{Parker, Unparker}; use parking_lot::Mutex; -use std::{ - ffi::c_void, - sync::Arc, - time::{Duration, SystemTime}, -}; +use std::{ffi::c_void, sync::Arc, time::Duration}; include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs")); @@ -62,16 +58,10 @@ impl PlatformDispatcher for MacDispatcher { } fn dispatch_after(&self, duration: Duration, runnable: Runnable) { - let now = SystemTime::now(); - let after_duration = now - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_nanos() as u64 - + duration.as_nanos() as u64; unsafe { let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0); - let when = dispatch_time(0, after_duration as i64); + let when = dispatch_time(DISPATCH_TIME_NOW as u64, duration.as_nanos() as i64); dispatch_after_f( when, queue, diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 46ac30592b..fe6f516e43 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -989,11 +989,14 @@ impl<'a> WindowContext<'a> { }); } else if let Some(active_tooltip) = self.app.active_tooltip.take() { self.stack(1, |cx| { - cx.with_element_offset(Some(cx.mouse_position()), |cx| { - let available_space = - size(AvailableSpace::MinContent, AvailableSpace::MinContent); - active_tooltip.draw(available_space, cx); - }); + cx.with_element_offset( + Some(cx.mouse_position() + Point::new(px(8.0), px(8.0))), + |cx| { + let available_space = + size(AvailableSpace::MinContent, AvailableSpace::MinContent); + active_tooltip.draw(available_space, cx); + }, + ); }); } diff --git a/crates/ui2/src/components/tooltip.rs b/crates/ui2/src/components/tooltip.rs index eb53b506eb..f94518224d 100644 --- a/crates/ui2/src/components/tooltip.rs +++ b/crates/ui2/src/components/tooltip.rs @@ -1,20 +1,44 @@ +use std::time::Duration; + use gpui2::{ - div, px, Div, ParentElement, Render, SharedString, Styled, View, ViewContext, VisualContext, + div, px, Component, Div, ParentElement, Render, SharedString, Styled, View, ViewContext, + VisualContext, WindowContext, }; use theme2::ActiveTheme; +const DELAY: Duration = Duration::from_millis(500); + #[derive(Clone, Debug)] pub struct TextTooltip { title: SharedString, + visible: bool, } impl TextTooltip { pub fn new(str: SharedString) -> Self { - Self { title: str } + Self { + title: str, + visible: false, + } } - pub fn build_view(str: SharedString, cx: &mut C) -> C::Result> { - cx.build_view(|cx| TextTooltip::new(str)) + pub fn build_view(str: SharedString, cx: &mut WindowContext) -> View { + let view = cx.build_view(|cx| TextTooltip::new(str)); + + let handle = view.downgrade(); + cx.spawn(|mut cx| async move { + cx.background_executor().timer(DELAY).await; + + handle + .update(&mut cx, |this, cx| { + this.visible = true; + cx.notify(); + }) + .ok(); + }) + .detach(); + + view } } @@ -24,9 +48,11 @@ impl Render for TextTooltip { fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let theme = cx.theme(); div() + .when(!self.visible, |this| this.invisible()) .bg(theme.colors().background) .rounded(px(8.)) .border() + .font("Zed Sans") .border_color(theme.colors().border) .text_color(theme.colors().text) .pl_2() diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index a1ec168488..64b995b538 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -9,8 +9,8 @@ use crate::{ use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use gpui::{ - AnyView, AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle, - Model, PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext, + AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle, Model, + PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext, }; use parking_lot::Mutex; use project2::{Project, ProjectEntryId, ProjectPath}; @@ -1398,13 +1398,9 @@ impl Pane { .group("") .id(item.id()) .cursor_pointer() - .on_hover(|_, hovered, _| { - dbg!(hovered); - }) .when_some(item.tab_tooltip_text(cx), |div, text| { div.tooltip(move |_, cx| TextTooltip::build_view(text.clone(), cx)) }) - // .tooltip(|pane, cx| cx.build_view(|cx| div().child(title))) // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) // .on_drop(|_view, state: View, cx| { From 4725cd2cd6b396c5adf19c0a27d87a1750598cf3 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 3 Nov 2023 18:37:15 -0400 Subject: [PATCH 15/32] Move more tooltip logic into gpui2 & fix tooltip moving on paint Co-Authored-By: Conrad Irwin --- crates/gpui2/src/app.rs | 7 +++- crates/gpui2/src/geometry.rs | 2 +- crates/gpui2/src/interactive.rs | 58 ++++++++++++++++++++++------ crates/gpui2/src/window.rs | 13 +++---- crates/ui2/src/components/tooltip.rs | 33 +--------------- crates/workspace2/src/pane.rs | 2 +- 6 files changed, 61 insertions(+), 54 deletions(-) diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 974395e897..9e454dc652 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -157,7 +157,7 @@ pub struct AppContext { flushing_effects: bool, pending_updates: usize, pub(crate) active_drag: Option, - pub(crate) active_tooltip: Option, + pub(crate) active_tooltip: Option, pub(crate) next_frame_callbacks: HashMap>, pub(crate) frame_consumers: HashMap>, pub(crate) background_executor: BackgroundExecutor, @@ -898,3 +898,8 @@ pub(crate) struct AnyDrag { pub view: AnyView, pub cursor_offset: Point, } + +pub(crate) struct AnyTooltip { + pub view: AnyView, + pub cursor_offset: Point, +} diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index 081b11aae0..d6755a5397 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -21,7 +21,7 @@ pub fn point(x: T, y: T) -> Point { } impl Point { - pub fn new(x: T, y: T) -> Self { + pub const fn new(x: T, y: T) -> Self { Self { x, y } } diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index f7a8f033c4..dd09ad512b 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -1,8 +1,8 @@ use crate::{ - div, point, px, Action, AnyDrag, AnyView, AppContext, BorrowWindow, Bounds, Component, - DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch, Keystroke, - Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style, StyleRefinement, View, - ViewContext, + div, point, px, Action, AnyDrag, AnyTooltip, AnyView, AppContext, BorrowWindow, Bounds, + Component, DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch, + Keystroke, Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style, + StyleRefinement, View, ViewContext, }; use collections::HashMap; use derive_more::{Deref, DerefMut}; @@ -17,9 +17,12 @@ use std::{ ops::Deref, path::PathBuf, sync::Arc, + time::Duration, }; const DRAG_THRESHOLD: f64 = 2.; +const TOOLTIP_DELAY: Duration = Duration::from_millis(500); +const TOOLTIP_OFFSET: Point = Point::new(px(10.0), px(8.0)); pub trait StatelessInteractive: Element { fn stateless_interaction(&mut self) -> &mut StatelessInteraction; @@ -621,7 +624,7 @@ pub trait ElementInteraction: 'static { } if let Some(tooltip_builder) = stateful.tooltip_builder.take() { - let tooltip_view = element_state.tooltip_view.clone(); + let active_tooltip = element_state.active_tooltip.clone(); let pending_mouse_down = element_state.pending_mouse_down.clone(); cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { @@ -631,19 +634,44 @@ pub trait ElementInteraction: 'static { let is_hovered = bounds.contains_point(&event.position) && pending_mouse_down.lock().is_none(); - let mut tooltip_view = tooltip_view.lock(); + let mut tooltip_lock = active_tooltip.lock(); if is_hovered { - if tooltip_view.is_none() { - *tooltip_view = Some(tooltip_builder(view_state, cx)); + if tooltip_lock.is_none() { + *tooltip_lock = Some(ActiveTooltip { + view: tooltip_builder(view_state, cx), + visible: false, + coordinates: event.position, + }); + + let active_tooltip = active_tooltip.clone(); + cx.spawn(move |view, mut cx| async move { + cx.background_executor().timer(TOOLTIP_DELAY).await; + + view.update(&mut cx, |_, cx| { + if let Some(active_tooltip) = active_tooltip.lock().as_mut() { + active_tooltip.visible = true; + active_tooltip.coordinates = + cx.mouse_position() + TOOLTIP_OFFSET; + } + cx.notify(); + }) + .ok() + }) + .detach(); } } else { - tooltip_view.take(); + tooltip_lock.take(); } }); - if let Some(active_tooltip) = element_state.tooltip_view.lock().as_ref() { - cx.active_tooltip = Some(active_tooltip.clone()); + if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() { + if active_tooltip.visible { + cx.active_tooltip = Some(AnyTooltip { + view: active_tooltip.view.clone(), + cursor_offset: active_tooltip.coordinates, + }); + } } } @@ -834,7 +862,13 @@ pub struct InteractiveElementState { hover_state: Arc>, pending_mouse_down: Arc>>, scroll_offset: Option>>>, - tooltip_view: Arc>>, + active_tooltip: Arc>>, +} + +struct ActiveTooltip { + view: AnyView, + visible: bool, + coordinates: Point, } impl InteractiveElementState { diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index fe6f516e43..9cab40082b 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -989,14 +989,11 @@ impl<'a> WindowContext<'a> { }); } else if let Some(active_tooltip) = self.app.active_tooltip.take() { self.stack(1, |cx| { - cx.with_element_offset( - Some(cx.mouse_position() + Point::new(px(8.0), px(8.0))), - |cx| { - let available_space = - size(AvailableSpace::MinContent, AvailableSpace::MinContent); - active_tooltip.draw(available_space, cx); - }, - ); + cx.with_element_offset(Some(active_tooltip.cursor_offset), |cx| { + let available_space = + size(AvailableSpace::MinContent, AvailableSpace::MinContent); + active_tooltip.view.draw(available_space, cx); + }); }); } diff --git a/crates/ui2/src/components/tooltip.rs b/crates/ui2/src/components/tooltip.rs index f94518224d..0bc00a9be1 100644 --- a/crates/ui2/src/components/tooltip.rs +++ b/crates/ui2/src/components/tooltip.rs @@ -1,44 +1,16 @@ use std::time::Duration; -use gpui2::{ - div, px, Component, Div, ParentElement, Render, SharedString, Styled, View, ViewContext, - VisualContext, WindowContext, -}; +use gpui2::{div, px, Div, ParentElement, Render, SharedString, Styled, ViewContext}; use theme2::ActiveTheme; -const DELAY: Duration = Duration::from_millis(500); - #[derive(Clone, Debug)] pub struct TextTooltip { title: SharedString, - visible: bool, } impl TextTooltip { pub fn new(str: SharedString) -> Self { - Self { - title: str, - visible: false, - } - } - - pub fn build_view(str: SharedString, cx: &mut WindowContext) -> View { - let view = cx.build_view(|cx| TextTooltip::new(str)); - - let handle = view.downgrade(); - cx.spawn(|mut cx| async move { - cx.background_executor().timer(DELAY).await; - - handle - .update(&mut cx, |this, cx| { - this.visible = true; - cx.notify(); - }) - .ok(); - }) - .detach(); - - view + Self { title: str } } } @@ -48,7 +20,6 @@ impl Render for TextTooltip { fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let theme = cx.theme(); div() - .when(!self.visible, |this| this.invisible()) .bg(theme.colors().background) .rounded(px(8.)) .border() diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 64b995b538..131a3e977b 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1399,7 +1399,7 @@ impl Pane { .id(item.id()) .cursor_pointer() .when_some(item.tab_tooltip_text(cx), |div, text| { - div.tooltip(move |_, cx| TextTooltip::build_view(text.clone(), cx)) + div.tooltip(move |_, cx| cx.build_view(|cx| TextTooltip::new(text.clone()))) }) // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) From de5458cfe0f419324a7258647b887c0eb8db5c71 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 3 Nov 2023 21:40:28 -0600 Subject: [PATCH 16/32] Update tooltip code a bit This fixes a tiny UX bug where the tooltip would appear to move if you hovered over an element, then moved your mouse out and back within 500ms. The fix is to retain the task, so we can drop it to cancel it when the mouse leaves. Also changes the time we construct the tooltip to the time it first shows. --- crates/gpui2/src/app.rs | 1 + crates/gpui2/src/interactive.rs | 64 ++++++++++++++-------------- crates/ui2/src/components/tooltip.rs | 2 - 3 files changed, 32 insertions(+), 35 deletions(-) diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 9e454dc652..bc9101fa0c 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -899,6 +899,7 @@ pub(crate) struct AnyDrag { pub cursor_offset: Point, } +#[derive(Clone)] pub(crate) struct AnyTooltip { pub view: AnyView, pub cursor_offset: Point, diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index dd09ad512b..da208b3813 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -2,7 +2,7 @@ use crate::{ div, point, px, Action, AnyDrag, AnyTooltip, AnyView, AppContext, BorrowWindow, Bounds, Component, DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch, Keystroke, Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style, - StyleRefinement, View, ViewContext, + StyleRefinement, Task, View, ViewContext, }; use collections::HashMap; use derive_more::{Deref, DerefMut}; @@ -627,50 +627,48 @@ pub trait ElementInteraction: 'static { let active_tooltip = element_state.active_tooltip.clone(); let pending_mouse_down = element_state.pending_mouse_down.clone(); - cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { + cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| { if phase != DispatchPhase::Bubble { return; } let is_hovered = bounds.contains_point(&event.position) && pending_mouse_down.lock().is_none(); - let mut tooltip_lock = active_tooltip.lock(); - - if is_hovered { - if tooltip_lock.is_none() { - *tooltip_lock = Some(ActiveTooltip { - view: tooltip_builder(view_state, cx), - visible: false, - coordinates: event.position, - }); + if !is_hovered { + active_tooltip.lock().take(); + return; + } + if active_tooltip.lock().is_none() { + let task = cx.spawn({ let active_tooltip = active_tooltip.clone(); - cx.spawn(move |view, mut cx| async move { - cx.background_executor().timer(TOOLTIP_DELAY).await; + let tooltip_builder = tooltip_builder.clone(); - view.update(&mut cx, |_, cx| { - if let Some(active_tooltip) = active_tooltip.lock().as_mut() { - active_tooltip.visible = true; - active_tooltip.coordinates = - cx.mouse_position() + TOOLTIP_OFFSET; - } + move |view, mut cx| async move { + cx.background_executor().timer(TOOLTIP_DELAY).await; + view.update(&mut cx, move |view_state, cx| { + active_tooltip.lock().replace(ActiveTooltip { + waiting: None, + tooltip: Some(AnyTooltip { + view: tooltip_builder(view_state, cx), + cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET, + }), + }); cx.notify(); }) - .ok() - }) - .detach(); - } - } else { - tooltip_lock.take(); + .ok(); + } + }); + active_tooltip.lock().replace(ActiveTooltip { + waiting: Some(task), + tooltip: None, + }); } }); if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() { - if active_tooltip.visible { - cx.active_tooltip = Some(AnyTooltip { - view: active_tooltip.view.clone(), - cursor_offset: active_tooltip.coordinates, - }); + if active_tooltip.tooltip.is_some() { + cx.active_tooltip = active_tooltip.tooltip.clone() } } } @@ -866,9 +864,9 @@ pub struct InteractiveElementState { } struct ActiveTooltip { - view: AnyView, - visible: bool, - coordinates: Point, + #[allow(unused)] // used to drop the task + waiting: Option>, + tooltip: Option, } impl InteractiveElementState { diff --git a/crates/ui2/src/components/tooltip.rs b/crates/ui2/src/components/tooltip.rs index 0bc00a9be1..c05214eea4 100644 --- a/crates/ui2/src/components/tooltip.rs +++ b/crates/ui2/src/components/tooltip.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use gpui2::{div, px, Div, ParentElement, Render, SharedString, Styled, ViewContext}; use theme2::ActiveTheme; From 9f40a5c53f29e6a5c2902cab4a62af3d9154d2d9 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 4 Nov 2023 13:54:34 +0200 Subject: [PATCH 17/32] Suppress unused vars warning generated by gpui macro --- crates/gpui_macros/src/gpui_macros.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/gpui_macros/src/gpui_macros.rs b/crates/gpui_macros/src/gpui_macros.rs index aa55c27eaa..62fba3b612 100644 --- a/crates/gpui_macros/src/gpui_macros.rs +++ b/crates/gpui_macros/src/gpui_macros.rs @@ -170,6 +170,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream { #max_retries, #detect_nondeterminism, &mut |cx, foreground_platform, deterministic, seed| { + // some of the macro contents do not use all variables, silence the warnings + let _ = (&cx, &foreground_platform, &deterministic, &seed); #cx_vars cx.foreground().run(#inner_fn_name(#inner_fn_args)); #cx_teardowns @@ -247,6 +249,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream { #max_retries, #detect_nondeterminism, &mut |cx, foreground_platform, deterministic, seed| { + // some of the macro contents do not use all variables, silence the warnings + let _ = (&cx, &foreground_platform, &deterministic, &seed); #cx_vars #inner_fn_name(#inner_fn_args); #cx_teardowns From b125cc279f5cbcb2042d7383322119fcddc2e00c Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Sat, 4 Nov 2023 11:24:31 -0400 Subject: [PATCH 18/32] Simplify toggle, do some reorganization --- crates/ui2/src/components.rs | 4 + crates/ui2/src/components/button.rs | 21 +- crates/ui2/src/components/context_menu.rs | 17 +- crates/ui2/src/components/list.rs | 324 +++--------------- crates/ui2/src/components/player.rs | 18 + crates/ui2/src/components/slot.rs | 14 + crates/ui2/src/components/toggle.rs | 61 ++++ crates/ui2/src/prelude.rs | 76 ---- crates/ui2/src/static_data.rs | 125 ++----- crates/ui2/src/to_extract/collab_panel.rs | 21 +- .../ui2/src/to_extract/notifications_panel.rs | 29 +- crates/ui2/src/to_extract/project_panel.rs | 13 +- 12 files changed, 224 insertions(+), 499 deletions(-) create mode 100644 crates/ui2/src/components/slot.rs create mode 100644 crates/ui2/src/components/toggle.rs diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index f7674c2bd4..692cd55e8e 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -16,9 +16,11 @@ mod palette; mod panel; mod player; mod player_stack; +mod slot; mod stack; mod tab; mod toast; +mod toggle; mod tool_divider; pub use avatar::*; @@ -39,7 +41,9 @@ pub use palette::*; pub use panel::*; pub use player::*; pub use player_stack::*; +pub use slot::*; pub use stack::*; pub use tab::*; pub use toast::*; +pub use toggle::*; pub use tool_divider::*; diff --git a/crates/ui2/src/components/button.rs b/crates/ui2/src/components/button.rs index 073bcdbb45..381db20a83 100644 --- a/crates/ui2/src/components/button.rs +++ b/crates/ui2/src/components/button.rs @@ -2,8 +2,27 @@ use std::sync::Arc; use gpui2::{div, rems, DefiniteLength, Hsla, MouseButton, WindowContext}; -use crate::prelude::*; use crate::{h_stack, Icon, IconColor, IconElement, Label, LabelColor, LineHeightStyle}; +use crate::{prelude::*, IconButton}; + +/// Provides the flexibility to use either a standard +/// button or an icon button in a given context. +pub enum ButtonOrIconButton { + Button(Button), + 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) + } +} #[derive(Default, PartialEq, Clone, Copy)] pub enum IconPosition { diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index 8345be1b35..4f265a376d 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -8,7 +8,7 @@ pub enum ContextMenuItem { } impl ContextMenuItem { - fn to_list_item(self) -> ListItem { + fn to_list_item(self) -> ListItem { match self { ContextMenuItem::Header(label) => ListSubHeader::new(label).into(), ContextMenuItem::Entry(label) => { @@ -49,15 +49,12 @@ impl ContextMenu { .bg(cx.theme().colors().elevated_surface) .border() .border_color(cx.theme().colors().border) - .child( - List::new( - self.items - .into_iter() - .map(ContextMenuItem::to_list_item) - .collect(), - ) - .toggle(ToggleState::Toggled), - ) + .child(List::new( + self.items + .into_iter() + .map(ContextMenuItem::to_list_item::) + .collect(), + )) } } diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index 50a86ff256..543432a893 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -1,11 +1,11 @@ -use gpui2::{div, px, relative, Div}; +use gpui2::div; use crate::settings::user_settings; use crate::{ - h_stack, v_stack, Avatar, ClickHandler, Icon, IconColor, IconElement, IconSize, Label, - LabelColor, + disclosure_control, h_stack, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label, + LabelColor, Toggle, }; -use crate::{prelude::*, Button}; +use crate::{prelude::*, GraphicSlot}; #[derive(Clone, Copy, Default, Debug, PartialEq)] pub enum ListItemVariant { @@ -29,7 +29,7 @@ pub struct ListHeader { left_icon: Option, meta: Option, variant: ListItemVariant, - toggleable: Toggleable, + toggle: Toggle, } impl ListHeader { @@ -39,17 +39,12 @@ impl ListHeader { left_icon: None, meta: None, variant: ListItemVariant::default(), - toggleable: Toggleable::NotToggleable, + toggle: Toggle::NotToggleable, } } - pub fn toggle(mut self, toggle: ToggleState) -> Self { - self.toggleable = toggle.into(); - self - } - - pub fn toggleable(mut self, toggleable: Toggleable) -> Self { - self.toggleable = toggleable; + pub fn toggle(mut self, toggle: Toggle) -> Self { + self.toggle = toggle; self } @@ -63,30 +58,8 @@ impl ListHeader { self } - fn disclosure_control(&self) -> Div { - let is_toggleable = self.toggleable != Toggleable::NotToggleable; - let is_toggled = Toggleable::is_toggled(&self.toggleable); - - match (is_toggleable, is_toggled) { - (false, _) => div(), - (_, true) => div().child( - IconElement::new(Icon::ChevronDown) - .color(IconColor::Muted) - .size(IconSize::Small), - ), - (_, false) => div().child( - IconElement::new(Icon::ChevronRight) - .color(IconColor::Muted) - .size(IconSize::Small), - ), - } - } - 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 disclosure_control = disclosure_control(self.toggle); let meta = match self.meta { Some(ListHeaderMeta::Tools(icons)) => div().child( @@ -193,12 +166,6 @@ impl ListSubHeader { } } -#[derive(Clone)] -pub enum LeftContent { - Icon(Icon), - Avatar(SharedString), -} - #[derive(Default, PartialEq, Copy, Clone)] pub enum ListEntrySize { #[default] @@ -207,44 +174,36 @@ pub enum ListEntrySize { } #[derive(Component)] -pub enum ListItem { +pub enum ListItem { Entry(ListEntry), - Details(ListDetailsEntry), Separator(ListSeparator), Header(ListSubHeader), } -impl From for ListItem { +impl From for ListItem { fn from(entry: ListEntry) -> Self { Self::Entry(entry) } } -impl From> for ListItem { - fn from(entry: ListDetailsEntry) -> Self { - Self::Details(entry) - } -} - -impl From for ListItem { +impl From for ListItem { fn from(entry: ListSeparator) -> Self { Self::Separator(entry) } } -impl From for ListItem { +impl From for ListItem { fn from(entry: ListSubHeader) -> Self { Self::Header(entry) } } -impl ListItem { - fn render(self, view: &mut V, cx: &mut ViewContext) -> impl Component { +impl ListItem { + fn render(self, view: &mut V, cx: &mut ViewContext) -> impl Component { match self { ListItem::Entry(entry) => div().child(entry.render(view, cx)), ListItem::Separator(separator) => div().child(separator.render(view, cx)), ListItem::Header(header) => div().child(header.render(view, cx)), - ListItem::Details(details) => div().child(details.render(view, cx)), } } @@ -263,31 +222,29 @@ impl ListItem { #[derive(Component)] pub struct ListEntry { - disclosure_control_style: DisclosureControlVisibility, + disabled: bool, + // TODO: Reintroduce this + // disclosure_control_style: DisclosureControlVisibility, indent_level: u32, label: Label, - left_content: Option, - variant: ListItemVariant, - size: ListEntrySize, - state: InteractionState, - toggle: Option, + left_slot: Option, overflow: OverflowStyle, + size: ListEntrySize, + toggle: Toggle, + variant: ListItemVariant, } impl ListEntry { pub fn new(label: Label) -> Self { Self { - disclosure_control_style: DisclosureControlVisibility::default(), + disabled: false, indent_level: 0, label, - variant: ListItemVariant::default(), - left_content: None, - size: ListEntrySize::default(), - state: InteractionState::default(), - // TODO: Should use Toggleable::NotToggleable - // or remove Toggleable::NotToggleable from the system - toggle: None, + left_slot: None, overflow: OverflowStyle::Hidden, + size: ListEntrySize::default(), + toggle: Toggle::NotToggleable, + variant: ListItemVariant::default(), } } @@ -301,28 +258,23 @@ impl ListEntry { self } - pub fn toggle(mut self, toggle: ToggleState) -> Self { - self.toggle = Some(toggle); + pub fn toggle(mut self, toggle: Toggle) -> Self { + self.toggle = toggle; self } - pub fn left_content(mut self, left_content: LeftContent) -> Self { - self.left_content = Some(left_content); + pub fn left_content(mut self, left_content: GraphicSlot) -> Self { + self.left_slot = Some(left_content); self } pub fn left_icon(mut self, left_icon: Icon) -> Self { - self.left_content = Some(LeftContent::Icon(left_icon)); + self.left_slot = Some(GraphicSlot::Icon(left_icon)); self } pub fn left_avatar(mut self, left_avatar: impl Into) -> Self { - self.left_content = Some(LeftContent::Avatar(left_avatar.into())); - self - } - - pub fn state(mut self, state: InteractionState) -> Self { - self.state = state; + self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into())); self } @@ -331,63 +283,19 @@ impl ListEntry { self } - pub fn disclosure_control_style( - mut self, - disclosure_control_style: DisclosureControlVisibility, - ) -> Self { - self.disclosure_control_style = disclosure_control_style; - self - } - - 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 disclosure_control( - &mut self, - cx: &mut ViewContext, - ) -> Option> { - let disclosure_control_icon = if let Some(ToggleState::Toggled) = self.toggle { - IconElement::new(Icon::ChevronDown) - } else { - IconElement::new(Icon::ChevronRight) - } - .color(IconColor::Muted) - .size(IconSize::Small); - - match (self.toggle, self.disclosure_control_style) { - (Some(_), DisclosureControlVisibility::OnHover) => { - Some(div().absolute().neg_left_5().child(disclosure_control_icon)) - } - (Some(_), DisclosureControlVisibility::Always) => { - Some(div().child(disclosure_control_icon)) - } - (None, _) => None, - } - } - - fn render(mut self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { let settings = user_settings(cx); - let left_content = match self.left_content.clone() { - Some(LeftContent::Icon(i)) => Some( + let left_content = match self.left_slot.clone() { + Some(GraphicSlot::Icon(i)) => Some( h_stack().child( IconElement::new(i) .size(IconSize::Small) .color(IconColor::Muted), ), ), - Some(LeftContent::Avatar(src)) => Some(h_stack().child(Avatar::new(src))), + Some(GraphicSlot::Avatar(src)) => Some(h_stack().child(Avatar::new(src))), + Some(GraphicSlot::PublicActor(src)) => Some(h_stack().child(Avatar::new(src))), None => None, }; @@ -400,10 +308,7 @@ impl ListEntry { .relative() .group("") .bg(cx.theme().colors().surface) - .when(self.state == InteractionState::Focused, |this| { - this.border() - .border_color(cx.theme().colors().border_focused) - }) + // TODO: Add focus state .child( sized_item .when(self.variant == ListItemVariant::Inset, |this| this.px_2()) @@ -425,131 +330,13 @@ impl ListEntry { .gap_1() .items_center() .relative() - .children(self.disclosure_control(cx)) + .child(disclosure_control(self.toggle)) .children(left_content) .child(self.label), ) } } -struct ListDetailsEntryHandlers { - click: Option>, -} - -impl Default for ListDetailsEntryHandlers { - fn default() -> Self { - Self { click: None } - } -} - -#[derive(Component)] -pub struct ListDetailsEntry { - label: SharedString, - meta: Option, - left_content: Option, - handlers: ListDetailsEntryHandlers, - actions: Option>>, - // TODO: make this more generic instead of - // specifically for notifications - seen: bool, -} - -impl ListDetailsEntry { - pub fn new(label: impl Into) -> Self { - Self { - label: label.into(), - meta: None, - left_content: None, - handlers: ListDetailsEntryHandlers::default(), - actions: None, - seen: false, - } - } - - pub fn meta(mut self, meta: impl Into) -> Self { - self.meta = Some(meta.into()); - self - } - - pub fn seen(mut self, seen: bool) -> Self { - self.seen = seen; - self - } - - pub fn on_click(mut self, handler: ClickHandler) -> Self { - self.handlers.click = Some(handler); - self - } - - pub fn actions(mut self, actions: Vec>) -> Self { - self.actions = Some(actions); - self - } - - 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) = ( - 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, - }; - - div() - .relative() - .group("") - .bg(item_bg) - .px_2() - .py_1p5() - .w_full() - .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( - v_stack() - .w_full() - .line_height(relative(1.2)) - .gap_1() - .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()), - ), - ) - } -} - #[derive(Clone, Component)] pub struct ListSeparator; @@ -564,20 +351,22 @@ impl ListSeparator { } #[derive(Component)] -pub struct List { - items: Vec>, +pub struct List { + items: Vec, + /// Message to display when the list is empty + /// Defaults to "No items" empty_message: SharedString, header: Option, - toggleable: Toggleable, + toggle: Toggle, } -impl List { - pub fn new(items: Vec>) -> Self { +impl List { + pub fn new(items: Vec) -> Self { Self { items, empty_message: "No items".into(), header: None, - toggleable: Toggleable::default(), + toggle: Toggle::NotToggleable, } } @@ -591,19 +380,16 @@ impl List { self } - pub fn toggle(mut self, toggle: ToggleState) -> Self { - self.toggleable = toggle.into(); + pub fn toggle(mut self, toggle: Toggle) -> Self { + self.toggle = toggle; self } - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let is_toggleable = self.toggleable != Toggleable::NotToggleable; - let is_toggled = Toggleable::is_toggled(&self.toggleable); - - let list_content = match (self.items.is_empty(), is_toggled) { + fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + let list_content = match (self.items.is_empty(), self.toggle) { (false, _) => div().children(self.items), - (true, false) => div(), - (true, true) => { + (true, Toggle::Toggled(false)) => div(), + (true, _) => { div().child(Label::new(self.empty_message.clone()).color(LabelColor::Muted)) } }; @@ -611,7 +397,7 @@ impl List { v_stack() .w_full() .py_1() - .children(self.header.map(|header| header.toggleable(self.toggleable))) + .children(self.header.map(|header| header)) .child(list_content) } } diff --git a/crates/ui2/src/components/player.rs b/crates/ui2/src/components/player.rs index c7b7ade1c1..b6c80400cf 100644 --- a/crates/ui2/src/components/player.rs +++ b/crates/ui2/src/components/player.rs @@ -2,6 +2,24 @@ use gpui2::{Hsla, ViewContext}; use crate::prelude::*; +/// Represents a person with a Zed account's public profile. +/// All data in this struct should be considered public. +pub struct PublicPlayer { + pub username: SharedString, + pub avatar: SharedString, + pub is_contact: bool, +} + +impl PublicPlayer { + 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)] pub enum PlayerStatus { #[default] diff --git a/crates/ui2/src/components/slot.rs b/crates/ui2/src/components/slot.rs new file mode 100644 index 0000000000..f980e2fbda --- /dev/null +++ b/crates/ui2/src/components/slot.rs @@ -0,0 +1,14 @@ +use gpui2::SharedString; + +use crate::Icon; + +#[derive(Debug, Clone)] +/// A slot utility that provides a way to to pass either +/// an icon or an image to a component. +/// +/// Can be filled with a [] +pub enum GraphicSlot { + Icon(Icon), + Avatar(SharedString), + PublicActor(SharedString), +} diff --git a/crates/ui2/src/components/toggle.rs b/crates/ui2/src/components/toggle.rs new file mode 100644 index 0000000000..adde367581 --- /dev/null +++ b/crates/ui2/src/components/toggle.rs @@ -0,0 +1,61 @@ +use gpui2::{div, Component, ParentElement}; + +use crate::{Icon, IconColor, IconElement, IconSize}; + +/// Whether the entry is toggleable, and if so, whether it is currently toggled. +/// +/// To make an element toggleable, simply add a `Toggle::Toggled(_)` and handle it's cases. +/// +/// You can check if an element is toggleable with `.is_toggleable()` +/// +/// Possible values: +/// - `Toggle::NotToggleable` - The entry is not toggleable +/// - `Toggle::Toggled(true)` - The entry is toggleable and toggled +/// - `Toggle::Toggled(false)` - The entry is toggleable and not toggled +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Toggle { + NotToggleable, + Toggled(bool), +} + +impl Toggle { + /// Returns true if the entry is toggled (or is not toggleable.) + /// + /// As element that isn't toggleable is always "expanded" or "enabled" + /// returning true in that case makes sense. + pub fn is_toggled(&self) -> bool { + match self { + Self::Toggled(false) => false, + _ => true, + } + } + + pub fn is_toggleable(&self) -> bool { + match self { + Self::Toggled(_) => true, + _ => false, + } + } +} + +impl From for Toggle { + fn from(toggled: bool) -> Self { + Toggle::Toggled(toggled) + } +} + +pub fn disclosure_control(toggle: Toggle) -> impl Component { + match (toggle.is_toggleable(), toggle.is_toggled()) { + (false, _) => div(), + (_, true) => div().child( + IconElement::new(Icon::ChevronDown) + .color(IconColor::Muted) + .size(IconSize::Small), + ), + (_, false) => div().child( + IconElement::new(Icon::ChevronRight) + .color(IconColor::Muted) + .size(IconSize::Small), + ), + } +} diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index fbb7ccc528..8ba74cce95 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -10,24 +10,6 @@ pub use theme2::ActiveTheme; use gpui2::Hsla; use strum::EnumIter; -/// 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] @@ -179,61 +161,3 @@ pub enum SelectedState { PartiallySelected, Selected, } - -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] -pub enum Toggleable { - Toggleable(ToggleState), - #[default] - NotToggleable, -} - -impl Toggleable { - pub fn is_toggled(&self) -> bool { - match self { - Self::Toggleable(ToggleState::Toggled) => true, - _ => false, - } - } -} - -impl From for Toggleable { - fn from(state: ToggleState) -> Self { - Self::Toggleable(state) - } -} - -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] -pub enum ToggleState { - /// The "on" state of a toggleable element. - /// - /// Example: - /// - A collasable list that is currently expanded - /// - A toggle button that is currently on. - Toggled, - /// The "off" state of a toggleable element. - /// - /// Example: - /// - A collasable list that is currently collapsed - /// - A toggle button that is currently off. - #[default] - NotToggled, -} - -impl From for ToggleState { - fn from(toggleable: Toggleable) -> Self { - match toggleable { - Toggleable::Toggleable(state) => state, - Toggleable::NotToggleable => ToggleState::NotToggled, - } - } -} - -impl From for ToggleState { - fn from(toggled: bool) -> Self { - if toggled { - ToggleState::Toggled - } else { - ToggleState::NotToggled - } - } -} diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 68f625c75d..5342e1fb16 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -7,13 +7,13 @@ use gpui2::{AppContext, ViewContext}; use rand::Rng; use theme2::ActiveTheme; +use crate::HighlightedText; use crate::{ Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus, - HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListSubHeader, - Livestream, MicStatus, ModifierKeys, Notification, PaletteItem, Player, PlayerCallStatus, - PlayerWithCallStatus, PublicActor, ScreenShareStatus, Symbol, Tab, ToggleState, VideoStatus, + HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, Livestream, + MicStatus, ModifierKeys, Notification, PaletteItem, Player, PlayerCallStatus, + PlayerWithCallStatus, PublicPlayer, ScreenShareStatus, Symbol, Tab, Toggle, VideoStatus, }; -use crate::{HighlightedText, ListDetailsEntry}; use crate::{ListItem, NotificationAction}; pub fn static_tabs_example() -> Vec { @@ -345,7 +345,7 @@ pub fn static_new_notification_items_2() -> Vec> { DateTime::parse_from_rfc3339("2023-11-02T12:09:07Z") .unwrap() .naive_local(), - PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( Button::new("Decline"), @@ -374,7 +374,7 @@ pub fn static_new_notification_items_2() -> Vec> { DateTime::parse_from_rfc3339("2023-11-01T12:09:07Z") .unwrap() .naive_local(), - PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( Button::new("Decline"), @@ -403,7 +403,7 @@ pub fn static_new_notification_items_2() -> Vec> { DateTime::parse_from_rfc3339("2022-10-25T12:09:07Z") .unwrap() .naive_local(), - PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( Button::new("Decline"), @@ -432,7 +432,7 @@ pub fn static_new_notification_items_2() -> Vec> { DateTime::parse_from_rfc3339("2021-10-12T12:09:07Z") .unwrap() .naive_local(), - PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( Button::new("Decline"), @@ -461,7 +461,7 @@ pub fn static_new_notification_items_2() -> Vec> { DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z") .unwrap() .naive_local(), - PublicActor::new("as-cii", "http://github.com/as-cii.png?s=50"), + PublicPlayer::new("as-cii", "http://github.com/as-cii.png?s=50"), [ NotificationAction::new( Button::new("Decline"), @@ -478,89 +478,12 @@ pub fn static_new_notification_items_2() -> Vec> { ] } -pub fn static_new_notification_items() -> Vec> { - vec![ - 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.", - )), - 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) - .collect() -} - -pub fn static_project_panel_project_items() -> Vec> { +pub fn static_project_panel_project_items() -> Vec { vec![ ListEntry::new(Label::new("zed")) .left_icon(Icon::FolderOpen.into()) .indent_level(0) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new(".cargo")) .left_icon(Icon::Folder.into()) .indent_level(1), @@ -579,14 +502,14 @@ pub fn static_project_panel_project_items() -> Vec> { ListEntry::new(Label::new("assets")) .left_icon(Icon::Folder.into()) .indent_level(1) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden)) .left_icon(Icon::Folder.into()) .indent_level(1), ListEntry::new(Label::new("crates")) .left_icon(Icon::FolderOpen.into()) .indent_level(1) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("activity_indicator")) .left_icon(Icon::Folder.into()) .indent_level(2), @@ -608,38 +531,38 @@ pub fn static_project_panel_project_items() -> Vec> { ListEntry::new(Label::new("sqlez").color(LabelColor::Modified)) .left_icon(Icon::Folder.into()) .indent_level(2) - .toggle(ToggleState::NotToggled), + .toggle(Toggle::Toggled(false)), ListEntry::new(Label::new("gpui2")) .left_icon(Icon::FolderOpen.into()) .indent_level(2) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("src")) .left_icon(Icon::FolderOpen.into()) .indent_level(3) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("derive_element.rs")) .left_icon(Icon::FileRust.into()) .indent_level(4), ListEntry::new(Label::new("storybook").color(LabelColor::Modified)) .left_icon(Icon::FolderOpen.into()) .indent_level(1) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("docs").color(LabelColor::Default)) .left_icon(Icon::Folder.into()) .indent_level(2) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("src").color(LabelColor::Modified)) .left_icon(Icon::FolderOpen.into()) .indent_level(3) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("ui").color(LabelColor::Modified)) .left_icon(Icon::FolderOpen.into()) .indent_level(4) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("component").color(LabelColor::Created)) .left_icon(Icon::FolderOpen.into()) .indent_level(5) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default)) .left_icon(Icon::FileRust.into()) .indent_level(6), @@ -682,7 +605,7 @@ pub fn static_project_panel_project_items() -> Vec> { .collect() } -pub fn static_project_panel_single_items() -> Vec> { +pub fn static_project_panel_single_items() -> Vec { vec![ ListEntry::new(Label::new("todo.md")) .left_icon(Icon::FileDoc.into()) @@ -699,7 +622,7 @@ pub fn static_project_panel_single_items() -> Vec> { .collect() } -pub fn static_collab_panel_current_call() -> Vec> { +pub fn static_collab_panel_current_call() -> Vec { vec![ ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"), ListEntry::new(Label::new("nathansobo")) @@ -712,7 +635,7 @@ pub fn static_collab_panel_current_call() -> Vec> { .collect() } -pub fn static_collab_panel_channels() -> Vec> { +pub fn static_collab_panel_channels() -> Vec { vec![ ListEntry::new(Label::new("zed")) .left_icon(Icon::Hash.into()) diff --git a/crates/ui2/src/to_extract/collab_panel.rs b/crates/ui2/src/to_extract/collab_panel.rs index a0e3b55f63..9019456dd1 100644 --- a/crates/ui2/src/to_extract/collab_panel.rs +++ b/crates/ui2/src/to_extract/collab_panel.rs @@ -1,7 +1,6 @@ -use crate::prelude::*; +use crate::{prelude::*, Toggle}; use crate::{ - static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, - ListHeader, ToggleState, + static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, ListHeader, }; #[derive(Component)] @@ -34,17 +33,17 @@ impl CollabPanel { .header( ListHeader::new("CRDB") .left_icon(Icon::Hash.into()) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ), ) .child( v_stack().id("channels").py_1().child( List::new(static_collab_panel_channels()) - .header(ListHeader::new("CHANNELS").toggle(ToggleState::Toggled)) + .header(ListHeader::new("CHANNELS").toggle(Toggle::Toggled(true))) .empty_message("No channels yet. Add a channel to get started.") - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ), ) .child( @@ -52,9 +51,9 @@ impl CollabPanel { List::new(static_collab_panel_current_call()) .header( ListHeader::new("CONTACTS – ONLINE") - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ) - .toggle(ToggleState::Toggled), + .toggle(Toggle::Toggled(true)), ), ) .child( @@ -62,9 +61,9 @@ impl CollabPanel { List::new(static_collab_panel_current_call()) .header( ListHeader::new("CONTACTS – OFFLINE") - .toggle(ToggleState::NotToggled), + .toggle(Toggle::Toggled(false)), ) - .toggle(ToggleState::NotToggled), + .toggle(Toggle::Toggled(false)), ), ), ) diff --git a/crates/ui2/src/to_extract/notifications_panel.rs b/crates/ui2/src/to_extract/notifications_panel.rs index 74f015ac06..789aa2b0aa 100644 --- a/crates/ui2/src/to_extract/notifications_panel.rs +++ b/crates/ui2/src/to_extract/notifications_panel.rs @@ -1,8 +1,8 @@ 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, - UnreadIndicator, + h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, ButtonOrIconButton, + Icon, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator, + PublicPlayer, UnreadIndicator, }; use crate::{ClickHandler, ListHeader}; @@ -57,23 +57,6 @@ impl NotificationsPanel { } } -pub enum ButtonOrIconButton { - Button(Button), - 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, @@ -102,7 +85,7 @@ impl NotificationAction { } pub enum ActorOrIcon { - Actor(PublicActor), + Actor(PublicPlayer), Icon(Icon), } @@ -171,7 +154,7 @@ impl Notification { id: impl Into, message: impl Into, date_received: NaiveDateTime, - actor: PublicActor, + actor: PublicPlayer, click_action: ClickHandler, ) -> Self { Self::new( @@ -210,7 +193,7 @@ impl Notification { id: impl Into, message: impl Into, date_received: NaiveDateTime, - actor: PublicActor, + actor: PublicPlayer, actions: [NotificationAction; 2], ) -> Self { Self::new( diff --git a/crates/ui2/src/to_extract/project_panel.rs b/crates/ui2/src/to_extract/project_panel.rs index 76fa50d338..807768427b 100644 --- a/crates/ui2/src/to_extract/project_panel.rs +++ b/crates/ui2/src/to_extract/project_panel.rs @@ -18,8 +18,7 @@ impl ProjectPanel { .id(self.id.clone()) .flex() .flex_col() - .w_full() - .h_full() + .size_full() .bg(cx.theme().colors().surface) .child( div() @@ -30,15 +29,13 @@ impl ProjectPanel { .overflow_y_scroll() .child( List::new(static_project_panel_single_items()) - .header(ListHeader::new("FILES").toggle(ToggleState::Toggled)) - .empty_message("No files in directory") - .toggle(ToggleState::Toggled), + .header(ListHeader::new("FILES")) + .empty_message("No files in directory"), ) .child( List::new(static_project_panel_project_items()) - .header(ListHeader::new("PROJECT").toggle(ToggleState::Toggled)) - .empty_message("No folders in directory") - .toggle(ToggleState::Toggled), + .header(ListHeader::new("PROJECT")) + .empty_message("No folders in directory"), ), ) .child( From 1bce5dcc69b4b89b9be06142ac7fe843c4bcdf06 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Sun, 5 Nov 2023 01:06:41 -0500 Subject: [PATCH 19/32] Add checkboxes and their stories --- assets/icons/dash.svg | 1 + crates/storybook2/src/story_selector.rs | 2 + crates/theme2/src/colors.rs | 2 + crates/theme2/src/default_colors.rs | 4 + crates/ui2/docs/building-ui.md | 10 ++ crates/ui2/src/components.rs | 2 + crates/ui2/src/components/checkbox.rs | 217 ++++++++++++++++++++++++ crates/ui2/src/components/icon.rs | 4 + crates/ui2/src/prelude.rs | 6 +- 9 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 assets/icons/dash.svg create mode 100644 crates/ui2/src/components/checkbox.rs diff --git a/assets/icons/dash.svg b/assets/icons/dash.svg new file mode 100644 index 0000000000..efff9eab5e --- /dev/null +++ b/assets/icons/dash.svg @@ -0,0 +1 @@ + diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index a78705c7bb..2adf2956d3 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -19,6 +19,7 @@ pub enum ComponentStory { Buffer, Button, ChatPanel, + Checkbox, CollabPanel, Colors, CommandPalette, @@ -61,6 +62,7 @@ impl ComponentStory { Self::Buffer => cx.build_view(|_| ui::BufferStory).into(), Self::Button => cx.build_view(|_| ButtonStory).into(), Self::ChatPanel => cx.build_view(|_| ui::ChatPanelStory).into(), + Self::Checkbox => cx.build_view(|_| ui::CheckboxStory).into(), Self::CollabPanel => cx.build_view(|_| ui::CollabPanelStory).into(), Self::Colors => cx.build_view(|_| ColorsStory).into(), Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into(), diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs index b02a9c14df..b9f8804205 100644 --- a/crates/theme2/src/colors.rs +++ b/crates/theme2/src/colors.rs @@ -54,7 +54,9 @@ pub struct ThemeColors { pub border: Hsla, pub border_variant: Hsla, pub border_focused: Hsla, + pub border_selected: Hsla, pub border_transparent: Hsla, + pub border_disabled: Hsla, pub elevated_surface: Hsla, pub surface: Hsla, pub background: Hsla, diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 802392d296..4ecae43b15 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -205,6 +205,8 @@ impl ThemeColors { border: neutral().light().step_6(), border_variant: neutral().light().step_5(), border_focused: blue().light().step_5(), + border_disabled: neutral().light().step_3(), + border_selected: blue().light().step_5(), border_transparent: system.transparent, elevated_surface: neutral().light().step_2(), surface: neutral().light().step_2(), @@ -250,6 +252,8 @@ impl ThemeColors { border: neutral().dark().step_6(), border_variant: neutral().dark().step_5(), border_focused: blue().dark().step_5(), + border_disabled: neutral().dark().step_3(), + border_selected: blue().dark().step_5(), border_transparent: system.transparent, elevated_surface: neutral().dark().step_2(), surface: neutral().dark().step_2(), diff --git a/crates/ui2/docs/building-ui.md b/crates/ui2/docs/building-ui.md index a2a3ff697b..e0160e336e 100644 --- a/crates/ui2/docs/building-ui.md +++ b/crates/ui2/docs/building-ui.md @@ -2,6 +2,16 @@ ## Common patterns +### Method ordering + +- id +- Flex properties +- Position properties +- Size properties +- Style properties +- Handlers +- State properties + ### Using the Label Component to Create UI Text The `Label` component helps in displaying text on user interfaces. It creates an interface where specific parameters such as label color, line height style, and strikethrough can be set. diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 692cd55e8e..a8a7ddfd46 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -1,5 +1,6 @@ mod avatar; mod button; +mod checkbox; mod context_menu; mod details; mod facepile; @@ -25,6 +26,7 @@ mod tool_divider; pub use avatar::*; pub use button::*; +pub use checkbox::*; pub use context_menu::*; pub use details::*; pub use facepile::*; diff --git a/crates/ui2/src/components/checkbox.rs b/crates/ui2/src/components/checkbox.rs new file mode 100644 index 0000000000..3add6cebac --- /dev/null +++ b/crates/ui2/src/components/checkbox.rs @@ -0,0 +1,217 @@ +///! # Checkbox +///! +///! Checkboxes are used for multiple choices, not for mutually exclusive choices. +///! Each checkbox works independently from other checkboxes in the list, +///! therefore checking an additional box does not affect any other selections. +use gpui2::{ + div, Component, ParentElement, SharedString, StatelessInteractive, Styled, ViewContext, +}; +use theme2::ActiveTheme; + +use crate::{Icon, IconColor, IconElement, Selected}; + +#[derive(Component)] +pub struct Checkbox { + id: SharedString, + checked: Selected, + disabled: bool, +} + +impl Checkbox { + pub fn new(id: impl Into) -> Self { + Self { + id: id.into(), + checked: Selected::Unselected, + disabled: false, + } + } + + pub fn toggle(mut self) -> Self { + self.checked = match self.checked { + Selected::Selected => Selected::Unselected, + Selected::Unselected => Selected::Selected, + Selected::Indeterminate => Selected::Selected, + }; + self + } + + pub fn set_indeterminate(mut self) -> Self { + self.checked = Selected::Indeterminate; + self + } + + pub fn set_disabled(mut self, disabled: bool) -> Self { + self.disabled = disabled; + self + } + + pub fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + let group_id = format!("checkbox_group_{}", self.id); + + // The icon is different depending on the state of the checkbox. + // + // We need the match to return all the same type, + // so we wrap the eatch result in a div. + // + // We are still exploring the best way to handle this. + let icon = match self.checked { + // When selected, we show a checkmark. + Selected::Selected => { + div().child( + IconElement::new(Icon::Check) + .size(crate::IconSize::Small) + .color( + // If the checkbox is disabled we change the color of the icon. + if self.disabled { + IconColor::Disabled + } else { + IconColor::Selected + }, + ), + ) + } + // In an indeterminate state, we show a dash. + Selected::Indeterminate => { + div().child( + IconElement::new(Icon::Dash) + .size(crate::IconSize::Small) + .color( + // If the checkbox is disabled we change the color of the icon. + if self.disabled { + IconColor::Disabled + } else { + IconColor::Selected + }, + ), + ) + } + // When unselected, we show nothing. + Selected::Unselected => div(), + }; + + // A checkbox could be in an indeterminate state, + // for example the indeterminate state could represent: + // - a group of options of which only some are selected + // - an enabled option that is no longer available + // - a previously agreed to license that has been updated + // + // For the sake of styles we treat the indeterminate state as selected, + // but it's icon will be different. + let selected = + self.checked == Selected::Selected || self.checked == Selected::Indeterminate; + + // We could use something like this to make the checkbox background when selected: + // + // ~~~rust + // ... + // .when(selected, |this| { + // this.bg(cx.theme().colors().element_selected) + // }) + // ~~~ + // + // But we use a match instead here because the checkbox might be disabled, + // and it could be disabled _while_ it is selected, as well as while it is not selected. + let (bg_color, border_color) = match (self.disabled, selected) { + (true, _) => ( + cx.theme().colors().ghost_element_disabled, + cx.theme().colors().border_disabled, + ), + (false, true) => ( + cx.theme().colors().element_selected, + cx.theme().colors().border, + ), + (false, false) => (cx.theme().colors().element, cx.theme().colors().border), + }; + + div() + // Rather than adding `px_1()` to add some space around the checkbox, + // we use a larger parent element to create a slightly larger + // click area for the checkbox. + .size_5() + // Because we've enlarged the click area, we need to create a + // `group` to pass down interaction events to the checkbox. + .group(group_id.clone()) + .child( + div() + .flex() + // This prevent the flex element from growing + // or shrinking in response to any size changes + .flex_none() + // The combo of `justify_center()` and `items_center()` + // is used frequently to center elements in a flex container. + // + // We use this to center the icon in the checkbox. + .justify_center() + .items_center() + .m_1() + .size_4() + .rounded_sm() + .bg(bg_color) + .border() + .border_color(border_color) + // We only want the interaction states to fire when we + // are in a checkbox that isn't disabled. + .when(!self.disabled, |this| { + // Here instead of `hover()` we use `group_hover()` + // to pass it the group id. + this.group_hover(group_id.clone(), |el| { + el.bg(cx.theme().colors().element_hover) + }) + }) + .child(icon), + ) + } +} + +#[cfg(feature = "stories")] +pub use stories::*; + +#[cfg(feature = "stories")] +mod stories { + use super::*; + use crate::{h_stack, Story}; + use gpui2::{Div, Render}; + + pub struct CheckboxStory; + + impl Render for CheckboxStory { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + Story::container(cx) + .child(Story::title_for::<_, Checkbox>(cx)) + .child(Story::label(cx, "Default")) + .child( + h_stack() + .p_2() + .gap_2() + .rounded_md() + .border() + .border_color(cx.theme().colors().border) + .child(Checkbox::new("checkbox-enabled")) + .child(Checkbox::new("checkbox-intermediate").set_indeterminate()) + .child(Checkbox::new("checkbox-selected").toggle()), + ) + .child(Story::label(cx, "Disabled")) + .child( + h_stack() + .p_2() + .gap_2() + .rounded_md() + .border() + .border_color(cx.theme().colors().border) + .child(Checkbox::new("checkbox-disabled").set_disabled(true)) + .child( + Checkbox::new("checkbox-disabled-intermediate") + .set_disabled(true) + .set_indeterminate(), + ) + .child( + Checkbox::new("checkbox-disabled-selected") + .set_disabled(true) + .toggle(), + ), + ) + } + } +} diff --git a/crates/ui2/src/components/icon.rs b/crates/ui2/src/components/icon.rs index 5885d76101..8075352b30 100644 --- a/crates/ui2/src/components/icon.rs +++ b/crates/ui2/src/components/icon.rs @@ -22,6 +22,7 @@ pub enum IconColor { Warning, Success, Info, + Selected, } impl IconColor { @@ -36,6 +37,7 @@ impl IconColor { IconColor::Warning => cx.theme().status().warning, IconColor::Success => cx.theme().status().success, IconColor::Info => cx.theme().status().info, + IconColor::Selected => cx.theme().colors().icon_accent, } } } @@ -55,6 +57,7 @@ pub enum Icon { ChevronRight, ChevronUp, Close, + Dash, Exit, ExclamationTriangle, File, @@ -112,6 +115,7 @@ impl Icon { Icon::ChevronRight => "icons/chevron_right.svg", Icon::ChevronUp => "icons/chevron_up.svg", Icon::Close => "icons/x.svg", + Icon::Dash => "icons/dash.svg", Icon::Exit => "icons/exit.svg", Icon::ExclamationTriangle => "icons/warning.svg", Icon::File => "icons/file.svg", diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 8ba74cce95..072ed00060 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -154,10 +154,10 @@ impl InteractionState { } } -#[derive(Default, PartialEq)] -pub enum SelectedState { +#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)] +pub enum Selected { #[default] Unselected, - PartiallySelected, + Indeterminate, Selected, } From 660c3371e4fbaceb2d9916b4e988a75999bc8763 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 5 Nov 2023 14:43:54 +0200 Subject: [PATCH 20/32] Refresh all possibly stale diagnostics pat --- crates/diagnostics/src/diagnostics.rs | 134 ++++++++++++++++---------- 1 file changed, 81 insertions(+), 53 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index a3d779531b..dc3dc0e061 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -3,7 +3,7 @@ mod project_diagnostics_settings; mod toolbar_controls; use anyhow::Result; -use collections::{BTreeSet, HashSet}; +use collections::{BTreeSet, HashMap, HashSet}; use editor::{ diagnostic_block_renderer, display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock}, @@ -13,7 +13,7 @@ use editor::{ }; use gpui::{ actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity, - ModelHandle, Task, View, ViewContext, ViewHandle, WeakViewHandle, + ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use language::{ Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, @@ -28,6 +28,7 @@ use std::{ any::{Any, TypeId}, borrow::Cow, cmp::Ordering, + mem, ops::Range, path::PathBuf, sync::Arc, @@ -61,7 +62,9 @@ struct ProjectDiagnosticsEditor { excerpts: ModelHandle, path_states: Vec, paths_to_update: BTreeSet<(ProjectPath, LanguageServerId)>, + current_diagnostics: HashMap>, include_warnings: bool, + _subscriptions: Vec, } struct PathState { @@ -149,25 +152,22 @@ impl ProjectDiagnosticsEditor { workspace: WeakViewHandle, cx: &mut ViewContext, ) -> Self { - cx.subscribe(&project_handle, |this, _, event, cx| match event { - project::Event::DiskBasedDiagnosticsFinished { language_server_id } => { - log::debug!("Disk based diagnostics finished for server {language_server_id}"); - this.update_excerpts(Some(*language_server_id), cx); - this.update_title(cx); - } - project::Event::DiagnosticsUpdated { - language_server_id, - path, - } => { - log::debug!("Adding path {path:?} to update for server {language_server_id}"); - this.paths_to_update - .insert((path.clone(), *language_server_id)); - this.update_excerpts(Some(*language_server_id), cx); - this.update_title(cx); - } - _ => {} - }) - .detach(); + let project_event_subscription = + cx.subscribe(&project_handle, |this, _, event, cx| match event { + project::Event::DiskBasedDiagnosticsFinished { language_server_id } => { + log::debug!("Disk based diagnostics finished for server {language_server_id}"); + this.update_excerpts(Some(*language_server_id), cx); + } + project::Event::DiagnosticsUpdated { + language_server_id, + path, + } => { + log::debug!("Adding path {path:?} to update for server {language_server_id}"); + this.paths_to_update + .insert((path.clone(), *language_server_id)); + } + _ => {} + }); let excerpts = cx.add_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id())); let editor = cx.add_view(|cx| { @@ -176,19 +176,14 @@ impl ProjectDiagnosticsEditor { editor.set_vertical_scroll_margin(5, cx); editor }); - cx.subscribe(&editor, |this, _, event, cx| { + let editor_event_subscription = cx.subscribe(&editor, |this, _, event, cx| { cx.emit(event.clone()); if event == &editor::Event::Focused && this.path_states.is_empty() { cx.focus_self() } - }) - .detach(); + }); let project = project_handle.read(cx); - let paths_to_update = project - .diagnostic_summaries(cx) - .map(|(path, server_id, _)| (path, server_id)) - .collect(); let summary = project.diagnostic_summary(cx); let mut this = Self { project: project_handle, @@ -197,8 +192,10 @@ impl ProjectDiagnosticsEditor { excerpts, editor, path_states: Default::default(), - paths_to_update, + paths_to_update: BTreeSet::new(), include_warnings: settings::get::(cx).include_warnings, + current_diagnostics: HashMap::default(), + _subscriptions: vec![project_event_subscription, editor_event_subscription], }; this.update_excerpts(None, cx); this @@ -218,12 +215,6 @@ impl ProjectDiagnosticsEditor { fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext) { self.include_warnings = !self.include_warnings; - self.paths_to_update = self - .project - .read(cx) - .diagnostic_summaries(cx) - .map(|(path, server_id, _)| (path, server_id)) - .collect(); self.update_excerpts(None, cx); cx.notify(); } @@ -234,29 +225,71 @@ impl ProjectDiagnosticsEditor { cx: &mut ViewContext, ) { log::debug!("Updating excerpts for server {language_server_id:?}"); - let mut paths = Vec::new(); - self.paths_to_update.retain(|(path, server_id)| { - if language_server_id - .map_or(true, |language_server_id| language_server_id == *server_id) - { - paths.push(path.clone()); - false - } else { - true + let mut paths_to_recheck = HashSet::default(); + let mut new_summaries: HashMap> = self + .project + .read(cx) + .diagnostic_summaries(cx) + .fold(HashMap::default(), |mut summaries, (path, server_id, _)| { + summaries.entry(server_id).or_default().insert(path); + summaries + }); + let mut old_diagnostics = + mem::replace(&mut self.current_diagnostics, new_summaries.clone()); + if let Some(language_server_id) = language_server_id { + new_summaries.retain(|server_id, _| server_id == &language_server_id); + old_diagnostics.retain(|server_id, _| server_id == &language_server_id); + self.paths_to_update.retain(|(path, server_id)| { + if server_id == &language_server_id { + paths_to_recheck.insert(path.clone()); + false + } else { + true + } + }); + } else { + paths_to_recheck.extend( + mem::replace(&mut self.paths_to_update, BTreeSet::new()) + .into_iter() + .map(|(path, _)| path), + ); + } + + for (server_id, new_paths) in new_summaries { + match old_diagnostics.remove(&server_id) { + Some(mut old_paths) => { + paths_to_recheck.extend( + new_paths + .into_iter() + .filter(|new_path| !old_paths.remove(new_path)), + ); + paths_to_recheck.extend(old_paths); + } + None => paths_to_recheck.extend(new_paths), } - }); + } + paths_to_recheck.extend(old_diagnostics.into_iter().flat_map(|(_, paths)| paths)); + let project = self.project.clone(); cx.spawn(|this, mut cx| { async move { - for path in paths { + let mut changed = false; + for path in paths_to_recheck { let buffer = project .update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx)) .await?; this.update(&mut cx, |this, cx| { - this.populate_excerpts(path, language_server_id, buffer, cx) + this.populate_excerpts(path, language_server_id, buffer, cx); + changed = true; })?; } - Result::<_, anyhow::Error>::Ok(()) + if changed { + this.update(&mut cx, |this, cx| { + this.summary = this.project.read(cx).diagnostic_summary(cx); + cx.emit(Event::TitleChanged); + })?; + } + anyhow::Ok(()) } .log_err() }) @@ -559,11 +592,6 @@ impl ProjectDiagnosticsEditor { } cx.notify(); } - - fn update_title(&mut self, cx: &mut ViewContext) { - self.summary = self.project.read(cx).diagnostic_summary(cx); - cx.emit(Event::TitleChanged); - } } impl Item for ProjectDiagnosticsEditor { From 7145fabb6d3f727fd260eb33be0220d9cc848c63 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 5 Nov 2023 14:44:22 +0200 Subject: [PATCH 21/32] Eagerly refresh diagnostics that do not intercept with user input --- crates/diagnostics/src/diagnostics.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index dc3dc0e061..4f66513f0f 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -165,6 +165,12 @@ impl ProjectDiagnosticsEditor { log::debug!("Adding path {path:?} to update for server {language_server_id}"); this.paths_to_update .insert((path.clone(), *language_server_id)); + let no_multiselections = this.editor.update(cx, |editor, cx| { + editor.selections.all::(cx).len() <= 1 + }); + if no_multiselections && !this.is_dirty(cx) { + this.update_excerpts(Some(*language_server_id), cx); + } } _ => {} }); From ff1d692e4602df3e0459330e793e6ee744a0c544 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 5 Nov 2023 14:53:13 +0200 Subject: [PATCH 22/32] Restructure inner path data --- crates/diagnostics/src/diagnostics.rs | 29 ++++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 4f66513f0f..a3d4bc9589 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -3,7 +3,7 @@ mod project_diagnostics_settings; mod toolbar_controls; use anyhow::Result; -use collections::{BTreeSet, HashMap, HashSet}; +use collections::{HashMap, HashSet}; use editor::{ diagnostic_block_renderer, display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock}, @@ -61,7 +61,7 @@ struct ProjectDiagnosticsEditor { summary: DiagnosticSummary, excerpts: ModelHandle, path_states: Vec, - paths_to_update: BTreeSet<(ProjectPath, LanguageServerId)>, + paths_to_update: HashMap>, current_diagnostics: HashMap>, include_warnings: bool, _subscriptions: Vec, @@ -128,9 +128,12 @@ impl View for ProjectDiagnosticsEditor { "summary": project.diagnostic_summary(cx), }), "summary": self.summary, - "paths_to_update": self.paths_to_update.iter().map(|(path, server_id)| - (path.path.to_string_lossy(), server_id.0) - ).collect::>(), + "paths_to_update": self.paths_to_update.iter().map(|(server_id, paths)| + (server_id.0, paths.into_iter().map(|path| path.path.to_string_lossy()).collect::>()) + ).collect::>(), + "current_diagnostics": self.current_diagnostics.iter().map(|(server_id, paths)| + (server_id.0, paths.into_iter().map(|path| path.path.to_string_lossy()).collect::>()) + ).collect::>(), "paths_states": self.path_states.iter().map(|state| json!({ "path": state.path.path.to_string_lossy(), @@ -164,7 +167,9 @@ impl ProjectDiagnosticsEditor { } => { log::debug!("Adding path {path:?} to update for server {language_server_id}"); this.paths_to_update - .insert((path.clone(), *language_server_id)); + .entry(*language_server_id) + .or_default() + .insert(path.clone()); let no_multiselections = this.editor.update(cx, |editor, cx| { editor.selections.all::(cx).len() <= 1 }); @@ -198,7 +203,7 @@ impl ProjectDiagnosticsEditor { excerpts, editor, path_states: Default::default(), - paths_to_update: BTreeSet::new(), + paths_to_update: HashMap::default(), include_warnings: settings::get::(cx).include_warnings, current_diagnostics: HashMap::default(), _subscriptions: vec![project_event_subscription, editor_event_subscription], @@ -245,20 +250,16 @@ impl ProjectDiagnosticsEditor { if let Some(language_server_id) = language_server_id { new_summaries.retain(|server_id, _| server_id == &language_server_id); old_diagnostics.retain(|server_id, _| server_id == &language_server_id); - self.paths_to_update.retain(|(path, server_id)| { + self.paths_to_update.retain(|server_id, paths| { if server_id == &language_server_id { - paths_to_recheck.insert(path.clone()); + paths_to_recheck.extend(paths.drain()); false } else { true } }); } else { - paths_to_recheck.extend( - mem::replace(&mut self.paths_to_update, BTreeSet::new()) - .into_iter() - .map(|(path, _)| path), - ); + paths_to_recheck.extend(self.paths_to_update.drain().flat_map(|(_, paths)| paths)); } for (server_id, new_paths) in new_summaries { From fdcb907644e598716c061c1c20edcab19ea79b51 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 5 Nov 2023 15:06:39 +0200 Subject: [PATCH 23/32] Parallelize diagnostics filling, add more logs --- Cargo.lock | 1 + crates/diagnostics/Cargo.toml | 1 + crates/diagnostics/src/diagnostics.rs | 50 ++++++++++++++++++--------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e33edf29f0..b63c46ce98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2459,6 +2459,7 @@ dependencies = [ "client", "collections", "editor", + "futures 0.3.28", "gpui", "language", "log", diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index 26a2a82999..0f9d108831 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -22,6 +22,7 @@ workspace = { path = "../workspace" } log.workspace = true anyhow.workspace = true +futures.workspace = true schemars.workspace = true serde.workspace = true serde_derive.workspace = true diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index a3d4bc9589..dec26f12c4 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -2,7 +2,7 @@ pub mod items; mod project_diagnostics_settings; mod toolbar_controls; -use anyhow::Result; +use anyhow::{Context, Result}; use collections::{HashMap, HashSet}; use editor::{ diagnostic_block_renderer, @@ -11,6 +11,7 @@ use editor::{ scroll::autoscroll::Autoscroll, Editor, ExcerptId, ExcerptRange, MultiBuffer, ToOffset, }; +use futures::future::try_join_all; use gpui::{ actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, @@ -277,25 +278,40 @@ impl ProjectDiagnosticsEditor { } paths_to_recheck.extend(old_diagnostics.into_iter().flat_map(|(_, paths)| paths)); + if paths_to_recheck.is_empty() { + log::debug!("No paths to recheck for language server {language_server_id:?}"); + return; + } + + log::debug!( + "Rechecking {} paths for language server {:?}", + paths_to_recheck.len(), + language_server_id + ); let project = self.project.clone(); cx.spawn(|this, mut cx| { async move { - let mut changed = false; - for path in paths_to_recheck { - let buffer = project - .update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx)) - .await?; - this.update(&mut cx, |this, cx| { - this.populate_excerpts(path, language_server_id, buffer, cx); - changed = true; - })?; - } - if changed { - this.update(&mut cx, |this, cx| { - this.summary = this.project.read(cx).diagnostic_summary(cx); - cx.emit(Event::TitleChanged); - })?; - } + let _ = try_join_all(paths_to_recheck.into_iter().map(|path| { + let mut cx = cx.clone(); + let project = project.clone(); + async move { + let buffer = project + .update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx)) + .await + .with_context(|| format!("opening buffer for path {path:?}"))?; + this.update(&mut cx, |this, cx| { + this.populate_excerpts(path, language_server_id, buffer, cx); + }) + .context("missing project")?; + anyhow::Ok(()) + } + })) + .await?; + + this.update(&mut cx, |this, cx| { + this.summary = this.project.read(cx).diagnostic_summary(cx); + cx.emit(Event::TitleChanged); + })?; anyhow::Ok(()) } .log_err() From ad93d9132f03ad398018332f450c541142ba0a75 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 5 Nov 2023 15:37:11 +0200 Subject: [PATCH 24/32] Correctly update old diagnostics --- crates/diagnostics/src/diagnostics.rs | 29 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index dec26f12c4..1ec4105fbd 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -246,11 +246,8 @@ impl ProjectDiagnosticsEditor { summaries.entry(server_id).or_default().insert(path); summaries }); - let mut old_diagnostics = - mem::replace(&mut self.current_diagnostics, new_summaries.clone()); - if let Some(language_server_id) = language_server_id { + let mut old_diagnostics = if let Some(language_server_id) = language_server_id { new_summaries.retain(|server_id, _| server_id == &language_server_id); - old_diagnostics.retain(|server_id, _| server_id == &language_server_id); self.paths_to_update.retain(|server_id, paths| { if server_id == &language_server_id { paths_to_recheck.extend(paths.drain()); @@ -259,10 +256,24 @@ impl ProjectDiagnosticsEditor { true } }); + let mut old_diagnostics = HashMap::default(); + if let Some(new_paths) = new_summaries.get(&language_server_id) { + if let Some(old_paths) = self + .current_diagnostics + .insert(language_server_id, new_paths.clone()) + { + old_diagnostics.insert(language_server_id, old_paths); + } + } else { + if let Some(old_paths) = self.current_diagnostics.remove(&language_server_id) { + old_diagnostics.insert(language_server_id, old_paths); + } + } + old_diagnostics } else { paths_to_recheck.extend(self.paths_to_update.drain().flat_map(|(_, paths)| paths)); - } - + mem::replace(&mut self.current_diagnostics, new_summaries.clone()) + }; for (server_id, new_paths) in new_summaries { match old_diagnostics.remove(&server_id) { Some(mut old_paths) => { @@ -282,7 +293,6 @@ impl ProjectDiagnosticsEditor { log::debug!("No paths to recheck for language server {language_server_id:?}"); return; } - log::debug!( "Rechecking {} paths for language server {:?}", paths_to_recheck.len(), @@ -291,7 +301,7 @@ impl ProjectDiagnosticsEditor { let project = self.project.clone(); cx.spawn(|this, mut cx| { async move { - let _ = try_join_all(paths_to_recheck.into_iter().map(|path| { + let _: Vec<()> = try_join_all(paths_to_recheck.into_iter().map(|path| { let mut cx = cx.clone(); let project = project.clone(); async move { @@ -306,7 +316,8 @@ impl ProjectDiagnosticsEditor { anyhow::Ok(()) } })) - .await?; + .await + .context("rechecking diagnostics for paths")?; this.update(&mut cx, |this, cx| { this.summary = this.project.read(cx).diagnostic_summary(cx); From 7374ca999bbb792d72f76c69b63979d92026afc3 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sun, 5 Nov 2023 22:56:35 +0100 Subject: [PATCH 25/32] chore: Update vue.js parser (fixes wonky HTML parsing) Vue.js defined a bunch of symbols in it's scanner that collided with those defined in HTML Tree-sitter grammar. I simply removed them as they were meant for consumption by the external parties interested in HTML parser with Vue support - since we handle that ourselves this is not really necessary to preserve anymore. cc was firing up a bunch of warnings about unused symbols, so yeah. --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e33edf29f0..5c0b98c6e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9728,7 +9728,7 @@ dependencies = [ [[package]] name = "tree-sitter-vue" version = "0.0.1" -source = "git+https://github.com/zed-industries/tree-sitter-vue?rev=95b2890#95b28908d90e928c308866f7631e73ef6e1d4b5f" +source = "git+https://github.com/zed-industries/tree-sitter-vue?rev=9b6cb221ccb8d0b956fcb17e9a1efac2feefeb58#9b6cb221ccb8d0b956fcb17e9a1efac2feefeb58" dependencies = [ "cc", "tree-sitter", diff --git a/Cargo.toml b/Cargo.toml index 772773c977..6245889530 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -176,7 +176,7 @@ tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", tree-sitter-lua = "0.0.14" tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" } tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "786689b0562b9799ce53e824cb45a1a2a04dc673"} -tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "95b2890"} +tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "9b6cb221ccb8d0b956fcb17e9a1efac2feefeb58"} [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "35a6052fbcafc5e5fc0f9415b8652be7dcaf7222" } async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" } From dbdb5f65190a3a2fd787c22a59ad5d77c8ef6ca3 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 30 Oct 2023 13:31:21 -0400 Subject: [PATCH 26/32] Actually find downloaded binary in Elixir cached binary method --- crates/zed/src/languages/elixir.rs | 32 +++++++++++++----------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/crates/zed/src/languages/elixir.rs b/crates/zed/src/languages/elixir.rs index df438d89ee..e2c79570bc 100644 --- a/crates/zed/src/languages/elixir.rs +++ b/crates/zed/src/languages/elixir.rs @@ -140,8 +140,8 @@ impl LspAdapter for ElixirLspAdapter { ) -> Result { let version = version.downcast::().unwrap(); let zip_path = container_dir.join(format!("elixir-ls_{}.zip", version.name)); - let version_dir = container_dir.join(format!("elixir-ls_{}", version.name)); - let binary_path = version_dir.join("language_server.sh"); + let folder_path = container_dir.join("elixir-ls"); + let binary_path = folder_path.join("language_server.sh"); if fs::metadata(&binary_path).await.is_err() { let mut response = delegate @@ -160,13 +160,13 @@ impl LspAdapter for ElixirLspAdapter { } futures::io::copy(response.body_mut(), &mut file).await?; - fs::create_dir_all(&version_dir) + fs::create_dir_all(&folder_path) .await - .with_context(|| format!("failed to create directory {}", version_dir.display()))?; + .with_context(|| format!("failed to create directory {}", folder_path.display()))?; let unzip_status = smol::process::Command::new("unzip") .arg(&zip_path) .arg("-d") - .arg(&version_dir) + .arg(&folder_path) .output() .await? .status; @@ -174,7 +174,7 @@ impl LspAdapter for ElixirLspAdapter { Err(anyhow!("failed to unzip elixir-ls archive"))?; } - remove_matching(&container_dir, |entry| entry != version_dir).await; + remove_matching(&container_dir, |entry| entry != folder_path).await; } Ok(LanguageServerBinary { @@ -285,20 +285,16 @@ impl LspAdapter for ElixirLspAdapter { async fn get_cached_server_binary_elixir_ls( container_dir: PathBuf, ) -> Option { - (|| async move { - let mut last = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - last = Some(entry?.path()); - } - last.map(|path| LanguageServerBinary { - path, + let server_path = container_dir.join("elixir-ls/language_server.sh"); + if server_path.exists() { + Some(LanguageServerBinary { + path: server_path, arguments: vec![], }) - .ok_or_else(|| anyhow!("no cached binary")) - })() - .await - .log_err() + } else { + log::error!("missing executable in directory {:?}", server_path); + None + } } pub struct NextLspAdapter; From 66b967532ff730ef778d9bc0a25fa82b465fa3f9 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 6 Nov 2023 11:31:31 -0500 Subject: [PATCH 27/32] zed2: Actually find downloaded binary in Elixir cached binary method --- crates/zed2/src/languages/elixir.rs | 32 +++++++++++++---------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/crates/zed2/src/languages/elixir.rs b/crates/zed2/src/languages/elixir.rs index bd38377c99..90352c78b4 100644 --- a/crates/zed2/src/languages/elixir.rs +++ b/crates/zed2/src/languages/elixir.rs @@ -140,8 +140,8 @@ impl LspAdapter for ElixirLspAdapter { ) -> Result { let version = version.downcast::().unwrap(); let zip_path = container_dir.join(format!("elixir-ls_{}.zip", version.name)); - let version_dir = container_dir.join(format!("elixir-ls_{}", version.name)); - let binary_path = version_dir.join("language_server.sh"); + let folder_path = container_dir.join("elixir-ls"); + let binary_path = folder_path.join("language_server.sh"); if fs::metadata(&binary_path).await.is_err() { let mut response = delegate @@ -160,13 +160,13 @@ impl LspAdapter for ElixirLspAdapter { } futures::io::copy(response.body_mut(), &mut file).await?; - fs::create_dir_all(&version_dir) + fs::create_dir_all(&folder_path) .await - .with_context(|| format!("failed to create directory {}", version_dir.display()))?; + .with_context(|| format!("failed to create directory {}", folder_path.display()))?; let unzip_status = smol::process::Command::new("unzip") .arg(&zip_path) .arg("-d") - .arg(&version_dir) + .arg(&folder_path) .output() .await? .status; @@ -174,7 +174,7 @@ impl LspAdapter for ElixirLspAdapter { Err(anyhow!("failed to unzip elixir-ls archive"))?; } - remove_matching(&container_dir, |entry| entry != version_dir).await; + remove_matching(&container_dir, |entry| entry != folder_path).await; } Ok(LanguageServerBinary { @@ -285,20 +285,16 @@ impl LspAdapter for ElixirLspAdapter { async fn get_cached_server_binary_elixir_ls( container_dir: PathBuf, ) -> Option { - (|| async move { - let mut last = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - last = Some(entry?.path()); - } - last.map(|path| LanguageServerBinary { - path, + let server_path = container_dir.join("elixir-ls/language_server.sh"); + if server_path.exists() { + Some(LanguageServerBinary { + path: server_path, arguments: vec![], }) - .ok_or_else(|| anyhow!("no cached binary")) - })() - .await - .log_err() + } else { + log::error!("missing executable in directory {:?}", server_path); + None + } } pub struct NextLspAdapter; From bf80c1da5bb7405008f73596a270c539d0e41a94 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 6 Nov 2023 17:53:27 +0100 Subject: [PATCH 28/32] Rename fields in `ThemeColors` (#3242) This PR applies a number of field renames in the `ThemeColors` struct from the `import-theme` branch. This will help prevent this branch from diverging too far from `main`. Release Notes: - N/A --------- Co-authored-by: Nate Butler Co-authored-by: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com> --- crates/theme2/src/colors.rs | 41 +++++++--- crates/theme2/src/default_colors.rs | 82 +++++++++++++------ crates/theme2/src/theme2.rs | 2 +- crates/theme2/src/themes/.gitkeep | 0 crates/ui2/src/components/button.rs | 4 +- crates/ui2/src/components/checkbox.rs | 5 +- crates/ui2/src/components/context_menu.rs | 2 +- crates/ui2/src/components/icon_button.rs | 4 +- crates/ui2/src/components/indicator.rs | 2 +- crates/ui2/src/components/input.rs | 4 +- crates/ui2/src/components/keybinding.rs | 2 +- crates/ui2/src/components/list.rs | 4 +- .../ui2/src/components/notification_toast.rs | 2 +- crates/ui2/src/components/palette.rs | 9 +- crates/ui2/src/components/panel.rs | 2 +- crates/ui2/src/components/tab.rs | 4 +- crates/ui2/src/components/toast.rs | 2 +- crates/ui2/src/to_extract/buffer.rs | 2 +- crates/ui2/src/to_extract/buffer_search.rs | 21 +++-- crates/ui2/src/to_extract/collab_panel.rs | 2 +- crates/ui2/src/to_extract/multi_buffer.rs | 2 +- .../ui2/src/to_extract/notifications_panel.rs | 4 +- crates/ui2/src/to_extract/panes.rs | 2 +- crates/ui2/src/to_extract/project_panel.rs | 2 +- crates/ui2/src/to_extract/status_bar.rs | 2 +- crates/ui2/src/to_extract/tab_bar.rs | 2 +- crates/ui2/src/to_extract/terminal.rs | 2 +- crates/ui2/src/to_extract/toolbar.rs | 2 +- crates/ui2/src/to_extract/traffic_lights.rs | 2 +- crates/workspace2/src/pane.rs | 6 +- crates/workspace2/src/status_bar.rs | 2 +- crates/workspace2/src/workspace2.rs | 2 +- 32 files changed, 144 insertions(+), 82 deletions(-) create mode 100644 crates/theme2/src/themes/.gitkeep diff --git a/crates/theme2/src/colors.rs b/crates/theme2/src/colors.rs index b9f8804205..1a1fd2e99e 100644 --- a/crates/theme2/src/colors.rs +++ b/crates/theme2/src/colors.rs @@ -57,17 +57,17 @@ pub struct ThemeColors { pub border_selected: Hsla, pub border_transparent: Hsla, pub border_disabled: Hsla, - pub elevated_surface: Hsla, - pub surface: Hsla, + pub elevated_surface_background: Hsla, + pub surface_background: Hsla, pub background: Hsla, - pub element: Hsla, + pub element_background: Hsla, pub element_hover: Hsla, pub element_active: Hsla, pub element_selected: Hsla, pub element_disabled: Hsla, pub element_placeholder: Hsla, pub element_drop_target: Hsla, - pub ghost_element: Hsla, + pub ghost_element_background: Hsla, pub ghost_element_hover: Hsla, pub ghost_element_active: Hsla, pub ghost_element_selected: Hsla, @@ -82,15 +82,32 @@ pub struct ThemeColors { pub icon_disabled: Hsla, pub icon_placeholder: Hsla, pub icon_accent: Hsla, - pub status_bar: Hsla, - 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 status_bar_background: Hsla, + pub title_bar_background: Hsla, + pub toolbar_background: Hsla, + pub tab_bar_background: Hsla, + pub tab_inactive_background: Hsla, + pub tab_active_background: Hsla, + pub editor_background: Hsla, + pub editor_subheader_background: Hsla, pub editor_active_line: Hsla, + pub terminal_background: Hsla, + pub terminal_ansi_bright_black: Hsla, + pub terminal_ansi_bright_red: Hsla, + pub terminal_ansi_bright_green: Hsla, + pub terminal_ansi_bright_yellow: Hsla, + pub terminal_ansi_bright_blue: Hsla, + pub terminal_ansi_bright_magenta: Hsla, + pub terminal_ansi_bright_cyan: Hsla, + pub terminal_ansi_bright_white: Hsla, + pub terminal_ansi_black: Hsla, + pub terminal_ansi_red: Hsla, + pub terminal_ansi_green: Hsla, + pub terminal_ansi_yellow: Hsla, + pub terminal_ansi_blue: Hsla, + pub terminal_ansi_magenta: Hsla, + pub terminal_ansi_cyan: Hsla, + pub terminal_ansi_white: Hsla, } #[derive(Refineable, Clone)] diff --git a/crates/theme2/src/default_colors.rs b/crates/theme2/src/default_colors.rs index 4ecae43b15..53e34acf16 100644 --- a/crates/theme2/src/default_colors.rs +++ b/crates/theme2/src/default_colors.rs @@ -208,17 +208,17 @@ impl ThemeColors { border_disabled: neutral().light().step_3(), border_selected: blue().light().step_5(), border_transparent: system.transparent, - elevated_surface: neutral().light().step_2(), - surface: neutral().light().step_2(), + elevated_surface_background: neutral().light().step_2(), + surface_background: neutral().light().step_2(), background: neutral().light().step_1(), - element: neutral().light().step_3(), + element_background: 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_background: system.transparent, ghost_element_hover: neutral().light().step_4(), ghost_element_active: neutral().light().step_5(), ghost_element_selected: neutral().light().step_5(), @@ -233,15 +233,32 @@ impl ThemeColors { 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(), + status_bar_background: neutral().light().step_2(), + title_bar_background: neutral().light().step_2(), + toolbar_background: neutral().light().step_1(), + tab_bar_background: neutral().light().step_2(), + tab_active_background: neutral().light().step_1(), + tab_inactive_background: neutral().light().step_2(), + editor_background: neutral().light().step_1(), + editor_subheader_background: neutral().light().step_2(), editor_active_line: neutral().light_alpha().step_3(), + terminal_background: neutral().light().step_1(), + terminal_ansi_black: black().light().step_12(), + terminal_ansi_red: red().light().step_11(), + terminal_ansi_green: green().light().step_11(), + terminal_ansi_yellow: yellow().light().step_11(), + terminal_ansi_blue: blue().light().step_11(), + terminal_ansi_magenta: violet().light().step_11(), + terminal_ansi_cyan: cyan().light().step_11(), + terminal_ansi_white: neutral().light().step_12(), + terminal_ansi_bright_black: black().light().step_11(), + terminal_ansi_bright_red: red().light().step_10(), + terminal_ansi_bright_green: green().light().step_10(), + terminal_ansi_bright_yellow: yellow().light().step_10(), + terminal_ansi_bright_blue: blue().light().step_10(), + terminal_ansi_bright_magenta: violet().light().step_10(), + terminal_ansi_bright_cyan: cyan().light().step_10(), + terminal_ansi_bright_white: neutral().light().step_11(), } } @@ -255,17 +272,17 @@ impl ThemeColors { border_disabled: neutral().dark().step_3(), border_selected: blue().dark().step_5(), border_transparent: system.transparent, - elevated_surface: neutral().dark().step_2(), - surface: neutral().dark().step_2(), + elevated_surface_background: neutral().dark().step_2(), + surface_background: neutral().dark().step_2(), background: neutral().dark().step_1(), - element: neutral().dark().step_3(), + element_background: 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_background: system.transparent, ghost_element_hover: neutral().dark().step_4(), ghost_element_active: neutral().dark().step_5(), ghost_element_selected: neutral().dark().step_5(), @@ -280,15 +297,32 @@ impl ThemeColors { 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(), + status_bar_background: neutral().dark().step_2(), + title_bar_background: neutral().dark().step_2(), + toolbar_background: neutral().dark().step_1(), + tab_bar_background: neutral().dark().step_2(), + tab_active_background: neutral().dark().step_1(), + tab_inactive_background: neutral().dark().step_2(), + editor_background: neutral().dark().step_1(), + editor_subheader_background: neutral().dark().step_2(), editor_active_line: neutral().dark_alpha().step_3(), + terminal_background: neutral().dark().step_1(), + terminal_ansi_black: black().dark().step_12(), + terminal_ansi_red: red().dark().step_11(), + terminal_ansi_green: green().dark().step_11(), + terminal_ansi_yellow: yellow().dark().step_11(), + terminal_ansi_blue: blue().dark().step_11(), + terminal_ansi_magenta: violet().dark().step_11(), + terminal_ansi_cyan: cyan().dark().step_11(), + terminal_ansi_white: neutral().dark().step_12(), + terminal_ansi_bright_black: black().dark().step_11(), + terminal_ansi_bright_red: red().dark().step_10(), + terminal_ansi_bright_green: green().dark().step_10(), + terminal_ansi_bright_yellow: yellow().dark().step_10(), + terminal_ansi_bright_blue: blue().dark().step_10(), + terminal_ansi_bright_magenta: violet().dark().step_10(), + terminal_ansi_bright_cyan: cyan().dark().step_10(), + terminal_ansi_bright_white: neutral().dark().step_11(), } } } diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index faf252e2e5..b8e22f8319 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -17,7 +17,7 @@ pub use syntax::*; use gpui::{AppContext, Hsla, SharedString}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum Appearance { Light, Dark, diff --git a/crates/theme2/src/themes/.gitkeep b/crates/theme2/src/themes/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/ui2/src/components/button.rs b/crates/ui2/src/components/button.rs index 381db20a83..c13460aadd 100644 --- a/crates/ui2/src/components/button.rs +++ b/crates/ui2/src/components/button.rs @@ -41,8 +41,8 @@ pub enum ButtonVariant { impl ButtonVariant { pub fn bg_color(&self, cx: &mut WindowContext) -> Hsla { match self { - ButtonVariant::Ghost => cx.theme().colors().ghost_element, - ButtonVariant::Filled => cx.theme().colors().element, + ButtonVariant::Ghost => cx.theme().colors().ghost_element_background, + ButtonVariant::Filled => cx.theme().colors().element_background, } } diff --git a/crates/ui2/src/components/checkbox.rs b/crates/ui2/src/components/checkbox.rs index 3add6cebac..4b6a6240bb 100644 --- a/crates/ui2/src/components/checkbox.rs +++ b/crates/ui2/src/components/checkbox.rs @@ -120,7 +120,10 @@ impl Checkbox { cx.theme().colors().element_selected, cx.theme().colors().border, ), - (false, false) => (cx.theme().colors().element, cx.theme().colors().border), + (false, false) => ( + cx.theme().colors().element_background, + cx.theme().colors().border, + ), }; div() diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index 4f265a376d..87be445c19 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -46,7 +46,7 @@ impl ContextMenu { fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { v_stack() .flex() - .bg(cx.theme().colors().elevated_surface) + .bg(cx.theme().colors().elevated_surface_background) .border() .border_color(cx.theme().colors().border) .child(List::new( diff --git a/crates/ui2/src/components/icon_button.rs b/crates/ui2/src/components/icon_button.rs index 101c845a76..8fe1a1fa9b 100644 --- a/crates/ui2/src/components/icon_button.rs +++ b/crates/ui2/src/components/icon_button.rs @@ -73,12 +73,12 @@ impl IconButton { let (bg_color, bg_hover_color, bg_active_color) = match self.variant { ButtonVariant::Filled => ( - cx.theme().colors().element, + cx.theme().colors().element_background, cx.theme().colors().element_hover, cx.theme().colors().element_active, ), ButtonVariant::Ghost => ( - cx.theme().colors().ghost_element, + cx.theme().colors().ghost_element_background, cx.theme().colors().ghost_element_hover, cx.theme().colors().ghost_element_active, ), diff --git a/crates/ui2/src/components/indicator.rs b/crates/ui2/src/components/indicator.rs index 1f6e00e621..94398ab7f6 100644 --- a/crates/ui2/src/components/indicator.rs +++ b/crates/ui2/src/components/indicator.rs @@ -14,7 +14,7 @@ impl UnreadIndicator { div() .rounded_full() .border_2() - .border_color(cx.theme().colors().surface) + .border_color(cx.theme().colors().surface_background) .w(px(9.0)) .h(px(9.0)) .z_index(2) diff --git a/crates/ui2/src/components/input.rs b/crates/ui2/src/components/input.rs index 2884470ce2..f288f3ca56 100644 --- a/crates/ui2/src/components/input.rs +++ b/crates/ui2/src/components/input.rs @@ -59,12 +59,12 @@ impl Input { fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { let (input_bg, input_hover_bg, input_active_bg) = match self.variant { InputVariant::Ghost => ( - cx.theme().colors().ghost_element, + cx.theme().colors().ghost_element_background, cx.theme().colors().ghost_element_hover, cx.theme().colors().ghost_element_active, ), InputVariant::Filled => ( - cx.theme().colors().element, + cx.theme().colors().element_background, cx.theme().colors().element_hover, cx.theme().colors().element_active, ), diff --git a/crates/ui2/src/components/keybinding.rs b/crates/ui2/src/components/keybinding.rs index 88cabbdc88..8b8fba8c08 100644 --- a/crates/ui2/src/components/keybinding.rs +++ b/crates/ui2/src/components/keybinding.rs @@ -66,7 +66,7 @@ impl Key { .rounded_md() .text_sm() .text_color(cx.theme().colors().text) - .bg(cx.theme().colors().element) + .bg(cx.theme().colors().element_background) .child(self.key.clone()) } } diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index 543432a893..b30beacd98 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -79,7 +79,7 @@ impl ListHeader { h_stack() .w_full() - .bg(cx.theme().colors().surface) + .bg(cx.theme().colors().surface_background) // TODO: Add focus state // .when(self.state == InteractionState::Focused, |this| { // this.border() @@ -307,7 +307,7 @@ impl ListEntry { div() .relative() .group("") - .bg(cx.theme().colors().surface) + .bg(cx.theme().colors().surface_background) // TODO: Add focus state .child( sized_item diff --git a/crates/ui2/src/components/notification_toast.rs b/crates/ui2/src/components/notification_toast.rs index 59078c98f4..e8739b925c 100644 --- a/crates/ui2/src/components/notification_toast.rs +++ b/crates/ui2/src/components/notification_toast.rs @@ -34,7 +34,7 @@ impl NotificationToast { .px_1p5() .rounded_lg() .shadow_md() - .bg(cx.theme().colors().elevated_surface) + .bg(cx.theme().colors().elevated_surface_background) .child(div().size_full().child(self.label.clone())) } } diff --git a/crates/ui2/src/components/palette.rs b/crates/ui2/src/components/palette.rs index a1f3eb7e1c..269b39d86d 100644 --- a/crates/ui2/src/components/palette.rs +++ b/crates/ui2/src/components/palette.rs @@ -47,7 +47,7 @@ impl Palette { .id(self.id.clone()) .w_96() .rounded_lg() - .bg(cx.theme().colors().elevated_surface) + .bg(cx.theme().colors().elevated_surface_background) .border() .border_color(cx.theme().colors().border) .child( @@ -56,7 +56,12 @@ impl Palette { .child(v_stack().py_0p5().px_1().child(div().px_2().py_0p5().child( Label::new(self.input_placeholder.clone()).color(LabelColor::Placeholder), ))) - .child(div().h_px().w_full().bg(cx.theme().colors().element)) + .child( + div() + .h_px() + .w_full() + .bg(cx.theme().colors().element_background), + ) .child( v_stack() .id("items") diff --git a/crates/ui2/src/components/panel.rs b/crates/ui2/src/components/panel.rs index 5d941eb50e..ba88abb337 100644 --- a/crates/ui2/src/components/panel.rs +++ b/crates/ui2/src/components/panel.rs @@ -107,7 +107,7 @@ impl Panel { PanelSide::Right => this.border_l(), PanelSide::Bottom => this.border_b().w_full().h(current_size), }) - .bg(cx.theme().colors().surface) + .bg(cx.theme().colors().surface_background) .border_color(cx.theme().colors().border) .children(self.children) } diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index e8b0ee3be5..47de0541f1 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -109,12 +109,12 @@ impl Tab { let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current { false => ( - cx.theme().colors().tab_inactive, + cx.theme().colors().tab_inactive_background, cx.theme().colors().ghost_element_hover, cx.theme().colors().ghost_element_active, ), true => ( - cx.theme().colors().tab_active, + cx.theme().colors().tab_active_background, cx.theme().colors().element_hover, cx.theme().colors().element_active, ), diff --git a/crates/ui2/src/components/toast.rs b/crates/ui2/src/components/toast.rs index 3b81ac42b4..4ab6625dba 100644 --- a/crates/ui2/src/components/toast.rs +++ b/crates/ui2/src/components/toast.rs @@ -54,7 +54,7 @@ impl Toast { .rounded_lg() .shadow_md() .overflow_hidden() - .bg(cx.theme().colors().elevated_surface) + .bg(cx.theme().colors().elevated_surface_background) .children(self.children) } } diff --git a/crates/ui2/src/to_extract/buffer.rs b/crates/ui2/src/to_extract/buffer.rs index 2b3db676ce..e12beff2fc 100644 --- a/crates/ui2/src/to_extract/buffer.rs +++ b/crates/ui2/src/to_extract/buffer.rs @@ -220,7 +220,7 @@ impl Buffer { .flex_1() .w_full() .h_full() - .bg(cx.theme().colors().editor) + .bg(cx.theme().colors().editor_background) .children(rows) } } diff --git a/crates/ui2/src/to_extract/buffer_search.rs b/crates/ui2/src/to_extract/buffer_search.rs index 5d7de1b408..02f689ca3e 100644 --- a/crates/ui2/src/to_extract/buffer_search.rs +++ b/crates/ui2/src/to_extract/buffer_search.rs @@ -30,14 +30,17 @@ impl Render for BufferSearch { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Div { - h_stack().bg(cx.theme().colors().toolbar).p_2().child( - h_stack().child(Input::new("Search")).child( - IconButton::::new("replace", Icon::Replace) - .when(self.is_replace_open, |this| this.color(IconColor::Accent)) - .on_click(|buffer_search, cx| { - buffer_search.toggle_replace(cx); - }), - ), - ) + h_stack() + .bg(cx.theme().colors().toolbar_background) + .p_2() + .child( + h_stack().child(Input::new("Search")).child( + IconButton::::new("replace", Icon::Replace) + .when(self.is_replace_open, |this| this.color(IconColor::Accent)) + .on_click(|buffer_search, cx| { + buffer_search.toggle_replace(cx); + }), + ), + ) } } diff --git a/crates/ui2/src/to_extract/collab_panel.rs b/crates/ui2/src/to_extract/collab_panel.rs index 9019456dd1..9d9dc861e2 100644 --- a/crates/ui2/src/to_extract/collab_panel.rs +++ b/crates/ui2/src/to_extract/collab_panel.rs @@ -17,7 +17,7 @@ impl CollabPanel { v_stack() .id(self.id.clone()) .h_full() - .bg(cx.theme().colors().surface) + .bg(cx.theme().colors().surface_background) .child( v_stack() .id("crdb") diff --git a/crates/ui2/src/to_extract/multi_buffer.rs b/crates/ui2/src/to_extract/multi_buffer.rs index ea130f20bd..1334703015 100644 --- a/crates/ui2/src/to_extract/multi_buffer.rs +++ b/crates/ui2/src/to_extract/multi_buffer.rs @@ -24,7 +24,7 @@ impl MultiBuffer { .items_center() .justify_between() .p_4() - .bg(cx.theme().colors().editor_subheader) + .bg(cx.theme().colors().editor_subheader_background) .child(Label::new("main.rs")) .child(IconButton::new("arrow_up_right", Icon::ArrowUpRight)), ) diff --git a/crates/ui2/src/to_extract/notifications_panel.rs b/crates/ui2/src/to_extract/notifications_panel.rs index 789aa2b0aa..84794b71b2 100644 --- a/crates/ui2/src/to_extract/notifications_panel.rs +++ b/crates/ui2/src/to_extract/notifications_panel.rs @@ -22,7 +22,7 @@ impl NotificationsPanel { .flex() .flex_col() .size_full() - .bg(cx.theme().colors().surface) + .bg(cx.theme().colors().surface_background) .child( ListHeader::new("Notifications").meta(Some(ListHeaderMeta::Tools(vec![ Icon::AtSign, @@ -43,7 +43,7 @@ impl NotificationsPanel { .p_1() // TODO: Add cursor style // .cursor(Cursor::IBeam) - .bg(cx.theme().colors().element) + .bg(cx.theme().colors().element_background) .border() .border_color(cx.theme().colors().border_variant) .child( diff --git a/crates/ui2/src/to_extract/panes.rs b/crates/ui2/src/to_extract/panes.rs index bf0f27d43f..a1e040b0ca 100644 --- a/crates/ui2/src/to_extract/panes.rs +++ b/crates/ui2/src/to_extract/panes.rs @@ -113,7 +113,7 @@ impl PaneGroup { .gap_px() .w_full() .h_full() - .bg(cx.theme().colors().editor) + .bg(cx.theme().colors().editor_background) .children(self.groups.into_iter().map(|group| group.render(view, cx))); if self.split_direction == SplitDirection::Horizontal { diff --git a/crates/ui2/src/to_extract/project_panel.rs b/crates/ui2/src/to_extract/project_panel.rs index 807768427b..a34a30bcbc 100644 --- a/crates/ui2/src/to_extract/project_panel.rs +++ b/crates/ui2/src/to_extract/project_panel.rs @@ -19,7 +19,7 @@ impl ProjectPanel { .flex() .flex_col() .size_full() - .bg(cx.theme().colors().surface) + .bg(cx.theme().colors().surface_background) .child( div() .id("project-panel-contents") diff --git a/crates/ui2/src/to_extract/status_bar.rs b/crates/ui2/src/to_extract/status_bar.rs index 136472f605..34a5993e69 100644 --- a/crates/ui2/src/to_extract/status_bar.rs +++ b/crates/ui2/src/to_extract/status_bar.rs @@ -93,7 +93,7 @@ impl StatusBar { .items_center() .justify_between() .w_full() - .bg(cx.theme().colors().status_bar) + .bg(cx.theme().colors().status_bar_background) .child(self.left_tools(view, cx)) .child(self.right_tools(view, cx)) } diff --git a/crates/ui2/src/to_extract/tab_bar.rs b/crates/ui2/src/to_extract/tab_bar.rs index bb7fca1153..a128044183 100644 --- a/crates/ui2/src/to_extract/tab_bar.rs +++ b/crates/ui2/src/to_extract/tab_bar.rs @@ -31,7 +31,7 @@ impl TabBar { .id(self.id.clone()) .w_full() .flex() - .bg(cx.theme().colors().tab_bar) + .bg(cx.theme().colors().tab_bar_background) // Left Side .child( div() diff --git a/crates/ui2/src/to_extract/terminal.rs b/crates/ui2/src/to_extract/terminal.rs index 051ebf7315..b912a59607 100644 --- a/crates/ui2/src/to_extract/terminal.rs +++ b/crates/ui2/src/to_extract/terminal.rs @@ -24,7 +24,7 @@ impl Terminal { div() .w_full() .flex() - .bg(cx.theme().colors().surface) + .bg(cx.theme().colors().surface_background) .child( div().px_1().flex().flex_none().gap_2().child( div() diff --git a/crates/ui2/src/to_extract/toolbar.rs b/crates/ui2/src/to_extract/toolbar.rs index 05a5c991d6..0e3e7c259f 100644 --- a/crates/ui2/src/to_extract/toolbar.rs +++ b/crates/ui2/src/to_extract/toolbar.rs @@ -56,7 +56,7 @@ impl Toolbar { fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { div() - .bg(cx.theme().colors().toolbar) + .bg(cx.theme().colors().toolbar_background) .p_2() .flex() .justify_between() diff --git a/crates/ui2/src/to_extract/traffic_lights.rs b/crates/ui2/src/to_extract/traffic_lights.rs index 9080276cdd..677fae886c 100644 --- a/crates/ui2/src/to_extract/traffic_lights.rs +++ b/crates/ui2/src/to_extract/traffic_lights.rs @@ -28,7 +28,7 @@ impl TrafficLight { (true, TrafficLightColor::Red) => system_colors.mac_os_traffic_light_red, (true, TrafficLightColor::Yellow) => system_colors.mac_os_traffic_light_yellow, (true, TrafficLightColor::Green) => system_colors.mac_os_traffic_light_green, - (false, _) => cx.theme().colors().element, + (false, _) => cx.theme().colors().element_background, }; div().w_3().h_3().rounded_full().bg(fill) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index acc41deba8..44f705ebb2 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1363,12 +1363,12 @@ impl Pane { let (tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index { false => ( - cx.theme().colors().tab_inactive, + cx.theme().colors().tab_inactive_background, cx.theme().colors().ghost_element_hover, cx.theme().colors().ghost_element_active, ), true => ( - cx.theme().colors().tab_active, + cx.theme().colors().tab_active_background, cx.theme().colors().element_hover, cx.theme().colors().element_active, ), @@ -1432,7 +1432,7 @@ impl Pane { .id("tab_bar") .w_full() .flex() - .bg(cx.theme().colors().tab_bar) + .bg(cx.theme().colors().tab_bar_background) // Left Side .child( div() diff --git a/crates/workspace2/src/status_bar.rs b/crates/workspace2/src/status_bar.rs index ca4ebcdb13..ac99be9f89 100644 --- a/crates/workspace2/src/status_bar.rs +++ b/crates/workspace2/src/status_bar.rs @@ -44,7 +44,7 @@ impl Render for StatusBar { .items_center() .justify_between() .w_full() - .bg(cx.theme().colors().status_bar) + .bg(cx.theme().colors().status_bar_background) .child(self.render_left_tools(cx)) .child(self.render_right_tools(cx)) } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 6e4c9e6cfd..7561c903d3 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -2697,7 +2697,7 @@ impl Workspace { fn render_titlebar(&self, cx: &mut ViewContext) -> impl Component { div() - .bg(cx.theme().colors().title_bar) + .bg(cx.theme().colors().title_bar_background) .when( !matches!(cx.window_bounds(), WindowBounds::Fullscreen), |s| s.pl_20(), From bfb860c06c7f5d1aa1db9e5596fab14c6f0e44cf Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 6 Nov 2023 19:29:31 +0200 Subject: [PATCH 29/32] Properly toggle diagnostics --- crates/diagnostics/src/diagnostics.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 1ec4105fbd..e794771434 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -227,6 +227,7 @@ impl ProjectDiagnosticsEditor { fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext) { self.include_warnings = !self.include_warnings; + self.paths_to_update = self.current_diagnostics.clone(); self.update_excerpts(None, cx); cx.notify(); } From d88dccffbc035178e8437204f9e41d23c3c04485 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 6 Nov 2023 11:02:30 -0700 Subject: [PATCH 30/32] Allow multiple subscriptions from one entity handle --- crates/gpui2/src/subscription.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui2/src/subscription.rs b/crates/gpui2/src/subscription.rs index 744e83bbbd..2f4ec0d2f1 100644 --- a/crates/gpui2/src/subscription.rs +++ b/crates/gpui2/src/subscription.rs @@ -38,7 +38,7 @@ where lock.subscribers .entry(emitter_key.clone()) .or_default() - .insert(Default::default()) + .get_or_insert_with(|| Default::default()) .insert(subscriber_id, callback); let this = self.0.clone(); Subscription { From d224f511fabbe9db12023bdf97a3cf3d4a148d3b Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 6 Nov 2023 19:22:25 +0100 Subject: [PATCH 31/32] Add interactivity to `Checkbox` component (#3240) This PR adds interactivity to the `Checkbox` component. They can now be checked and unchecked by clicking them. Release Notes: - N/A --- crates/gpui2/src/element.rs | 13 +++ crates/ui2/src/components/button.rs | 2 +- crates/ui2/src/components/checkbox.rs | 117 +++++++++++++------------ crates/ui2/src/prelude.rs | 11 ++- crates/ui2/src/to_extract/workspace.rs | 23 ++++- 5 files changed, 108 insertions(+), 58 deletions(-) diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index a92dbd6ff9..2a0f557272 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -212,6 +212,19 @@ pub trait Component { { self.map(|this| if condition { then(this) } else { this }) } + + fn when_some(self, option: Option, then: impl FnOnce(Self, T) -> Self) -> Self + where + Self: Sized, + { + self.map(|this| { + if let Some(value) = option { + then(this, value) + } else { + this + } + }) + } } impl Component for AnyElement { diff --git a/crates/ui2/src/components/button.rs b/crates/ui2/src/components/button.rs index c13460aadd..178813dd5f 100644 --- a/crates/ui2/src/components/button.rs +++ b/crates/ui2/src/components/button.rs @@ -61,7 +61,7 @@ impl ButtonVariant { } } -pub type ClickHandler = Arc) + Send + Sync>; +pub type ClickHandler = Arc) + Send + Sync>; struct ButtonHandlers { click: Option>, diff --git a/crates/ui2/src/components/checkbox.rs b/crates/ui2/src/components/checkbox.rs index 4b6a6240bb..9632a66810 100644 --- a/crates/ui2/src/components/checkbox.rs +++ b/crates/ui2/src/components/checkbox.rs @@ -1,63 +1,58 @@ -///! # Checkbox -///! -///! Checkboxes are used for multiple choices, not for mutually exclusive choices. -///! Each checkbox works independently from other checkboxes in the list, -///! therefore checking an additional box does not affect any other selections. +use std::sync::Arc; + use gpui2::{ - div, Component, ParentElement, SharedString, StatelessInteractive, Styled, ViewContext, + div, Component, ElementId, ParentElement, StatefulInteractive, StatelessInteractive, Styled, + ViewContext, }; use theme2::ActiveTheme; -use crate::{Icon, IconColor, IconElement, Selected}; +use crate::{Icon, IconColor, IconElement, Selection}; +pub type CheckHandler = Arc) + Send + Sync>; + +/// # Checkbox +/// +/// Checkboxes are used for multiple choices, not for mutually exclusive choices. +/// Each checkbox works independently from other checkboxes in the list, +/// therefore checking an additional box does not affect any other selections. #[derive(Component)] -pub struct Checkbox { - id: SharedString, - checked: Selected, +pub struct Checkbox { + id: ElementId, + checked: Selection, disabled: bool, + on_click: Option>, } -impl Checkbox { - pub fn new(id: impl Into) -> Self { +impl Checkbox { + pub fn new(id: impl Into, checked: Selection) -> Self { Self { id: id.into(), - checked: Selected::Unselected, + checked, disabled: false, + on_click: None, } } - pub fn toggle(mut self) -> Self { - self.checked = match self.checked { - Selected::Selected => Selected::Unselected, - Selected::Unselected => Selected::Selected, - Selected::Indeterminate => Selected::Selected, - }; - self - } - - pub fn set_indeterminate(mut self) -> Self { - self.checked = Selected::Indeterminate; - self - } - - pub fn set_disabled(mut self, disabled: bool) -> Self { + pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } - pub fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { - let group_id = format!("checkbox_group_{}", self.id); + pub fn on_click( + mut self, + handler: impl 'static + Fn(Selection, &mut V, &mut ViewContext) + Send + Sync, + ) -> Self { + self.on_click = Some(Arc::new(handler)); + self + } + + pub fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + let group_id = format!("checkbox_group_{:?}", self.id); - // The icon is different depending on the state of the checkbox. - // - // We need the match to return all the same type, - // so we wrap the eatch result in a div. - // - // We are still exploring the best way to handle this. let icon = match self.checked { // When selected, we show a checkmark. - Selected::Selected => { - div().child( + Selection::Selected => { + Some( IconElement::new(Icon::Check) .size(crate::IconSize::Small) .color( @@ -71,8 +66,8 @@ impl Checkbox { ) } // In an indeterminate state, we show a dash. - Selected::Indeterminate => { - div().child( + Selection::Indeterminate => { + Some( IconElement::new(Icon::Dash) .size(crate::IconSize::Small) .color( @@ -86,7 +81,7 @@ impl Checkbox { ) } // When unselected, we show nothing. - Selected::Unselected => div(), + Selection::Unselected => None, }; // A checkbox could be in an indeterminate state, @@ -98,7 +93,7 @@ impl Checkbox { // For the sake of styles we treat the indeterminate state as selected, // but it's icon will be different. let selected = - self.checked == Selected::Selected || self.checked == Selected::Indeterminate; + self.checked == Selection::Selected || self.checked == Selection::Indeterminate; // We could use something like this to make the checkbox background when selected: // @@ -127,6 +122,7 @@ impl Checkbox { }; div() + .id(self.id) // Rather than adding `px_1()` to add some space around the checkbox, // we use a larger parent element to create a slightly larger // click area for the checkbox. @@ -161,7 +157,13 @@ impl Checkbox { el.bg(cx.theme().colors().element_hover) }) }) - .child(icon), + .children(icon), + ) + .when_some( + self.on_click.filter(|_| !self.disabled), + |this, on_click| { + this.on_click(move |view, _, cx| on_click(self.checked.inverse(), view, cx)) + }, ) } } @@ -182,7 +184,7 @@ mod stories { fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) - .child(Story::title_for::<_, Checkbox>(cx)) + .child(Story::title_for::<_, Checkbox>(cx)) .child(Story::label(cx, "Default")) .child( h_stack() @@ -191,9 +193,12 @@ mod stories { .rounded_md() .border() .border_color(cx.theme().colors().border) - .child(Checkbox::new("checkbox-enabled")) - .child(Checkbox::new("checkbox-intermediate").set_indeterminate()) - .child(Checkbox::new("checkbox-selected").toggle()), + .child(Checkbox::new("checkbox-enabled", Selection::Unselected)) + .child(Checkbox::new( + "checkbox-intermediate", + Selection::Indeterminate, + )) + .child(Checkbox::new("checkbox-selected", Selection::Selected)), ) .child(Story::label(cx, "Disabled")) .child( @@ -203,16 +208,20 @@ mod stories { .rounded_md() .border() .border_color(cx.theme().colors().border) - .child(Checkbox::new("checkbox-disabled").set_disabled(true)) .child( - Checkbox::new("checkbox-disabled-intermediate") - .set_disabled(true) - .set_indeterminate(), + Checkbox::new("checkbox-disabled", Selection::Unselected) + .disabled(true), ) .child( - Checkbox::new("checkbox-disabled-selected") - .set_disabled(true) - .toggle(), + Checkbox::new( + "checkbox-disabled-intermediate", + Selection::Indeterminate, + ) + .disabled(true), + ) + .child( + Checkbox::new("checkbox-disabled-selected", Selection::Selected) + .disabled(true), ), ) } diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 072ed00060..134b293d3d 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -155,9 +155,18 @@ impl InteractionState { } #[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)] -pub enum Selected { +pub enum Selection { #[default] Unselected, Indeterminate, Selected, } + +impl Selection { + pub fn inverse(&self) -> Self { + match self { + Self::Unselected | Self::Indeterminate => Self::Selected, + Self::Selected => Self::Unselected, + } + } +} diff --git a/crates/ui2/src/to_extract/workspace.rs b/crates/ui2/src/to_extract/workspace.rs index 97570a33e3..77b9bc4539 100644 --- a/crates/ui2/src/to_extract/workspace.rs +++ b/crates/ui2/src/to_extract/workspace.rs @@ -7,8 +7,8 @@ use theme2::ThemeSettings; use crate::prelude::*; use crate::{ - static_livestream, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, CollabPanel, - EditorPane, Label, LanguageSelector, NotificationsPanel, Pane, PaneGroup, Panel, + static_livestream, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, Checkbox, + CollabPanel, EditorPane, Label, LanguageSelector, NotificationsPanel, Pane, PaneGroup, Panel, PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar, Toast, ToastOrigin, }; @@ -42,6 +42,7 @@ pub struct Workspace { show_terminal: bool, show_debug: bool, show_language_selector: bool, + test_checkbox_selection: Selection, debug: Gpui2UiDebug, } @@ -58,6 +59,7 @@ impl Workspace { show_language_selector: false, show_debug: false, show_notifications_panel: true, + test_checkbox_selection: Selection::Unselected, debug: Gpui2UiDebug::default(), } } @@ -217,6 +219,23 @@ impl Render for Workspace { .text_color(cx.theme().colors().text) .bg(cx.theme().colors().background) .child(self.title_bar.clone()) + .child( + div() + .absolute() + .top_12() + .left_12() + .z_index(99) + .bg(cx.theme().colors().background) + .child( + Checkbox::new("test_checkbox", self.test_checkbox_selection).on_click( + |selection, workspace: &mut Workspace, cx| { + workspace.test_checkbox_selection = selection; + + cx.notify(); + }, + ), + ), + ) .child( div() .flex_1() From 496518f3e8f8b1be75e7367e3b80ba4ce774496c Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 6 Nov 2023 11:46:10 -0700 Subject: [PATCH 32/32] Use gpui instead of gpui2 consistenytly --- crates/gpui2/src/styled.rs | 2 +- crates/gpui2_macros/src/derive_component.rs | 6 +++--- crates/gpui2_macros/src/style_helpers.rs | 4 ++-- crates/live_kit_client2/Cargo.toml | 6 +++--- crates/live_kit_client2/examples/test_app.rs | 6 +++--- crates/live_kit_client2/src/test.rs | 2 +- crates/storybook2/Cargo.toml | 4 ++-- crates/storybook2/src/assets.rs | 2 +- crates/storybook2/src/stories/colors.rs | 4 ++-- crates/storybook2/src/stories/focus.rs | 4 ++-- crates/storybook2/src/stories/kitchen_sink.rs | 2 +- crates/storybook2/src/stories/scroll.rs | 4 ++-- crates/storybook2/src/stories/text.rs | 4 ++-- crates/storybook2/src/stories/z_index.rs | 2 +- crates/storybook2/src/story_selector.rs | 2 +- crates/storybook2/src/storybook2.rs | 8 ++++---- crates/terminal2/src/mappings/colors.rs | 2 +- crates/ui2/Cargo.toml | 2 +- crates/ui2/docs/hello-world.md | 10 +++++----- crates/ui2/src/components/avatar.rs | 6 +++--- crates/ui2/src/components/button.rs | 4 ++-- crates/ui2/src/components/checkbox.rs | 4 ++-- crates/ui2/src/components/context_menu.rs | 2 +- crates/ui2/src/components/details.rs | 2 +- crates/ui2/src/components/facepile.rs | 2 +- crates/ui2/src/components/icon.rs | 4 ++-- crates/ui2/src/components/icon_button.rs | 2 +- crates/ui2/src/components/indicator.rs | 2 +- crates/ui2/src/components/input.rs | 2 +- crates/ui2/src/components/keybinding.rs | 2 +- crates/ui2/src/components/label.rs | 4 ++-- crates/ui2/src/components/list.rs | 2 +- crates/ui2/src/components/modal.rs | 2 +- crates/ui2/src/components/notification_toast.rs | 2 +- crates/ui2/src/components/palette.rs | 4 ++-- crates/ui2/src/components/panel.rs | 4 ++-- crates/ui2/src/components/player.rs | 2 +- crates/ui2/src/components/slot.rs | 2 +- crates/ui2/src/components/stack.rs | 2 +- crates/ui2/src/components/tab.rs | 2 +- crates/ui2/src/components/toast.rs | 4 ++-- crates/ui2/src/components/toggle.rs | 2 +- crates/ui2/src/components/tooltip.rs | 2 +- crates/ui2/src/prelude.rs | 4 ++-- crates/ui2/src/settings.rs | 2 +- crates/ui2/src/static_data.rs | 2 +- crates/ui2/src/story.rs | 2 +- crates/ui2/src/to_extract/assistant_panel.rs | 4 ++-- crates/ui2/src/to_extract/breadcrumb.rs | 4 ++-- crates/ui2/src/to_extract/buffer.rs | 4 ++-- crates/ui2/src/to_extract/buffer_search.rs | 2 +- crates/ui2/src/to_extract/chat_panel.rs | 2 +- crates/ui2/src/to_extract/collab_panel.rs | 2 +- crates/ui2/src/to_extract/command_palette.rs | 2 +- crates/ui2/src/to_extract/copilot.rs | 2 +- crates/ui2/src/to_extract/editor_pane.rs | 2 +- crates/ui2/src/to_extract/language_selector.rs | 2 +- crates/ui2/src/to_extract/multi_buffer.rs | 2 +- crates/ui2/src/to_extract/notifications_panel.rs | 4 ++-- crates/ui2/src/to_extract/panes.rs | 2 +- crates/ui2/src/to_extract/project_panel.rs | 4 ++-- crates/ui2/src/to_extract/recent_projects.rs | 2 +- crates/ui2/src/to_extract/tab_bar.rs | 4 ++-- crates/ui2/src/to_extract/terminal.rs | 4 ++-- crates/ui2/src/to_extract/theme_selector.rs | 2 +- crates/ui2/src/to_extract/title_bar.rs | 2 +- crates/ui2/src/to_extract/toolbar.rs | 4 ++-- crates/ui2/src/to_extract/traffic_lights.rs | 2 +- crates/ui2/src/to_extract/workspace.rs | 4 ++-- crates/zed2/src/languages/language_plugin.rs | 2 +- test.rs | 6 +++--- 71 files changed, 111 insertions(+), 111 deletions(-) diff --git a/crates/gpui2/src/styled.rs b/crates/gpui2/src/styled.rs index 4a9a5d7ecf..a272ab95b1 100644 --- a/crates/gpui2/src/styled.rs +++ b/crates/gpui2/src/styled.rs @@ -1,5 +1,5 @@ use crate::{ - self as gpui2, hsla, point, px, relative, rems, AlignItems, CursorStyle, DefiniteLength, + self as gpui, hsla, point, px, relative, rems, AlignItems, CursorStyle, DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString, StyleRefinement, Visibility, }; diff --git a/crates/gpui2_macros/src/derive_component.rs b/crates/gpui2_macros/src/derive_component.rs index a946703310..aaf814497a 100644 --- a/crates/gpui2_macros/src/derive_component.rs +++ b/crates/gpui2_macros/src/derive_component.rs @@ -28,9 +28,9 @@ pub fn derive_component(input: TokenStream) -> TokenStream { let (_, ty_generics, _) = ast.generics.split_for_impl(); let expanded = quote! { - impl #impl_generics gpui2::Component<#view_type> for #name #ty_generics #where_clause { - fn render(self) -> gpui2::AnyElement<#view_type> { - (move |view_state: &mut #view_type, cx: &mut gpui2::ViewContext<'_, #view_type>| self.render(view_state, cx)) + impl #impl_generics gpui::Component<#view_type> for #name #ty_generics #where_clause { + fn render(self) -> gpui::AnyElement<#view_type> { + (move |view_state: &mut #view_type, cx: &mut gpui::ViewContext<'_, #view_type>| self.render(view_state, cx)) .render() } } diff --git a/crates/gpui2_macros/src/style_helpers.rs b/crates/gpui2_macros/src/style_helpers.rs index 9e47d85ef1..57aef03afa 100644 --- a/crates/gpui2_macros/src/style_helpers.rs +++ b/crates/gpui2_macros/src/style_helpers.rs @@ -123,7 +123,7 @@ fn generate_predefined_setter( .iter() .map(|field_tokens| { quote! { - style.#field_tokens = Some((#negation_token gpui2::#length_tokens).into()); + style.#field_tokens = Some((#negation_token gpui::#length_tokens).into()); } }) .collect::>(); @@ -163,7 +163,7 @@ fn generate_custom_value_setter( let method = quote! { #[doc = #doc_string] - fn #method_name(mut self, length: impl std::clone::Clone + Into) -> Self where Self: std::marker::Sized { + fn #method_name(mut self, length: impl std::clone::Clone + Into) -> Self where Self: std::marker::Sized { let style = self.style(); #(#field_assignments)* self diff --git a/crates/live_kit_client2/Cargo.toml b/crates/live_kit_client2/Cargo.toml index 5adb711948..b606434b05 100644 --- a/crates/live_kit_client2/Cargo.toml +++ b/crates/live_kit_client2/Cargo.toml @@ -16,14 +16,14 @@ name = "test_app" test-support = [ "async-trait", "collections/test-support", - "gpui2/test-support", + "gpui/test-support", "live_kit_server", "nanoid", ] [dependencies] collections = { path = "../collections", optional = true } -gpui2 = { package = "gpui2", path = "../gpui2", optional = true } +gpui = { package = "gpui2", path = "../gpui2", optional = true } live_kit_server = { path = "../live_kit_server", optional = true } media = { path = "../media" } @@ -41,7 +41,7 @@ nanoid = { version ="0.4", optional = true} [dev-dependencies] collections = { path = "../collections", features = ["test-support"] } -gpui2 = { package = "gpui2", path = "../gpui2", features = ["test-support"] } +gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] } live_kit_server = { path = "../live_kit_server" } media = { path = "../media" } nanoid = "0.4" diff --git a/crates/live_kit_client2/examples/test_app.rs b/crates/live_kit_client2/examples/test_app.rs index 4062441a06..98302eb35c 100644 --- a/crates/live_kit_client2/examples/test_app.rs +++ b/crates/live_kit_client2/examples/test_app.rs @@ -1,7 +1,7 @@ use std::{sync::Arc, time::Duration}; use futures::StreamExt; -use gpui2::KeyBinding; +use gpui::KeyBinding; use live_kit_client2::{ LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room, }; @@ -16,7 +16,7 @@ struct Quit; fn main() { SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - gpui2::App::production(Arc::new(())).run(|cx| { + gpui::App::production(Arc::new(())).run(|cx| { #[cfg(any(test, feature = "test-support"))] println!("USING TEST LIVEKIT"); @@ -173,6 +173,6 @@ fn main() { }); } -fn quit(_: &Quit, cx: &mut gpui2::AppContext) { +fn quit(_: &Quit, cx: &mut gpui::AppContext) { cx.quit(); } diff --git a/crates/live_kit_client2/src/test.rs b/crates/live_kit_client2/src/test.rs index 10c97e8d81..6367e53ba8 100644 --- a/crates/live_kit_client2/src/test.rs +++ b/crates/live_kit_client2/src/test.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use collections::{BTreeMap, HashMap}; use futures::Stream; -use gpui2::BackgroundExecutor; +use gpui::BackgroundExecutor; use live_kit_server::token; use media::core_video::CVImageBuffer; use parking_lot::Mutex; diff --git a/crates/storybook2/Cargo.toml b/crates/storybook2/Cargo.toml index 0f335ae984..1f3a0b33cc 100644 --- a/crates/storybook2/Cargo.toml +++ b/crates/storybook2/Cargo.toml @@ -14,7 +14,7 @@ anyhow.workspace = true backtrace-on-stack-overflow = "0.3.0" clap = { version = "4.4", features = ["derive", "string"] } chrono = "0.4" -gpui2 = { path = "../gpui2" } +gpui = { package = "gpui2", path = "../gpui2" } itertools = "0.11.0" log.workspace = true rust-embed.workspace = true @@ -29,4 +29,4 @@ ui = { package = "ui2", path = "../ui2", features = ["stories"] } util = { path = "../util" } [dev-dependencies] -gpui2 = { path = "../gpui2", features = ["test-support"] } +gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] } diff --git a/crates/storybook2/src/assets.rs b/crates/storybook2/src/assets.rs index 3d42984548..9fc71917b4 100644 --- a/crates/storybook2/src/assets.rs +++ b/crates/storybook2/src/assets.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use anyhow::{anyhow, Result}; -use gpui2::{AssetSource, SharedString}; +use gpui::{AssetSource, SharedString}; use rust_embed::RustEmbed; #[derive(RustEmbed)] diff --git a/crates/storybook2/src/stories/colors.rs b/crates/storybook2/src/stories/colors.rs index c1c65d62fa..13b7b36a8c 100644 --- a/crates/storybook2/src/stories/colors.rs +++ b/crates/storybook2/src/stories/colors.rs @@ -1,5 +1,5 @@ use crate::story::Story; -use gpui2::{px, Div, Render}; +use gpui::{px, Div, Render}; use theme2::{default_color_scales, ColorScaleStep}; use ui::prelude::*; @@ -20,7 +20,7 @@ impl Render for ColorsStory { .flex_col() .gap_1() .overflow_y_scroll() - .text_color(gpui2::white()) + .text_color(gpui::white()) .children(color_scales.into_iter().map(|scale| { div() .flex() diff --git a/crates/storybook2/src/stories/focus.rs b/crates/storybook2/src/stories/focus.rs index aa71040b47..16c03f87d5 100644 --- a/crates/storybook2/src/stories/focus.rs +++ b/crates/storybook2/src/stories/focus.rs @@ -1,4 +1,4 @@ -use gpui2::{ +use gpui::{ div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render, StatefulInteraction, StatelessInteractive, Styled, View, VisualContext, WindowContext, }; @@ -33,7 +33,7 @@ impl FocusStory { impl Render for FocusStory { type Element = Div, FocusEnabled>; - fn render(&mut self, cx: &mut gpui2::ViewContext) -> Self::Element { + fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { let theme = cx.theme(); let color_1 = theme.styles.git.created; let color_2 = theme.styles.git.modified; diff --git a/crates/storybook2/src/stories/kitchen_sink.rs b/crates/storybook2/src/stories/kitchen_sink.rs index cf8277c4f4..54d6f2a3a9 100644 --- a/crates/storybook2/src/stories/kitchen_sink.rs +++ b/crates/storybook2/src/stories/kitchen_sink.rs @@ -1,5 +1,5 @@ use crate::{story::Story, story_selector::ComponentStory}; -use gpui2::{Div, Render, StatefulInteraction, View, VisualContext}; +use gpui::{Div, Render, StatefulInteraction, View, VisualContext}; use strum::IntoEnumIterator; use ui::prelude::*; diff --git a/crates/storybook2/src/stories/scroll.rs b/crates/storybook2/src/stories/scroll.rs index 9236629c34..cdb48603e0 100644 --- a/crates/storybook2/src/stories/scroll.rs +++ b/crates/storybook2/src/stories/scroll.rs @@ -1,4 +1,4 @@ -use gpui2::{ +use gpui::{ div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteraction, Styled, View, VisualContext, WindowContext, }; @@ -15,7 +15,7 @@ impl ScrollStory { impl Render for ScrollStory { type Element = Div>; - fn render(&mut self, cx: &mut gpui2::ViewContext) -> Self::Element { + fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { let theme = cx.theme(); let color_1 = theme.styles.git.created; let color_2 = theme.styles.git.modified; diff --git a/crates/storybook2/src/stories/text.rs b/crates/storybook2/src/stories/text.rs index 85a9fd51a6..b4a4c86e7e 100644 --- a/crates/storybook2/src/stories/text.rs +++ b/crates/storybook2/src/stories/text.rs @@ -1,4 +1,4 @@ -use gpui2::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext}; +use gpui::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext}; pub struct TextStory; @@ -11,7 +11,7 @@ impl TextStory { impl Render for TextStory { type Element = Div; - fn render(&mut self, cx: &mut gpui2::ViewContext) -> Self::Element { + fn render(&mut self, cx: &mut gpui::ViewContext) -> Self::Element { div().size_full().bg(white()).child(concat!( "The quick brown fox jumps over the lazy dog. ", "Meanwhile, the lazy dog decided it was time for a change. ", diff --git a/crates/storybook2/src/stories/z_index.rs b/crates/storybook2/src/stories/z_index.rs index c0e1456bc0..46ec0f4a35 100644 --- a/crates/storybook2/src/stories/z_index.rs +++ b/crates/storybook2/src/stories/z_index.rs @@ -1,4 +1,4 @@ -use gpui2::{px, rgb, Div, Hsla, Render}; +use gpui::{px, rgb, Div, Hsla, Render}; use ui::prelude::*; use crate::story::Story; diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 2adf2956d3..f59208ccb8 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -5,7 +5,7 @@ use crate::stories::*; use anyhow::anyhow; use clap::builder::PossibleValue; use clap::ValueEnum; -use gpui2::{AnyView, VisualContext}; +use gpui::{AnyView, VisualContext}; use strum::{EnumIter, EnumString, IntoEnumIterator}; use ui::prelude::*; use ui::{AvatarStory, ButtonStory, DetailsStory, IconStory, InputStory, LabelStory}; diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 4e2c439db0..c8849c1342 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -8,7 +8,7 @@ mod story_selector; use std::sync::Arc; use clap::Parser; -use gpui2::{ +use gpui::{ div, px, size, AnyView, AppContext, Bounds, Div, Render, ViewContext, VisualContext, WindowBounds, WindowOptions, }; @@ -22,7 +22,7 @@ use ui::prelude::*; use crate::assets::Assets; use crate::story_selector::StorySelector; -// gpui2::actions! { +// gpui::actions! { // storybook, // [ToggleInspector] // } @@ -51,7 +51,7 @@ fn main() { let theme_name = args.theme.unwrap_or("Zed Pro Moonlight".to_string()); let asset_source = Arc::new(Assets); - gpui2::App::production(asset_source).run(move |cx| { + gpui::App::production(asset_source).run(move |cx| { load_embedded_fonts(cx).unwrap(); let mut store = SettingsStore::default(); @@ -116,7 +116,7 @@ impl Render for StoryWrapper { } } -fn load_embedded_fonts(cx: &AppContext) -> gpui2::Result<()> { +fn load_embedded_fonts(cx: &AppContext) -> gpui::Result<()> { let font_paths = cx.asset_source().list("fonts")?; let mut embedded_fonts = Vec::new(); for font_path in font_paths { diff --git a/crates/terminal2/src/mappings/colors.rs b/crates/terminal2/src/mappings/colors.rs index fc3557b4e8..d3c8443cbf 100644 --- a/crates/terminal2/src/mappings/colors.rs +++ b/crates/terminal2/src/mappings/colors.rs @@ -1,6 +1,6 @@ // todo!() use alacritty_terminal::term::color::Rgb as AlacRgb; -// use gpui2::color::Color; +// use gpui::color::Color; // use theme2::TerminalStyle; ///Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent diff --git a/crates/ui2/Cargo.toml b/crates/ui2/Cargo.toml index f11fd652b6..754bca371f 100644 --- a/crates/ui2/Cargo.toml +++ b/crates/ui2/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] anyhow.workspace = true chrono = "0.4" -gpui2 = { path = "../gpui2" } +gpui = { package = "gpui2", path = "../gpui2" } itertools = { version = "0.11.0", optional = true } serde.workspace = true settings2 = { path = "../settings2" } diff --git a/crates/ui2/docs/hello-world.md b/crates/ui2/docs/hello-world.md index c6ded9ce34..e8ed3bb944 100644 --- a/crates/ui2/docs/hello-world.md +++ b/crates/ui2/docs/hello-world.md @@ -40,12 +40,12 @@ impl TodoList { All of this is relatively straightforward. -We use [gpui2::SharedString] in components instead of [std::string::String]. This allows us to [TODO: someone who actually knows please explain why we use SharedString]. +We use [gpui::SharedString] in components instead of [std::string::String]. This allows us to [TODO: someone who actually knows please explain why we use SharedString]. When we want to pass an action we pass a `ClickHandler`. Whenever we want to add an action, the struct it belongs to needs to be generic over the view type `V`. ~~~rust -use gpui2::hsla +use gpui::hsla impl TodoList { // ... @@ -74,7 +74,7 @@ As you start using the Tailwind-style conventions you will be surprised how quic **Why `50.0/360.0` in `hsla()`?** -gpui [gpui2::Hsla] use `0.0-1.0` for all it's values, but it is common for tools to use `0-360` for hue. +gpui [gpui::Hsla] use `0.0-1.0` for all it's values, but it is common for tools to use `0-360` for hue. This may change in the future, but this is a little trick that let's you use familiar looking values. @@ -98,7 +98,7 @@ impl TodoList { Now we have access to the complete set of colors defined in the theme. ~~~rust -use gpui2::hsla +use gpui::hsla impl TodoList { // ... @@ -113,7 +113,7 @@ impl TodoList { Let's finish up some basic styles for the container then move on to adding the other elements. ~~~rust -use gpui2::hsla +use gpui::hsla impl TodoList { // ... diff --git a/crates/ui2/src/components/avatar.rs b/crates/ui2/src/components/avatar.rs index ff574a2042..9c993dfc06 100644 --- a/crates/ui2/src/components/avatar.rs +++ b/crates/ui2/src/components/avatar.rs @@ -1,4 +1,4 @@ -use gpui2::img; +use gpui::img; use crate::prelude::*; @@ -33,7 +33,7 @@ impl Avatar { img.uri(self.src.clone()) .size_4() // todo!(Pull the avatar fallback background from the theme.) - .bg(gpui2::red()) + .bg(gpui::red()) } } @@ -44,7 +44,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct AvatarStory; diff --git a/crates/ui2/src/components/button.rs b/crates/ui2/src/components/button.rs index 178813dd5f..437daaa982 100644 --- a/crates/ui2/src/components/button.rs +++ b/crates/ui2/src/components/button.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use gpui2::{div, rems, DefiniteLength, Hsla, MouseButton, WindowContext}; +use gpui::{div, rems, DefiniteLength, Hsla, MouseButton, WindowContext}; use crate::{h_stack, Icon, IconColor, IconElement, Label, LabelColor, LineHeightStyle}; use crate::{prelude::*, IconButton}; @@ -234,7 +234,7 @@ pub use stories::*; mod stories { use super::*; use crate::{h_stack, v_stack, LabelColor, Story}; - use gpui2::{rems, Div, Render}; + use gpui::{rems, Div, Render}; use strum::IntoEnumIterator; pub struct ButtonStory; diff --git a/crates/ui2/src/components/checkbox.rs b/crates/ui2/src/components/checkbox.rs index 9632a66810..9f7c10a104 100644 --- a/crates/ui2/src/components/checkbox.rs +++ b/crates/ui2/src/components/checkbox.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use gpui2::{ +use gpui::{ div, Component, ElementId, ParentElement, StatefulInteractive, StatelessInteractive, Styled, ViewContext, }; @@ -175,7 +175,7 @@ pub use stories::*; mod stories { use super::*; use crate::{h_stack, Story}; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct CheckboxStory; diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index 87be445c19..117be12779 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -65,7 +65,7 @@ pub use stories::*; mod stories { use super::*; use crate::story::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct ContextMenuStory; diff --git a/crates/ui2/src/components/details.rs b/crates/ui2/src/components/details.rs index 1d22c81774..c7f6cc1839 100644 --- a/crates/ui2/src/components/details.rs +++ b/crates/ui2/src/components/details.rs @@ -47,7 +47,7 @@ pub use stories::*; mod stories { use super::*; use crate::{Button, Story}; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct DetailsStory; diff --git a/crates/ui2/src/components/facepile.rs b/crates/ui2/src/components/facepile.rs index 21dd848a28..efac4925f8 100644 --- a/crates/ui2/src/components/facepile.rs +++ b/crates/ui2/src/components/facepile.rs @@ -33,7 +33,7 @@ pub use stories::*; mod stories { use super::*; use crate::{static_players, Story}; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct FacepileStory; diff --git a/crates/ui2/src/components/icon.rs b/crates/ui2/src/components/icon.rs index fa1e1c6315..907f3f9187 100644 --- a/crates/ui2/src/components/icon.rs +++ b/crates/ui2/src/components/icon.rs @@ -1,4 +1,4 @@ -use gpui2::{rems, svg, Hsla}; +use gpui::{rems, svg, Hsla}; use strum::EnumIter; use crate::prelude::*; @@ -204,7 +204,7 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use strum::IntoEnumIterator; use crate::Story; diff --git a/crates/ui2/src/components/icon_button.rs b/crates/ui2/src/components/icon_button.rs index 8fe1a1fa9b..cb4fb4d7f0 100644 --- a/crates/ui2/src/components/icon_button.rs +++ b/crates/ui2/src/components/icon_button.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use gpui2::{rems, MouseButton}; +use gpui::{rems, MouseButton}; use crate::{h_stack, prelude::*}; use crate::{ClickHandler, Icon, IconColor, IconElement}; diff --git a/crates/ui2/src/components/indicator.rs b/crates/ui2/src/components/indicator.rs index 94398ab7f6..83030ebbee 100644 --- a/crates/ui2/src/components/indicator.rs +++ b/crates/ui2/src/components/indicator.rs @@ -1,4 +1,4 @@ -use gpui2::px; +use gpui::px; use crate::prelude::*; diff --git a/crates/ui2/src/components/input.rs b/crates/ui2/src/components/input.rs index f288f3ca56..e9f520346c 100644 --- a/crates/ui2/src/components/input.rs +++ b/crates/ui2/src/components/input.rs @@ -111,7 +111,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct InputStory; diff --git a/crates/ui2/src/components/keybinding.rs b/crates/ui2/src/components/keybinding.rs index 8b8fba8c08..22bbc747a2 100644 --- a/crates/ui2/src/components/keybinding.rs +++ b/crates/ui2/src/components/keybinding.rs @@ -158,7 +158,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use itertools::Itertools; pub struct KeybindingStory; diff --git a/crates/ui2/src/components/label.rs b/crates/ui2/src/components/label.rs index d1d4d6630c..dd078d2331 100644 --- a/crates/ui2/src/components/label.rs +++ b/crates/ui2/src/components/label.rs @@ -1,4 +1,4 @@ -use gpui2::{relative, rems, Hsla, WindowContext}; +use gpui::{relative, rems, Hsla, WindowContext}; use smallvec::SmallVec; use crate::prelude::*; @@ -194,7 +194,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct LabelStory; diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index b30beacd98..c11860e9a5 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -1,4 +1,4 @@ -use gpui2::div; +use gpui::div; use crate::settings::user_settings; use crate::{ diff --git a/crates/ui2/src/components/modal.rs b/crates/ui2/src/components/modal.rs index 26986474e0..75528b5c34 100644 --- a/crates/ui2/src/components/modal.rs +++ b/crates/ui2/src/components/modal.rs @@ -1,4 +1,4 @@ -use gpui2::AnyElement; +use gpui::AnyElement; use smallvec::SmallVec; use crate::{h_stack, prelude::*, v_stack, Button, Icon, IconButton, Label}; diff --git a/crates/ui2/src/components/notification_toast.rs b/crates/ui2/src/components/notification_toast.rs index e8739b925c..aeb2aa6ed9 100644 --- a/crates/ui2/src/components/notification_toast.rs +++ b/crates/ui2/src/components/notification_toast.rs @@ -1,4 +1,4 @@ -use gpui2::rems; +use gpui::rems; use crate::prelude::*; use crate::{h_stack, Icon}; diff --git a/crates/ui2/src/components/palette.rs b/crates/ui2/src/components/palette.rs index 269b39d86d..7f736433fc 100644 --- a/crates/ui2/src/components/palette.rs +++ b/crates/ui2/src/components/palette.rs @@ -153,13 +153,13 @@ impl PaletteItem { } } -use gpui2::ElementId; +use gpui::ElementId; #[cfg(feature = "stories")] pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use crate::{ModifierKeys, Story}; diff --git a/crates/ui2/src/components/panel.rs b/crates/ui2/src/components/panel.rs index ba88abb337..1762003a2c 100644 --- a/crates/ui2/src/components/panel.rs +++ b/crates/ui2/src/components/panel.rs @@ -1,4 +1,4 @@ -use gpui2::{AbsoluteLength, AnyElement}; +use gpui::{AbsoluteLength, AnyElement}; use smallvec::SmallVec; use crate::prelude::*; @@ -126,7 +126,7 @@ pub use stories::*; mod stories { use super::*; use crate::{Label, Story}; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct PanelStory; diff --git a/crates/ui2/src/components/player.rs b/crates/ui2/src/components/player.rs index b6c80400cf..dc034a5373 100644 --- a/crates/ui2/src/components/player.rs +++ b/crates/ui2/src/components/player.rs @@ -1,4 +1,4 @@ -use gpui2::{Hsla, ViewContext}; +use gpui::{Hsla, ViewContext}; use crate::prelude::*; diff --git a/crates/ui2/src/components/slot.rs b/crates/ui2/src/components/slot.rs index f980e2fbda..a672694dc5 100644 --- a/crates/ui2/src/components/slot.rs +++ b/crates/ui2/src/components/slot.rs @@ -1,4 +1,4 @@ -use gpui2::SharedString; +use gpui::SharedString; use crate::Icon; diff --git a/crates/ui2/src/components/stack.rs b/crates/ui2/src/components/stack.rs index 1b019ad4d9..d7bd0eb04f 100644 --- a/crates/ui2/src/components/stack.rs +++ b/crates/ui2/src/components/stack.rs @@ -1,4 +1,4 @@ -use gpui2::{div, Div}; +use gpui::{div, Div}; use crate::prelude::*; diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index 47de0541f1..416db2d172 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::{red, Div, ElementId, Render, View, VisualContext}; +use gpui::{red, Div, ElementId, Render, View, VisualContext}; #[derive(Component, Clone)] pub struct Tab { diff --git a/crates/ui2/src/components/toast.rs b/crates/ui2/src/components/toast.rs index 4ab6625dba..4164be2c3e 100644 --- a/crates/ui2/src/components/toast.rs +++ b/crates/ui2/src/components/toast.rs @@ -1,4 +1,4 @@ -use gpui2::AnyElement; +use gpui::AnyElement; use smallvec::SmallVec; use crate::prelude::*; @@ -70,7 +70,7 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use crate::{Label, Story}; diff --git a/crates/ui2/src/components/toggle.rs b/crates/ui2/src/components/toggle.rs index adde367581..368c95662f 100644 --- a/crates/ui2/src/components/toggle.rs +++ b/crates/ui2/src/components/toggle.rs @@ -1,4 +1,4 @@ -use gpui2::{div, Component, ParentElement}; +use gpui::{div, Component, ParentElement}; use crate::{Icon, IconColor, IconElement, IconSize}; diff --git a/crates/ui2/src/components/tooltip.rs b/crates/ui2/src/components/tooltip.rs index c05214eea4..8463092012 100644 --- a/crates/ui2/src/components/tooltip.rs +++ b/crates/ui2/src/components/tooltip.rs @@ -1,4 +1,4 @@ -use gpui2::{div, px, Div, ParentElement, Render, SharedString, Styled, ViewContext}; +use gpui::{div, px, Div, ParentElement, Render, SharedString, Styled, ViewContext}; use theme2::ActiveTheme; #[derive(Clone, Debug)] diff --git a/crates/ui2/src/prelude.rs b/crates/ui2/src/prelude.rs index 134b293d3d..c942f0aa3b 100644 --- a/crates/ui2/src/prelude.rs +++ b/crates/ui2/src/prelude.rs @@ -1,4 +1,4 @@ -pub use gpui2::{ +pub use gpui::{ div, Component, Element, ElementId, ParentElement, SharedString, StatefulInteractive, StatelessInteractive, Styled, ViewContext, WindowContext, }; @@ -7,7 +7,7 @@ pub use crate::elevation::*; pub use crate::ButtonVariant; pub use theme2::ActiveTheme; -use gpui2::Hsla; +use gpui::Hsla; use strum::EnumIter; #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] diff --git a/crates/ui2/src/settings.rs b/crates/ui2/src/settings.rs index 6a9426f623..c3ac78316e 100644 --- a/crates/ui2/src/settings.rs +++ b/crates/ui2/src/settings.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use gpui2::{rems, AbsoluteLength, AppContext, WindowContext}; +use gpui::{rems, AbsoluteLength, AppContext, WindowContext}; use crate::prelude::*; diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 5342e1fb16..ffdd3fee98 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use std::sync::Arc; use chrono::DateTime; -use gpui2::{AppContext, ViewContext}; +use gpui::{AppContext, ViewContext}; use rand::Rng; use theme2::ActiveTheme; diff --git a/crates/ui2/src/story.rs b/crates/ui2/src/story.rs index dea4e342b4..94e38267f4 100644 --- a/crates/ui2/src/story.rs +++ b/crates/ui2/src/story.rs @@ -1,4 +1,4 @@ -use gpui2::Div; +use gpui::Div; use crate::prelude::*; diff --git a/crates/ui2/src/to_extract/assistant_panel.rs b/crates/ui2/src/to_extract/assistant_panel.rs index 51c123ad9e..8a35757f5c 100644 --- a/crates/ui2/src/to_extract/assistant_panel.rs +++ b/crates/ui2/src/to_extract/assistant_panel.rs @@ -1,6 +1,6 @@ use crate::prelude::*; use crate::{Icon, IconButton, Label, Panel, PanelSide}; -use gpui2::{rems, AbsoluteLength}; +use gpui::{rems, AbsoluteLength}; #[derive(Component)] pub struct AssistantPanel { @@ -77,7 +77,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct AssistantPanelStory; impl Render for AssistantPanelStory { diff --git a/crates/ui2/src/to_extract/breadcrumb.rs b/crates/ui2/src/to_extract/breadcrumb.rs index 163dfabfb0..cd7df87646 100644 --- a/crates/ui2/src/to_extract/breadcrumb.rs +++ b/crates/ui2/src/to_extract/breadcrumb.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use crate::prelude::*; use crate::{h_stack, HighlightedText}; -use gpui2::Div; +use gpui::Div; #[derive(Clone)] pub struct Symbol(pub Vec); @@ -73,7 +73,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::Render; + use gpui::Render; use std::str::FromStr; pub struct BreadcrumbStory; diff --git a/crates/ui2/src/to_extract/buffer.rs b/crates/ui2/src/to_extract/buffer.rs index e12beff2fc..8ab435e994 100644 --- a/crates/ui2/src/to_extract/buffer.rs +++ b/crates/ui2/src/to_extract/buffer.rs @@ -1,4 +1,4 @@ -use gpui2::{Hsla, WindowContext}; +use gpui::{Hsla, WindowContext}; use crate::prelude::*; use crate::{h_stack, v_stack, Icon, IconElement}; @@ -235,7 +235,7 @@ mod stories { empty_buffer_example, hello_world_rust_buffer_example, hello_world_rust_buffer_with_status_example, Story, }; - use gpui2::{rems, Div, Render}; + use gpui::{rems, Div, Render}; pub struct BufferStory; diff --git a/crates/ui2/src/to_extract/buffer_search.rs b/crates/ui2/src/to_extract/buffer_search.rs index 02f689ca3e..9993cd3612 100644 --- a/crates/ui2/src/to_extract/buffer_search.rs +++ b/crates/ui2/src/to_extract/buffer_search.rs @@ -1,4 +1,4 @@ -use gpui2::{Div, Render, View, VisualContext}; +use gpui::{Div, Render, View, VisualContext}; use crate::prelude::*; use crate::{h_stack, Icon, IconButton, IconColor, Input}; diff --git a/crates/ui2/src/to_extract/chat_panel.rs b/crates/ui2/src/to_extract/chat_panel.rs index f4f1ddf433..538b5dfceb 100644 --- a/crates/ui2/src/to_extract/chat_panel.rs +++ b/crates/ui2/src/to_extract/chat_panel.rs @@ -108,7 +108,7 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { use chrono::DateTime; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use crate::{Panel, Story}; diff --git a/crates/ui2/src/to_extract/collab_panel.rs b/crates/ui2/src/to_extract/collab_panel.rs index 9d9dc861e2..7b785ae9e1 100644 --- a/crates/ui2/src/to_extract/collab_panel.rs +++ b/crates/ui2/src/to_extract/collab_panel.rs @@ -92,7 +92,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct CollabPanelStory; diff --git a/crates/ui2/src/to_extract/command_palette.rs b/crates/ui2/src/to_extract/command_palette.rs index 63db4359e7..8a9461c796 100644 --- a/crates/ui2/src/to_extract/command_palette.rs +++ b/crates/ui2/src/to_extract/command_palette.rs @@ -27,7 +27,7 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use crate::Story; diff --git a/crates/ui2/src/to_extract/copilot.rs b/crates/ui2/src/to_extract/copilot.rs index 51523d48f0..8750ab3c51 100644 --- a/crates/ui2/src/to_extract/copilot.rs +++ b/crates/ui2/src/to_extract/copilot.rs @@ -25,7 +25,7 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use crate::Story; diff --git a/crates/ui2/src/to_extract/editor_pane.rs b/crates/ui2/src/to_extract/editor_pane.rs index 8e54d2c2e8..fd21e81242 100644 --- a/crates/ui2/src/to_extract/editor_pane.rs +++ b/crates/ui2/src/to_extract/editor_pane.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use gpui2::{Div, Render, View, VisualContext}; +use gpui::{Div, Render, View, VisualContext}; use crate::prelude::*; use crate::{ diff --git a/crates/ui2/src/to_extract/language_selector.rs b/crates/ui2/src/to_extract/language_selector.rs index fa7f5b2bd7..694ca78e9c 100644 --- a/crates/ui2/src/to_extract/language_selector.rs +++ b/crates/ui2/src/to_extract/language_selector.rs @@ -40,7 +40,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct LanguageSelectorStory; diff --git a/crates/ui2/src/to_extract/multi_buffer.rs b/crates/ui2/src/to_extract/multi_buffer.rs index 1334703015..78a22d51d0 100644 --- a/crates/ui2/src/to_extract/multi_buffer.rs +++ b/crates/ui2/src/to_extract/multi_buffer.rs @@ -40,7 +40,7 @@ pub use stories::*; mod stories { use super::*; use crate::{hello_world_rust_buffer_example, Story}; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct MultiBufferStory; diff --git a/crates/ui2/src/to_extract/notifications_panel.rs b/crates/ui2/src/to_extract/notifications_panel.rs index 84794b71b2..b2cc4a7846 100644 --- a/crates/ui2/src/to_extract/notifications_panel.rs +++ b/crates/ui2/src/to_extract/notifications_panel.rs @@ -345,7 +345,7 @@ impl Notification { } use chrono::NaiveDateTime; -use gpui2::{px, Styled}; +use gpui::{px, Styled}; #[cfg(feature = "stories")] pub use stories::*; @@ -353,7 +353,7 @@ pub use stories::*; mod stories { use super::*; use crate::{Panel, Story}; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct NotificationsPanelStory; diff --git a/crates/ui2/src/to_extract/panes.rs b/crates/ui2/src/to_extract/panes.rs index a1e040b0ca..b57b77d5ee 100644 --- a/crates/ui2/src/to_extract/panes.rs +++ b/crates/ui2/src/to_extract/panes.rs @@ -1,4 +1,4 @@ -use gpui2::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size, View}; +use gpui::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size, View}; use smallvec::SmallVec; use crate::prelude::*; diff --git a/crates/ui2/src/to_extract/project_panel.rs b/crates/ui2/src/to_extract/project_panel.rs index a34a30bcbc..d4f5c72426 100644 --- a/crates/ui2/src/to_extract/project_panel.rs +++ b/crates/ui2/src/to_extract/project_panel.rs @@ -46,7 +46,7 @@ impl ProjectPanel { } } -use gpui2::ElementId; +use gpui::ElementId; #[cfg(feature = "stories")] pub use stories::*; @@ -54,7 +54,7 @@ pub use stories::*; mod stories { use super::*; use crate::{Panel, Story}; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct ProjectPanelStory; diff --git a/crates/ui2/src/to_extract/recent_projects.rs b/crates/ui2/src/to_extract/recent_projects.rs index d5a9dd1b22..3d4f551490 100644 --- a/crates/ui2/src/to_extract/recent_projects.rs +++ b/crates/ui2/src/to_extract/recent_projects.rs @@ -36,7 +36,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct RecentProjectsStory; diff --git a/crates/ui2/src/to_extract/tab_bar.rs b/crates/ui2/src/to_extract/tab_bar.rs index a128044183..aff095c639 100644 --- a/crates/ui2/src/to_extract/tab_bar.rs +++ b/crates/ui2/src/to_extract/tab_bar.rs @@ -92,7 +92,7 @@ impl TabBar { } } -use gpui2::ElementId; +use gpui::ElementId; #[cfg(feature = "stories")] pub use stories::*; @@ -100,7 +100,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct TabBarStory; diff --git a/crates/ui2/src/to_extract/terminal.rs b/crates/ui2/src/to_extract/terminal.rs index b912a59607..6c36f35152 100644 --- a/crates/ui2/src/to_extract/terminal.rs +++ b/crates/ui2/src/to_extract/terminal.rs @@ -1,4 +1,4 @@ -use gpui2::{relative, rems, Size}; +use gpui::{relative, rems, Size}; use crate::prelude::*; use crate::{Icon, IconButton, Pane, Tab}; @@ -83,7 +83,7 @@ pub use stories::*; mod stories { use super::*; use crate::Story; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; pub struct TerminalStory; impl Render for TerminalStory { diff --git a/crates/ui2/src/to_extract/theme_selector.rs b/crates/ui2/src/to_extract/theme_selector.rs index 5c67f1cd3e..7f911b50bf 100644 --- a/crates/ui2/src/to_extract/theme_selector.rs +++ b/crates/ui2/src/to_extract/theme_selector.rs @@ -39,7 +39,7 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use crate::Story; diff --git a/crates/ui2/src/to_extract/title_bar.rs b/crates/ui2/src/to_extract/title_bar.rs index 2fa201440a..87d7dd4146 100644 --- a/crates/ui2/src/to_extract/title_bar.rs +++ b/crates/ui2/src/to_extract/title_bar.rs @@ -1,7 +1,7 @@ use std::sync::atomic::AtomicBool; use std::sync::Arc; -use gpui2::{Div, Render, View, VisualContext}; +use gpui::{Div, Render, View, VisualContext}; use crate::prelude::*; use crate::settings::user_settings; diff --git a/crates/ui2/src/to_extract/toolbar.rs b/crates/ui2/src/to_extract/toolbar.rs index 0e3e7c259f..81918f34a7 100644 --- a/crates/ui2/src/to_extract/toolbar.rs +++ b/crates/ui2/src/to_extract/toolbar.rs @@ -1,4 +1,4 @@ -use gpui2::AnyElement; +use gpui::AnyElement; use smallvec::SmallVec; use crate::prelude::*; @@ -73,7 +73,7 @@ mod stories { use std::path::PathBuf; use std::str::FromStr; - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol}; diff --git a/crates/ui2/src/to_extract/traffic_lights.rs b/crates/ui2/src/to_extract/traffic_lights.rs index 677fae886c..245ff377f2 100644 --- a/crates/ui2/src/to_extract/traffic_lights.rs +++ b/crates/ui2/src/to_extract/traffic_lights.rs @@ -77,7 +77,7 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { - use gpui2::{Div, Render}; + use gpui::{Div, Render}; use crate::Story; diff --git a/crates/ui2/src/to_extract/workspace.rs b/crates/ui2/src/to_extract/workspace.rs index 77b9bc4539..d6de8a8288 100644 --- a/crates/ui2/src/to_extract/workspace.rs +++ b/crates/ui2/src/to_extract/workspace.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use chrono::DateTime; -use gpui2::{px, relative, Div, Render, Size, View, VisualContext}; +use gpui::{px, relative, Div, Render, Size, View, VisualContext}; use settings2::Settings; use theme2::ThemeSettings; @@ -373,7 +373,7 @@ pub use stories::*; #[cfg(feature = "stories")] mod stories { use super::*; - use gpui2::VisualContext; + use gpui::VisualContext; pub struct WorkspaceStory { workspace: View, diff --git a/crates/zed2/src/languages/language_plugin.rs b/crates/zed2/src/languages/language_plugin.rs index a160cca228..968cc819fd 100644 --- a/crates/zed2/src/languages/language_plugin.rs +++ b/crates/zed2/src/languages/language_plugin.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use collections::HashMap; use futures::lock::Mutex; -use gpui2::executor::Background; +use gpui::executor::Background; use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp2::LanguageServerBinary; use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn}; diff --git a/test.rs b/test.rs index 6553e633c8..4a7b9faa8f 100644 --- a/test.rs +++ b/test.rs @@ -410,7 +410,7 @@ mod components { themes::rose_pine, }; use gpui::{platform::MouseButton, ViewContext}; - use gpui2_macros::Element; + use gpui_macros::Element; use std::{marker::PhantomData, rc::Rc}; struct ButtonHandlers { click: Option)>>, @@ -535,7 +535,7 @@ mod element { platform::{MouseButton, MouseButtonEvent}, EngineLayout, EventContext, RenderContext, ViewContext, }; - use gpui2_macros::tailwind_lengths; + use gpui_macros::tailwind_lengths; use std::{ any::{Any, TypeId}, cell::Cell, @@ -4572,7 +4572,7 @@ mod frame { }; use anyhow::{anyhow, Result}; use gpui::LayoutNodeId; - use gpui2_macros::IntoElement; + use gpui_macros::IntoElement; #[element_crate = "crate"] pub struct Frame { style: OptionalStyle,