mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-18 18:08:07 +03:00
Merge branch 'main' into add-collab-tests
This commit is contained in:
commit
3050c440f4
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -2531,6 +2531,7 @@ dependencies = [
|
|||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"editor",
|
"editor",
|
||||||
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
"language",
|
"language",
|
||||||
"log",
|
"log",
|
||||||
@ -9802,7 +9803,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tree-sitter-vue"
|
name = "tree-sitter-vue"
|
||||||
version = "0.0.1"
|
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 = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
|
@ -177,7 +177,7 @@ tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml",
|
|||||||
tree-sitter-lua = "0.0.14"
|
tree-sitter-lua = "0.0.14"
|
||||||
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
|
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-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]
|
[patch.crates-io]
|
||||||
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "35a6052fbcafc5e5fc0f9415b8652be7dcaf7222" }
|
tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "35a6052fbcafc5e5fc0f9415b8652be7dcaf7222" }
|
||||||
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
|
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
|
||||||
|
1
assets/icons/dash.svg
Normal file
1
assets/icons/dash.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-minus"><path d="M5 12h14"/></svg>
|
After Width: | Height: | Size: 229 B |
@ -22,6 +22,7 @@ workspace = { path = "../workspace" }
|
|||||||
|
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_derive.workspace = true
|
serde_derive.workspace = true
|
||||||
|
@ -2,8 +2,8 @@ pub mod items;
|
|||||||
mod project_diagnostics_settings;
|
mod project_diagnostics_settings;
|
||||||
mod toolbar_controls;
|
mod toolbar_controls;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use collections::{BTreeSet, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use editor::{
|
use editor::{
|
||||||
diagnostic_block_renderer,
|
diagnostic_block_renderer,
|
||||||
display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock},
|
display_map::{BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock},
|
||||||
@ -11,9 +11,10 @@ use editor::{
|
|||||||
scroll::autoscroll::Autoscroll,
|
scroll::autoscroll::Autoscroll,
|
||||||
Editor, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
Editor, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
||||||
};
|
};
|
||||||
|
use futures::future::try_join_all;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity,
|
actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity,
|
||||||
ModelHandle, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection,
|
Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection,
|
||||||
@ -28,6 +29,7 @@ use std::{
|
|||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
|
mem,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
@ -60,8 +62,10 @@ struct ProjectDiagnosticsEditor {
|
|||||||
summary: DiagnosticSummary,
|
summary: DiagnosticSummary,
|
||||||
excerpts: ModelHandle<MultiBuffer>,
|
excerpts: ModelHandle<MultiBuffer>,
|
||||||
path_states: Vec<PathState>,
|
path_states: Vec<PathState>,
|
||||||
paths_to_update: BTreeSet<(ProjectPath, LanguageServerId)>,
|
paths_to_update: HashMap<LanguageServerId, HashSet<ProjectPath>>,
|
||||||
|
current_diagnostics: HashMap<LanguageServerId, HashSet<ProjectPath>>,
|
||||||
include_warnings: bool,
|
include_warnings: bool,
|
||||||
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PathState {
|
struct PathState {
|
||||||
@ -125,9 +129,12 @@ impl View for ProjectDiagnosticsEditor {
|
|||||||
"summary": project.diagnostic_summary(cx),
|
"summary": project.diagnostic_summary(cx),
|
||||||
}),
|
}),
|
||||||
"summary": self.summary,
|
"summary": self.summary,
|
||||||
"paths_to_update": self.paths_to_update.iter().map(|(path, server_id)|
|
"paths_to_update": self.paths_to_update.iter().map(|(server_id, paths)|
|
||||||
(path.path.to_string_lossy(), server_id.0)
|
(server_id.0, paths.into_iter().map(|path| path.path.to_string_lossy()).collect::<Vec<_>>())
|
||||||
).collect::<Vec<_>>(),
|
).collect::<HashMap<_, _>>(),
|
||||||
|
"current_diagnostics": self.current_diagnostics.iter().map(|(server_id, paths)|
|
||||||
|
(server_id.0, paths.into_iter().map(|path| path.path.to_string_lossy()).collect::<Vec<_>>())
|
||||||
|
).collect::<HashMap<_, _>>(),
|
||||||
"paths_states": self.path_states.iter().map(|state|
|
"paths_states": self.path_states.iter().map(|state|
|
||||||
json!({
|
json!({
|
||||||
"path": state.path.path.to_string_lossy(),
|
"path": state.path.path.to_string_lossy(),
|
||||||
@ -149,25 +156,30 @@ impl ProjectDiagnosticsEditor {
|
|||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
cx.subscribe(&project_handle, |this, _, event, cx| match event {
|
let project_event_subscription =
|
||||||
project::Event::DiskBasedDiagnosticsFinished { language_server_id } => {
|
cx.subscribe(&project_handle, |this, _, event, cx| match event {
|
||||||
log::debug!("Disk based diagnostics finished for server {language_server_id}");
|
project::Event::DiskBasedDiagnosticsFinished { language_server_id } => {
|
||||||
this.update_excerpts(Some(*language_server_id), cx);
|
log::debug!("Disk based diagnostics finished for server {language_server_id}");
|
||||||
this.update_title(cx);
|
this.update_excerpts(Some(*language_server_id), cx);
|
||||||
}
|
}
|
||||||
project::Event::DiagnosticsUpdated {
|
project::Event::DiagnosticsUpdated {
|
||||||
language_server_id,
|
language_server_id,
|
||||||
path,
|
path,
|
||||||
} => {
|
} => {
|
||||||
log::debug!("Adding path {path:?} to update for server {language_server_id}");
|
log::debug!("Adding path {path:?} to update for server {language_server_id}");
|
||||||
this.paths_to_update
|
this.paths_to_update
|
||||||
.insert((path.clone(), *language_server_id));
|
.entry(*language_server_id)
|
||||||
this.update_excerpts(Some(*language_server_id), cx);
|
.or_default()
|
||||||
this.update_title(cx);
|
.insert(path.clone());
|
||||||
}
|
let no_multiselections = this.editor.update(cx, |editor, cx| {
|
||||||
_ => {}
|
editor.selections.all::<usize>(cx).len() <= 1
|
||||||
})
|
});
|
||||||
.detach();
|
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 excerpts = cx.add_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
|
||||||
let editor = cx.add_view(|cx| {
|
let editor = cx.add_view(|cx| {
|
||||||
@ -176,19 +188,14 @@ impl ProjectDiagnosticsEditor {
|
|||||||
editor.set_vertical_scroll_margin(5, cx);
|
editor.set_vertical_scroll_margin(5, cx);
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
cx.subscribe(&editor, |this, _, event, cx| {
|
let editor_event_subscription = cx.subscribe(&editor, |this, _, event, cx| {
|
||||||
cx.emit(event.clone());
|
cx.emit(event.clone());
|
||||||
if event == &editor::Event::Focused && this.path_states.is_empty() {
|
if event == &editor::Event::Focused && this.path_states.is_empty() {
|
||||||
cx.focus_self()
|
cx.focus_self()
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.detach();
|
|
||||||
|
|
||||||
let project = project_handle.read(cx);
|
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 summary = project.diagnostic_summary(cx);
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
project: project_handle,
|
project: project_handle,
|
||||||
@ -197,8 +204,10 @@ impl ProjectDiagnosticsEditor {
|
|||||||
excerpts,
|
excerpts,
|
||||||
editor,
|
editor,
|
||||||
path_states: Default::default(),
|
path_states: Default::default(),
|
||||||
paths_to_update,
|
paths_to_update: HashMap::default(),
|
||||||
include_warnings: settings::get::<ProjectDiagnosticsSettings>(cx).include_warnings,
|
include_warnings: settings::get::<ProjectDiagnosticsSettings>(cx).include_warnings,
|
||||||
|
current_diagnostics: HashMap::default(),
|
||||||
|
_subscriptions: vec![project_event_subscription, editor_event_subscription],
|
||||||
};
|
};
|
||||||
this.update_excerpts(None, cx);
|
this.update_excerpts(None, cx);
|
||||||
this
|
this
|
||||||
@ -218,12 +227,7 @@ impl ProjectDiagnosticsEditor {
|
|||||||
|
|
||||||
fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext<Self>) {
|
fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext<Self>) {
|
||||||
self.include_warnings = !self.include_warnings;
|
self.include_warnings = !self.include_warnings;
|
||||||
self.paths_to_update = self
|
self.paths_to_update = self.current_diagnostics.clone();
|
||||||
.project
|
|
||||||
.read(cx)
|
|
||||||
.diagnostic_summaries(cx)
|
|
||||||
.map(|(path, server_id, _)| (path, server_id))
|
|
||||||
.collect();
|
|
||||||
self.update_excerpts(None, cx);
|
self.update_excerpts(None, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
@ -234,29 +238,93 @@ impl ProjectDiagnosticsEditor {
|
|||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
log::debug!("Updating excerpts for server {language_server_id:?}");
|
log::debug!("Updating excerpts for server {language_server_id:?}");
|
||||||
let mut paths = Vec::new();
|
let mut paths_to_recheck = HashSet::default();
|
||||||
self.paths_to_update.retain(|(path, server_id)| {
|
let mut new_summaries: HashMap<LanguageServerId, HashSet<ProjectPath>> = self
|
||||||
if language_server_id
|
.project
|
||||||
.map_or(true, |language_server_id| language_server_id == *server_id)
|
.read(cx)
|
||||||
{
|
.diagnostic_summaries(cx)
|
||||||
paths.push(path.clone());
|
.fold(HashMap::default(), |mut summaries, (path, server_id, _)| {
|
||||||
false
|
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 {
|
} 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();
|
let project = self.project.clone();
|
||||||
cx.spawn(|this, mut cx| {
|
cx.spawn(|this, mut cx| {
|
||||||
async move {
|
async move {
|
||||||
for path in paths {
|
let _: Vec<()> = try_join_all(paths_to_recheck.into_iter().map(|path| {
|
||||||
let buffer = project
|
let mut cx = cx.clone();
|
||||||
.update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))
|
let project = project.clone();
|
||||||
.await?;
|
async move {
|
||||||
this.update(&mut cx, |this, cx| {
|
let buffer = project
|
||||||
this.populate_excerpts(path, language_server_id, buffer, cx)
|
.update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))
|
||||||
})?;
|
.await
|
||||||
}
|
.with_context(|| format!("opening buffer for path {path:?}"))?;
|
||||||
Result::<_, anyhow::Error>::Ok(())
|
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()
|
.log_err()
|
||||||
})
|
})
|
||||||
@ -559,11 +627,6 @@ impl ProjectDiagnosticsEditor {
|
|||||||
}
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_title(&mut self, cx: &mut ViewContext<Self>) {
|
|
||||||
self.summary = self.project.read(cx).diagnostic_summary(cx);
|
|
||||||
cx.emit(Event::TitleChanged);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item for ProjectDiagnosticsEditor {
|
impl Item for ProjectDiagnosticsEditor {
|
||||||
|
@ -37,8 +37,8 @@ use futures::FutureExt;
|
|||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, AnyElement, AppContext, BackgroundExecutor, Context, Div, Element, EventEmitter,
|
div, AnyElement, AppContext, BackgroundExecutor, Context, Div, Element, EventEmitter,
|
||||||
FocusHandle, Hsla, Model, Pixels, Render, Subscription, Task, TextStyle, View, ViewContext,
|
FocusHandle, Hsla, Model, Pixels, Render, Styled, Subscription, Task, TextStyle, View,
|
||||||
VisualContext, WeakView, WindowContext,
|
ViewContext, VisualContext, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||||
use hover_popover::{hide_hover, HoverState};
|
use hover_popover::{hide_hover, HoverState};
|
||||||
@ -68,6 +68,7 @@ use scroll::{
|
|||||||
use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
|
use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -8347,51 +8348,51 @@ impl Editor {
|
|||||||
// .text()
|
// .text()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
|
pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
|
||||||
// let mut wrap_guides = smallvec::smallvec![];
|
let mut wrap_guides = smallvec::smallvec![];
|
||||||
|
|
||||||
// if self.show_wrap_guides == Some(false) {
|
if self.show_wrap_guides == Some(false) {
|
||||||
// return wrap_guides;
|
return wrap_guides;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// let settings = self.buffer.read(cx).settings_at(0, cx);
|
let settings = self.buffer.read(cx).settings_at(0, cx);
|
||||||
// if settings.show_wrap_guides {
|
if settings.show_wrap_guides {
|
||||||
// if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
|
if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
|
||||||
// wrap_guides.push((soft_wrap as usize, true));
|
wrap_guides.push((soft_wrap as usize, true));
|
||||||
// }
|
}
|
||||||
// wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
|
wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
|
||||||
// }
|
}
|
||||||
|
|
||||||
// wrap_guides
|
wrap_guides
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
|
pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
|
||||||
// let settings = self.buffer.read(cx).settings_at(0, cx);
|
let settings = self.buffer.read(cx).settings_at(0, cx);
|
||||||
// let mode = self
|
let mode = self
|
||||||
// .soft_wrap_mode_override
|
.soft_wrap_mode_override
|
||||||
// .unwrap_or_else(|| settings.soft_wrap);
|
.unwrap_or_else(|| settings.soft_wrap);
|
||||||
// match mode {
|
match mode {
|
||||||
// language_settings::SoftWrap::None => SoftWrap::None,
|
language_settings::SoftWrap::None => SoftWrap::None,
|
||||||
// language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
|
language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
|
||||||
// language_settings::SoftWrap::PreferredLineLength => {
|
language_settings::SoftWrap::PreferredLineLength => {
|
||||||
// SoftWrap::Column(settings.preferred_line_length)
|
SoftWrap::Column(settings.preferred_line_length)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub fn set_soft_wrap_mode(
|
pub fn set_soft_wrap_mode(
|
||||||
// &mut self,
|
&mut self,
|
||||||
// mode: language_settings::SoftWrap,
|
mode: language_settings::SoftWrap,
|
||||||
// cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
// ) {
|
) {
|
||||||
// self.soft_wrap_mode_override = Some(mode);
|
self.soft_wrap_mode_override = Some(mode);
|
||||||
// cx.notify();
|
cx.notify();
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
|
pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
|
||||||
// self.display_map
|
self.display_map
|
||||||
// .update(cx, |map, cx| map.set_wrap_width(width, cx))
|
.update(cx, |map, cx| map.set_wrap_width(width, cx))
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
|
// pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
|
||||||
// if self.soft_wrap_mode_override.is_some() {
|
// if self.soft_wrap_mode_override.is_some() {
|
||||||
@ -9321,11 +9322,14 @@ impl EventEmitter for Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Render for Editor {
|
impl Render for Editor {
|
||||||
type Element = Div<Self>;
|
type Element = EditorElement;
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
// todo!()
|
EditorElement::new(EditorStyle {
|
||||||
div()
|
text: cx.text_style(),
|
||||||
|
line_height_scalar: 1.,
|
||||||
|
theme_id: 0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,17 +3,18 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::{BlockStyle, DisplaySnapshot},
|
display_map::{BlockStyle, DisplaySnapshot},
|
||||||
EditorStyle,
|
EditorMode, EditorStyle, SoftWrap,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
black, px, relative, AnyElement, Bounds, Element, Hsla, Line, Pixels, Size, Style, TextRun,
|
black, point, px, relative, size, AnyElement, Bounds, Element, Hsla, Line, Pixels, Size, Style,
|
||||||
TextSystem,
|
TextRun, TextSystem, ViewContext,
|
||||||
};
|
};
|
||||||
use language::{CursorShape, Selection};
|
use language::{CursorShape, Selection};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{ops::Range, sync::Arc};
|
use std::{cmp, ops::Range, sync::Arc};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
|
use theme::ActiveTheme;
|
||||||
|
|
||||||
enum FoldMarkers {}
|
enum FoldMarkers {}
|
||||||
|
|
||||||
@ -1321,29 +1322,31 @@ impl EditorElement {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// fn column_pixels(&self, column: usize, cx: &ViewContext<Editor>) -> f32 {
|
fn column_pixels(&self, column: usize, cx: &ViewContext<Editor>) -> Pixels {
|
||||||
// let style = &self.style;
|
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[0].width
|
||||||
// .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()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 {
|
fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> Pixels {
|
||||||
// let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
|
let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
|
||||||
// self.column_pixels(digit_count, cx)
|
self.column_pixels(digit_count, cx)
|
||||||
// }
|
}
|
||||||
|
|
||||||
//Folds contained in a hunk are ignored apart from shrinking visual size
|
//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
|
//If a fold contains any hunks then that fold line is marked as modified
|
||||||
@ -2002,6 +2005,7 @@ impl Element<Editor> for EditorElement {
|
|||||||
element_state: &mut Self::ElementState,
|
element_state: &mut Self::ElementState,
|
||||||
cx: &mut gpui::ViewContext<Editor>,
|
cx: &mut gpui::ViewContext<Editor>,
|
||||||
) -> gpui::LayoutId {
|
) -> gpui::LayoutId {
|
||||||
|
let rem_size = cx.rem_size();
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
style.size.width = relative(1.).into();
|
style.size.width = relative(1.).into();
|
||||||
style.size.height = relative(1.).into();
|
style.size.height = relative(1.).into();
|
||||||
@ -2011,18 +2015,125 @@ impl Element<Editor> for EditorElement {
|
|||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: Bounds<gpui::Pixels>,
|
bounds: Bounds<gpui::Pixels>,
|
||||||
view_state: &mut Editor,
|
editor: &mut Editor,
|
||||||
element_state: &mut Self::ElementState,
|
element_state: &mut Self::ElementState,
|
||||||
cx: &mut gpui::ViewContext<Editor>,
|
cx: &mut gpui::ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
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(
|
let snapshot = editor.snapshot(cx);
|
||||||
"hello world",
|
let style = self.style.clone();
|
||||||
text_style.font_size * cx.rem_size(),
|
let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
|
||||||
&[text_style.to_run("hello world".len())],
|
let font_size = style.text.font_size * cx.rem_size();
|
||||||
None,
|
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::<SmallVec<[_; 2]>>();
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,18 +578,24 @@ impl Item for Editor {
|
|||||||
|
|
||||||
fn tab_content<T: 'static>(&self, detail: Option<usize>, cx: &AppContext) -> AnyElement<T> {
|
fn tab_content<T: 'static>(&self, detail: Option<usize>, cx: &AppContext) -> AnyElement<T> {
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
|
|
||||||
AnyElement::new(
|
AnyElement::new(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_row()
|
.flex_row()
|
||||||
.items_center()
|
.items_center()
|
||||||
.bg(gpui::white())
|
.gap_2()
|
||||||
.text_color(gpui::white())
|
|
||||||
.child(self.title(cx).to_string())
|
.child(self.title(cx).to_string())
|
||||||
.children(detail.and_then(|detail| {
|
.children(detail.and_then(|detail| {
|
||||||
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
|
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
|
||||||
let description = path.to_string_lossy();
|
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<Self>) {
|
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let selection = self.selections.newest_anchor();
|
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<Self>) {
|
fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -303,20 +303,20 @@ impl Editor {
|
|||||||
self.scroll_manager.visible_line_count
|
self.scroll_manager.visible_line_count
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub(crate) fn set_visible_line_count(&mut self, lines: f32, cx: &mut ViewContext<Self>) {
|
pub(crate) fn set_visible_line_count(&mut self, lines: f32, cx: &mut ViewContext<Self>) {
|
||||||
// let opened_first_time = self.scroll_manager.visible_line_count.is_none();
|
let opened_first_time = self.scroll_manager.visible_line_count.is_none();
|
||||||
// self.scroll_manager.visible_line_count = Some(lines);
|
self.scroll_manager.visible_line_count = Some(lines);
|
||||||
// if opened_first_time {
|
if opened_first_time {
|
||||||
// cx.spawn(|editor, mut cx| async move {
|
cx.spawn(|editor, mut cx| async move {
|
||||||
// editor
|
editor
|
||||||
// .update(&mut cx, |editor, cx| {
|
.update(&mut cx, |editor, cx| {
|
||||||
// editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)
|
editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)
|
||||||
// })
|
})
|
||||||
// .ok()
|
.ok()
|
||||||
// })
|
})
|
||||||
// .detach()
|
.detach()
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn set_scroll_position(
|
pub fn set_scroll_position(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -48,11 +48,11 @@ impl AutoscrollStrategy {
|
|||||||
impl Editor {
|
impl Editor {
|
||||||
pub fn autoscroll_vertically(
|
pub fn autoscroll_vertically(
|
||||||
&mut self,
|
&mut self,
|
||||||
viewport_height: f32,
|
viewport_height: Pixels,
|
||||||
line_height: f32,
|
line_height: Pixels,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> bool {
|
) -> 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 display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
|
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
|
||||||
let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
|
let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
|
||||||
|
@ -20,6 +20,7 @@ fn generate_dispatch_bindings() {
|
|||||||
.header("src/platform/mac/dispatch.h")
|
.header("src/platform/mac/dispatch.h")
|
||||||
.allowlist_var("_dispatch_main_q")
|
.allowlist_var("_dispatch_main_q")
|
||||||
.allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
|
.allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
|
||||||
|
.allowlist_var("DISPATCH_TIME_NOW")
|
||||||
.allowlist_function("dispatch_get_global_queue")
|
.allowlist_function("dispatch_get_global_queue")
|
||||||
.allowlist_function("dispatch_async_f")
|
.allowlist_function("dispatch_async_f")
|
||||||
.allowlist_function("dispatch_after_f")
|
.allowlist_function("dispatch_after_f")
|
||||||
|
@ -165,6 +165,7 @@ pub struct AppContext {
|
|||||||
flushing_effects: bool,
|
flushing_effects: bool,
|
||||||
pending_updates: usize,
|
pending_updates: usize,
|
||||||
pub(crate) active_drag: Option<AnyDrag>,
|
pub(crate) active_drag: Option<AnyDrag>,
|
||||||
|
pub(crate) active_tooltip: Option<AnyTooltip>,
|
||||||
pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
|
pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
|
||||||
pub(crate) frame_consumers: HashMap<DisplayId, Task<()>>,
|
pub(crate) frame_consumers: HashMap<DisplayId, Task<()>>,
|
||||||
pub(crate) background_executor: BackgroundExecutor,
|
pub(crate) background_executor: BackgroundExecutor,
|
||||||
@ -223,6 +224,7 @@ impl AppContext {
|
|||||||
flushing_effects: false,
|
flushing_effects: false,
|
||||||
pending_updates: 0,
|
pending_updates: 0,
|
||||||
active_drag: None,
|
active_drag: None,
|
||||||
|
active_tooltip: None,
|
||||||
next_frame_callbacks: HashMap::default(),
|
next_frame_callbacks: HashMap::default(),
|
||||||
frame_consumers: HashMap::default(),
|
frame_consumers: HashMap::default(),
|
||||||
background_executor: executor,
|
background_executor: executor,
|
||||||
@ -1012,3 +1014,9 @@ pub(crate) struct AnyDrag {
|
|||||||
pub view: AnyView,
|
pub view: AnyView,
|
||||||
pub cursor_offset: Point<Pixels>,
|
pub cursor_offset: Point<Pixels>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct AnyTooltip {
|
||||||
|
pub view: AnyView,
|
||||||
|
pub cursor_offset: Point<Pixels>,
|
||||||
|
}
|
||||||
|
@ -212,6 +212,19 @@ pub trait Component<V> {
|
|||||||
{
|
{
|
||||||
self.map(|this| if condition { then(this) } else { this })
|
self.map(|this| if condition { then(this) } else { this })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.map(|this| {
|
||||||
|
if let Some(value) = option {
|
||||||
|
then(this, value)
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> Component<V> for AnyElement<V> {
|
impl<V> Component<V> for AnyElement<V> {
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
ElementInteraction, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable,
|
ElementInteraction, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable,
|
||||||
GlobalElementId, GroupBounds, InteractiveElementState, LayoutId, Overflow, ParentElement,
|
GlobalElementId, GroupBounds, InteractiveElementState, LayoutId, Overflow, ParentElement,
|
||||||
Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
|
Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
|
||||||
StatelessInteractive, Style, StyleRefinement, Styled, ViewContext,
|
StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, Visibility,
|
||||||
};
|
};
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -249,11 +249,22 @@ where
|
|||||||
cx: &mut ViewContext<V>,
|
cx: &mut ViewContext<V>,
|
||||||
) {
|
) {
|
||||||
self.with_element_id(cx, |this, _global_id, cx| {
|
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() {
|
if let Some(group) = this.group.clone() {
|
||||||
GroupBounds::push(group, bounds, cx);
|
GroupBounds::push(group, bounds, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let style = this.compute_style(bounds, element_state, cx);
|
|
||||||
let z_index = style.z_index.unwrap_or(0);
|
let z_index = style.z_index.unwrap_or(0);
|
||||||
|
|
||||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||||
|
@ -21,7 +21,7 @@ pub fn point<T: Clone + Debug + Default>(x: T, y: T) -> Point<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + Debug + Default> Point<T> {
|
impl<T: Clone + Debug + Default> Point<T> {
|
||||||
pub fn new(x: T, y: T) -> Self {
|
pub const fn new(x: T, y: T) -> Self {
|
||||||
Self { x, y }
|
Self { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -825,6 +825,12 @@ impl From<Pixels> for u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Pixels {
|
||||||
|
fn from(pixels: u32) -> Self {
|
||||||
|
Pixels(pixels as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Pixels> for usize {
|
impl From<Pixels> for usize {
|
||||||
fn from(pixels: Pixels) -> Self {
|
fn from(pixels: Pixels) -> Self {
|
||||||
pixels.0 as usize
|
pixels.0 as usize
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
div, point, px, Action, AnyDrag, AnyView, AppContext, BorrowWindow, Bounds, Component,
|
div, point, px, Action, AnyDrag, AnyTooltip, AnyView, AppContext, BorrowWindow, Bounds,
|
||||||
DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch, Keystroke,
|
Component, DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch,
|
||||||
Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style, StyleRefinement, View,
|
Keystroke, Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style,
|
||||||
ViewContext,
|
StyleRefinement, Task, View, ViewContext,
|
||||||
};
|
};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
@ -17,9 +17,12 @@ use std::{
|
|||||||
ops::Deref,
|
ops::Deref,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DRAG_THRESHOLD: f64 = 2.;
|
const DRAG_THRESHOLD: f64 = 2.;
|
||||||
|
const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
|
||||||
|
const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
|
||||||
|
|
||||||
pub trait StatelessInteractive<V: 'static>: Element<V> {
|
pub trait StatelessInteractive<V: 'static>: Element<V> {
|
||||||
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V>;
|
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V>;
|
||||||
@ -333,6 +336,37 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
|
|||||||
}));
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> 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<W>(
|
||||||
|
mut self,
|
||||||
|
build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + '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<V: 'static>: 'static {
|
pub trait ElementInteraction<V: 'static>: 'static {
|
||||||
@ -568,6 +602,77 @@ pub trait ElementInteraction<V: 'static>: '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();
|
let active_state = element_state.active_state.clone();
|
||||||
if active_state.lock().is_none() {
|
if active_state.lock().is_none() {
|
||||||
let active_group_bounds = stateful
|
let active_group_bounds = stateful
|
||||||
@ -639,6 +744,8 @@ pub struct StatefulInteraction<V> {
|
|||||||
active_style: StyleRefinement,
|
active_style: StyleRefinement,
|
||||||
group_active_style: Option<GroupStyle>,
|
group_active_style: Option<GroupStyle>,
|
||||||
drag_listener: Option<DragListener<V>>,
|
drag_listener: Option<DragListener<V>>,
|
||||||
|
hover_listener: Option<HoverListener<V>>,
|
||||||
|
tooltip_builder: Option<TooltipBuilder<V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static> ElementInteraction<V> for StatefulInteraction<V> {
|
impl<V: 'static> ElementInteraction<V> for StatefulInteraction<V> {
|
||||||
@ -666,6 +773,8 @@ impl<V> From<ElementId> for StatefulInteraction<V> {
|
|||||||
stateless: StatelessInteraction::default(),
|
stateless: StatelessInteraction::default(),
|
||||||
click_listeners: SmallVec::new(),
|
click_listeners: SmallVec::new(),
|
||||||
drag_listener: None,
|
drag_listener: None,
|
||||||
|
hover_listener: None,
|
||||||
|
tooltip_builder: None,
|
||||||
active_style: StyleRefinement::default(),
|
active_style: StyleRefinement::default(),
|
||||||
group_active_style: None,
|
group_active_style: None,
|
||||||
}
|
}
|
||||||
@ -695,6 +804,8 @@ impl<V> StatelessInteraction<V> {
|
|||||||
stateless: self,
|
stateless: self,
|
||||||
click_listeners: SmallVec::new(),
|
click_listeners: SmallVec::new(),
|
||||||
drag_listener: None,
|
drag_listener: None,
|
||||||
|
hover_listener: None,
|
||||||
|
tooltip_builder: None,
|
||||||
active_style: StyleRefinement::default(),
|
active_style: StyleRefinement::default(),
|
||||||
group_active_style: None,
|
group_active_style: None,
|
||||||
}
|
}
|
||||||
@ -746,8 +857,16 @@ impl ActiveState {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct InteractiveElementState {
|
pub struct InteractiveElementState {
|
||||||
active_state: Arc<Mutex<ActiveState>>,
|
active_state: Arc<Mutex<ActiveState>>,
|
||||||
|
hover_state: Arc<Mutex<bool>>,
|
||||||
pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
|
pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
|
||||||
scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
|
scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
|
||||||
|
active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ActiveTooltip {
|
||||||
|
#[allow(unused)] // used to drop the task
|
||||||
|
waiting: Option<Task<()>>,
|
||||||
|
tooltip: Option<AnyTooltip>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InteractiveElementState {
|
impl InteractiveElementState {
|
||||||
@ -1097,6 +1216,10 @@ pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>)
|
|||||||
pub(crate) type DragListener<V> =
|
pub(crate) type DragListener<V> =
|
||||||
Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
|
Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
|
||||||
|
|
||||||
|
pub(crate) type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
|
||||||
|
|
||||||
|
pub(crate) type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
|
||||||
|
|
||||||
pub type KeyListener<V> = Box<
|
pub type KeyListener<V> = Box<
|
||||||
dyn Fn(
|
dyn Fn(
|
||||||
&mut V,
|
&mut V,
|
||||||
|
@ -11,11 +11,7 @@ use objc::{
|
|||||||
};
|
};
|
||||||
use parking::{Parker, Unparker};
|
use parking::{Parker, Unparker};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{ffi::c_void, sync::Arc, time::Duration};
|
||||||
ffi::c_void,
|
|
||||||
sync::Arc,
|
|
||||||
time::{Duration, SystemTime},
|
|
||||||
};
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
|
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
|
||||||
|
|
||||||
@ -62,16 +58,10 @@ impl PlatformDispatcher for MacDispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_after(&self, duration: Duration, runnable: Runnable) {
|
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 {
|
unsafe {
|
||||||
let queue =
|
let queue =
|
||||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0);
|
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(
|
dispatch_after_f(
|
||||||
when,
|
when,
|
||||||
queue,
|
queue,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
|
black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
|
||||||
Corners, CornersRefinement, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures,
|
Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
|
||||||
FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems, Result, Rgba,
|
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems,
|
||||||
SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext, WindowContext,
|
Result, Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
use refineable::{Cascade, Refineable};
|
use refineable::{Cascade, Refineable};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -19,6 +19,9 @@ pub struct Style {
|
|||||||
/// What layout strategy should be used?
|
/// What layout strategy should be used?
|
||||||
pub display: Display,
|
pub display: Display,
|
||||||
|
|
||||||
|
/// Should the element be painted on screen?
|
||||||
|
pub visibility: Visibility,
|
||||||
|
|
||||||
// Overflow properties
|
// Overflow properties
|
||||||
/// How children overflowing their container should affect layout
|
/// How children overflowing their container should affect layout
|
||||||
#[refineable]
|
#[refineable]
|
||||||
@ -98,6 +101,9 @@ pub struct Style {
|
|||||||
/// TEXT
|
/// TEXT
|
||||||
pub text: TextStyleRefinement,
|
pub text: TextStyleRefinement,
|
||||||
|
|
||||||
|
/// The mouse cursor style shown when the mouse pointer is over an element.
|
||||||
|
pub mouse_cursor: Option<CursorStyle>,
|
||||||
|
|
||||||
pub z_index: Option<u32>,
|
pub z_index: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +113,13 @@ impl Styled for StyleRefinement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Visibility {
|
||||||
|
#[default]
|
||||||
|
Visible,
|
||||||
|
Hidden,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BoxShadow {
|
pub struct BoxShadow {
|
||||||
pub color: Hsla,
|
pub color: Hsla,
|
||||||
@ -297,6 +310,7 @@ impl Default for Style {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Style {
|
Style {
|
||||||
display: Display::Block,
|
display: Display::Block,
|
||||||
|
visibility: Visibility::Visible,
|
||||||
overflow: Point {
|
overflow: Point {
|
||||||
x: Overflow::Visible,
|
x: Overflow::Visible,
|
||||||
y: Overflow::Visible,
|
y: Overflow::Visible,
|
||||||
@ -328,6 +342,7 @@ impl Default for Style {
|
|||||||
corner_radii: Corners::default(),
|
corner_radii: Corners::default(),
|
||||||
box_shadow: Default::default(),
|
box_shadow: Default::default(),
|
||||||
text: TextStyleRefinement::default(),
|
text: TextStyleRefinement::default(),
|
||||||
|
mouse_cursor: None,
|
||||||
z_index: None,
|
z_index: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
self as gpui2, hsla, point, px, relative, rems, AlignItems, DefiniteLength, Display, Fill,
|
self as gpui, hsla, point, px, relative, rems, AlignItems, CursorStyle, DefiniteLength,
|
||||||
FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString, StyleRefinement,
|
Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString,
|
||||||
|
StyleRefinement, Visibility,
|
||||||
};
|
};
|
||||||
use crate::{BoxShadow, TextStyleRefinement};
|
use crate::{BoxShadow, TextStyleRefinement};
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
@ -60,6 +61,54 @@ pub trait Styled {
|
|||||||
self
|
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`.
|
/// Sets the flex direction of the element to `column`.
|
||||||
/// [Docs](https://tailwindcss.com/docs/flex-direction#column)
|
/// [Docs](https://tailwindcss.com/docs/flex-direction#column)
|
||||||
fn flex_col(mut self) -> Self
|
fn flex_col(mut self) -> Self
|
||||||
|
@ -14,7 +14,7 @@ impl<EmitterKey, Callback> Clone for SubscriberSet<EmitterKey, Callback> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct SubscriberSetState<EmitterKey, Callback> {
|
struct SubscriberSetState<EmitterKey, Callback> {
|
||||||
subscribers: BTreeMap<EmitterKey, BTreeMap<usize, Callback>>,
|
subscribers: BTreeMap<EmitterKey, Option<BTreeMap<usize, Callback>>>,
|
||||||
dropped_subscribers: BTreeSet<(EmitterKey, usize)>,
|
dropped_subscribers: BTreeSet<(EmitterKey, usize)>,
|
||||||
next_subscriber_id: usize,
|
next_subscriber_id: usize,
|
||||||
}
|
}
|
||||||
@ -38,12 +38,18 @@ where
|
|||||||
lock.subscribers
|
lock.subscribers
|
||||||
.entry(emitter_key.clone())
|
.entry(emitter_key.clone())
|
||||||
.or_default()
|
.or_default()
|
||||||
|
.get_or_insert_with(|| Default::default())
|
||||||
.insert(subscriber_id, callback);
|
.insert(subscriber_id, callback);
|
||||||
let this = self.0.clone();
|
let this = self.0.clone();
|
||||||
Subscription {
|
Subscription {
|
||||||
unsubscribe: Some(Box::new(move || {
|
unsubscribe: Some(Box::new(move || {
|
||||||
let mut lock = this.lock();
|
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);
|
subscribers.remove(&subscriber_id);
|
||||||
if subscribers.is_empty() {
|
if subscribers.is_empty() {
|
||||||
lock.subscribers.remove(&emitter_key);
|
lock.subscribers.remove(&emitter_key);
|
||||||
@ -62,34 +68,43 @@ where
|
|||||||
|
|
||||||
pub fn remove(&self, emitter: &EmitterKey) -> impl IntoIterator<Item = Callback> {
|
pub fn remove(&self, emitter: &EmitterKey) -> impl IntoIterator<Item = Callback> {
|
||||||
let subscribers = self.0.lock().subscribers.remove(&emitter);
|
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<F>(&self, emitter: &EmitterKey, mut f: F)
|
pub fn retain<F>(&self, emitter: &EmitterKey, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut Callback) -> bool,
|
F: FnMut(&mut Callback) -> bool,
|
||||||
{
|
{
|
||||||
let entry = self.0.lock().subscribers.remove_entry(emitter);
|
let Some(mut subscribers) = self
|
||||||
if let Some((emitter, mut subscribers)) = entry {
|
.0
|
||||||
subscribers.retain(|_, callback| f(callback));
|
.lock()
|
||||||
let mut lock = 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.
|
subscribers.retain(|_, callback| f(callback));
|
||||||
if let Some(new_subscribers) = lock.subscribers.remove(&emitter) {
|
let mut lock = self.0.lock();
|
||||||
subscribers.extend(new_subscribers);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove any dropped subscriptions that were dropped while invoking the callback.
|
// Add any new subscribers that were added while invoking the callback.
|
||||||
for (dropped_emitter, dropped_subscription_id) in
|
if let Some(Some(new_subscribers)) = lock.subscribers.remove(&emitter) {
|
||||||
mem::take(&mut lock.dropped_subscribers)
|
subscribers.extend(new_subscribers);
|
||||||
{
|
}
|
||||||
debug_assert_eq!(emitter, dropped_emitter);
|
|
||||||
subscribers.remove(&dropped_subscription_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !subscribers.is_empty() {
|
// Remove any dropped subscriptions that were dropped while invoking the callback.
|
||||||
lock.subscribers.insert(emitter, subscribers);
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
|
px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
|
||||||
Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect,
|
Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DispatchContext, DisplayId,
|
||||||
Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId,
|
Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId,
|
||||||
Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
|
GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
|
||||||
Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent,
|
KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
|
||||||
MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformWindow, Point,
|
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay,
|
||||||
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
|
PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams,
|
||||||
RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet,
|
RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
|
||||||
Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext,
|
Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View,
|
||||||
WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
|
VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
@ -190,6 +190,7 @@ pub struct Window {
|
|||||||
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
|
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
|
||||||
default_prevented: bool,
|
default_prevented: bool,
|
||||||
mouse_position: Point<Pixels>,
|
mouse_position: Point<Pixels>,
|
||||||
|
requested_cursor_style: Option<CursorStyle>,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
bounds: WindowBounds,
|
bounds: WindowBounds,
|
||||||
bounds_observers: SubscriberSet<(), AnyObserver>,
|
bounds_observers: SubscriberSet<(), AnyObserver>,
|
||||||
@ -283,6 +284,7 @@ impl Window {
|
|||||||
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
||||||
default_prevented: true,
|
default_prevented: true,
|
||||||
mouse_position,
|
mouse_position,
|
||||||
|
requested_cursor_style: None,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
bounds,
|
bounds,
|
||||||
bounds_observers: SubscriberSet::new(),
|
bounds_observers: SubscriberSet::new(),
|
||||||
@ -669,6 +671,10 @@ impl<'a> WindowContext<'a> {
|
|||||||
self.window.mouse_position
|
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
|
/// 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`.
|
/// z-index is interpreted relative to the previous call to `stack`.
|
||||||
pub fn stack<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
|
pub fn stack<R>(&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);
|
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);
|
self.window.root_view = Some(root_view);
|
||||||
let scene = self.window.scene_builder.build();
|
let scene = self.window.scene_builder.build();
|
||||||
|
|
||||||
self.window.platform_window.draw(scene);
|
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;
|
self.window.dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
|||||||
let (_, ty_generics, _) = ast.generics.split_for_impl();
|
let (_, ty_generics, _) = ast.generics.split_for_impl();
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
impl #impl_generics gpui2::Component<#view_type> for #name #ty_generics #where_clause {
|
impl #impl_generics gpui::Component<#view_type> for #name #ty_generics #where_clause {
|
||||||
fn render(self) -> gpui2::AnyElement<#view_type> {
|
fn render(self) -> gpui::AnyElement<#view_type> {
|
||||||
(move |view_state: &mut #view_type, cx: &mut gpui2::ViewContext<'_, #view_type>| self.render(view_state, cx))
|
(move |view_state: &mut #view_type, cx: &mut gpui::ViewContext<'_, #view_type>| self.render(view_state, cx))
|
||||||
.render()
|
.render()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ fn generate_predefined_setter(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|field_tokens| {
|
.map(|field_tokens| {
|
||||||
quote! {
|
quote! {
|
||||||
style.#field_tokens = Some((#negation_token gpui2::#length_tokens).into());
|
style.#field_tokens = Some((#negation_token gpui::#length_tokens).into());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -163,7 +163,7 @@ fn generate_custom_value_setter(
|
|||||||
|
|
||||||
let method = quote! {
|
let method = quote! {
|
||||||
#[doc = #doc_string]
|
#[doc = #doc_string]
|
||||||
fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui2::#length_type>) -> Self where Self: std::marker::Sized {
|
fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self where Self: std::marker::Sized {
|
||||||
let style = self.style();
|
let style = self.style();
|
||||||
#(#field_assignments)*
|
#(#field_assignments)*
|
||||||
self
|
self
|
||||||
|
@ -170,6 +170,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
|
|||||||
#max_retries,
|
#max_retries,
|
||||||
#detect_nondeterminism,
|
#detect_nondeterminism,
|
||||||
&mut |cx, foreground_platform, deterministic, seed| {
|
&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_vars
|
||||||
cx.foreground().run(#inner_fn_name(#inner_fn_args));
|
cx.foreground().run(#inner_fn_name(#inner_fn_args));
|
||||||
#cx_teardowns
|
#cx_teardowns
|
||||||
@ -247,6 +249,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
|
|||||||
#max_retries,
|
#max_retries,
|
||||||
#detect_nondeterminism,
|
#detect_nondeterminism,
|
||||||
&mut |cx, foreground_platform, deterministic, seed| {
|
&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_vars
|
||||||
#inner_fn_name(#inner_fn_args);
|
#inner_fn_name(#inner_fn_args);
|
||||||
#cx_teardowns
|
#cx_teardowns
|
||||||
|
@ -16,14 +16,14 @@ name = "test_app"
|
|||||||
test-support = [
|
test-support = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"collections/test-support",
|
"collections/test-support",
|
||||||
"gpui2/test-support",
|
"gpui/test-support",
|
||||||
"live_kit_server",
|
"live_kit_server",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
collections = { path = "../collections", optional = true }
|
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 }
|
live_kit_server = { path = "../live_kit_server", optional = true }
|
||||||
media = { path = "../media" }
|
media = { path = "../media" }
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ nanoid = { version ="0.4", optional = true}
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
collections = { path = "../collections", features = ["test-support"] }
|
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" }
|
live_kit_server = { path = "../live_kit_server" }
|
||||||
media = { path = "../media" }
|
media = { path = "../media" }
|
||||||
nanoid = "0.4"
|
nanoid = "0.4"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui2::KeyBinding;
|
use gpui::KeyBinding;
|
||||||
use live_kit_client2::{
|
use live_kit_client2::{
|
||||||
LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room,
|
LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room,
|
||||||
};
|
};
|
||||||
@ -16,7 +16,7 @@ struct Quit;
|
|||||||
fn main() {
|
fn main() {
|
||||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
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"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
println!("USING TEST LIVEKIT");
|
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();
|
cx.quit();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use collections::{BTreeMap, HashMap};
|
use collections::{BTreeMap, HashMap};
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use gpui2::BackgroundExecutor;
|
use gpui::BackgroundExecutor;
|
||||||
use live_kit_server::token;
|
use live_kit_server::token;
|
||||||
use media::core_video::CVImageBuffer;
|
use media::core_video::CVImageBuffer;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -14,7 +14,7 @@ anyhow.workspace = true
|
|||||||
backtrace-on-stack-overflow = "0.3.0"
|
backtrace-on-stack-overflow = "0.3.0"
|
||||||
clap = { version = "4.4", features = ["derive", "string"] }
|
clap = { version = "4.4", features = ["derive", "string"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
gpui2 = { path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
itertools = "0.11.0"
|
itertools = "0.11.0"
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
rust-embed.workspace = true
|
rust-embed.workspace = true
|
||||||
@ -29,4 +29,4 @@ ui = { package = "ui2", path = "../ui2", features = ["stories"] }
|
|||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
gpui2 = { path = "../gpui2", features = ["test-support"] }
|
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use gpui2::{AssetSource, SharedString};
|
use gpui::{AssetSource, SharedString};
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::story::Story;
|
use crate::story::Story;
|
||||||
use gpui2::{px, Div, Render};
|
use gpui::{px, Div, Render};
|
||||||
use theme2::{default_color_scales, ColorScaleStep};
|
use theme2::{default_color_scales, ColorScaleStep};
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ impl Render for ColorsStory {
|
|||||||
.flex_col()
|
.flex_col()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.overflow_y_scroll()
|
.overflow_y_scroll()
|
||||||
.text_color(gpui2::white())
|
.text_color(gpui::white())
|
||||||
.children(color_scales.into_iter().map(|scale| {
|
.children(color_scales.into_iter().map(|scale| {
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{
|
use gpui::{
|
||||||
div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render, StatefulInteraction,
|
div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render, StatefulInteraction,
|
||||||
StatelessInteractive, Styled, View, VisualContext, WindowContext,
|
StatelessInteractive, Styled, View, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
@ -33,7 +33,7 @@ impl FocusStory {
|
|||||||
impl Render for FocusStory {
|
impl Render for FocusStory {
|
||||||
type Element = Div<Self, StatefulInteraction<Self>, FocusEnabled<Self>>;
|
type Element = Div<Self, StatefulInteraction<Self>, FocusEnabled<Self>>;
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut gpui2::ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
let color_1 = theme.styles.git.created;
|
let color_1 = theme.styles.git.created;
|
||||||
let color_2 = theme.styles.git.modified;
|
let color_2 = theme.styles.git.modified;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{story::Story, story_selector::ComponentStory};
|
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 strum::IntoEnumIterator;
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{
|
use gpui::{
|
||||||
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteraction, Styled,
|
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteraction, Styled,
|
||||||
View, VisualContext, WindowContext,
|
View, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
@ -15,7 +15,7 @@ impl ScrollStory {
|
|||||||
impl Render for ScrollStory {
|
impl Render for ScrollStory {
|
||||||
type Element = Div<Self, StatefulInteraction<Self>>;
|
type Element = Div<Self, StatefulInteraction<Self>>;
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut gpui2::ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
let color_1 = theme.styles.git.created;
|
let color_1 = theme.styles.git.created;
|
||||||
let color_2 = theme.styles.git.modified;
|
let color_2 = theme.styles.git.modified;
|
||||||
|
@ -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;
|
pub struct TextStory;
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ impl TextStory {
|
|||||||
impl Render for TextStory {
|
impl Render for TextStory {
|
||||||
type Element = Div<Self>;
|
type Element = Div<Self>;
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut gpui2::ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||||
div().size_full().bg(white()).child(concat!(
|
div().size_full().bg(white()).child(concat!(
|
||||||
"The quick brown fox jumps over the lazy dog. ",
|
"The quick brown fox jumps over the lazy dog. ",
|
||||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{px, rgb, Div, Hsla, Render};
|
use gpui::{px, rgb, Div, Hsla, Render};
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
|
|
||||||
use crate::story::Story;
|
use crate::story::Story;
|
||||||
|
@ -5,7 +5,7 @@ use crate::stories::*;
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use clap::builder::PossibleValue;
|
use clap::builder::PossibleValue;
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use gpui2::{AnyView, VisualContext};
|
use gpui::{AnyView, VisualContext};
|
||||||
use strum::{EnumIter, EnumString, IntoEnumIterator};
|
use strum::{EnumIter, EnumString, IntoEnumIterator};
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use ui::{AvatarStory, ButtonStory, DetailsStory, IconStory, InputStory, LabelStory};
|
use ui::{AvatarStory, ButtonStory, DetailsStory, IconStory, InputStory, LabelStory};
|
||||||
@ -19,6 +19,7 @@ pub enum ComponentStory {
|
|||||||
Buffer,
|
Buffer,
|
||||||
Button,
|
Button,
|
||||||
ChatPanel,
|
ChatPanel,
|
||||||
|
Checkbox,
|
||||||
CollabPanel,
|
CollabPanel,
|
||||||
Colors,
|
Colors,
|
||||||
CommandPalette,
|
CommandPalette,
|
||||||
@ -61,6 +62,7 @@ impl ComponentStory {
|
|||||||
Self::Buffer => cx.build_view(|_| ui::BufferStory).into(),
|
Self::Buffer => cx.build_view(|_| ui::BufferStory).into(),
|
||||||
Self::Button => cx.build_view(|_| ButtonStory).into(),
|
Self::Button => cx.build_view(|_| ButtonStory).into(),
|
||||||
Self::ChatPanel => cx.build_view(|_| ui::ChatPanelStory).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::CollabPanel => cx.build_view(|_| ui::CollabPanelStory).into(),
|
||||||
Self::Colors => cx.build_view(|_| ColorsStory).into(),
|
Self::Colors => cx.build_view(|_| ColorsStory).into(),
|
||||||
Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into(),
|
Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into(),
|
||||||
|
@ -8,7 +8,7 @@ mod story_selector;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use gpui2::{
|
use gpui::{
|
||||||
div, px, size, AnyView, AppContext, Bounds, Div, Render, ViewContext, VisualContext,
|
div, px, size, AnyView, AppContext, Bounds, Div, Render, ViewContext, VisualContext,
|
||||||
WindowBounds, WindowOptions,
|
WindowBounds, WindowOptions,
|
||||||
};
|
};
|
||||||
@ -22,7 +22,7 @@ use ui::prelude::*;
|
|||||||
use crate::assets::Assets;
|
use crate::assets::Assets;
|
||||||
use crate::story_selector::StorySelector;
|
use crate::story_selector::StorySelector;
|
||||||
|
|
||||||
// gpui2::actions! {
|
// gpui::actions! {
|
||||||
// storybook,
|
// storybook,
|
||||||
// [ToggleInspector]
|
// [ToggleInspector]
|
||||||
// }
|
// }
|
||||||
@ -51,7 +51,7 @@ fn main() {
|
|||||||
let theme_name = args.theme.unwrap_or("Zed Pro Moonlight".to_string());
|
let theme_name = args.theme.unwrap_or("Zed Pro Moonlight".to_string());
|
||||||
|
|
||||||
let asset_source = Arc::new(Assets);
|
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();
|
load_embedded_fonts(cx).unwrap();
|
||||||
|
|
||||||
let mut store = SettingsStore::default();
|
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 font_paths = cx.asset_source().list("fonts")?;
|
||||||
let mut embedded_fonts = Vec::new();
|
let mut embedded_fonts = Vec::new();
|
||||||
for font_path in font_paths {
|
for font_path in font_paths {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// todo!()
|
// todo!()
|
||||||
use alacritty_terminal::term::color::Rgb as AlacRgb;
|
use alacritty_terminal::term::color::Rgb as AlacRgb;
|
||||||
// use gpui2::color::Color;
|
// use gpui::color::Color;
|
||||||
// use theme2::TerminalStyle;
|
// use theme2::TerminalStyle;
|
||||||
|
|
||||||
///Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent
|
///Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent
|
||||||
|
@ -54,18 +54,20 @@ pub struct ThemeColors {
|
|||||||
pub border: Hsla,
|
pub border: Hsla,
|
||||||
pub border_variant: Hsla,
|
pub border_variant: Hsla,
|
||||||
pub border_focused: Hsla,
|
pub border_focused: Hsla,
|
||||||
|
pub border_selected: Hsla,
|
||||||
pub border_transparent: Hsla,
|
pub border_transparent: Hsla,
|
||||||
pub elevated_surface: Hsla,
|
pub border_disabled: Hsla,
|
||||||
pub surface: Hsla,
|
pub elevated_surface_background: Hsla,
|
||||||
|
pub surface_background: Hsla,
|
||||||
pub background: Hsla,
|
pub background: Hsla,
|
||||||
pub element: Hsla,
|
pub element_background: Hsla,
|
||||||
pub element_hover: Hsla,
|
pub element_hover: Hsla,
|
||||||
pub element_active: Hsla,
|
pub element_active: Hsla,
|
||||||
pub element_selected: Hsla,
|
pub element_selected: Hsla,
|
||||||
pub element_disabled: Hsla,
|
pub element_disabled: Hsla,
|
||||||
pub element_placeholder: Hsla,
|
pub element_placeholder: Hsla,
|
||||||
pub element_drop_target: Hsla,
|
pub element_drop_target: Hsla,
|
||||||
pub ghost_element: Hsla,
|
pub ghost_element_background: Hsla,
|
||||||
pub ghost_element_hover: Hsla,
|
pub ghost_element_hover: Hsla,
|
||||||
pub ghost_element_active: Hsla,
|
pub ghost_element_active: Hsla,
|
||||||
pub ghost_element_selected: Hsla,
|
pub ghost_element_selected: Hsla,
|
||||||
@ -80,15 +82,32 @@ pub struct ThemeColors {
|
|||||||
pub icon_disabled: Hsla,
|
pub icon_disabled: Hsla,
|
||||||
pub icon_placeholder: Hsla,
|
pub icon_placeholder: Hsla,
|
||||||
pub icon_accent: Hsla,
|
pub icon_accent: Hsla,
|
||||||
pub status_bar: Hsla,
|
pub status_bar_background: Hsla,
|
||||||
pub title_bar: Hsla,
|
pub title_bar_background: Hsla,
|
||||||
pub toolbar: Hsla,
|
pub toolbar_background: Hsla,
|
||||||
pub tab_bar: Hsla,
|
pub tab_bar_background: Hsla,
|
||||||
pub tab_inactive: Hsla,
|
pub tab_inactive_background: Hsla,
|
||||||
pub tab_active: Hsla,
|
pub tab_active_background: Hsla,
|
||||||
pub editor: Hsla,
|
pub editor_background: Hsla,
|
||||||
pub editor_subheader: Hsla,
|
pub editor_subheader_background: Hsla,
|
||||||
pub editor_active_line: 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)]
|
#[derive(Refineable, Clone)]
|
||||||
|
@ -205,18 +205,20 @@ impl ThemeColors {
|
|||||||
border: neutral().light().step_6(),
|
border: neutral().light().step_6(),
|
||||||
border_variant: neutral().light().step_5(),
|
border_variant: neutral().light().step_5(),
|
||||||
border_focused: blue().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,
|
border_transparent: system.transparent,
|
||||||
elevated_surface: neutral().light().step_2(),
|
elevated_surface_background: neutral().light().step_2(),
|
||||||
surface: neutral().light().step_2(),
|
surface_background: neutral().light().step_2(),
|
||||||
background: neutral().light().step_1(),
|
background: neutral().light().step_1(),
|
||||||
element: neutral().light().step_3(),
|
element_background: neutral().light().step_3(),
|
||||||
element_hover: neutral().light().step_4(),
|
element_hover: neutral().light().step_4(),
|
||||||
element_active: neutral().light().step_5(),
|
element_active: neutral().light().step_5(),
|
||||||
element_selected: neutral().light().step_5(),
|
element_selected: neutral().light().step_5(),
|
||||||
element_disabled: neutral().light_alpha().step_3(),
|
element_disabled: neutral().light_alpha().step_3(),
|
||||||
element_placeholder: neutral().light().step_11(),
|
element_placeholder: neutral().light().step_11(),
|
||||||
element_drop_target: blue().light_alpha().step_2(),
|
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_hover: neutral().light().step_4(),
|
||||||
ghost_element_active: neutral().light().step_5(),
|
ghost_element_active: neutral().light().step_5(),
|
||||||
ghost_element_selected: neutral().light().step_5(),
|
ghost_element_selected: neutral().light().step_5(),
|
||||||
@ -231,15 +233,32 @@ impl ThemeColors {
|
|||||||
icon_disabled: neutral().light().step_9(),
|
icon_disabled: neutral().light().step_9(),
|
||||||
icon_placeholder: neutral().light().step_10(),
|
icon_placeholder: neutral().light().step_10(),
|
||||||
icon_accent: blue().light().step_11(),
|
icon_accent: blue().light().step_11(),
|
||||||
status_bar: neutral().light().step_2(),
|
status_bar_background: neutral().light().step_2(),
|
||||||
title_bar: neutral().light().step_2(),
|
title_bar_background: neutral().light().step_2(),
|
||||||
toolbar: neutral().light().step_1(),
|
toolbar_background: neutral().light().step_1(),
|
||||||
tab_bar: neutral().light().step_2(),
|
tab_bar_background: neutral().light().step_2(),
|
||||||
tab_active: neutral().light().step_1(),
|
tab_active_background: neutral().light().step_1(),
|
||||||
tab_inactive: neutral().light().step_2(),
|
tab_inactive_background: neutral().light().step_2(),
|
||||||
editor: neutral().light().step_1(),
|
editor_background: neutral().light().step_1(),
|
||||||
editor_subheader: neutral().light().step_2(),
|
editor_subheader_background: neutral().light().step_2(),
|
||||||
editor_active_line: neutral().light_alpha().step_3(),
|
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: neutral().dark().step_6(),
|
||||||
border_variant: neutral().dark().step_5(),
|
border_variant: neutral().dark().step_5(),
|
||||||
border_focused: blue().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,
|
border_transparent: system.transparent,
|
||||||
elevated_surface: neutral().dark().step_2(),
|
elevated_surface_background: neutral().dark().step_2(),
|
||||||
surface: neutral().dark().step_2(),
|
surface_background: neutral().dark().step_2(),
|
||||||
background: neutral().dark().step_1(),
|
background: neutral().dark().step_1(),
|
||||||
element: neutral().dark().step_3(),
|
element_background: neutral().dark().step_3(),
|
||||||
element_hover: neutral().dark().step_4(),
|
element_hover: neutral().dark().step_4(),
|
||||||
element_active: neutral().dark().step_5(),
|
element_active: neutral().dark().step_5(),
|
||||||
element_selected: neutral().dark().step_5(),
|
element_selected: neutral().dark().step_5(),
|
||||||
element_disabled: neutral().dark_alpha().step_3(),
|
element_disabled: neutral().dark_alpha().step_3(),
|
||||||
element_placeholder: neutral().dark().step_11(),
|
element_placeholder: neutral().dark().step_11(),
|
||||||
element_drop_target: blue().dark_alpha().step_2(),
|
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_hover: neutral().dark().step_4(),
|
||||||
ghost_element_active: neutral().dark().step_5(),
|
ghost_element_active: neutral().dark().step_5(),
|
||||||
ghost_element_selected: neutral().dark().step_5(),
|
ghost_element_selected: neutral().dark().step_5(),
|
||||||
@ -276,15 +297,32 @@ impl ThemeColors {
|
|||||||
icon_disabled: neutral().dark().step_9(),
|
icon_disabled: neutral().dark().step_9(),
|
||||||
icon_placeholder: neutral().dark().step_10(),
|
icon_placeholder: neutral().dark().step_10(),
|
||||||
icon_accent: blue().dark().step_11(),
|
icon_accent: blue().dark().step_11(),
|
||||||
status_bar: neutral().dark().step_2(),
|
status_bar_background: neutral().dark().step_2(),
|
||||||
title_bar: neutral().dark().step_2(),
|
title_bar_background: neutral().dark().step_2(),
|
||||||
toolbar: neutral().dark().step_1(),
|
toolbar_background: neutral().dark().step_1(),
|
||||||
tab_bar: neutral().dark().step_2(),
|
tab_bar_background: neutral().dark().step_2(),
|
||||||
tab_active: neutral().dark().step_1(),
|
tab_active_background: neutral().dark().step_1(),
|
||||||
tab_inactive: neutral().dark().step_2(),
|
tab_inactive_background: neutral().dark().step_2(),
|
||||||
editor: neutral().dark().step_1(),
|
editor_background: neutral().dark().step_1(),
|
||||||
editor_subheader: neutral().dark().step_2(),
|
editor_subheader_background: neutral().dark().step_2(),
|
||||||
editor_active_line: neutral().dark_alpha().step_3(),
|
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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ pub use syntax::*;
|
|||||||
|
|
||||||
use gpui::{AppContext, Hsla, SharedString};
|
use gpui::{AppContext, Hsla, SharedString};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum Appearance {
|
pub enum Appearance {
|
||||||
Light,
|
Light,
|
||||||
Dark,
|
Dark,
|
||||||
|
0
crates/theme2/src/themes/.gitkeep
Normal file
0
crates/theme2/src/themes/.gitkeep
Normal file
@ -7,7 +7,7 @@ publish = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
gpui2 = { path = "../gpui2" }
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
itertools = { version = "0.11.0", optional = true }
|
itertools = { version = "0.11.0", optional = true }
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
settings2 = { path = "../settings2" }
|
settings2 = { path = "../settings2" }
|
||||||
|
@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
## Common patterns
|
## Common patterns
|
||||||
|
|
||||||
|
### Method ordering
|
||||||
|
|
||||||
|
- id
|
||||||
|
- Flex properties
|
||||||
|
- Position properties
|
||||||
|
- Size properties
|
||||||
|
- Style properties
|
||||||
|
- Handlers
|
||||||
|
- State properties
|
||||||
|
|
||||||
### Using the Label Component to Create UI Text
|
### 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.
|
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.
|
||||||
|
@ -40,12 +40,12 @@ impl<V: 'static> TodoList<V> {
|
|||||||
|
|
||||||
All of this is relatively straightforward.
|
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`.
|
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
|
~~~rust
|
||||||
use gpui2::hsla
|
use gpui::hsla
|
||||||
|
|
||||||
impl<V: 'static> TodoList<V> {
|
impl<V: 'static> TodoList<V> {
|
||||||
// ...
|
// ...
|
||||||
@ -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()`?**
|
**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.
|
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<V: 'static> TodoList<V> {
|
|||||||
Now we have access to the complete set of colors defined in the theme.
|
Now we have access to the complete set of colors defined in the theme.
|
||||||
|
|
||||||
~~~rust
|
~~~rust
|
||||||
use gpui2::hsla
|
use gpui::hsla
|
||||||
|
|
||||||
impl<V: 'static> TodoList<V> {
|
impl<V: 'static> TodoList<V> {
|
||||||
// ...
|
// ...
|
||||||
@ -113,7 +113,7 @@ impl<V: 'static> TodoList<V> {
|
|||||||
Let's finish up some basic styles for the container then move on to adding the other elements.
|
Let's finish up some basic styles for the container then move on to adding the other elements.
|
||||||
|
|
||||||
~~~rust
|
~~~rust
|
||||||
use gpui2::hsla
|
use gpui::hsla
|
||||||
|
|
||||||
impl<V: 'static> TodoList<V> {
|
impl<V: 'static> TodoList<V> {
|
||||||
// ...
|
// ...
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
mod avatar;
|
mod avatar;
|
||||||
mod button;
|
mod button;
|
||||||
|
mod checkbox;
|
||||||
mod context_menu;
|
mod context_menu;
|
||||||
mod details;
|
mod details;
|
||||||
mod facepile;
|
mod facepile;
|
||||||
@ -16,13 +17,17 @@ mod palette;
|
|||||||
mod panel;
|
mod panel;
|
||||||
mod player;
|
mod player;
|
||||||
mod player_stack;
|
mod player_stack;
|
||||||
|
mod slot;
|
||||||
mod stack;
|
mod stack;
|
||||||
mod tab;
|
mod tab;
|
||||||
mod toast;
|
mod toast;
|
||||||
|
mod toggle;
|
||||||
mod tool_divider;
|
mod tool_divider;
|
||||||
|
mod tooltip;
|
||||||
|
|
||||||
pub use avatar::*;
|
pub use avatar::*;
|
||||||
pub use button::*;
|
pub use button::*;
|
||||||
|
pub use checkbox::*;
|
||||||
pub use context_menu::*;
|
pub use context_menu::*;
|
||||||
pub use details::*;
|
pub use details::*;
|
||||||
pub use facepile::*;
|
pub use facepile::*;
|
||||||
@ -39,7 +44,10 @@ pub use palette::*;
|
|||||||
pub use panel::*;
|
pub use panel::*;
|
||||||
pub use player::*;
|
pub use player::*;
|
||||||
pub use player_stack::*;
|
pub use player_stack::*;
|
||||||
|
pub use slot::*;
|
||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
pub use tab::*;
|
pub use tab::*;
|
||||||
pub use toast::*;
|
pub use toast::*;
|
||||||
|
pub use toggle::*;
|
||||||
pub use tool_divider::*;
|
pub use tool_divider::*;
|
||||||
|
pub use tooltip::*;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::img;
|
use gpui::img;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ impl Avatar {
|
|||||||
img.uri(self.src.clone())
|
img.uri(self.src.clone())
|
||||||
.size_4()
|
.size_4()
|
||||||
// todo!(Pull the avatar fallback background from the theme.)
|
// todo!(Pull the avatar fallback background from the theme.)
|
||||||
.bg(gpui2::red())
|
.bg(gpui::red())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct AvatarStory;
|
pub struct AvatarStory;
|
||||||
|
|
||||||
|
@ -1,9 +1,28 @@
|
|||||||
use std::sync::Arc;
|
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::{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<V: 'static> {
|
||||||
|
Button(Button<V>),
|
||||||
|
IconButton(IconButton<V>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> From<Button<V>> for ButtonOrIconButton<V> {
|
||||||
|
fn from(value: Button<V>) -> Self {
|
||||||
|
Self::Button(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> From<IconButton<V>> for ButtonOrIconButton<V> {
|
||||||
|
fn from(value: IconButton<V>) -> Self {
|
||||||
|
Self::IconButton(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone, Copy)]
|
#[derive(Default, PartialEq, Clone, Copy)]
|
||||||
pub enum IconPosition {
|
pub enum IconPosition {
|
||||||
@ -22,8 +41,8 @@ pub enum ButtonVariant {
|
|||||||
impl ButtonVariant {
|
impl ButtonVariant {
|
||||||
pub fn bg_color(&self, cx: &mut WindowContext) -> Hsla {
|
pub fn bg_color(&self, cx: &mut WindowContext) -> Hsla {
|
||||||
match self {
|
match self {
|
||||||
ButtonVariant::Ghost => cx.theme().colors().ghost_element,
|
ButtonVariant::Ghost => cx.theme().colors().ghost_element_background,
|
||||||
ButtonVariant::Filled => cx.theme().colors().element,
|
ButtonVariant::Filled => cx.theme().colors().element_background,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +61,7 @@ impl ButtonVariant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ClickHandler<S> = Arc<dyn Fn(&mut S, &mut ViewContext<S>) + Send + Sync>;
|
pub type ClickHandler<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) + Send + Sync>;
|
||||||
|
|
||||||
struct ButtonHandlers<V: 'static> {
|
struct ButtonHandlers<V: 'static> {
|
||||||
click: Option<ClickHandler<V>>,
|
click: Option<ClickHandler<V>>,
|
||||||
@ -215,7 +234,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{h_stack, v_stack, LabelColor, Story};
|
use crate::{h_stack, v_stack, LabelColor, Story};
|
||||||
use gpui2::{rems, Div, Render};
|
use gpui::{rems, Div, Render};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
pub struct ButtonStory;
|
pub struct ButtonStory;
|
||||||
|
229
crates/ui2/src/components/checkbox.rs
Normal file
229
crates/ui2/src/components/checkbox.rs
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use gpui::{
|
||||||
|
div, Component, ElementId, ParentElement, StatefulInteractive, StatelessInteractive, Styled,
|
||||||
|
ViewContext,
|
||||||
|
};
|
||||||
|
use theme2::ActiveTheme;
|
||||||
|
|
||||||
|
use crate::{Icon, IconColor, IconElement, Selection};
|
||||||
|
|
||||||
|
pub type CheckHandler<V> = Arc<dyn Fn(Selection, &mut V, &mut ViewContext<V>) + 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<V: 'static> {
|
||||||
|
id: ElementId,
|
||||||
|
checked: Selection,
|
||||||
|
disabled: bool,
|
||||||
|
on_click: Option<CheckHandler<V>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> Checkbox<V> {
|
||||||
|
pub fn new(id: impl Into<ElementId>, checked: Selection) -> Self {
|
||||||
|
Self {
|
||||||
|
id: id.into(),
|
||||||
|
checked,
|
||||||
|
disabled: false,
|
||||||
|
on_click: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disabled(mut self, disabled: bool) -> Self {
|
||||||
|
self.disabled = disabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_click(
|
||||||
|
mut self,
|
||||||
|
handler: impl 'static + Fn(Selection, &mut V, &mut ViewContext<V>) + Send + Sync,
|
||||||
|
) -> Self {
|
||||||
|
self.on_click = Some(Arc::new(handler));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
|
let group_id = format!("checkbox_group_{:?}", self.id);
|
||||||
|
|
||||||
|
let icon = match self.checked {
|
||||||
|
// When selected, we show a checkmark.
|
||||||
|
Selection::Selected => {
|
||||||
|
Some(
|
||||||
|
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.
|
||||||
|
Selection::Indeterminate => {
|
||||||
|
Some(
|
||||||
|
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.
|
||||||
|
Selection::Unselected => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 == Selection::Selected || self.checked == Selection::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_background,
|
||||||
|
cx.theme().colors().border,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
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.
|
||||||
|
.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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.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))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "stories")]
|
||||||
|
pub use stories::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "stories")]
|
||||||
|
mod stories {
|
||||||
|
use super::*;
|
||||||
|
use crate::{h_stack, Story};
|
||||||
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
|
pub struct CheckboxStory;
|
||||||
|
|
||||||
|
impl Render for CheckboxStory {
|
||||||
|
type Element = Div<Self>;
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
|
Story::container(cx)
|
||||||
|
.child(Story::title_for::<_, Checkbox<Self>>(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", Selection::Unselected))
|
||||||
|
.child(Checkbox::new(
|
||||||
|
"checkbox-intermediate",
|
||||||
|
Selection::Indeterminate,
|
||||||
|
))
|
||||||
|
.child(Checkbox::new("checkbox-selected", Selection::Selected)),
|
||||||
|
)
|
||||||
|
.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", Selection::Unselected)
|
||||||
|
.disabled(true),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Checkbox::new(
|
||||||
|
"checkbox-disabled-intermediate",
|
||||||
|
Selection::Indeterminate,
|
||||||
|
)
|
||||||
|
.disabled(true),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Checkbox::new("checkbox-disabled-selected", Selection::Selected)
|
||||||
|
.disabled(true),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ pub enum ContextMenuItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ContextMenuItem {
|
impl ContextMenuItem {
|
||||||
fn to_list_item<V: 'static>(self) -> ListItem<V> {
|
fn to_list_item<V: 'static>(self) -> ListItem {
|
||||||
match self {
|
match self {
|
||||||
ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
|
ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
|
||||||
ContextMenuItem::Entry(label) => {
|
ContextMenuItem::Entry(label) => {
|
||||||
@ -46,18 +46,15 @@ impl ContextMenu {
|
|||||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
v_stack()
|
v_stack()
|
||||||
.flex()
|
.flex()
|
||||||
.bg(cx.theme().colors().elevated_surface)
|
.bg(cx.theme().colors().elevated_surface_background)
|
||||||
.border()
|
.border()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
.child(
|
.child(List::new(
|
||||||
List::new(
|
self.items
|
||||||
self.items
|
.into_iter()
|
||||||
.into_iter()
|
.map(ContextMenuItem::to_list_item::<V>)
|
||||||
.map(ContextMenuItem::to_list_item)
|
.collect(),
|
||||||
.collect(),
|
))
|
||||||
)
|
|
||||||
.toggle(ToggleState::Toggled),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +65,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::story::Story;
|
use crate::story::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct ContextMenuStory;
|
pub struct ContextMenuStory;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Button, Story};
|
use crate::{Button, Story};
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct DetailsStory;
|
pub struct DetailsStory;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{static_players, Story};
|
use crate::{static_players, Story};
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct FacepileStory;
|
pub struct FacepileStory;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{rems, svg, Hsla};
|
use gpui::{rems, svg, Hsla};
|
||||||
use strum::EnumIter;
|
use strum::EnumIter;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -22,6 +22,7 @@ pub enum IconColor {
|
|||||||
Warning,
|
Warning,
|
||||||
Success,
|
Success,
|
||||||
Info,
|
Info,
|
||||||
|
Selected,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IconColor {
|
impl IconColor {
|
||||||
@ -36,6 +37,7 @@ impl IconColor {
|
|||||||
IconColor::Warning => cx.theme().status().warning,
|
IconColor::Warning => cx.theme().status().warning,
|
||||||
IconColor::Success => cx.theme().status().success,
|
IconColor::Success => cx.theme().status().success,
|
||||||
IconColor::Info => cx.theme().status().info,
|
IconColor::Info => cx.theme().status().info,
|
||||||
|
IconColor::Selected => cx.theme().colors().icon_accent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,6 +57,7 @@ pub enum Icon {
|
|||||||
ChevronRight,
|
ChevronRight,
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
Close,
|
Close,
|
||||||
|
Dash,
|
||||||
Exit,
|
Exit,
|
||||||
ExclamationTriangle,
|
ExclamationTriangle,
|
||||||
File,
|
File,
|
||||||
@ -112,6 +115,7 @@ impl Icon {
|
|||||||
Icon::ChevronRight => "icons/chevron_right.svg",
|
Icon::ChevronRight => "icons/chevron_right.svg",
|
||||||
Icon::ChevronUp => "icons/chevron_up.svg",
|
Icon::ChevronUp => "icons/chevron_up.svg",
|
||||||
Icon::Close => "icons/x.svg",
|
Icon::Close => "icons/x.svg",
|
||||||
|
Icon::Dash => "icons/dash.svg",
|
||||||
Icon::Exit => "icons/exit.svg",
|
Icon::Exit => "icons/exit.svg",
|
||||||
Icon::ExclamationTriangle => "icons/warning.svg",
|
Icon::ExclamationTriangle => "icons/warning.svg",
|
||||||
Icon::File => "icons/file.svg",
|
Icon::File => "icons/file.svg",
|
||||||
@ -182,7 +186,6 @@ impl IconElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
let fill = self.color.color(cx);
|
|
||||||
let svg_size = match self.size {
|
let svg_size = match self.size {
|
||||||
IconSize::Small => rems(0.75),
|
IconSize::Small => rems(0.75),
|
||||||
IconSize::Medium => rems(0.9375),
|
IconSize::Medium => rems(0.9375),
|
||||||
@ -192,7 +195,7 @@ impl IconElement {
|
|||||||
.size(svg_size)
|
.size(svg_size)
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.path(self.icon.path())
|
.path(self.icon.path())
|
||||||
.text_color(fill)
|
.text_color(self.color.color(cx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +204,7 @@ pub use stories::*;
|
|||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use gpui2::{rems, MouseButton};
|
use gpui::{rems, MouseButton};
|
||||||
|
|
||||||
use crate::{h_stack, prelude::*};
|
use crate::{h_stack, prelude::*};
|
||||||
use crate::{ClickHandler, Icon, IconColor, IconElement};
|
use crate::{ClickHandler, Icon, IconColor, IconElement};
|
||||||
@ -73,12 +73,12 @@ impl<V: 'static> IconButton<V> {
|
|||||||
|
|
||||||
let (bg_color, bg_hover_color, bg_active_color) = match self.variant {
|
let (bg_color, bg_hover_color, bg_active_color) = match self.variant {
|
||||||
ButtonVariant::Filled => (
|
ButtonVariant::Filled => (
|
||||||
cx.theme().colors().element,
|
cx.theme().colors().element_background,
|
||||||
cx.theme().colors().element_hover,
|
cx.theme().colors().element_hover,
|
||||||
cx.theme().colors().element_active,
|
cx.theme().colors().element_active,
|
||||||
),
|
),
|
||||||
ButtonVariant::Ghost => (
|
ButtonVariant::Ghost => (
|
||||||
cx.theme().colors().ghost_element,
|
cx.theme().colors().ghost_element_background,
|
||||||
cx.theme().colors().ghost_element_hover,
|
cx.theme().colors().ghost_element_hover,
|
||||||
cx.theme().colors().ghost_element_active,
|
cx.theme().colors().ghost_element_active,
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::px;
|
use gpui::px;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ impl UnreadIndicator {
|
|||||||
div()
|
div()
|
||||||
.rounded_full()
|
.rounded_full()
|
||||||
.border_2()
|
.border_2()
|
||||||
.border_color(cx.theme().colors().surface)
|
.border_color(cx.theme().colors().surface_background)
|
||||||
.w(px(9.0))
|
.w(px(9.0))
|
||||||
.h(px(9.0))
|
.h(px(9.0))
|
||||||
.z_index(2)
|
.z_index(2)
|
||||||
|
@ -59,12 +59,12 @@ impl Input {
|
|||||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
|
let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
|
||||||
InputVariant::Ghost => (
|
InputVariant::Ghost => (
|
||||||
cx.theme().colors().ghost_element,
|
cx.theme().colors().ghost_element_background,
|
||||||
cx.theme().colors().ghost_element_hover,
|
cx.theme().colors().ghost_element_hover,
|
||||||
cx.theme().colors().ghost_element_active,
|
cx.theme().colors().ghost_element_active,
|
||||||
),
|
),
|
||||||
InputVariant::Filled => (
|
InputVariant::Filled => (
|
||||||
cx.theme().colors().element,
|
cx.theme().colors().element_background,
|
||||||
cx.theme().colors().element_hover,
|
cx.theme().colors().element_hover,
|
||||||
cx.theme().colors().element_active,
|
cx.theme().colors().element_active,
|
||||||
),
|
),
|
||||||
@ -111,7 +111,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct InputStory;
|
pub struct InputStory;
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ impl Key {
|
|||||||
.rounded_md()
|
.rounded_md()
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.text_color(cx.theme().colors().text)
|
.text_color(cx.theme().colors().text)
|
||||||
.bg(cx.theme().colors().element)
|
.bg(cx.theme().colors().element_background)
|
||||||
.child(self.key.clone())
|
.child(self.key.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
pub struct KeybindingStory;
|
pub struct KeybindingStory;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{relative, rems, Hsla, WindowContext};
|
use gpui::{relative, rems, Hsla, WindowContext};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -194,7 +194,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct LabelStory;
|
pub struct LabelStory;
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use gpui2::{div, px, relative, Div};
|
use gpui::div;
|
||||||
|
|
||||||
use crate::settings::user_settings;
|
use crate::settings::user_settings;
|
||||||
use crate::{
|
use crate::{
|
||||||
h_stack, v_stack, Avatar, ClickHandler, Icon, IconColor, IconElement, IconSize, Label,
|
disclosure_control, h_stack, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label,
|
||||||
LabelColor,
|
LabelColor, Toggle,
|
||||||
};
|
};
|
||||||
use crate::{prelude::*, Button};
|
use crate::{prelude::*, GraphicSlot};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Debug, PartialEq)]
|
#[derive(Clone, Copy, Default, Debug, PartialEq)]
|
||||||
pub enum ListItemVariant {
|
pub enum ListItemVariant {
|
||||||
@ -29,7 +29,7 @@ pub struct ListHeader {
|
|||||||
left_icon: Option<Icon>,
|
left_icon: Option<Icon>,
|
||||||
meta: Option<ListHeaderMeta>,
|
meta: Option<ListHeaderMeta>,
|
||||||
variant: ListItemVariant,
|
variant: ListItemVariant,
|
||||||
toggleable: Toggleable,
|
toggle: Toggle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListHeader {
|
impl ListHeader {
|
||||||
@ -39,17 +39,12 @@ impl ListHeader {
|
|||||||
left_icon: None,
|
left_icon: None,
|
||||||
meta: None,
|
meta: None,
|
||||||
variant: ListItemVariant::default(),
|
variant: ListItemVariant::default(),
|
||||||
toggleable: Toggleable::NotToggleable,
|
toggle: Toggle::NotToggleable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle(mut self, toggle: ToggleState) -> Self {
|
pub fn toggle(mut self, toggle: Toggle) -> Self {
|
||||||
self.toggleable = toggle.into();
|
self.toggle = toggle;
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggleable(mut self, toggleable: Toggleable) -> Self {
|
|
||||||
self.toggleable = toggleable;
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,30 +58,8 @@ impl ListHeader {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disclosure_control<V: 'static>(&self) -> Div<V> {
|
|
||||||
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<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
let disclosure_control = disclosure_control(self.toggle);
|
||||||
let is_toggled = self.toggleable.is_toggled();
|
|
||||||
|
|
||||||
let disclosure_control = self.disclosure_control();
|
|
||||||
|
|
||||||
let meta = match self.meta {
|
let meta = match self.meta {
|
||||||
Some(ListHeaderMeta::Tools(icons)) => div().child(
|
Some(ListHeaderMeta::Tools(icons)) => div().child(
|
||||||
@ -106,7 +79,7 @@ impl ListHeader {
|
|||||||
|
|
||||||
h_stack()
|
h_stack()
|
||||||
.w_full()
|
.w_full()
|
||||||
.bg(cx.theme().colors().surface)
|
.bg(cx.theme().colors().surface_background)
|
||||||
// TODO: Add focus state
|
// TODO: Add focus state
|
||||||
// .when(self.state == InteractionState::Focused, |this| {
|
// .when(self.state == InteractionState::Focused, |this| {
|
||||||
// this.border()
|
// this.border()
|
||||||
@ -193,12 +166,6 @@ impl ListSubHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum LeftContent {
|
|
||||||
Icon(Icon),
|
|
||||||
Avatar(SharedString),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
#[derive(Default, PartialEq, Copy, Clone)]
|
||||||
pub enum ListEntrySize {
|
pub enum ListEntrySize {
|
||||||
#[default]
|
#[default]
|
||||||
@ -207,44 +174,36 @@ pub enum ListEntrySize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub enum ListItem<V: 'static> {
|
pub enum ListItem {
|
||||||
Entry(ListEntry),
|
Entry(ListEntry),
|
||||||
Details(ListDetailsEntry<V>),
|
|
||||||
Separator(ListSeparator),
|
Separator(ListSeparator),
|
||||||
Header(ListSubHeader),
|
Header(ListSubHeader),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static> From<ListEntry> for ListItem<V> {
|
impl From<ListEntry> for ListItem {
|
||||||
fn from(entry: ListEntry) -> Self {
|
fn from(entry: ListEntry) -> Self {
|
||||||
Self::Entry(entry)
|
Self::Entry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static> From<ListDetailsEntry<V>> for ListItem<V> {
|
impl From<ListSeparator> for ListItem {
|
||||||
fn from(entry: ListDetailsEntry<V>) -> Self {
|
|
||||||
Self::Details(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> From<ListSeparator> for ListItem<V> {
|
|
||||||
fn from(entry: ListSeparator) -> Self {
|
fn from(entry: ListSeparator) -> Self {
|
||||||
Self::Separator(entry)
|
Self::Separator(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static> From<ListSubHeader> for ListItem<V> {
|
impl From<ListSubHeader> for ListItem {
|
||||||
fn from(entry: ListSubHeader) -> Self {
|
fn from(entry: ListSubHeader) -> Self {
|
||||||
Self::Header(entry)
|
Self::Header(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static> ListItem<V> {
|
impl ListItem {
|
||||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render<V: 'static>(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
match self {
|
match self {
|
||||||
ListItem::Entry(entry) => div().child(entry.render(view, cx)),
|
ListItem::Entry(entry) => div().child(entry.render(view, cx)),
|
||||||
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
||||||
ListItem::Header(header) => div().child(header.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<V: 'static> ListItem<V> {
|
|||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct ListEntry {
|
pub struct ListEntry {
|
||||||
disclosure_control_style: DisclosureControlVisibility,
|
disabled: bool,
|
||||||
|
// TODO: Reintroduce this
|
||||||
|
// disclosure_control_style: DisclosureControlVisibility,
|
||||||
indent_level: u32,
|
indent_level: u32,
|
||||||
label: Label,
|
label: Label,
|
||||||
left_content: Option<LeftContent>,
|
left_slot: Option<GraphicSlot>,
|
||||||
variant: ListItemVariant,
|
|
||||||
size: ListEntrySize,
|
|
||||||
state: InteractionState,
|
|
||||||
toggle: Option<ToggleState>,
|
|
||||||
overflow: OverflowStyle,
|
overflow: OverflowStyle,
|
||||||
|
size: ListEntrySize,
|
||||||
|
toggle: Toggle,
|
||||||
|
variant: ListItemVariant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListEntry {
|
impl ListEntry {
|
||||||
pub fn new(label: Label) -> Self {
|
pub fn new(label: Label) -> Self {
|
||||||
Self {
|
Self {
|
||||||
disclosure_control_style: DisclosureControlVisibility::default(),
|
disabled: false,
|
||||||
indent_level: 0,
|
indent_level: 0,
|
||||||
label,
|
label,
|
||||||
variant: ListItemVariant::default(),
|
left_slot: None,
|
||||||
left_content: None,
|
|
||||||
size: ListEntrySize::default(),
|
|
||||||
state: InteractionState::default(),
|
|
||||||
// TODO: Should use Toggleable::NotToggleable
|
|
||||||
// or remove Toggleable::NotToggleable from the system
|
|
||||||
toggle: None,
|
|
||||||
overflow: OverflowStyle::Hidden,
|
overflow: OverflowStyle::Hidden,
|
||||||
|
size: ListEntrySize::default(),
|
||||||
|
toggle: Toggle::NotToggleable,
|
||||||
|
variant: ListItemVariant::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,28 +258,23 @@ impl ListEntry {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle(mut self, toggle: ToggleState) -> Self {
|
pub fn toggle(mut self, toggle: Toggle) -> Self {
|
||||||
self.toggle = Some(toggle);
|
self.toggle = toggle;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn left_content(mut self, left_content: LeftContent) -> Self {
|
pub fn left_content(mut self, left_content: GraphicSlot) -> Self {
|
||||||
self.left_content = Some(left_content);
|
self.left_slot = Some(left_content);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn left_icon(mut self, left_icon: Icon) -> 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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn left_avatar(mut self, left_avatar: impl Into<SharedString>) -> Self {
|
pub fn left_avatar(mut self, left_avatar: impl Into<SharedString>) -> Self {
|
||||||
self.left_content = Some(LeftContent::Avatar(left_avatar.into()));
|
self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into()));
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn state(mut self, state: InteractionState) -> Self {
|
|
||||||
self.state = state;
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,63 +283,19 @@ impl ListEntry {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disclosure_control_style(
|
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
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<V: 'static>(
|
|
||||||
&mut self,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) -> Option<impl Component<V>> {
|
|
||||||
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<V: 'static>(mut self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
|
||||||
let settings = user_settings(cx);
|
let settings = user_settings(cx);
|
||||||
|
|
||||||
let left_content = match self.left_content.clone() {
|
let left_content = match self.left_slot.clone() {
|
||||||
Some(LeftContent::Icon(i)) => Some(
|
Some(GraphicSlot::Icon(i)) => Some(
|
||||||
h_stack().child(
|
h_stack().child(
|
||||||
IconElement::new(i)
|
IconElement::new(i)
|
||||||
.size(IconSize::Small)
|
.size(IconSize::Small)
|
||||||
.color(IconColor::Muted),
|
.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,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -399,11 +307,8 @@ impl ListEntry {
|
|||||||
div()
|
div()
|
||||||
.relative()
|
.relative()
|
||||||
.group("")
|
.group("")
|
||||||
.bg(cx.theme().colors().surface)
|
.bg(cx.theme().colors().surface_background)
|
||||||
.when(self.state == InteractionState::Focused, |this| {
|
// TODO: Add focus state
|
||||||
this.border()
|
|
||||||
.border_color(cx.theme().colors().border_focused)
|
|
||||||
})
|
|
||||||
.child(
|
.child(
|
||||||
sized_item
|
sized_item
|
||||||
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
||||||
@ -425,131 +330,13 @@ impl ListEntry {
|
|||||||
.gap_1()
|
.gap_1()
|
||||||
.items_center()
|
.items_center()
|
||||||
.relative()
|
.relative()
|
||||||
.children(self.disclosure_control(cx))
|
.child(disclosure_control(self.toggle))
|
||||||
.children(left_content)
|
.children(left_content)
|
||||||
.child(self.label),
|
.child(self.label),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ListDetailsEntryHandlers<V: 'static> {
|
|
||||||
click: Option<ClickHandler<V>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Default for ListDetailsEntryHandlers<V> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { click: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct ListDetailsEntry<V: 'static> {
|
|
||||||
label: SharedString,
|
|
||||||
meta: Option<SharedString>,
|
|
||||||
left_content: Option<LeftContent>,
|
|
||||||
handlers: ListDetailsEntryHandlers<V>,
|
|
||||||
actions: Option<Vec<Button<V>>>,
|
|
||||||
// TODO: make this more generic instead of
|
|
||||||
// specifically for notifications
|
|
||||||
seen: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> ListDetailsEntry<V> {
|
|
||||||
pub fn new(label: impl Into<SharedString>) -> 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<SharedString>) -> 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<V>) -> Self {
|
|
||||||
self.handlers.click = Some(handler);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn actions(mut self, actions: Vec<Button<V>>) -> Self {
|
|
||||||
self.actions = Some(actions);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
|
||||||
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)]
|
#[derive(Clone, Component)]
|
||||||
pub struct ListSeparator;
|
pub struct ListSeparator;
|
||||||
|
|
||||||
@ -564,20 +351,22 @@ impl ListSeparator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct List<V: 'static> {
|
pub struct List {
|
||||||
items: Vec<ListItem<V>>,
|
items: Vec<ListItem>,
|
||||||
|
/// Message to display when the list is empty
|
||||||
|
/// Defaults to "No items"
|
||||||
empty_message: SharedString,
|
empty_message: SharedString,
|
||||||
header: Option<ListHeader>,
|
header: Option<ListHeader>,
|
||||||
toggleable: Toggleable,
|
toggle: Toggle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static> List<V> {
|
impl List {
|
||||||
pub fn new(items: Vec<ListItem<V>>) -> Self {
|
pub fn new(items: Vec<ListItem>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
items,
|
items,
|
||||||
empty_message: "No items".into(),
|
empty_message: "No items".into(),
|
||||||
header: None,
|
header: None,
|
||||||
toggleable: Toggleable::default(),
|
toggle: Toggle::NotToggleable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,19 +380,16 @@ impl<V: 'static> List<V> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle(mut self, toggle: ToggleState) -> Self {
|
pub fn toggle(mut self, toggle: Toggle) -> Self {
|
||||||
self.toggleable = toggle.into();
|
self.toggle = toggle;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
let list_content = match (self.items.is_empty(), self.toggle) {
|
||||||
let is_toggled = Toggleable::is_toggled(&self.toggleable);
|
|
||||||
|
|
||||||
let list_content = match (self.items.is_empty(), is_toggled) {
|
|
||||||
(false, _) => div().children(self.items),
|
(false, _) => div().children(self.items),
|
||||||
(true, false) => div(),
|
(true, Toggle::Toggled(false)) => div(),
|
||||||
(true, true) => {
|
(true, _) => {
|
||||||
div().child(Label::new(self.empty_message.clone()).color(LabelColor::Muted))
|
div().child(Label::new(self.empty_message.clone()).color(LabelColor::Muted))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -611,7 +397,7 @@ impl<V: 'static> List<V> {
|
|||||||
v_stack()
|
v_stack()
|
||||||
.w_full()
|
.w_full()
|
||||||
.py_1()
|
.py_1()
|
||||||
.children(self.header.map(|header| header.toggleable(self.toggleable)))
|
.children(self.header.map(|header| header))
|
||||||
.child(list_content)
|
.child(list_content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::AnyElement;
|
use gpui::AnyElement;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{h_stack, prelude::*, v_stack, Button, Icon, IconButton, Label};
|
use crate::{h_stack, prelude::*, v_stack, Button, Icon, IconButton, Label};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::rems;
|
use gpui::rems;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{h_stack, Icon};
|
use crate::{h_stack, Icon};
|
||||||
@ -34,7 +34,7 @@ impl NotificationToast {
|
|||||||
.px_1p5()
|
.px_1p5()
|
||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.shadow_md()
|
.shadow_md()
|
||||||
.bg(cx.theme().colors().elevated_surface)
|
.bg(cx.theme().colors().elevated_surface_background)
|
||||||
.child(div().size_full().child(self.label.clone()))
|
.child(div().size_full().child(self.label.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ impl Palette {
|
|||||||
.id(self.id.clone())
|
.id(self.id.clone())
|
||||||
.w_96()
|
.w_96()
|
||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.bg(cx.theme().colors().elevated_surface)
|
.bg(cx.theme().colors().elevated_surface_background)
|
||||||
.border()
|
.border()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
.child(
|
.child(
|
||||||
@ -56,7 +56,12 @@ impl Palette {
|
|||||||
.child(v_stack().py_0p5().px_1().child(div().px_2().py_0p5().child(
|
.child(v_stack().py_0p5().px_1().child(div().px_2().py_0p5().child(
|
||||||
Label::new(self.input_placeholder.clone()).color(LabelColor::Placeholder),
|
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(
|
.child(
|
||||||
v_stack()
|
v_stack()
|
||||||
.id("items")
|
.id("items")
|
||||||
@ -148,13 +153,13 @@ impl PaletteItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use gpui2::ElementId;
|
use gpui::ElementId;
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
pub use stories::*;
|
pub use stories::*;
|
||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
use crate::{ModifierKeys, Story};
|
use crate::{ModifierKeys, Story};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{AbsoluteLength, AnyElement};
|
use gpui::{AbsoluteLength, AnyElement};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -107,7 +107,7 @@ impl<V: 'static> Panel<V> {
|
|||||||
PanelSide::Right => this.border_l(),
|
PanelSide::Right => this.border_l(),
|
||||||
PanelSide::Bottom => this.border_b().w_full().h(current_size),
|
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)
|
.border_color(cx.theme().colors().border)
|
||||||
.children(self.children)
|
.children(self.children)
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Label, Story};
|
use crate::{Label, Story};
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct PanelStory;
|
pub struct PanelStory;
|
||||||
|
|
||||||
|
@ -1,7 +1,25 @@
|
|||||||
use gpui2::{Hsla, ViewContext};
|
use gpui::{Hsla, ViewContext};
|
||||||
|
|
||||||
use crate::prelude::*;
|
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<SharedString>, avatar: impl Into<SharedString>) -> Self {
|
||||||
|
Self {
|
||||||
|
username: username.into(),
|
||||||
|
avatar: avatar.into(),
|
||||||
|
is_contact: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
pub enum PlayerStatus {
|
pub enum PlayerStatus {
|
||||||
#[default]
|
#[default]
|
||||||
|
14
crates/ui2/src/components/slot.rs
Normal file
14
crates/ui2/src/components/slot.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use gpui::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),
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{div, Div};
|
use gpui::{div, Div};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{Icon, IconColor, IconElement, Label, LabelColor};
|
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)]
|
#[derive(Component, Clone)]
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
@ -109,12 +109,12 @@ impl Tab {
|
|||||||
|
|
||||||
let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current {
|
let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current {
|
||||||
false => (
|
false => (
|
||||||
cx.theme().colors().tab_inactive,
|
cx.theme().colors().tab_inactive_background,
|
||||||
cx.theme().colors().ghost_element_hover,
|
cx.theme().colors().ghost_element_hover,
|
||||||
cx.theme().colors().ghost_element_active,
|
cx.theme().colors().ghost_element_active,
|
||||||
),
|
),
|
||||||
true => (
|
true => (
|
||||||
cx.theme().colors().tab_active,
|
cx.theme().colors().tab_active_background,
|
||||||
cx.theme().colors().element_hover,
|
cx.theme().colors().element_hover,
|
||||||
cx.theme().colors().element_active,
|
cx.theme().colors().element_active,
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::AnyElement;
|
use gpui::AnyElement;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -54,7 +54,7 @@ impl<V: 'static> Toast<V> {
|
|||||||
.rounded_lg()
|
.rounded_lg()
|
||||||
.shadow_md()
|
.shadow_md()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.bg(cx.theme().colors().elevated_surface)
|
.bg(cx.theme().colors().elevated_surface_background)
|
||||||
.children(self.children)
|
.children(self.children)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ pub use stories::*;
|
|||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
use crate::{Label, Story};
|
use crate::{Label, Story};
|
||||||
|
|
||||||
|
61
crates/ui2/src/components/toggle.rs
Normal file
61
crates/ui2/src/components/toggle.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use gpui::{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<bool> for Toggle {
|
||||||
|
fn from(toggled: bool) -> Self {
|
||||||
|
Toggle::Toggled(toggled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disclosure_control<V: 'static>(toggle: Toggle) -> impl Component<V> {
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
31
crates/ui2/src/components/tooltip.rs
Normal file
31
crates/ui2/src/components/tooltip.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use gpui::{div, px, Div, ParentElement, Render, SharedString, Styled, ViewContext};
|
||||||
|
use theme2::ActiveTheme;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TextTooltip {
|
||||||
|
title: SharedString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextTooltip {
|
||||||
|
pub fn new(str: SharedString) -> Self {
|
||||||
|
Self { title: str }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for TextTooltip {
|
||||||
|
type Element = Div<Self>;
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
|
let theme = cx.theme();
|
||||||
|
div()
|
||||||
|
.bg(theme.colors().background)
|
||||||
|
.rounded(px(8.))
|
||||||
|
.border()
|
||||||
|
.font("Zed Sans")
|
||||||
|
.border_color(theme.colors().border)
|
||||||
|
.text_color(theme.colors().text)
|
||||||
|
.pl_2()
|
||||||
|
.pr_2()
|
||||||
|
.child(self.title.clone())
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
pub use gpui2::{
|
pub use gpui::{
|
||||||
div, Component, Element, ElementId, ParentElement, SharedString, StatefulInteractive,
|
div, Component, Element, ElementId, ParentElement, SharedString, StatefulInteractive,
|
||||||
StatelessInteractive, Styled, ViewContext, WindowContext,
|
StatelessInteractive, Styled, ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
@ -7,27 +7,9 @@ pub use crate::elevation::*;
|
|||||||
pub use crate::ButtonVariant;
|
pub use crate::ButtonVariant;
|
||||||
pub use theme2::ActiveTheme;
|
pub use theme2::ActiveTheme;
|
||||||
|
|
||||||
use gpui2::Hsla;
|
use gpui::Hsla;
|
||||||
use strum::EnumIter;
|
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<SharedString>, avatar: impl Into<SharedString>) -> Self {
|
|
||||||
Self {
|
|
||||||
username: username.into(),
|
|
||||||
avatar: avatar.into(),
|
|
||||||
is_contact: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||||
pub enum FileSystemStatus {
|
pub enum FileSystemStatus {
|
||||||
#[default]
|
#[default]
|
||||||
@ -172,68 +154,19 @@ impl InteractionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
pub enum SelectedState {
|
pub enum Selection {
|
||||||
#[default]
|
#[default]
|
||||||
Unselected,
|
Unselected,
|
||||||
PartiallySelected,
|
Indeterminate,
|
||||||
Selected,
|
Selected,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
impl Selection {
|
||||||
pub enum Toggleable {
|
pub fn inverse(&self) -> Self {
|
||||||
Toggleable(ToggleState),
|
|
||||||
#[default]
|
|
||||||
NotToggleable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Toggleable {
|
|
||||||
pub fn is_toggled(&self) -> bool {
|
|
||||||
match self {
|
match self {
|
||||||
Self::Toggleable(ToggleState::Toggled) => true,
|
Self::Unselected | Self::Indeterminate => Self::Selected,
|
||||||
_ => false,
|
Self::Selected => Self::Unselected,
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ToggleState> 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<Toggleable> for ToggleState {
|
|
||||||
fn from(toggleable: Toggleable) -> Self {
|
|
||||||
match toggleable {
|
|
||||||
Toggleable::Toggleable(state) => state,
|
|
||||||
Toggleable::NotToggleable => ToggleState::NotToggled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for ToggleState {
|
|
||||||
fn from(toggled: bool) -> Self {
|
|
||||||
if toggled {
|
|
||||||
ToggleState::Toggled
|
|
||||||
} else {
|
|
||||||
ToggleState::NotToggled
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use gpui2::{rems, AbsoluteLength, AppContext, WindowContext};
|
use gpui::{rems, AbsoluteLength, AppContext, WindowContext};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
@ -3,17 +3,17 @@ use std::str::FromStr;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use gpui2::{AppContext, ViewContext};
|
use gpui::{AppContext, ViewContext};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use theme2::ActiveTheme;
|
use theme2::ActiveTheme;
|
||||||
|
|
||||||
|
use crate::HighlightedText;
|
||||||
use crate::{
|
use crate::{
|
||||||
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
|
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
|
||||||
HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListSubHeader,
|
HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, Livestream,
|
||||||
Livestream, MicStatus, ModifierKeys, Notification, PaletteItem, Player, PlayerCallStatus,
|
MicStatus, ModifierKeys, Notification, PaletteItem, Player, PlayerCallStatus,
|
||||||
PlayerWithCallStatus, PublicActor, ScreenShareStatus, Symbol, Tab, ToggleState, VideoStatus,
|
PlayerWithCallStatus, PublicPlayer, ScreenShareStatus, Symbol, Tab, Toggle, VideoStatus,
|
||||||
};
|
};
|
||||||
use crate::{HighlightedText, ListDetailsEntry};
|
|
||||||
use crate::{ListItem, NotificationAction};
|
use crate::{ListItem, NotificationAction};
|
||||||
|
|
||||||
pub fn static_tabs_example() -> Vec<Tab> {
|
pub fn static_tabs_example() -> Vec<Tab> {
|
||||||
@ -345,7 +345,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
|||||||
DateTime::parse_from_rfc3339("2023-11-02T12:09:07Z")
|
DateTime::parse_from_rfc3339("2023-11-02T12:09:07Z")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.naive_local(),
|
.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(
|
NotificationAction::new(
|
||||||
Button::new("Decline"),
|
Button::new("Decline"),
|
||||||
@ -374,7 +374,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
|||||||
DateTime::parse_from_rfc3339("2023-11-01T12:09:07Z")
|
DateTime::parse_from_rfc3339("2023-11-01T12:09:07Z")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.naive_local(),
|
.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(
|
NotificationAction::new(
|
||||||
Button::new("Decline"),
|
Button::new("Decline"),
|
||||||
@ -403,7 +403,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
|||||||
DateTime::parse_from_rfc3339("2022-10-25T12:09:07Z")
|
DateTime::parse_from_rfc3339("2022-10-25T12:09:07Z")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.naive_local(),
|
.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(
|
NotificationAction::new(
|
||||||
Button::new("Decline"),
|
Button::new("Decline"),
|
||||||
@ -432,7 +432,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
|||||||
DateTime::parse_from_rfc3339("2021-10-12T12:09:07Z")
|
DateTime::parse_from_rfc3339("2021-10-12T12:09:07Z")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.naive_local(),
|
.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(
|
NotificationAction::new(
|
||||||
Button::new("Decline"),
|
Button::new("Decline"),
|
||||||
@ -461,7 +461,7 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
|||||||
DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z")
|
DateTime::parse_from_rfc3339("1969-07-20T00:00:00Z")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.naive_local(),
|
.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(
|
NotificationAction::new(
|
||||||
Button::new("Decline"),
|
Button::new("Decline"),
|
||||||
@ -478,89 +478,12 @@ pub fn static_new_notification_items_2<V: 'static>() -> Vec<Notification<V>> {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_new_notification_items<V: 'static>() -> Vec<ListItem<V>> {
|
pub fn static_project_panel_project_items() -> Vec<ListItem> {
|
||||||
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<V: 'static>() -> Vec<ListItem<V>> {
|
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("zed"))
|
ListEntry::new(Label::new("zed"))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
.indent_level(0)
|
.indent_level(0)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new(".cargo"))
|
ListEntry::new(Label::new(".cargo"))
|
||||||
.left_icon(Icon::Folder.into())
|
.left_icon(Icon::Folder.into())
|
||||||
.indent_level(1),
|
.indent_level(1),
|
||||||
@ -579,14 +502,14 @@ pub fn static_project_panel_project_items<V: 'static>() -> Vec<ListItem<V>> {
|
|||||||
ListEntry::new(Label::new("assets"))
|
ListEntry::new(Label::new("assets"))
|
||||||
.left_icon(Icon::Folder.into())
|
.left_icon(Icon::Folder.into())
|
||||||
.indent_level(1)
|
.indent_level(1)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden))
|
ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden))
|
||||||
.left_icon(Icon::Folder.into())
|
.left_icon(Icon::Folder.into())
|
||||||
.indent_level(1),
|
.indent_level(1),
|
||||||
ListEntry::new(Label::new("crates"))
|
ListEntry::new(Label::new("crates"))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
.indent_level(1)
|
.indent_level(1)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("activity_indicator"))
|
ListEntry::new(Label::new("activity_indicator"))
|
||||||
.left_icon(Icon::Folder.into())
|
.left_icon(Icon::Folder.into())
|
||||||
.indent_level(2),
|
.indent_level(2),
|
||||||
@ -608,38 +531,38 @@ pub fn static_project_panel_project_items<V: 'static>() -> Vec<ListItem<V>> {
|
|||||||
ListEntry::new(Label::new("sqlez").color(LabelColor::Modified))
|
ListEntry::new(Label::new("sqlez").color(LabelColor::Modified))
|
||||||
.left_icon(Icon::Folder.into())
|
.left_icon(Icon::Folder.into())
|
||||||
.indent_level(2)
|
.indent_level(2)
|
||||||
.toggle(ToggleState::NotToggled),
|
.toggle(Toggle::Toggled(false)),
|
||||||
ListEntry::new(Label::new("gpui2"))
|
ListEntry::new(Label::new("gpui2"))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
.indent_level(2)
|
.indent_level(2)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("src"))
|
ListEntry::new(Label::new("src"))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
.indent_level(3)
|
.indent_level(3)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("derive_element.rs"))
|
ListEntry::new(Label::new("derive_element.rs"))
|
||||||
.left_icon(Icon::FileRust.into())
|
.left_icon(Icon::FileRust.into())
|
||||||
.indent_level(4),
|
.indent_level(4),
|
||||||
ListEntry::new(Label::new("storybook").color(LabelColor::Modified))
|
ListEntry::new(Label::new("storybook").color(LabelColor::Modified))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
.indent_level(1)
|
.indent_level(1)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("docs").color(LabelColor::Default))
|
ListEntry::new(Label::new("docs").color(LabelColor::Default))
|
||||||
.left_icon(Icon::Folder.into())
|
.left_icon(Icon::Folder.into())
|
||||||
.indent_level(2)
|
.indent_level(2)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("src").color(LabelColor::Modified))
|
ListEntry::new(Label::new("src").color(LabelColor::Modified))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
.indent_level(3)
|
.indent_level(3)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("ui").color(LabelColor::Modified))
|
ListEntry::new(Label::new("ui").color(LabelColor::Modified))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
.indent_level(4)
|
.indent_level(4)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("component").color(LabelColor::Created))
|
ListEntry::new(Label::new("component").color(LabelColor::Created))
|
||||||
.left_icon(Icon::FolderOpen.into())
|
.left_icon(Icon::FolderOpen.into())
|
||||||
.indent_level(5)
|
.indent_level(5)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default))
|
ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default))
|
||||||
.left_icon(Icon::FileRust.into())
|
.left_icon(Icon::FileRust.into())
|
||||||
.indent_level(6),
|
.indent_level(6),
|
||||||
@ -682,7 +605,7 @@ pub fn static_project_panel_project_items<V: 'static>() -> Vec<ListItem<V>> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_project_panel_single_items<V: 'static>() -> Vec<ListItem<V>> {
|
pub fn static_project_panel_single_items() -> Vec<ListItem> {
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("todo.md"))
|
ListEntry::new(Label::new("todo.md"))
|
||||||
.left_icon(Icon::FileDoc.into())
|
.left_icon(Icon::FileDoc.into())
|
||||||
@ -699,7 +622,7 @@ pub fn static_project_panel_single_items<V: 'static>() -> Vec<ListItem<V>> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_collab_panel_current_call<V: 'static>() -> Vec<ListItem<V>> {
|
pub fn static_collab_panel_current_call() -> Vec<ListItem> {
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"),
|
ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"),
|
||||||
ListEntry::new(Label::new("nathansobo"))
|
ListEntry::new(Label::new("nathansobo"))
|
||||||
@ -712,7 +635,7 @@ pub fn static_collab_panel_current_call<V: 'static>() -> Vec<ListItem<V>> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_collab_panel_channels<V: 'static>() -> Vec<ListItem<V>> {
|
pub fn static_collab_panel_channels() -> Vec<ListItem> {
|
||||||
vec![
|
vec![
|
||||||
ListEntry::new(Label::new("zed"))
|
ListEntry::new(Label::new("zed"))
|
||||||
.left_icon(Icon::Hash.into())
|
.left_icon(Icon::Hash.into())
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::Div;
|
use gpui::Div;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{Icon, IconButton, Label, Panel, PanelSide};
|
use crate::{Icon, IconButton, Label, Panel, PanelSide};
|
||||||
use gpui2::{rems, AbsoluteLength};
|
use gpui::{rems, AbsoluteLength};
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct AssistantPanel {
|
pub struct AssistantPanel {
|
||||||
@ -77,7 +77,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
pub struct AssistantPanelStory;
|
pub struct AssistantPanelStory;
|
||||||
|
|
||||||
impl Render for AssistantPanelStory {
|
impl Render for AssistantPanelStory {
|
||||||
|
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{h_stack, HighlightedText};
|
use crate::{h_stack, HighlightedText};
|
||||||
use gpui2::Div;
|
use gpui::Div;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Symbol(pub Vec<HighlightedText>);
|
pub struct Symbol(pub Vec<HighlightedText>);
|
||||||
@ -73,7 +73,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::Render;
|
use gpui::Render;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub struct BreadcrumbStory;
|
pub struct BreadcrumbStory;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{Hsla, WindowContext};
|
use gpui::{Hsla, WindowContext};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{h_stack, v_stack, Icon, IconElement};
|
use crate::{h_stack, v_stack, Icon, IconElement};
|
||||||
@ -220,7 +220,7 @@ impl Buffer {
|
|||||||
.flex_1()
|
.flex_1()
|
||||||
.w_full()
|
.w_full()
|
||||||
.h_full()
|
.h_full()
|
||||||
.bg(cx.theme().colors().editor)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.children(rows)
|
.children(rows)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ mod stories {
|
|||||||
empty_buffer_example, hello_world_rust_buffer_example,
|
empty_buffer_example, hello_world_rust_buffer_example,
|
||||||
hello_world_rust_buffer_with_status_example, Story,
|
hello_world_rust_buffer_with_status_example, Story,
|
||||||
};
|
};
|
||||||
use gpui2::{rems, Div, Render};
|
use gpui::{rems, Div, Render};
|
||||||
|
|
||||||
pub struct BufferStory;
|
pub struct BufferStory;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{Div, Render, View, VisualContext};
|
use gpui::{Div, Render, View, VisualContext};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{h_stack, Icon, IconButton, IconColor, Input};
|
use crate::{h_stack, Icon, IconButton, IconColor, Input};
|
||||||
@ -30,14 +30,17 @@ impl Render for BufferSearch {
|
|||||||
type Element = Div<Self>;
|
type Element = Div<Self>;
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
|
||||||
h_stack().bg(cx.theme().colors().toolbar).p_2().child(
|
h_stack()
|
||||||
h_stack().child(Input::new("Search")).child(
|
.bg(cx.theme().colors().toolbar_background)
|
||||||
IconButton::<Self>::new("replace", Icon::Replace)
|
.p_2()
|
||||||
.when(self.is_replace_open, |this| this.color(IconColor::Accent))
|
.child(
|
||||||
.on_click(|buffer_search, cx| {
|
h_stack().child(Input::new("Search")).child(
|
||||||
buffer_search.toggle_replace(cx);
|
IconButton::<Self>::new("replace", Icon::Replace)
|
||||||
}),
|
.when(self.is_replace_open, |this| this.color(IconColor::Accent))
|
||||||
),
|
.on_click(|buffer_search, cx| {
|
||||||
)
|
buffer_search.toggle_replace(cx);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ pub use stories::*;
|
|||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
use crate::{Panel, Story};
|
use crate::{Panel, Story};
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::prelude::*;
|
use crate::{prelude::*, Toggle};
|
||||||
use crate::{
|
use crate::{
|
||||||
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List,
|
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, ListHeader,
|
||||||
ListHeader, ToggleState,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -18,7 +17,7 @@ impl CollabPanel {
|
|||||||
v_stack()
|
v_stack()
|
||||||
.id(self.id.clone())
|
.id(self.id.clone())
|
||||||
.h_full()
|
.h_full()
|
||||||
.bg(cx.theme().colors().surface)
|
.bg(cx.theme().colors().surface_background)
|
||||||
.child(
|
.child(
|
||||||
v_stack()
|
v_stack()
|
||||||
.id("crdb")
|
.id("crdb")
|
||||||
@ -34,17 +33,17 @@ impl CollabPanel {
|
|||||||
.header(
|
.header(
|
||||||
ListHeader::new("CRDB")
|
ListHeader::new("CRDB")
|
||||||
.left_icon(Icon::Hash.into())
|
.left_icon(Icon::Hash.into())
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
)
|
)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
v_stack().id("channels").py_1().child(
|
v_stack().id("channels").py_1().child(
|
||||||
List::new(static_collab_panel_channels())
|
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.")
|
.empty_message("No channels yet. Add a channel to get started.")
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@ -52,9 +51,9 @@ impl CollabPanel {
|
|||||||
List::new(static_collab_panel_current_call())
|
List::new(static_collab_panel_current_call())
|
||||||
.header(
|
.header(
|
||||||
ListHeader::new("CONTACTS – ONLINE")
|
ListHeader::new("CONTACTS – ONLINE")
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
)
|
)
|
||||||
.toggle(ToggleState::Toggled),
|
.toggle(Toggle::Toggled(true)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@ -62,9 +61,9 @@ impl CollabPanel {
|
|||||||
List::new(static_collab_panel_current_call())
|
List::new(static_collab_panel_current_call())
|
||||||
.header(
|
.header(
|
||||||
ListHeader::new("CONTACTS – OFFLINE")
|
ListHeader::new("CONTACTS – OFFLINE")
|
||||||
.toggle(ToggleState::NotToggled),
|
.toggle(Toggle::Toggled(false)),
|
||||||
)
|
)
|
||||||
.toggle(ToggleState::NotToggled),
|
.toggle(Toggle::Toggled(false)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -93,7 +92,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct CollabPanelStory;
|
pub struct CollabPanelStory;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ pub use stories::*;
|
|||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ pub use stories::*;
|
|||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use gpui2::{Div, Render, View, VisualContext};
|
use gpui::{Div, Render, View, VisualContext};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -40,7 +40,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct LanguageSelectorStory;
|
pub struct LanguageSelectorStory;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ impl MultiBuffer {
|
|||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.p_4()
|
.p_4()
|
||||||
.bg(cx.theme().colors().editor_subheader)
|
.bg(cx.theme().colors().editor_subheader_background)
|
||||||
.child(Label::new("main.rs"))
|
.child(Label::new("main.rs"))
|
||||||
.child(IconButton::new("arrow_up_right", Icon::ArrowUpRight)),
|
.child(IconButton::new("arrow_up_right", Icon::ArrowUpRight)),
|
||||||
)
|
)
|
||||||
@ -40,7 +40,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{hello_world_rust_buffer_example, Story};
|
use crate::{hello_world_rust_buffer_example, Story};
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct MultiBufferStory;
|
pub struct MultiBufferStory;
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::utils::naive_format_distance_from_now;
|
use crate::utils::naive_format_distance_from_now;
|
||||||
use crate::{
|
use crate::{
|
||||||
h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, Button, Icon,
|
h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, ButtonOrIconButton,
|
||||||
IconButton, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator,
|
Icon, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator,
|
||||||
UnreadIndicator,
|
PublicPlayer, UnreadIndicator,
|
||||||
};
|
};
|
||||||
use crate::{ClickHandler, ListHeader};
|
use crate::{ClickHandler, ListHeader};
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ impl NotificationsPanel {
|
|||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.size_full()
|
.size_full()
|
||||||
.bg(cx.theme().colors().surface)
|
.bg(cx.theme().colors().surface_background)
|
||||||
.child(
|
.child(
|
||||||
ListHeader::new("Notifications").meta(Some(ListHeaderMeta::Tools(vec![
|
ListHeader::new("Notifications").meta(Some(ListHeaderMeta::Tools(vec![
|
||||||
Icon::AtSign,
|
Icon::AtSign,
|
||||||
@ -43,7 +43,7 @@ impl NotificationsPanel {
|
|||||||
.p_1()
|
.p_1()
|
||||||
// TODO: Add cursor style
|
// TODO: Add cursor style
|
||||||
// .cursor(Cursor::IBeam)
|
// .cursor(Cursor::IBeam)
|
||||||
.bg(cx.theme().colors().element)
|
.bg(cx.theme().colors().element_background)
|
||||||
.border()
|
.border()
|
||||||
.border_color(cx.theme().colors().border_variant)
|
.border_color(cx.theme().colors().border_variant)
|
||||||
.child(
|
.child(
|
||||||
@ -57,23 +57,6 @@ impl NotificationsPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ButtonOrIconButton<V: 'static> {
|
|
||||||
Button(Button<V>),
|
|
||||||
IconButton(IconButton<V>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> From<Button<V>> for ButtonOrIconButton<V> {
|
|
||||||
fn from(value: Button<V>) -> Self {
|
|
||||||
Self::Button(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> From<IconButton<V>> for ButtonOrIconButton<V> {
|
|
||||||
fn from(value: IconButton<V>) -> Self {
|
|
||||||
Self::IconButton(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NotificationAction<V: 'static> {
|
pub struct NotificationAction<V: 'static> {
|
||||||
button: ButtonOrIconButton<V>,
|
button: ButtonOrIconButton<V>,
|
||||||
tooltip: SharedString,
|
tooltip: SharedString,
|
||||||
@ -102,7 +85,7 @@ impl<V: 'static> NotificationAction<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub enum ActorOrIcon {
|
pub enum ActorOrIcon {
|
||||||
Actor(PublicActor),
|
Actor(PublicPlayer),
|
||||||
Icon(Icon),
|
Icon(Icon),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +154,7 @@ impl<V> Notification<V> {
|
|||||||
id: impl Into<ElementId>,
|
id: impl Into<ElementId>,
|
||||||
message: impl Into<SharedString>,
|
message: impl Into<SharedString>,
|
||||||
date_received: NaiveDateTime,
|
date_received: NaiveDateTime,
|
||||||
actor: PublicActor,
|
actor: PublicPlayer,
|
||||||
click_action: ClickHandler<V>,
|
click_action: ClickHandler<V>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
@ -210,7 +193,7 @@ impl<V> Notification<V> {
|
|||||||
id: impl Into<ElementId>,
|
id: impl Into<ElementId>,
|
||||||
message: impl Into<SharedString>,
|
message: impl Into<SharedString>,
|
||||||
date_received: NaiveDateTime,
|
date_received: NaiveDateTime,
|
||||||
actor: PublicActor,
|
actor: PublicPlayer,
|
||||||
actions: [NotificationAction<V>; 2],
|
actions: [NotificationAction<V>; 2],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
@ -362,7 +345,7 @@ impl<V> Notification<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use gpui2::{px, Styled};
|
use gpui::{px, Styled};
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
pub use stories::*;
|
pub use stories::*;
|
||||||
|
|
||||||
@ -370,7 +353,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Panel, Story};
|
use crate::{Panel, Story};
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct NotificationsPanelStory;
|
pub struct NotificationsPanelStory;
|
||||||
|
|
||||||
|
@ -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 smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -113,7 +113,7 @@ impl<V: 'static> PaneGroup<V> {
|
|||||||
.gap_px()
|
.gap_px()
|
||||||
.w_full()
|
.w_full()
|
||||||
.h_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)));
|
.children(self.groups.into_iter().map(|group| group.render(view, cx)));
|
||||||
|
|
||||||
if self.split_direction == SplitDirection::Horizontal {
|
if self.split_direction == SplitDirection::Horizontal {
|
||||||
|
@ -18,9 +18,8 @@ impl ProjectPanel {
|
|||||||
.id(self.id.clone())
|
.id(self.id.clone())
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.w_full()
|
.size_full()
|
||||||
.h_full()
|
.bg(cx.theme().colors().surface_background)
|
||||||
.bg(cx.theme().colors().surface)
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.id("project-panel-contents")
|
.id("project-panel-contents")
|
||||||
@ -30,15 +29,13 @@ impl ProjectPanel {
|
|||||||
.overflow_y_scroll()
|
.overflow_y_scroll()
|
||||||
.child(
|
.child(
|
||||||
List::new(static_project_panel_single_items())
|
List::new(static_project_panel_single_items())
|
||||||
.header(ListHeader::new("FILES").toggle(ToggleState::Toggled))
|
.header(ListHeader::new("FILES"))
|
||||||
.empty_message("No files in directory")
|
.empty_message("No files in directory"),
|
||||||
.toggle(ToggleState::Toggled),
|
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
List::new(static_project_panel_project_items())
|
List::new(static_project_panel_project_items())
|
||||||
.header(ListHeader::new("PROJECT").toggle(ToggleState::Toggled))
|
.header(ListHeader::new("PROJECT"))
|
||||||
.empty_message("No folders in directory")
|
.empty_message("No folders in directory"),
|
||||||
.toggle(ToggleState::Toggled),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@ -49,7 +46,7 @@ impl ProjectPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use gpui2::ElementId;
|
use gpui::ElementId;
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
pub use stories::*;
|
pub use stories::*;
|
||||||
|
|
||||||
@ -57,7 +54,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Panel, Story};
|
use crate::{Panel, Story};
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct ProjectPanelStory;
|
pub struct ProjectPanelStory;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct RecentProjectsStory;
|
pub struct RecentProjectsStory;
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ impl StatusBar {
|
|||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.w_full()
|
.w_full()
|
||||||
.bg(cx.theme().colors().status_bar)
|
.bg(cx.theme().colors().status_bar_background)
|
||||||
.child(self.left_tools(view, cx))
|
.child(self.left_tools(view, cx))
|
||||||
.child(self.right_tools(view, cx))
|
.child(self.right_tools(view, cx))
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ impl TabBar {
|
|||||||
.id(self.id.clone())
|
.id(self.id.clone())
|
||||||
.w_full()
|
.w_full()
|
||||||
.flex()
|
.flex()
|
||||||
.bg(cx.theme().colors().tab_bar)
|
.bg(cx.theme().colors().tab_bar_background)
|
||||||
// Left Side
|
// Left Side
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
@ -92,7 +92,7 @@ impl TabBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use gpui2::ElementId;
|
use gpui::ElementId;
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
pub use stories::*;
|
pub use stories::*;
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
pub struct TabBarStory;
|
pub struct TabBarStory;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::{relative, rems, Size};
|
use gpui::{relative, rems, Size};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{Icon, IconButton, Pane, Tab};
|
use crate::{Icon, IconButton, Pane, Tab};
|
||||||
@ -24,7 +24,7 @@ impl Terminal {
|
|||||||
div()
|
div()
|
||||||
.w_full()
|
.w_full()
|
||||||
.flex()
|
.flex()
|
||||||
.bg(cx.theme().colors().surface)
|
.bg(cx.theme().colors().surface_background)
|
||||||
.child(
|
.child(
|
||||||
div().px_1().flex().flex_none().gap_2().child(
|
div().px_1().flex().flex_none().gap_2().child(
|
||||||
div()
|
div()
|
||||||
@ -83,7 +83,7 @@ pub use stories::*;
|
|||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
pub struct TerminalStory;
|
pub struct TerminalStory;
|
||||||
|
|
||||||
impl Render for TerminalStory {
|
impl Render for TerminalStory {
|
||||||
|
@ -39,7 +39,7 @@ pub use stories::*;
|
|||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use gpui2::{Div, Render, View, VisualContext};
|
use gpui::{Div, Render, View, VisualContext};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::settings::user_settings;
|
use crate::settings::user_settings;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gpui2::AnyElement;
|
use gpui::AnyElement;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -56,7 +56,7 @@ impl<V: 'static> Toolbar<V> {
|
|||||||
|
|
||||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||||
div()
|
div()
|
||||||
.bg(cx.theme().colors().toolbar)
|
.bg(cx.theme().colors().toolbar_background)
|
||||||
.p_2()
|
.p_2()
|
||||||
.flex()
|
.flex()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
@ -73,7 +73,7 @@ mod stories {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol};
|
use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol};
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ impl TrafficLight {
|
|||||||
(true, TrafficLightColor::Red) => system_colors.mac_os_traffic_light_red,
|
(true, TrafficLightColor::Red) => system_colors.mac_os_traffic_light_red,
|
||||||
(true, TrafficLightColor::Yellow) => system_colors.mac_os_traffic_light_yellow,
|
(true, TrafficLightColor::Yellow) => system_colors.mac_os_traffic_light_yellow,
|
||||||
(true, TrafficLightColor::Green) => system_colors.mac_os_traffic_light_green,
|
(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)
|
div().w_3().h_3().rounded_full().bg(fill)
|
||||||
@ -77,7 +77,7 @@ pub use stories::*;
|
|||||||
|
|
||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use gpui2::{Div, Render};
|
use gpui::{Div, Render};
|
||||||
|
|
||||||
use crate::Story;
|
use crate::Story;
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::DateTime;
|
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 settings2::Settings;
|
||||||
use theme2::ThemeSettings;
|
use theme2::ThemeSettings;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
static_livestream, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, CollabPanel,
|
static_livestream, v_stack, AssistantPanel, Button, ChatMessage, ChatPanel, Checkbox,
|
||||||
EditorPane, Label, LanguageSelector, NotificationsPanel, Pane, PaneGroup, Panel,
|
CollabPanel, EditorPane, Label, LanguageSelector, NotificationsPanel, Pane, PaneGroup, Panel,
|
||||||
PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar,
|
PanelAllowedSides, PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar,
|
||||||
Toast, ToastOrigin,
|
Toast, ToastOrigin,
|
||||||
};
|
};
|
||||||
@ -42,6 +42,7 @@ pub struct Workspace {
|
|||||||
show_terminal: bool,
|
show_terminal: bool,
|
||||||
show_debug: bool,
|
show_debug: bool,
|
||||||
show_language_selector: bool,
|
show_language_selector: bool,
|
||||||
|
test_checkbox_selection: Selection,
|
||||||
debug: Gpui2UiDebug,
|
debug: Gpui2UiDebug,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ impl Workspace {
|
|||||||
show_language_selector: false,
|
show_language_selector: false,
|
||||||
show_debug: false,
|
show_debug: false,
|
||||||
show_notifications_panel: true,
|
show_notifications_panel: true,
|
||||||
|
test_checkbox_selection: Selection::Unselected,
|
||||||
debug: Gpui2UiDebug::default(),
|
debug: Gpui2UiDebug::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,6 +219,23 @@ impl Render for Workspace {
|
|||||||
.text_color(cx.theme().colors().text)
|
.text_color(cx.theme().colors().text)
|
||||||
.bg(cx.theme().colors().background)
|
.bg(cx.theme().colors().background)
|
||||||
.child(self.title_bar.clone())
|
.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(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
@ -354,7 +373,7 @@ pub use stories::*;
|
|||||||
#[cfg(feature = "stories")]
|
#[cfg(feature = "stories")]
|
||||||
mod stories {
|
mod stories {
|
||||||
use super::*;
|
use super::*;
|
||||||
use gpui2::VisualContext;
|
use gpui::VisualContext;
|
||||||
|
|
||||||
pub struct WorkspaceStory {
|
pub struct WorkspaceStory {
|
||||||
workspace: View<Workspace>,
|
workspace: View<Workspace>,
|
||||||
|
@ -26,7 +26,7 @@ use std::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ui::v_stack;
|
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;
|
use util::truncate_and_remove_front;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
||||||
@ -1359,16 +1359,31 @@ impl Pane {
|
|||||||
cx: &mut ViewContext<'_, Pane>,
|
cx: &mut ViewContext<'_, Pane>,
|
||||||
) -> impl Component<Self> {
|
) -> impl Component<Self> {
|
||||||
let label = item.tab_content(Some(detail), cx);
|
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();
|
||||||
|
|
||||||
let (tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index {
|
div()
|
||||||
|
.id(item.id())
|
||||||
|
.invisible()
|
||||||
|
.group_hover("", |style| style.visible())
|
||||||
|
.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 {
|
||||||
false => (
|
false => (
|
||||||
cx.theme().colors().tab_inactive,
|
cx.theme().colors().text_muted,
|
||||||
|
cx.theme().colors().tab_inactive_background,
|
||||||
cx.theme().colors().ghost_element_hover,
|
cx.theme().colors().ghost_element_hover,
|
||||||
cx.theme().colors().ghost_element_active,
|
cx.theme().colors().ghost_element_active,
|
||||||
),
|
),
|
||||||
true => (
|
true => (
|
||||||
cx.theme().colors().tab_active,
|
cx.theme().colors().text,
|
||||||
|
cx.theme().colors().tab_active_background,
|
||||||
cx.theme().colors().element_hover,
|
cx.theme().colors().element_hover,
|
||||||
cx.theme().colors().element_active,
|
cx.theme().colors().element_active,
|
||||||
),
|
),
|
||||||
@ -1377,7 +1392,12 @@ impl Pane {
|
|||||||
let close_right = ItemSettings::get_global(cx).close_position.right();
|
let close_right = ItemSettings::get_global(cx).close_position.right();
|
||||||
|
|
||||||
div()
|
div()
|
||||||
|
.group("")
|
||||||
.id(item.id())
|
.id(item.id())
|
||||||
|
.cursor_pointer()
|
||||||
|
.when_some(item.tab_tooltip_text(cx), |div, text| {
|
||||||
|
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))
|
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
|
||||||
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
|
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
|
||||||
// .on_drop(|_view, state: View<DraggedTab>, cx| {
|
// .on_drop(|_view, state: View<DraggedTab>, cx| {
|
||||||
@ -1397,6 +1417,7 @@ impl Pane {
|
|||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
|
.text_color(text_color)
|
||||||
.children(if item.has_conflict(cx) {
|
.children(if item.has_conflict(cx) {
|
||||||
Some(
|
Some(
|
||||||
IconElement::new(Icon::ExclamationTriangle)
|
IconElement::new(Icon::ExclamationTriangle)
|
||||||
@ -1432,7 +1453,7 @@ impl Pane {
|
|||||||
.id("tab_bar")
|
.id("tab_bar")
|
||||||
.w_full()
|
.w_full()
|
||||||
.flex()
|
.flex()
|
||||||
.bg(cx.theme().colors().tab_bar)
|
.bg(cx.theme().colors().tab_bar_background)
|
||||||
// Left Side
|
// Left Side
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
@ -1457,7 +1478,7 @@ impl Pane {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div().w_0().flex_1().h_full().child(
|
div().flex_1().h_full().child(
|
||||||
div().id("tabs").flex().overflow_x_scroll().children(
|
div().id("tabs").flex().overflow_x_scroll().children(
|
||||||
self.items
|
self.items
|
||||||
.iter()
|
.iter()
|
||||||
@ -1888,13 +1909,14 @@ impl Render for Pane {
|
|||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
v_stack()
|
v_stack()
|
||||||
|
.size_full()
|
||||||
.child(self.render_tab_bar(cx))
|
.child(self.render_tab_bar(cx))
|
||||||
.child(div() /* toolbar */)
|
.child(div() /* todo!(toolbar) */)
|
||||||
.child(if let Some(item) = self.active_item() {
|
.child(if let Some(item) = self.active_item() {
|
||||||
item.to_any().render()
|
div().flex_1().child(item.to_any())
|
||||||
} else {
|
} else {
|
||||||
// todo!()
|
// todo!()
|
||||||
div().child("Empty Pane").render()
|
div().child("Empty Pane")
|
||||||
})
|
})
|
||||||
|
|
||||||
// enum MouseNavigationHandler {}
|
// enum MouseNavigationHandler {}
|
||||||
|
@ -201,7 +201,7 @@ impl Member {
|
|||||||
// Some(pane)
|
// Some(pane)
|
||||||
// };
|
// };
|
||||||
|
|
||||||
div().child(pane.clone()).render()
|
div().size_full().child(pane.clone()).render()
|
||||||
|
|
||||||
// Stack::new()
|
// Stack::new()
|
||||||
// .with_child(pane_element.contained().with_border(leader_border))
|
// .with_child(pane_element.contained().with_border(leader_border))
|
||||||
|
@ -44,7 +44,8 @@ impl Render for StatusBar {
|
|||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.w_full()
|
.w_full()
|
||||||
.bg(cx.theme().colors().status_bar)
|
.h_8()
|
||||||
|
.bg(cx.theme().colors().status_bar_background)
|
||||||
.child(self.render_left_tools(cx))
|
.child(self.render_left_tools(cx))
|
||||||
.child(self.render_right_tools(cx))
|
.child(self.render_right_tools(cx))
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user