diff --git a/Cargo.lock b/Cargo.lock
index 4b2a74c297..71090e06e0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2531,6 +2531,7 @@ dependencies = [
"client",
"collections",
"editor",
+ "futures 0.3.28",
"gpui",
"language",
"log",
@@ -9802,7 +9803,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 acc8659117..9e1201e428 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -177,7 +177,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" }
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/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 a3d779531b..e794771434 100644
--- a/crates/diagnostics/src/diagnostics.rs
+++ b/crates/diagnostics/src/diagnostics.rs
@@ -2,8 +2,8 @@ pub mod items;
mod project_diagnostics_settings;
mod toolbar_controls;
-use anyhow::Result;
-use collections::{BTreeSet, HashSet};
+use anyhow::{Context, Result};
+use collections::{HashMap, HashSet};
use editor::{
diagnostic_block_renderer,
display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock},
@@ -11,9 +11,10 @@ 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, Task, View, ViewContext, ViewHandle, WeakViewHandle,
+ ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use language::{
Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection,
@@ -28,6 +29,7 @@ use std::{
any::{Any, TypeId},
borrow::Cow,
cmp::Ordering,
+ mem,
ops::Range,
path::PathBuf,
sync::Arc,
@@ -60,8 +62,10 @@ 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,
}
struct PathState {
@@ -125,9 +129,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(),
@@ -149,25 +156,30 @@ 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
+ .entry(*language_server_id)
+ .or_default()
+ .insert(path.clone());
+ 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);
+ }
+ }
+ _ => {}
+ });
let excerpts = cx.add_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
let editor = cx.add_view(|cx| {
@@ -176,19 +188,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 +204,10 @@ impl ProjectDiagnosticsEditor {
excerpts,
editor,
path_states: Default::default(),
- paths_to_update,
+ paths_to_update: HashMap::default(),
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 +227,7 @@ 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.paths_to_update = self.current_diagnostics.clone();
self.update_excerpts(None, cx);
cx.notify();
}
@@ -234,29 +238,93 @@ 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
+ 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 = if let Some(language_server_id) = language_server_id {
+ new_summaries.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());
+ false
+ } else {
+ 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 {
- true
+ 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) => {
+ 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));
+
+ 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 {
- for path in paths {
- 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)
- })?;
- }
- Result::<_, anyhow::Error>::Ok(())
+ let _: Vec<()> = 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
+ .context("rechecking diagnostics for paths")?;
+
+ 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 +627,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 {
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/items.rs b/crates/editor2/src/items.rs
index c439adcc44..a4d34ad36f 100644
--- a/crates/editor2/src/items.rs
+++ b/crates/editor2/src/items.rs
@@ -578,18 +578,24 @@ 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())
+ .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)),
+ )
})),
)
}
@@ -625,8 +631,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/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/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/app.rs b/crates/gpui2/src/app.rs
index 05ab7b2932..e016c193f2 100644
--- a/crates/gpui2/src/app.rs
+++ b/crates/gpui2/src/app.rs
@@ -165,6 +165,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,
@@ -223,6 +224,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,
@@ -1012,3 +1014,9 @@ pub(crate) struct AnyDrag {
pub view: AnyView,
pub cursor_offset: Point,
}
+
+#[derive(Clone)]
+pub(crate) struct AnyTooltip {
+ pub view: AnyView,
+ pub cursor_offset: Point,
+}
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..e011041bae 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,22 @@ 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(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);
}
- 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/geometry.rs b/crates/gpui2/src/geometry.rs
index b2fad4efda..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 }
}
@@ -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
diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs
index 020cb82cd2..da208b3813 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, Task, 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;
@@ -333,6 +336,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(Arc::new(move |view_state, cx| {
+ build_tooltip(view_state, cx).into()
+ }));
+
+ self
+ }
}
pub trait ElementInteraction: 'static {
@@ -568,6 +602,77 @@ 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.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.lock().is_none();
+ 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);
+ }
+ });
+ }
+
+ if let Some(tooltip_builder) = stateful.tooltip_builder.take() {
+ let active_tooltip = element_state.active_tooltip.clone();
+ let pending_mouse_down = element_state.pending_mouse_down.clone();
+
+ 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();
+ if !is_hovered {
+ active_tooltip.lock().take();
+ return;
+ }
+
+ if active_tooltip.lock().is_none() {
+ let task = cx.spawn({
+ let active_tooltip = active_tooltip.clone();
+ let tooltip_builder = tooltip_builder.clone();
+
+ 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();
+ }
+ });
+ 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.tooltip.is_some() {
+ cx.active_tooltip = active_tooltip.tooltip.clone()
+ }
+ }
+ }
+
let active_state = element_state.active_state.clone();
if active_state.lock().is_none() {
let active_group_bounds = stateful
@@ -639,6 +744,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 +773,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 +804,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,8 +857,16 @@ impl ActiveState {
#[derive(Default)]
pub struct InteractiveElementState {
active_state: Arc>,
+ hover_state: Arc>,
pending_mouse_down: Arc>>,
scroll_offset: Option>>>,
+ active_tooltip: Arc>>,
+}
+
+struct ActiveTooltip {
+ #[allow(unused)] // used to drop the task
+ waiting: Option>,
+ tooltip: Option,
}
impl InteractiveElementState {
@@ -1097,6 +1216,10 @@ pub type ClickListener = Box)
pub(crate) type DragListener =
Box, &mut ViewContext) -> AnyDrag + 'static>;
+pub(crate) type HoverListener = Box) + 'static>;
+
+pub(crate) type TooltipBuilder = Arc) -> AnyView + 'static>;
+
pub type KeyListener = Box<
dyn Fn(
&mut V,
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/style.rs b/crates/gpui2/src/style.rs
index b30d4aa002..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;
@@ -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]
@@ -98,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,
}
@@ -107,6 +113,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 +310,7 @@ impl Default for Style {
fn default() -> Self {
Style {
display: Display::Block,
+ visibility: Visibility::Visible,
overflow: Point {
x: Overflow::Visible,
y: Overflow::Visible,
@@ -328,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 1eed74f096..a272ab95b1 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,
+ self as gpui, 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;
@@ -60,6 +61,54 @@ 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
+ }
+
+ 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/subscription.rs b/crates/gpui2/src/subscription.rs
index 64fcd74dd2..2f4ec0d2f1 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()
+ .get_or_insert_with(|| 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));
}
}
}
diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs
index 056d4d334e..a722dca140 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 {
@@ -981,12 +987,27 @@ 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(active_tooltip.cursor_offset), |cx| {
+ let available_space =
+ size(AvailableSpace::MinContent, AvailableSpace::MinContent);
+ active_tooltip.view.draw(available_space, cx);
+ });
+ });
}
self.window.root_view = Some(root_view);
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/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/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
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 275b8e844d..1106e66f31 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 a78705c7bb..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};
@@ -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/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/theme2/src/colors.rs b/crates/theme2/src/colors.rs
index b02a9c14df..1a1fd2e99e 100644
--- a/crates/theme2/src/colors.rs
+++ b/crates/theme2/src/colors.rs
@@ -54,18 +54,20 @@ pub struct ThemeColors {
pub border: Hsla,
pub border_variant: Hsla,
pub border_focused: Hsla,
+ pub border_selected: Hsla,
pub border_transparent: Hsla,
- pub elevated_surface: Hsla,
- pub surface: Hsla,
+ pub border_disabled: 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,
@@ -80,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 802392d296..53e34acf16 100644
--- a/crates/theme2/src/default_colors.rs
+++ b/crates/theme2/src/default_colors.rs
@@ -205,18 +205,20 @@ 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(),
+ 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(),
@@ -231,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(),
}
}
@@ -250,18 +269,20 @@ 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(),
+ 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(),
@@ -276,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/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/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/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.rs b/crates/ui2/src/components.rs
index f7674c2bd4..857d0f1042 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;
@@ -16,13 +17,17 @@ mod palette;
mod panel;
mod player;
mod player_stack;
+mod slot;
mod stack;
mod tab;
mod toast;
+mod toggle;
mod tool_divider;
+mod tooltip;
pub use avatar::*;
pub use button::*;
+pub use checkbox::*;
pub use context_menu::*;
pub use details::*;
pub use facepile::*;
@@ -39,7 +44,10 @@ 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::*;
+pub use tooltip::*;
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 073bcdbb45..437daaa982 100644
--- a/crates/ui2/src/components/button.rs
+++ b/crates/ui2/src/components/button.rs
@@ -1,9 +1,28 @@
use std::sync::Arc;
-use gpui2::{div, rems, DefiniteLength, Hsla, MouseButton, WindowContext};
+use gpui::{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