Remove old ui
and storybook
crates (#3125)
This PR deletes the old `ui` and `storybook` crates in favor of their newer variants that we'll be landing to `main` in the near future. ### Motivation These crates are based off the old version of GPUI 2 (the `gpui2` crate). At this point we have since transitioned to the new version of GPUI 2 (the `gpui3` crate, currently still on the `gpui2` branch). Having both copies around is confusing, so the old ones are going the way of the dinosaurs. Release Notes: - N/A
This commit is contained in:
parent
bac43ae38e
commit
45f3a98359
91 changed files with 0 additions and 10580 deletions
59
Cargo.lock
generated
59
Cargo.lock
generated
|
@ -6623,12 +6623,6 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustybuzz"
|
name = "rustybuzz"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -7682,28 +7676,6 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "storybook"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"chrono",
|
|
||||||
"clap 4.4.4",
|
|
||||||
"fs",
|
|
||||||
"futures 0.3.28",
|
|
||||||
"gpui2",
|
|
||||||
"itertools 0.11.0",
|
|
||||||
"log",
|
|
||||||
"rust-embed",
|
|
||||||
"serde",
|
|
||||||
"settings",
|
|
||||||
"simplelog",
|
|
||||||
"strum",
|
|
||||||
"theme",
|
|
||||||
"ui",
|
|
||||||
"util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stringprep"
|
name = "stringprep"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -7726,22 +7698,6 @@ name = "strum"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
||||||
dependencies = [
|
|
||||||
"strum_macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum_macros"
|
|
||||||
version = "0.25.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059"
|
|
||||||
dependencies = [
|
|
||||||
"heck 0.4.1",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rustversion",
|
|
||||||
"syn 2.0.37",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
|
@ -8908,21 +8864,6 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ui"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"chrono",
|
|
||||||
"gpui2",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"serde",
|
|
||||||
"settings",
|
|
||||||
"smallvec",
|
|
||||||
"strum",
|
|
||||||
"theme",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
|
|
|
@ -66,13 +66,11 @@ members = [
|
||||||
"crates/sqlez_macros",
|
"crates/sqlez_macros",
|
||||||
"crates/feature_flags",
|
"crates/feature_flags",
|
||||||
"crates/rich_text",
|
"crates/rich_text",
|
||||||
"crates/storybook",
|
|
||||||
"crates/sum_tree",
|
"crates/sum_tree",
|
||||||
"crates/terminal",
|
"crates/terminal",
|
||||||
"crates/text",
|
"crates/text",
|
||||||
"crates/theme",
|
"crates/theme",
|
||||||
"crates/theme_selector",
|
"crates/theme_selector",
|
||||||
"crates/ui",
|
|
||||||
"crates/util",
|
"crates/util",
|
||||||
"crates/semantic_index",
|
"crates/semantic_index",
|
||||||
"crates/vim",
|
"crates/vim",
|
||||||
|
|
2919
crates/storybook/Cargo.lock
generated
2919
crates/storybook/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,30 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "storybook"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "storybook"
|
|
||||||
path = "src/storybook.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow.workspace = true
|
|
||||||
clap = { version = "4.4", features = ["derive", "string"] }
|
|
||||||
chrono = "0.4"
|
|
||||||
fs = { path = "../fs" }
|
|
||||||
futures.workspace = true
|
|
||||||
gpui2 = { path = "../gpui2" }
|
|
||||||
itertools = "0.11.0"
|
|
||||||
log.workspace = true
|
|
||||||
rust-embed.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
settings = { path = "../settings" }
|
|
||||||
simplelog = "0.9"
|
|
||||||
strum = { version = "0.25.0", features = ["derive"] }
|
|
||||||
theme = { path = "../theme" }
|
|
||||||
ui = { path = "../ui" }
|
|
||||||
util = { path = "../util" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
gpui2 = { path = "../gpui2", features = ["test-support"] }
|
|
|
@ -1,72 +0,0 @@
|
||||||
Much of element styling is now handled by an external engine.
|
|
||||||
|
|
||||||
|
|
||||||
How do I make an element hover.
|
|
||||||
|
|
||||||
There's a hover style.
|
|
||||||
|
|
||||||
Hoverable needs to wrap another element. That element can be styled.
|
|
||||||
|
|
||||||
```rs
|
|
||||||
struct Hoverable<E: Element> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Element<V> for Hoverable {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```rs
|
|
||||||
#[derive(Styled, Interactive)]
|
|
||||||
pub struct Div {
|
|
||||||
declared_style: StyleRefinement,
|
|
||||||
interactions: Interactions
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Styled {
|
|
||||||
fn declared_style(&mut self) -> &mut StyleRefinement;
|
|
||||||
fn compute_style(&mut self) -> Style {
|
|
||||||
Style::default().refine(self.declared_style())
|
|
||||||
}
|
|
||||||
|
|
||||||
// All the tailwind classes, modifying self.declared_style()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Style {
|
|
||||||
pub fn paint_background<V>(layout: Layout, cx: &mut PaintContext<V>);
|
|
||||||
pub fn paint_foreground<V>(layout: Layout, cx: &mut PaintContext<V>);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Interactive<V> {
|
|
||||||
fn interactions(&mut self) -> &mut Interactions<V>;
|
|
||||||
|
|
||||||
fn on_click(self, )
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Interactions<V> {
|
|
||||||
click: SmallVec<[<Rc<dyn Fn(&mut V, &dyn Any, )>; 1]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
```rs
|
|
||||||
|
|
||||||
|
|
||||||
trait Stylable {
|
|
||||||
type Style;
|
|
||||||
|
|
||||||
fn with_style(self, style: Self::Style) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
|
@ -1,3 +0,0 @@
|
||||||
pub mod components;
|
|
||||||
pub mod elements;
|
|
||||||
pub mod kitchen_sink;
|
|
|
@ -1,22 +0,0 @@
|
||||||
pub mod assistant_panel;
|
|
||||||
pub mod breadcrumb;
|
|
||||||
pub mod buffer;
|
|
||||||
pub mod chat_panel;
|
|
||||||
pub mod collab_panel;
|
|
||||||
pub mod context_menu;
|
|
||||||
pub mod facepile;
|
|
||||||
pub mod keybinding;
|
|
||||||
pub mod language_selector;
|
|
||||||
pub mod multi_buffer;
|
|
||||||
pub mod palette;
|
|
||||||
pub mod panel;
|
|
||||||
pub mod project_panel;
|
|
||||||
pub mod recent_projects;
|
|
||||||
pub mod status_bar;
|
|
||||||
pub mod tab;
|
|
||||||
pub mod tab_bar;
|
|
||||||
pub mod terminal;
|
|
||||||
pub mod theme_selector;
|
|
||||||
pub mod title_bar;
|
|
||||||
pub mod toolbar;
|
|
||||||
pub mod traffic_lights;
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::AssistantPanel;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct AssistantPanelStory {}
|
|
||||||
|
|
||||||
impl AssistantPanelStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, AssistantPanel<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(AssistantPanel::new())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{Breadcrumb, HighlightedText, Symbol};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct BreadcrumbStory {}
|
|
||||||
|
|
||||||
impl BreadcrumbStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Breadcrumb>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Breadcrumb::new(
|
|
||||||
PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
|
|
||||||
vec![
|
|
||||||
Symbol(vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "impl ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "BreadcrumbStory".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Symbol(vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "fn ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "render".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
use gpui2::geometry::rems;
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{
|
|
||||||
empty_buffer_example, hello_world_rust_buffer_example,
|
|
||||||
hello_world_rust_buffer_with_status_example, Buffer,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct BufferStory {}
|
|
||||||
|
|
||||||
impl BufferStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Buffer>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(div().w(rems(64.)).h_96().child(empty_buffer_example()))
|
|
||||||
.child(Story::label(cx, "Hello World (Rust)"))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.w(rems(64.))
|
|
||||||
.h_96()
|
|
||||||
.child(hello_world_rust_buffer_example(&theme)),
|
|
||||||
)
|
|
||||||
.child(Story::label(cx, "Hello World (Rust) with Status"))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.w(rems(64.))
|
|
||||||
.h_96()
|
|
||||||
.child(hello_world_rust_buffer_with_status_example(&theme)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
use chrono::DateTime;
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{ChatMessage, ChatPanel, Panel};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct ChatPanelStory {}
|
|
||||||
|
|
||||||
impl ChatPanelStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, ChatPanel<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Panel::new(
|
|
||||||
ScrollState::default(),
|
|
||||||
|_, _| vec![ChatPanel::new(ScrollState::default()).into_any()],
|
|
||||||
Box::new(()),
|
|
||||||
))
|
|
||||||
.child(Story::label(cx, "With Mesages"))
|
|
||||||
.child(Panel::new(
|
|
||||||
ScrollState::default(),
|
|
||||||
|_, _| {
|
|
||||||
vec![ChatPanel::new(ScrollState::default())
|
|
||||||
.with_messages(vec![
|
|
||||||
ChatMessage::new(
|
|
||||||
"osiewicz".to_string(),
|
|
||||||
"is this thing on?".to_string(),
|
|
||||||
DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
|
|
||||||
.unwrap()
|
|
||||||
.naive_local(),
|
|
||||||
),
|
|
||||||
ChatMessage::new(
|
|
||||||
"maxdeviant".to_string(),
|
|
||||||
"Reading you loud and clear!".to_string(),
|
|
||||||
DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
|
|
||||||
.unwrap()
|
|
||||||
.naive_local(),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.into_any()]
|
|
||||||
},
|
|
||||||
Box::new(()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::CollabPanel;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct CollabPanelStory {}
|
|
||||||
|
|
||||||
impl CollabPanelStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, CollabPanel<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(CollabPanel::new(ScrollState::default()))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{ContextMenu, ContextMenuItem, Label};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct ContextMenuStory {}
|
|
||||||
|
|
||||||
impl ContextMenuStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
//.fill(theme.middle.base.default.background)
|
|
||||||
.child(Story::title_for::<_, ContextMenu>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(ContextMenu::new([
|
|
||||||
ContextMenuItem::header("Section header"),
|
|
||||||
ContextMenuItem::Separator,
|
|
||||||
ContextMenuItem::entry(Label::new("Some entry")),
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{static_players, Facepile};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct FacepileStory {}
|
|
||||||
|
|
||||||
impl FacepileStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let players = static_players();
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Facepile>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_3()
|
|
||||||
.child(Facepile::new(players.clone().into_iter().take(1)))
|
|
||||||
.child(Facepile::new(players.clone().into_iter().take(2)))
|
|
||||||
.child(Facepile::new(players.clone().into_iter().take(3))),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
use itertools::Itertools;
|
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{Keybinding, ModifierKey, ModifierKeys};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct KeybindingStory {}
|
|
||||||
|
|
||||||
impl KeybindingStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let all_modifier_permutations = ModifierKey::iter().permutations(2);
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Keybinding>(cx))
|
|
||||||
.child(Story::label(cx, "Single Key"))
|
|
||||||
.child(Keybinding::new("Z".to_string(), ModifierKeys::new()))
|
|
||||||
.child(Story::label(cx, "Single Key with Modifier"))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_3()
|
|
||||||
.children(ModifierKey::iter().map(|modifier| {
|
|
||||||
Keybinding::new("C".to_string(), ModifierKeys::new().add(modifier))
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.child(Story::label(cx, "Single Key with Modifier (Permuted)"))
|
|
||||||
.child(
|
|
||||||
div().flex().flex_col().children(
|
|
||||||
all_modifier_permutations
|
|
||||||
.chunks(4)
|
|
||||||
.into_iter()
|
|
||||||
.map(|chunk| {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_4()
|
|
||||||
.py_3()
|
|
||||||
.children(chunk.map(|permutation| {
|
|
||||||
let mut modifiers = ModifierKeys::new();
|
|
||||||
|
|
||||||
for modifier in permutation {
|
|
||||||
modifiers = modifiers.add(modifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
Keybinding::new("X".to_string(), modifiers)
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(Story::label(cx, "Single Key with All Modifiers"))
|
|
||||||
.child(Keybinding::new("Z".to_string(), ModifierKeys::all()))
|
|
||||||
.child(Story::label(cx, "Chord"))
|
|
||||||
.child(Keybinding::new_chord(
|
|
||||||
("A".to_string(), ModifierKeys::new()),
|
|
||||||
("Z".to_string(), ModifierKeys::new()),
|
|
||||||
))
|
|
||||||
.child(Story::label(cx, "Chord with Modifier"))
|
|
||||||
.child(Keybinding::new_chord(
|
|
||||||
("A".to_string(), ModifierKeys::new().control(true)),
|
|
||||||
("Z".to_string(), ModifierKeys::new().shift(true)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::LanguageSelector;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct LanguageSelectorStory {}
|
|
||||||
|
|
||||||
impl LanguageSelectorStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, LanguageSelector>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(LanguageSelector::new())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{hello_world_rust_buffer_example, MultiBuffer};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct MultiBufferStory {}
|
|
||||||
|
|
||||||
impl MultiBufferStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, MultiBuffer<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(MultiBuffer::new(vec![
|
|
||||||
hello_world_rust_buffer_example(&theme),
|
|
||||||
hello_world_rust_buffer_example(&theme),
|
|
||||||
hello_world_rust_buffer_example(&theme),
|
|
||||||
hello_world_rust_buffer_example(&theme),
|
|
||||||
hello_world_rust_buffer_example(&theme),
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{Keybinding, ModifierKeys, Palette, PaletteItem};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct PaletteStory {}
|
|
||||||
|
|
||||||
impl PaletteStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Palette<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Palette::new(ScrollState::default()))
|
|
||||||
.child(Story::label(cx, "With Items"))
|
|
||||||
.child(
|
|
||||||
Palette::new(ScrollState::default())
|
|
||||||
.placeholder("Execute a command...")
|
|
||||||
.items(vec![
|
|
||||||
PaletteItem::new("theme selector: toggle").keybinding(
|
|
||||||
Keybinding::new_chord(
|
|
||||||
("k".to_string(), ModifierKeys::new().command(true)),
|
|
||||||
("t".to_string(), ModifierKeys::new().command(true)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PaletteItem::new("assistant: inline assist").keybinding(Keybinding::new(
|
|
||||||
"enter".to_string(),
|
|
||||||
ModifierKeys::new().command(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("assistant: quote selection").keybinding(Keybinding::new(
|
|
||||||
">".to_string(),
|
|
||||||
ModifierKeys::new().command(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("assistant: toggle focus").keybinding(Keybinding::new(
|
|
||||||
"?".to_string(),
|
|
||||||
ModifierKeys::new().command(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("auto update: check"),
|
|
||||||
PaletteItem::new("auto update: view release notes"),
|
|
||||||
PaletteItem::new("branches: open recent").keybinding(Keybinding::new(
|
|
||||||
"b".to_string(),
|
|
||||||
ModifierKeys::new().command(true).alt(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("chat panel: toggle focus"),
|
|
||||||
PaletteItem::new("cli: install"),
|
|
||||||
PaletteItem::new("client: sign in"),
|
|
||||||
PaletteItem::new("client: sign out"),
|
|
||||||
PaletteItem::new("editor: cancel")
|
|
||||||
.keybinding(Keybinding::new("escape".to_string(), ModifierKeys::new())),
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{Label, Panel};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct PanelStory {}
|
|
||||||
|
|
||||||
impl PanelStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Panel<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Panel::new(
|
|
||||||
ScrollState::default(),
|
|
||||||
|_, _| {
|
|
||||||
vec![div()
|
|
||||||
.overflow_y_scroll(ScrollState::default())
|
|
||||||
.children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1))))
|
|
||||||
.into_any()]
|
|
||||||
},
|
|
||||||
Box::new(()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{Panel, ProjectPanel};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct ProjectPanelStory {}
|
|
||||||
|
|
||||||
impl ProjectPanelStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, ProjectPanel<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Panel::new(
|
|
||||||
ScrollState::default(),
|
|
||||||
|_, _| vec![ProjectPanel::new(ScrollState::default()).into_any()],
|
|
||||||
Box::new(()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::RecentProjects;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct RecentProjectsStory {}
|
|
||||||
|
|
||||||
impl RecentProjectsStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, RecentProjects>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(RecentProjects::new())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::StatusBar;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct StatusBarStory {}
|
|
||||||
|
|
||||||
impl StatusBarStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, StatusBar<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(StatusBar::new())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{h_stack, v_stack, Tab};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct TabStory {}
|
|
||||||
|
|
||||||
impl TabStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let git_statuses = GitStatus::iter();
|
|
||||||
let fs_statuses = FileSystemStatus::iter();
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Tab>(cx))
|
|
||||||
.child(
|
|
||||||
h_stack().child(
|
|
||||||
v_stack()
|
|
||||||
.gap_2()
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Tab::new()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_stack().child(
|
|
||||||
v_stack().gap_2().child(Story::label(cx, "Current")).child(
|
|
||||||
h_stack()
|
|
||||||
.gap_4()
|
|
||||||
.child(Tab::new().title("Current".to_string()).current(true))
|
|
||||||
.child(Tab::new().title("Not Current".to_string()).current(false)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_stack().child(
|
|
||||||
v_stack()
|
|
||||||
.gap_2()
|
|
||||||
.child(Story::label(cx, "Titled"))
|
|
||||||
.child(Tab::new().title("label".to_string())),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_stack().child(
|
|
||||||
v_stack()
|
|
||||||
.gap_2()
|
|
||||||
.child(Story::label(cx, "With Icon"))
|
|
||||||
.child(
|
|
||||||
Tab::new()
|
|
||||||
.title("label".to_string())
|
|
||||||
.icon(Some(ui::Icon::Envelope)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_stack().child(
|
|
||||||
v_stack()
|
|
||||||
.gap_2()
|
|
||||||
.child(Story::label(cx, "Close Side"))
|
|
||||||
.child(
|
|
||||||
h_stack()
|
|
||||||
.gap_4()
|
|
||||||
.child(
|
|
||||||
Tab::new()
|
|
||||||
.title("Left".to_string())
|
|
||||||
.close_side(IconSide::Left),
|
|
||||||
)
|
|
||||||
.child(Tab::new().title("Right".to_string())),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.gap_2()
|
|
||||||
.child(Story::label(cx, "Git Status"))
|
|
||||||
.child(h_stack().gap_4().children(git_statuses.map(|git_status| {
|
|
||||||
Tab::new()
|
|
||||||
.title(git_status.to_string())
|
|
||||||
.git_status(git_status)
|
|
||||||
}))),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.gap_2()
|
|
||||||
.child(Story::label(cx, "File System Status"))
|
|
||||||
.child(h_stack().gap_4().children(fs_statuses.map(|fs_status| {
|
|
||||||
Tab::new().title(fs_status.to_string()).fs_status(fs_status)
|
|
||||||
}))),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{Tab, TabBar};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct TabBarStory {}
|
|
||||||
|
|
||||||
impl TabBarStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, TabBar<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(TabBar::new(vec![
|
|
||||||
Tab::new()
|
|
||||||
.title("Cargo.toml".to_string())
|
|
||||||
.current(false)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
Tab::new()
|
|
||||||
.title("Channels Panel".to_string())
|
|
||||||
.current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("channels_panel.rs".to_string())
|
|
||||||
.current(true)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
Tab::new()
|
|
||||||
.title("workspace.rs".to_string())
|
|
||||||
.current(false)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
Tab::new()
|
|
||||||
.title("icon_button.rs".to_string())
|
|
||||||
.current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("storybook.rs".to_string())
|
|
||||||
.current(false)
|
|
||||||
.git_status(GitStatus::Created),
|
|
||||||
Tab::new().title("theme.rs".to_string()).current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("theme_registry.rs".to_string())
|
|
||||||
.current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("styleable_helpers.rs".to_string())
|
|
||||||
.current(false),
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::Terminal;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct TerminalStory {}
|
|
||||||
|
|
||||||
impl TerminalStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Terminal>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Terminal::new())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::ThemeSelector;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct ThemeSelectorStory {}
|
|
||||||
|
|
||||||
impl ThemeSelectorStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, ThemeSelector>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(ThemeSelector::new())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::TitleBar;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct TitleBarStory {}
|
|
||||||
|
|
||||||
impl TitleBarStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, TitleBar<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(TitleBar::new(cx))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{theme, Breadcrumb, HighlightColor, HighlightedText, Icon, IconButton, Symbol, Toolbar};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct ToolbarStory {}
|
|
||||||
|
|
||||||
impl ToolbarStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
struct LeftItemsPayload {
|
|
||||||
pub theme: Arc<Theme>,
|
|
||||||
}
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Toolbar<V>>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Toolbar::new(
|
|
||||||
|_, payload| {
|
|
||||||
let payload = payload.downcast_ref::<LeftItemsPayload>().unwrap();
|
|
||||||
|
|
||||||
let theme = payload.theme.clone();
|
|
||||||
|
|
||||||
vec![Breadcrumb::new(
|
|
||||||
PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
|
|
||||||
vec![
|
|
||||||
Symbol(vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "impl ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "ToolbarStory".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
Symbol(vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "fn ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "render".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.into_any()]
|
|
||||||
},
|
|
||||||
Box::new(LeftItemsPayload {
|
|
||||||
theme: theme.clone(),
|
|
||||||
}),
|
|
||||||
|_, _| {
|
|
||||||
vec![
|
|
||||||
IconButton::new(Icon::InlayHint).into_any(),
|
|
||||||
IconButton::new(Icon::MagnifyingGlass).into_any(),
|
|
||||||
IconButton::new(Icon::MagicWand).into_any(),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
Box::new(()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::TrafficLights;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct TrafficLightsStory {}
|
|
||||||
|
|
||||||
impl TrafficLightsStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, TrafficLights>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(TrafficLights::new())
|
|
||||||
.child(Story::label(cx, "Unfocused"))
|
|
||||||
.child(TrafficLights::new().window_has_focus(false))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
pub mod avatar;
|
|
||||||
pub mod button;
|
|
||||||
pub mod icon;
|
|
||||||
pub mod input;
|
|
||||||
pub mod label;
|
|
|
@ -1,23 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::Avatar;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct AvatarStory {}
|
|
||||||
|
|
||||||
impl AvatarStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Avatar>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Avatar::new(
|
|
||||||
"https://avatars.githubusercontent.com/u/1714999?v=4",
|
|
||||||
))
|
|
||||||
.child(Story::label(cx, "Rounded rectangle"))
|
|
||||||
.child(
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
|
|
||||||
.shape(Shape::RoundedRectangle),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,192 +0,0 @@
|
||||||
use gpui2::elements::div;
|
|
||||||
use gpui2::geometry::rems;
|
|
||||||
use gpui2::{Element, IntoElement, ViewContext};
|
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{h_stack, v_stack, Button, Icon, IconPosition, Label};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct ButtonStory {}
|
|
||||||
|
|
||||||
impl ButtonStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let states = InteractionState::iter();
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Button<V>>(cx))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_8()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.child(Story::label(cx, "Ghost (Default)"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Ghost)
|
|
||||||
.state(state),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label(cx, "Ghost – Left Icon"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Ghost)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Left)
|
|
||||||
.state(state),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label(cx, "Ghost – Right Icon"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Ghost)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Right)
|
|
||||||
.state(state),
|
|
||||||
)
|
|
||||||
}))),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.child(Story::label(cx, "Filled"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
.state(state),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label(cx, "Filled – Left Button"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Left)
|
|
||||||
.state(state),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label(cx, "Filled – Right Button"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Right)
|
|
||||||
.state(state),
|
|
||||||
)
|
|
||||||
}))),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.child(Story::label(cx, "Fixed With"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
.state(state)
|
|
||||||
.width(Some(rems(6.).into())),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label(cx, "Fixed With – Left Icon"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
.state(state)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Left)
|
|
||||||
.width(Some(rems(6.).into())),
|
|
||||||
)
|
|
||||||
})))
|
|
||||||
.child(Story::label(cx, "Fixed With – Right Icon"))
|
|
||||||
.child(h_stack().gap_2().children(states.clone().map(|state| {
|
|
||||||
v_stack()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(state.to_string())
|
|
||||||
.color(ui::LabelColor::Muted)
|
|
||||||
.size(ui::LabelSize::Small),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Filled)
|
|
||||||
.state(state)
|
|
||||||
.icon(Icon::Plus)
|
|
||||||
.icon_position(IconPosition::Right)
|
|
||||||
.width(Some(rems(6.).into())),
|
|
||||||
)
|
|
||||||
}))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(Story::label(cx, "Button with `on_click`"))
|
|
||||||
.child(
|
|
||||||
Button::new("Label")
|
|
||||||
.variant(ButtonVariant::Ghost)
|
|
||||||
// NOTE: There currently appears to be a bug in GPUI2 where only the last event handler will fire.
|
|
||||||
// So adding additional buttons with `on_click`s after this one will cause this `on_click` to not fire.
|
|
||||||
.on_click(|_view, _cx| println!("Button clicked.")),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{Icon, IconElement};
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct IconStory {}
|
|
||||||
|
|
||||||
impl IconStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let icons = Icon::iter();
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, IconElement>(cx))
|
|
||||||
.child(Story::label(cx, "All Icons"))
|
|
||||||
.child(div().flex().gap_3().children(icons.map(IconElement::new)))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::Input;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct InputStory {}
|
|
||||||
|
|
||||||
impl InputStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Input>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(div().flex().child(Input::new("Search")))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::Label;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct LabelStory {}
|
|
||||||
|
|
||||||
impl LabelStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
Story::container(cx)
|
|
||||||
.child(Story::title_for::<_, Label>(cx))
|
|
||||||
.child(Story::label(cx, "Default"))
|
|
||||||
.child(Label::new("Hello, world!"))
|
|
||||||
.child(Story::label(cx, "Highlighted"))
|
|
||||||
.child(Label::new("Hello, world!").with_highlights(vec![0, 1, 2, 7, 8, 12]))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
use ui::prelude::*;
|
|
||||||
|
|
||||||
use crate::story::Story;
|
|
||||||
use crate::story_selector::{ComponentStory, ElementStory};
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct KitchenSinkStory {}
|
|
||||||
|
|
||||||
impl KitchenSinkStory {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let element_stories = ElementStory::iter().map(|selector| selector.story());
|
|
||||||
let component_stories = ComponentStory::iter().map(|selector| selector.story());
|
|
||||||
|
|
||||||
Story::container(cx)
|
|
||||||
.overflow_y_scroll(ScrollState::default())
|
|
||||||
.child(Story::title(cx, "Kitchen Sink"))
|
|
||||||
.child(Story::label(cx, "Elements"))
|
|
||||||
.child(div().flex().flex_col().children_any(element_stories))
|
|
||||||
.child(Story::label(cx, "Components"))
|
|
||||||
.child(div().flex().flex_col().children_any(component_stories))
|
|
||||||
// Add a bit of space at the bottom of the kitchen sink so elements
|
|
||||||
// don't end up squished right up against the bottom of the screen.
|
|
||||||
.child(div().p_4())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
use gpui2::elements::div::Div;
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::theme;
|
|
||||||
|
|
||||||
pub struct Story {}
|
|
||||||
|
|
||||||
impl Story {
|
|
||||||
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.size_full()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.pt_2()
|
|
||||||
.px_4()
|
|
||||||
.font("Zed Mono Extended")
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: &str) -> impl Element<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.text_xl()
|
|
||||||
.text_color(theme.lowest.base.default.foreground)
|
|
||||||
.child(title.to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Element<V> {
|
|
||||||
Self::title(cx, std::any::type_name::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn label<V: 'static>(cx: &mut ViewContext<V>, label: &str) -> impl Element<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.mt_4()
|
|
||||||
.mb_2()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(theme.lowest.base.default.foreground)
|
|
||||||
.child(label.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,178 +0,0 @@
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Context};
|
|
||||||
use clap::builder::PossibleValue;
|
|
||||||
use clap::ValueEnum;
|
|
||||||
use gpui2::{AnyElement, Element};
|
|
||||||
use strum::{EnumIter, EnumString, IntoEnumIterator};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)]
|
|
||||||
#[strum(serialize_all = "snake_case")]
|
|
||||||
pub enum ElementStory {
|
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Label,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ElementStory {
|
|
||||||
pub fn story<V: 'static>(&self) -> AnyElement<V> {
|
|
||||||
use crate::stories::elements;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::Avatar => elements::avatar::AvatarStory::default().into_any(),
|
|
||||||
Self::Button => elements::button::ButtonStory::default().into_any(),
|
|
||||||
Self::Icon => elements::icon::IconStory::default().into_any(),
|
|
||||||
Self::Input => elements::input::InputStory::default().into_any(),
|
|
||||||
Self::Label => elements::label::LabelStory::default().into_any(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)]
|
|
||||||
#[strum(serialize_all = "snake_case")]
|
|
||||||
pub enum ComponentStory {
|
|
||||||
AssistantPanel,
|
|
||||||
Breadcrumb,
|
|
||||||
Buffer,
|
|
||||||
ContextMenu,
|
|
||||||
ChatPanel,
|
|
||||||
CollabPanel,
|
|
||||||
Facepile,
|
|
||||||
Keybinding,
|
|
||||||
LanguageSelector,
|
|
||||||
MultiBuffer,
|
|
||||||
Palette,
|
|
||||||
Panel,
|
|
||||||
ProjectPanel,
|
|
||||||
RecentProjects,
|
|
||||||
StatusBar,
|
|
||||||
Tab,
|
|
||||||
TabBar,
|
|
||||||
Terminal,
|
|
||||||
ThemeSelector,
|
|
||||||
TitleBar,
|
|
||||||
Toolbar,
|
|
||||||
TrafficLights,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentStory {
|
|
||||||
pub fn story<V: 'static>(&self) -> AnyElement<V> {
|
|
||||||
use crate::stories::components;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::AssistantPanel => {
|
|
||||||
components::assistant_panel::AssistantPanelStory::default().into_any()
|
|
||||||
}
|
|
||||||
Self::Breadcrumb => components::breadcrumb::BreadcrumbStory::default().into_any(),
|
|
||||||
Self::Buffer => components::buffer::BufferStory::default().into_any(),
|
|
||||||
Self::ContextMenu => components::context_menu::ContextMenuStory::default().into_any(),
|
|
||||||
Self::ChatPanel => components::chat_panel::ChatPanelStory::default().into_any(),
|
|
||||||
Self::CollabPanel => components::collab_panel::CollabPanelStory::default().into_any(),
|
|
||||||
Self::Facepile => components::facepile::FacepileStory::default().into_any(),
|
|
||||||
Self::Keybinding => components::keybinding::KeybindingStory::default().into_any(),
|
|
||||||
Self::LanguageSelector => {
|
|
||||||
components::language_selector::LanguageSelectorStory::default().into_any()
|
|
||||||
}
|
|
||||||
Self::MultiBuffer => components::multi_buffer::MultiBufferStory::default().into_any(),
|
|
||||||
Self::Palette => components::palette::PaletteStory::default().into_any(),
|
|
||||||
Self::Panel => components::panel::PanelStory::default().into_any(),
|
|
||||||
Self::ProjectPanel => {
|
|
||||||
components::project_panel::ProjectPanelStory::default().into_any()
|
|
||||||
}
|
|
||||||
Self::RecentProjects => {
|
|
||||||
components::recent_projects::RecentProjectsStory::default().into_any()
|
|
||||||
}
|
|
||||||
Self::StatusBar => components::status_bar::StatusBarStory::default().into_any(),
|
|
||||||
Self::Tab => components::tab::TabStory::default().into_any(),
|
|
||||||
Self::TabBar => components::tab_bar::TabBarStory::default().into_any(),
|
|
||||||
Self::Terminal => components::terminal::TerminalStory::default().into_any(),
|
|
||||||
Self::ThemeSelector => {
|
|
||||||
components::theme_selector::ThemeSelectorStory::default().into_any()
|
|
||||||
}
|
|
||||||
Self::TitleBar => components::title_bar::TitleBarStory::default().into_any(),
|
|
||||||
Self::Toolbar => components::toolbar::ToolbarStory::default().into_any(),
|
|
||||||
Self::TrafficLights => {
|
|
||||||
components::traffic_lights::TrafficLightsStory::default().into_any()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum StorySelector {
|
|
||||||
Element(ElementStory),
|
|
||||||
Component(ComponentStory),
|
|
||||||
KitchenSink,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for StorySelector {
|
|
||||||
type Err = anyhow::Error;
|
|
||||||
|
|
||||||
fn from_str(raw_story_name: &str) -> std::result::Result<Self, Self::Err> {
|
|
||||||
let story = raw_story_name.to_ascii_lowercase();
|
|
||||||
|
|
||||||
if story == "kitchen_sink" {
|
|
||||||
return Ok(Self::KitchenSink);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((_, story)) = story.split_once("elements/") {
|
|
||||||
let element_story = ElementStory::from_str(story)
|
|
||||||
.with_context(|| format!("story not found for element '{story}'"))?;
|
|
||||||
|
|
||||||
return Ok(Self::Element(element_story));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((_, story)) = story.split_once("components/") {
|
|
||||||
let component_story = ComponentStory::from_str(story)
|
|
||||||
.with_context(|| format!("story not found for component '{story}'"))?;
|
|
||||||
|
|
||||||
return Ok(Self::Component(component_story));
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(anyhow!("story not found for '{raw_story_name}'"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StorySelector {
|
|
||||||
pub fn story<V: 'static>(&self) -> AnyElement<V> {
|
|
||||||
match self {
|
|
||||||
Self::Element(element_story) => element_story.story(),
|
|
||||||
Self::Component(component_story) => component_story.story(),
|
|
||||||
Self::KitchenSink => {
|
|
||||||
crate::stories::kitchen_sink::KitchenSinkStory::default().into_any()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of all stories available in the storybook.
|
|
||||||
static ALL_STORY_SELECTORS: OnceLock<Vec<StorySelector>> = OnceLock::new();
|
|
||||||
|
|
||||||
impl ValueEnum for StorySelector {
|
|
||||||
fn value_variants<'a>() -> &'a [Self] {
|
|
||||||
let stories = ALL_STORY_SELECTORS.get_or_init(|| {
|
|
||||||
let element_stories = ElementStory::iter().map(StorySelector::Element);
|
|
||||||
let component_stories = ComponentStory::iter().map(StorySelector::Component);
|
|
||||||
|
|
||||||
element_stories
|
|
||||||
.chain(component_stories)
|
|
||||||
.chain(std::iter::once(StorySelector::KitchenSink))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
});
|
|
||||||
|
|
||||||
stories
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
|
|
||||||
let value = match self {
|
|
||||||
Self::Element(story) => format!("elements/{story}"),
|
|
||||||
Self::Component(story) => format!("components/{story}"),
|
|
||||||
Self::KitchenSink => "kitchen_sink".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(PossibleValue::new(value))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,198 +0,0 @@
|
||||||
#![allow(dead_code, unused_variables)]
|
|
||||||
|
|
||||||
mod stories;
|
|
||||||
mod story;
|
|
||||||
mod story_selector;
|
|
||||||
|
|
||||||
use std::{process::Command, sync::Arc};
|
|
||||||
|
|
||||||
use ::theme as legacy_theme;
|
|
||||||
use clap::Parser;
|
|
||||||
use gpui2::{
|
|
||||||
serde_json, vec2f, view, Element, IntoElement, ParentElement, RectF, ViewContext, WindowBounds,
|
|
||||||
};
|
|
||||||
use legacy_theme::{ThemeRegistry, ThemeSettings};
|
|
||||||
use log::LevelFilter;
|
|
||||||
use settings::{default_settings, SettingsStore};
|
|
||||||
use simplelog::SimpleLogger;
|
|
||||||
use ui::prelude::*;
|
|
||||||
use ui::{ElementExt, Theme, WorkspaceElement};
|
|
||||||
|
|
||||||
use crate::story_selector::StorySelector;
|
|
||||||
|
|
||||||
gpui2::actions! {
|
|
||||||
storybook,
|
|
||||||
[ToggleInspector]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[command(author, version, about, long_about = None)]
|
|
||||||
struct Args {
|
|
||||||
#[arg(value_enum)]
|
|
||||||
story: Option<StorySelector>,
|
|
||||||
|
|
||||||
/// The name of the theme to use in the storybook.
|
|
||||||
///
|
|
||||||
/// If not provided, the default theme will be used.
|
|
||||||
#[arg(long)]
|
|
||||||
theme: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn watch_zed_changes(fs: Arc<dyn fs::Fs>) -> Option<()> {
|
|
||||||
if std::env::var("ZED_HOT_RELOAD").is_err() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
use futures::StreamExt;
|
|
||||||
let mut events = fs
|
|
||||||
.watch(".".as_ref(), std::time::Duration::from_millis(100))
|
|
||||||
.await;
|
|
||||||
let mut current_child: Option<std::process::Child> = None;
|
|
||||||
while let Some(events) = events.next().await {
|
|
||||||
if !events.iter().any(|event| {
|
|
||||||
event
|
|
||||||
.path
|
|
||||||
.to_str()
|
|
||||||
.map(|path| path.contains("/crates/"))
|
|
||||||
.unwrap_or_default()
|
|
||||||
}) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let child = current_child.take().map(|mut child| child.kill());
|
|
||||||
log::info!("Storybook changed, rebuilding...");
|
|
||||||
current_child = Some(
|
|
||||||
Command::new("cargo")
|
|
||||||
.args(["run", "-p", "storybook"])
|
|
||||||
.spawn()
|
|
||||||
.ok()?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
|
||||||
|
|
||||||
let args = Args::parse();
|
|
||||||
|
|
||||||
let fs = Arc::new(fs::RealFs);
|
|
||||||
|
|
||||||
gpui2::App::new(Assets).unwrap().run(move |cx| {
|
|
||||||
let mut store = SettingsStore::default();
|
|
||||||
store
|
|
||||||
.set_default_settings(default_settings().as_ref(), cx)
|
|
||||||
.unwrap();
|
|
||||||
cx.set_global(store);
|
|
||||||
legacy_theme::init(Assets, cx);
|
|
||||||
// load_embedded_fonts(cx.platform().as_ref());
|
|
||||||
|
|
||||||
let theme_registry = cx.global::<Arc<ThemeRegistry>>();
|
|
||||||
|
|
||||||
let theme_override = args
|
|
||||||
.theme
|
|
||||||
.and_then(|theme| {
|
|
||||||
theme_registry
|
|
||||||
.list_names(true)
|
|
||||||
.find(|known_theme| theme == *known_theme)
|
|
||||||
})
|
|
||||||
.and_then(|theme_name| theme_registry.get(&theme_name).ok());
|
|
||||||
|
|
||||||
cx.spawn(|_| async move {
|
|
||||||
watch_zed_changes(fs).await;
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
cx.add_window(
|
|
||||||
gpui2::WindowOptions {
|
|
||||||
bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(1700., 980.))),
|
|
||||||
center: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
|cx| match args.story {
|
|
||||||
Some(selector) => view(move |cx| {
|
|
||||||
render_story(
|
|
||||||
&mut ViewContext::new(cx),
|
|
||||||
theme_override.clone(),
|
|
||||||
div().flex().flex_col().h_full().child_any(selector.story()),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
None => view(move |cx| {
|
|
||||||
render_story(
|
|
||||||
&mut ViewContext::new(cx),
|
|
||||||
theme_override.clone(),
|
|
||||||
WorkspaceElement::default(),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
cx.platform().activate(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_story<V: 'static, S: IntoElement<V>>(
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
theme_override: Option<Arc<legacy_theme::Theme>>,
|
|
||||||
story: S,
|
|
||||||
) -> impl Element<V> {
|
|
||||||
let theme = current_theme(cx, theme_override);
|
|
||||||
|
|
||||||
story.into_element().themed(theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_theme<V: 'static>(
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
theme_override: Option<Arc<legacy_theme::Theme>>,
|
|
||||||
) -> Theme {
|
|
||||||
let legacy_theme =
|
|
||||||
theme_override.unwrap_or_else(|| settings::get::<ThemeSettings>(cx).theme.clone());
|
|
||||||
|
|
||||||
let new_theme: Theme = serde_json::from_value(legacy_theme.base_theme.clone()).unwrap();
|
|
||||||
|
|
||||||
add_base_theme_to_legacy_theme(&legacy_theme, new_theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nathan: During the transition to gpui2, we will include the base theme on the legacy Theme struct.
|
|
||||||
fn add_base_theme_to_legacy_theme(legacy_theme: &legacy_theme::Theme, new_theme: Theme) -> Theme {
|
|
||||||
legacy_theme
|
|
||||||
.deserialized_base_theme
|
|
||||||
.lock()
|
|
||||||
.get_or_insert_with(|| Box::new(new_theme))
|
|
||||||
.downcast_ref::<Theme>()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use gpui2::AssetSource;
|
|
||||||
use rust_embed::RustEmbed;
|
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
|
||||||
#[folder = "../../assets"]
|
|
||||||
#[include = "themes/**/*"]
|
|
||||||
#[include = "fonts/**/*"]
|
|
||||||
#[include = "icons/**/*"]
|
|
||||||
#[exclude = "*.DS_Store"]
|
|
||||||
pub struct Assets;
|
|
||||||
|
|
||||||
impl AssetSource for Assets {
|
|
||||||
fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
|
|
||||||
Self::get(path)
|
|
||||||
.map(|f| f.data)
|
|
||||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list(&self, path: &str) -> Vec<std::borrow::Cow<'static, str>> {
|
|
||||||
Self::iter().filter(|p| p.starts_with(path)).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn load_embedded_fonts(platform: &dyn gpui2::Platform) {
|
|
||||||
// let font_paths = Assets.list("fonts");
|
|
||||||
// let mut embedded_fonts = Vec::new();
|
|
||||||
// for font_path in &font_paths {
|
|
||||||
// if font_path.ends_with(".ttf") {
|
|
||||||
// let font_path = &*font_path;
|
|
||||||
// let font_bytes = Assets.load(font_path).unwrap().to_vec();
|
|
||||||
// embedded_fonts.push(Arc::from(font_bytes));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// platform.fonts().add_fonts(&embedded_fonts).unwrap();
|
|
||||||
// }
|
|
|
@ -1,16 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "ui"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow.workspace = true
|
|
||||||
chrono = "0.4"
|
|
||||||
gpui2 = { path = "../gpui2" }
|
|
||||||
serde.workspace = true
|
|
||||||
settings = { path = "../settings" }
|
|
||||||
smallvec.workspace = true
|
|
||||||
strum = { version = "0.25.0", features = ["derive"] }
|
|
||||||
theme = { path = "../theme" }
|
|
||||||
rand = "0.8"
|
|
|
@ -1,13 +0,0 @@
|
||||||
## Project Plan
|
|
||||||
|
|
||||||
- Port existing UI to GPUI2
|
|
||||||
- Update UI in places that GPUI1 was limiting us*
|
|
||||||
- Understand the needs &/|| struggles the engineers have been having with building UI in the past and address as many of those as possible as we go
|
|
||||||
- Ship a simple, straightforward system with documentation that is easy to use to build UI
|
|
||||||
|
|
||||||
## Component Classification
|
|
||||||
|
|
||||||
To simplify the understanding of components and minimize unnecessary cognitive load, let's categorize components into two types:
|
|
||||||
|
|
||||||
- An element refers to a standalone component that doesn't import any other 'ui' components.
|
|
||||||
- A component indicates a component that utilizes or imports other 'ui' components.
|
|
|
@ -1,57 +0,0 @@
|
||||||
# Elevation
|
|
||||||
|
|
||||||
Elevation in Zed applies to all surfaces and components. Elevation is categorized into levels.
|
|
||||||
|
|
||||||
Elevation accomplishes the following:
|
|
||||||
- Allows surfaces to move in front of or behind others, such as content scrolling beneath app top bars.
|
|
||||||
- Reflects spatial relationships, for instance, how a floating action button’s shadow intimates its disconnection from a collection of cards.
|
|
||||||
- Directs attention to structures at the highest elevation, like a temporary dialog arising in front of other surfaces.
|
|
||||||
|
|
||||||
Elevations are the initial elevation values assigned to components by default.
|
|
||||||
|
|
||||||
Components may transition to a higher elevation in some cases, like user interations.
|
|
||||||
|
|
||||||
On such occasions, components transition to predetermined dynamic elevation offsets. These are the typical elevations to which components move when they are not at rest.
|
|
||||||
|
|
||||||
## Understanding Elevation
|
|
||||||
|
|
||||||
Elevation can be thought of as the physical closeness of an element to the user. Elements with lower elevations are physically further away from the user on the z-axis and appear to be underneath elements with higher elevations.
|
|
||||||
|
|
||||||
Material Design 3 has a some great visualizations of elevation that may be helpful to understanding the mental modal of elevation. [Material Design – Elevation](https://m3.material.io/styles/elevation/overview)
|
|
||||||
|
|
||||||
## Elevation Levels
|
|
||||||
|
|
||||||
Zed integrates six unique elevation levels in its design system. The elevation of a surface is expressed as a whole number ranging from 0 to 5, both numbers inclusive. A component’s elevation is ascertained by combining the component’s resting elevation with any dynamic elevation offsets.
|
|
||||||
|
|
||||||
The levels are detailed as follows:
|
|
||||||
|
|
||||||
0. App Background
|
|
||||||
1. UI Surface
|
|
||||||
2. Elevated Elements
|
|
||||||
3. Wash
|
|
||||||
4. Focused Element
|
|
||||||
5. Dragged Element
|
|
||||||
|
|
||||||
### 0. App Background
|
|
||||||
|
|
||||||
The app background constitutes the lowest elevation layer, appearing behind all other surfaces and components. It is predominantly used for the background color of the app.
|
|
||||||
|
|
||||||
### 1. UI Surface
|
|
||||||
|
|
||||||
The UI Surface is the standard elevation for components and is placed above the app background. It is generally used for the background color of the app bar, card, and sheet.
|
|
||||||
|
|
||||||
### 2. Elevated Elements
|
|
||||||
|
|
||||||
Elevated elements appear above the UI surface layer surfaces and components. Elevated elements are predominantly used for creating popovers, context menus, and tooltips.
|
|
||||||
|
|
||||||
### 3. Wash
|
|
||||||
|
|
||||||
Wash denotes a distinct elevation reserved to isolate app UI layers from high elevation components such as modals, notifications, and overlaid panels. The wash may not consistently be visible when these components are active. This layer is often referred to as a scrim or overlay and the background color of the wash is typically deployed in its design.
|
|
||||||
|
|
||||||
### 4. Focused Element
|
|
||||||
|
|
||||||
Focused elements obtain a higher elevation above surfaces and components at wash elevation. They are often used for modals, notifications, and overlaid panels and indicate that they are the sole element the user is interacting with at the moment.
|
|
||||||
|
|
||||||
### 5. Dragged Element
|
|
||||||
|
|
||||||
Dragged elements gain the highest elevation, thus appearing above surfaces and components at the elevation of focused elements. These are typically used for elements that are being dragged, following the cursor
|
|
|
@ -1,7 +0,0 @@
|
||||||
use std::any::Any;
|
|
||||||
|
|
||||||
use gpui2::{AnyElement, ViewContext};
|
|
||||||
|
|
||||||
pub type HackyChildren<V> = fn(&mut ViewContext<V>, &dyn Any) -> Vec<AnyElement<V>>;
|
|
||||||
|
|
||||||
pub type HackyChildrenPayload = Box<dyn Any>;
|
|
|
@ -1,163 +0,0 @@
|
||||||
mod assistant_panel;
|
|
||||||
mod breadcrumb;
|
|
||||||
mod buffer;
|
|
||||||
mod chat_panel;
|
|
||||||
mod collab_panel;
|
|
||||||
mod command_palette;
|
|
||||||
mod context_menu;
|
|
||||||
mod editor_pane;
|
|
||||||
mod facepile;
|
|
||||||
mod icon_button;
|
|
||||||
mod keybinding;
|
|
||||||
mod language_selector;
|
|
||||||
mod list;
|
|
||||||
mod multi_buffer;
|
|
||||||
mod palette;
|
|
||||||
mod panel;
|
|
||||||
mod panes;
|
|
||||||
mod player_stack;
|
|
||||||
mod project_panel;
|
|
||||||
mod recent_projects;
|
|
||||||
mod status_bar;
|
|
||||||
mod tab;
|
|
||||||
mod tab_bar;
|
|
||||||
mod terminal;
|
|
||||||
mod theme_selector;
|
|
||||||
mod title_bar;
|
|
||||||
mod toast;
|
|
||||||
mod toolbar;
|
|
||||||
mod traffic_lights;
|
|
||||||
mod workspace;
|
|
||||||
|
|
||||||
pub use assistant_panel::*;
|
|
||||||
pub use breadcrumb::*;
|
|
||||||
pub use buffer::*;
|
|
||||||
pub use chat_panel::*;
|
|
||||||
pub use collab_panel::*;
|
|
||||||
pub use command_palette::*;
|
|
||||||
pub use context_menu::*;
|
|
||||||
pub use editor_pane::*;
|
|
||||||
pub use facepile::*;
|
|
||||||
pub use icon_button::*;
|
|
||||||
pub use keybinding::*;
|
|
||||||
pub use language_selector::*;
|
|
||||||
pub use list::*;
|
|
||||||
pub use multi_buffer::*;
|
|
||||||
pub use palette::*;
|
|
||||||
pub use panel::*;
|
|
||||||
pub use panes::*;
|
|
||||||
pub use player_stack::*;
|
|
||||||
pub use project_panel::*;
|
|
||||||
pub use recent_projects::*;
|
|
||||||
pub use status_bar::*;
|
|
||||||
pub use tab::*;
|
|
||||||
pub use tab_bar::*;
|
|
||||||
pub use terminal::*;
|
|
||||||
pub use theme_selector::*;
|
|
||||||
pub use title_bar::*;
|
|
||||||
pub use toast::*;
|
|
||||||
pub use toolbar::*;
|
|
||||||
pub use traffic_lights::*;
|
|
||||||
pub use workspace::*;
|
|
||||||
|
|
||||||
// Nate: Commenting this out for now, unsure if we need it.
|
|
||||||
|
|
||||||
// use std::marker::PhantomData;
|
|
||||||
// use std::rc::Rc;
|
|
||||||
|
|
||||||
// use gpui2::elements::div;
|
|
||||||
// use gpui2::interactive::Interactive;
|
|
||||||
// use gpui2::platform::MouseButton;
|
|
||||||
// use gpui2::{ArcCow, Element, EventContext, IntoElement, ParentElement, ViewContext};
|
|
||||||
|
|
||||||
// struct ButtonHandlers<V, D> {
|
|
||||||
// click: Option<Rc<dyn Fn(&mut V, &D, &mut EventContext<V>)>>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl<V, D> Default for ButtonHandlers<V, D> {
|
|
||||||
// fn default() -> Self {
|
|
||||||
// Self { click: None }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[derive(Element)]
|
|
||||||
// pub struct Button<V: 'static, D: 'static> {
|
|
||||||
// handlers: ButtonHandlers<V, D>,
|
|
||||||
// label: Option<ArcCow<'static, str>>,
|
|
||||||
// icon: Option<ArcCow<'static, str>>,
|
|
||||||
// data: Rc<D>,
|
|
||||||
// view_type: PhantomData<V>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Impl block for buttons without data.
|
|
||||||
// // See below for an impl block for any button.
|
|
||||||
// impl<V: 'static> Button<V, ()> {
|
|
||||||
// fn new() -> Self {
|
|
||||||
// Self {
|
|
||||||
// handlers: ButtonHandlers::default(),
|
|
||||||
// label: None,
|
|
||||||
// icon: None,
|
|
||||||
// data: Rc::new(()),
|
|
||||||
// view_type: PhantomData,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
|
|
||||||
// Button {
|
|
||||||
// handlers: ButtonHandlers::default(),
|
|
||||||
// label: self.label,
|
|
||||||
// icon: self.icon,
|
|
||||||
// data: Rc::new(data),
|
|
||||||
// view_type: PhantomData,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Impl block for button regardless of its data type.
|
|
||||||
// impl<V: 'static, D: 'static> Button<V, D> {
|
|
||||||
// pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
|
|
||||||
// self.label = Some(label.into());
|
|
||||||
// self
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
|
|
||||||
// self.icon = Some(icon.into());
|
|
||||||
// self
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn on_click(
|
|
||||||
// mut self,
|
|
||||||
// handler: impl Fn(&mut V, &D, &mut EventContext<V>) + 'static,
|
|
||||||
// ) -> Self {
|
|
||||||
// self.handlers.click = Some(Rc::new(handler));
|
|
||||||
// self
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn button<V>() -> Button<V, ()> {
|
|
||||||
// Button::new()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl<V: 'static, D: 'static> Button<V, D> {
|
|
||||||
// fn render(
|
|
||||||
// &mut self,
|
|
||||||
// view: &mut V,
|
|
||||||
// cx: &mut ViewContext<V>,
|
|
||||||
// ) -> impl IntoElement<V> + Interactive<V> {
|
|
||||||
// // let colors = &cx.theme::<Theme>().colors;
|
|
||||||
|
|
||||||
// let button = div()
|
|
||||||
// // .fill(colors.error(0.5))
|
|
||||||
// .h_4()
|
|
||||||
// .children(self.label.clone());
|
|
||||||
|
|
||||||
// if let Some(handler) = self.handlers.click.clone() {
|
|
||||||
// let data = self.data.clone();
|
|
||||||
// button.on_mouse_down(MouseButton::Left, move |view, event, cx| {
|
|
||||||
// handler(view, data.as_ref(), cx)
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// button
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
|
@ -1,91 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use gpui2::geometry::rems;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme::theme;
|
|
||||||
use crate::{Icon, IconButton, Label, Panel, PanelSide};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct AssistantPanel<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
current_side: PanelSide,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> AssistantPanel<V> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state: ScrollState::default(),
|
|
||||||
current_side: PanelSide::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn side(mut self, side: PanelSide) -> Self {
|
|
||||||
self.current_side = side;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
struct PanelPayload {
|
|
||||||
pub scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
Panel::new(
|
|
||||||
self.scroll_state.clone(),
|
|
||||||
|_, payload| {
|
|
||||||
let payload = payload.downcast_ref::<PanelPayload>().unwrap();
|
|
||||||
|
|
||||||
vec![div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.h_full()
|
|
||||||
.px_2()
|
|
||||||
.gap_2()
|
|
||||||
// Header
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.justify_between()
|
|
||||||
.gap_2()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.child(IconButton::new(Icon::Menu))
|
|
||||||
.child(Label::new("New Conversation")),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_px()
|
|
||||||
.child(IconButton::new(Icon::SplitMessage))
|
|
||||||
.child(IconButton::new(Icon::Quote))
|
|
||||||
.child(IconButton::new(Icon::MagicWand))
|
|
||||||
.child(IconButton::new(Icon::Plus))
|
|
||||||
.child(IconButton::new(Icon::Maximize)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
// Chat Body
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.w_full()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_3()
|
|
||||||
.overflow_y_scroll(payload.scroll_state.clone())
|
|
||||||
.child(Label::new("Is this thing on?")),
|
|
||||||
)
|
|
||||||
.into_any()]
|
|
||||||
},
|
|
||||||
Box::new(PanelPayload {
|
|
||||||
scroll_state: self.scroll_state.clone(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.side(self.current_side)
|
|
||||||
.width(rems(32.))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use gpui2::elements::div::Div;
|
|
||||||
|
|
||||||
use crate::{h_stack, theme};
|
|
||||||
use crate::{prelude::*, HighlightedText};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Symbol(pub Vec<HighlightedText>);
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Breadcrumb {
|
|
||||||
path: PathBuf,
|
|
||||||
symbols: Vec<Symbol>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Breadcrumb {
|
|
||||||
pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
|
|
||||||
Self { path, symbols }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_separator<V: 'static>(&self, theme: &Theme) -> Div<V> {
|
|
||||||
div()
|
|
||||||
.child(" › ")
|
|
||||||
.text_color(HighlightColor::Default.hsla(theme))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
let symbols_len = self.symbols.len();
|
|
||||||
|
|
||||||
h_stack()
|
|
||||||
.px_1()
|
|
||||||
// TODO: Read font from theme (or settings?).
|
|
||||||
.font("Zed Mono Extended")
|
|
||||||
.text_sm()
|
|
||||||
.text_color(theme.middle.base.default.foreground)
|
|
||||||
.rounded_md()
|
|
||||||
.hover()
|
|
||||||
.fill(theme.highest.base.hovered.background)
|
|
||||||
.child(self.path.clone().to_str().unwrap().to_string())
|
|
||||||
.child(if !self.symbols.is_empty() {
|
|
||||||
self.render_separator(&theme)
|
|
||||||
} else {
|
|
||||||
div()
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
div().flex().children(
|
|
||||||
self.symbols
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
// TODO: Could use something like `intersperse` here instead.
|
|
||||||
.flat_map(|(ix, symbol)| {
|
|
||||||
let mut items =
|
|
||||||
vec![div().flex().children(symbol.0.iter().map(|segment| {
|
|
||||||
div().child(segment.text.clone()).text_color(segment.color)
|
|
||||||
}))];
|
|
||||||
|
|
||||||
let is_last_segment = ix == symbols_len - 1;
|
|
||||||
if !is_last_segment {
|
|
||||||
items.push(self.render_separator(&theme));
|
|
||||||
}
|
|
||||||
|
|
||||||
items
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,233 +0,0 @@
|
||||||
use gpui2::{Hsla, WindowContext};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{h_stack, theme, v_stack, Icon, IconElement};
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
|
||||||
pub struct PlayerCursor {
|
|
||||||
color: Hsla,
|
|
||||||
index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone)]
|
|
||||||
pub struct HighlightedText {
|
|
||||||
pub text: String,
|
|
||||||
pub color: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone)]
|
|
||||||
pub struct HighlightedLine {
|
|
||||||
pub highlighted_texts: Vec<HighlightedText>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone)]
|
|
||||||
pub struct BufferRow {
|
|
||||||
pub line_number: usize,
|
|
||||||
pub code_action: bool,
|
|
||||||
pub current: bool,
|
|
||||||
pub line: Option<HighlightedLine>,
|
|
||||||
pub cursors: Option<Vec<PlayerCursor>>,
|
|
||||||
pub status: GitStatus,
|
|
||||||
pub show_line_number: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct BufferRows {
|
|
||||||
pub show_line_numbers: bool,
|
|
||||||
pub rows: Vec<BufferRow>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for BufferRows {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
show_line_numbers: true,
|
|
||||||
rows: vec![BufferRow {
|
|
||||||
line_number: 1,
|
|
||||||
code_action: false,
|
|
||||||
current: true,
|
|
||||||
line: None,
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number: true,
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BufferRow {
|
|
||||||
pub fn new(line_number: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
line_number,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: None,
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_line(mut self, line: Option<HighlightedLine>) -> Self {
|
|
||||||
self.line = line;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cursors(mut self, cursors: Option<Vec<PlayerCursor>>) -> Self {
|
|
||||||
self.cursors = cursors;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_cursor(mut self, cursor: PlayerCursor) -> Self {
|
|
||||||
if let Some(cursors) = &mut self.cursors {
|
|
||||||
cursors.push(cursor);
|
|
||||||
} else {
|
|
||||||
self.cursors = Some(vec![cursor]);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_status(mut self, status: GitStatus) -> Self {
|
|
||||||
self.status = status;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_show_line_number(mut self, show_line_number: bool) -> Self {
|
|
||||||
self.show_line_number = show_line_number;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_code_action(mut self, code_action: bool) -> Self {
|
|
||||||
self.code_action = code_action;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_current(mut self, current: bool) -> Self {
|
|
||||||
self.current = current;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct Buffer {
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
rows: Option<BufferRows>,
|
|
||||||
readonly: bool,
|
|
||||||
language: Option<String>,
|
|
||||||
title: Option<String>,
|
|
||||||
path: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Buffer {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
scroll_state: ScrollState::default(),
|
|
||||||
rows: Some(BufferRows::default()),
|
|
||||||
readonly: false,
|
|
||||||
language: None,
|
|
||||||
title: Some("untitled".to_string()),
|
|
||||||
path: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) {
|
|
||||||
self.scroll_state = scroll_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_title<T: Into<Option<String>>>(mut self, title: T) -> Self {
|
|
||||||
self.title = title.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_path<P: Into<Option<String>>>(mut self, path: P) -> Self {
|
|
||||||
self.path = path.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_readonly(mut self, readonly: bool) -> Self {
|
|
||||||
self.readonly = readonly;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_rows<R: Into<Option<BufferRows>>>(mut self, rows: R) -> Self {
|
|
||||||
self.rows = rows.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_language<L: Into<Option<String>>>(mut self, language: L) -> Self {
|
|
||||||
self.language = language.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_row<V: 'static>(row: BufferRow, cx: &WindowContext) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
let line_background = if row.current {
|
|
||||||
theme.middle.base.default.background
|
|
||||||
} else {
|
|
||||||
system_color.transparent
|
|
||||||
};
|
|
||||||
|
|
||||||
let line_number_color = if row.current {
|
|
||||||
HighlightColor::Default.hsla(&theme)
|
|
||||||
} else {
|
|
||||||
HighlightColor::Comment.hsla(&theme)
|
|
||||||
};
|
|
||||||
|
|
||||||
h_stack()
|
|
||||||
.fill(line_background)
|
|
||||||
.w_full()
|
|
||||||
.gap_2()
|
|
||||||
.px_1()
|
|
||||||
.child(
|
|
||||||
h_stack()
|
|
||||||
.w_4()
|
|
||||||
.h_full()
|
|
||||||
.px_0p5()
|
|
||||||
.when(row.code_action, |c| {
|
|
||||||
div().child(IconElement::new(Icon::Bolt))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.when(row.show_line_number, |this| {
|
|
||||||
this.child(
|
|
||||||
h_stack().justify_end().px_0p5().w_3().child(
|
|
||||||
div()
|
|
||||||
.text_color(line_number_color)
|
|
||||||
.child(row.line_number.to_string()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.child(div().mx_0p5().w_1().h_full().fill(row.status.hsla(cx)))
|
|
||||||
.children(row.line.map(|line| {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.children(line.highlighted_texts.iter().map(|highlighted_text| {
|
|
||||||
div()
|
|
||||||
.text_color(highlighted_text.color)
|
|
||||||
.child(highlighted_text.text.clone())
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_rows<V: 'static>(&self, cx: &WindowContext) -> Vec<impl IntoElement<V>> {
|
|
||||||
match &self.rows {
|
|
||||||
Some(rows) => rows
|
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.map(|row| Self::render_row(row.clone(), cx))
|
|
||||||
.collect(),
|
|
||||||
None => vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let rows = self.render_rows(cx);
|
|
||||||
v_stack()
|
|
||||||
.flex_1()
|
|
||||||
.w_full()
|
|
||||||
.h_full()
|
|
||||||
.fill(theme.highest.base.default.background)
|
|
||||||
.children(rows)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme::theme;
|
|
||||||
use crate::{Icon, IconButton, Input, Label, LabelColor};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct ChatPanel<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
messages: Vec<ChatMessage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> ChatPanel<V> {
|
|
||||||
pub fn new(scroll_state: ScrollState) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state,
|
|
||||||
messages: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_messages(mut self, messages: Vec<ChatMessage>) -> Self {
|
|
||||||
self.messages = messages;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.justify_between()
|
|
||||||
.h_full()
|
|
||||||
.px_2()
|
|
||||||
.gap_2()
|
|
||||||
// Header
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.justify_between()
|
|
||||||
.py_2()
|
|
||||||
.child(div().flex().child(Label::new("#design")))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_px()
|
|
||||||
.child(IconButton::new(Icon::File))
|
|
||||||
.child(IconButton::new(Icon::AudioOn)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
// Chat Body
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.w_full()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_3()
|
|
||||||
.overflow_y_scroll(self.scroll_state.clone())
|
|
||||||
.children(self.messages.clone()),
|
|
||||||
)
|
|
||||||
// Composer
|
|
||||||
.child(div().flex().my_2().child(Input::new("Message #design"))),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct ChatMessage {
|
|
||||||
author: String,
|
|
||||||
text: String,
|
|
||||||
sent_at: NaiveDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChatMessage {
|
|
||||||
pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
|
|
||||||
Self {
|
|
||||||
author,
|
|
||||||
text,
|
|
||||||
sent_at,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_2()
|
|
||||||
.child(Label::new(self.author.clone()))
|
|
||||||
.child(
|
|
||||||
Label::new(self.sent_at.format("%m/%d/%Y").to_string())
|
|
||||||
.color(LabelColor::Muted),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(div().child(Label::new(self.text.clone())))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use gpui2::elements::{img, svg};
|
|
||||||
use gpui2::ArcCow;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme::{theme, Theme};
|
|
||||||
use crate::{
|
|
||||||
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List,
|
|
||||||
ListHeader, ToggleState,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct CollabPanel<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> CollabPanel<V> {
|
|
||||||
pub fn new(scroll_state: ScrollState) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
v_stack()
|
|
||||||
.w_64()
|
|
||||||
.h_full()
|
|
||||||
.fill(theme.middle.base.default.background)
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.w_full()
|
|
||||||
.overflow_y_scroll(self.scroll_state.clone())
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.pb_1()
|
|
||||||
.border_color(theme.lowest.base.default.border)
|
|
||||||
.border_b()
|
|
||||||
.child(
|
|
||||||
List::new(static_collab_panel_current_call())
|
|
||||||
.header(
|
|
||||||
ListHeader::new("CRDB")
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_stack().py_1().child(
|
|
||||||
List::new(static_collab_panel_channels())
|
|
||||||
.header(
|
|
||||||
ListHeader::new("CHANNELS").set_toggle(ToggleState::Toggled),
|
|
||||||
)
|
|
||||||
.empty_message("No channels yet. Add a channel to get started.")
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_stack().py_1().child(
|
|
||||||
List::new(static_collab_panel_current_call())
|
|
||||||
.header(
|
|
||||||
ListHeader::new("CONTACTS – ONLINE")
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_stack().py_1().child(
|
|
||||||
List::new(static_collab_panel_current_call())
|
|
||||||
.header(
|
|
||||||
ListHeader::new("CONTACTS – OFFLINE")
|
|
||||||
.set_toggle(ToggleState::NotToggled),
|
|
||||||
)
|
|
||||||
.set_toggle(ToggleState::NotToggled),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.h_7()
|
|
||||||
.px_2()
|
|
||||||
.border_t()
|
|
||||||
.border_color(theme.middle.variant.default.border)
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_sm()
|
|
||||||
.text_color(theme.middle.variant.default.foreground)
|
|
||||||
.child("Find..."),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_section_header(
|
|
||||||
&self,
|
|
||||||
label: impl Into<ArcCow<'static, str>>,
|
|
||||||
expanded: bool,
|
|
||||||
theme: &Theme,
|
|
||||||
) -> impl Element<V> {
|
|
||||||
div()
|
|
||||||
.h_7()
|
|
||||||
.px_2()
|
|
||||||
.flex()
|
|
||||||
.justify_between()
|
|
||||||
.items_center()
|
|
||||||
.child(div().flex().gap_1().text_sm().child(label))
|
|
||||||
.child(
|
|
||||||
div().flex().h_full().gap_1().items_center().child(
|
|
||||||
svg()
|
|
||||||
.path(if expanded {
|
|
||||||
"icons/caret_down.svg"
|
|
||||||
} else {
|
|
||||||
"icons/caret_up.svg"
|
|
||||||
})
|
|
||||||
.w_3p5()
|
|
||||||
.h_3p5()
|
|
||||||
.fill(theme.middle.variant.default.foreground),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_item(
|
|
||||||
&self,
|
|
||||||
avatar_uri: impl Into<ArcCow<'static, str>>,
|
|
||||||
label: impl Into<ArcCow<'static, str>>,
|
|
||||||
theme: &Theme,
|
|
||||||
) -> impl Element<V> {
|
|
||||||
div()
|
|
||||||
.h_7()
|
|
||||||
.px_2()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.hover()
|
|
||||||
.fill(theme.lowest.variant.hovered.background)
|
|
||||||
.active()
|
|
||||||
.fill(theme.lowest.variant.pressed.background)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.text_sm()
|
|
||||||
.child(
|
|
||||||
img()
|
|
||||||
.uri(avatar_uri)
|
|
||||||
.size_3p5()
|
|
||||||
.rounded_full()
|
|
||||||
.fill(theme.middle.positive.default.foreground),
|
|
||||||
)
|
|
||||||
.child(label),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{example_editor_actions, OrderMethod, Palette};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct CommandPalette<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> CommandPalette<V> {
|
|
||||||
pub fn new(scroll_state: ScrollState) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
div().child(
|
|
||||||
Palette::new(self.scroll_state.clone())
|
|
||||||
.items(example_editor_actions())
|
|
||||||
.placeholder("Execute a command...")
|
|
||||||
.empty_string("No items found.")
|
|
||||||
.default_order(OrderMethod::Ascending),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme::theme;
|
|
||||||
use crate::{
|
|
||||||
v_stack, Label, List, ListEntry, ListItem, ListItemVariant, ListSeparator, ListSubHeader,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum ContextMenuItem {
|
|
||||||
Header(&'static str),
|
|
||||||
Entry(Label),
|
|
||||||
Separator,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContextMenuItem {
|
|
||||||
fn to_list_item(self) -> ListItem {
|
|
||||||
match self {
|
|
||||||
ContextMenuItem::Header(label) => ListSubHeader::new(label).into(),
|
|
||||||
ContextMenuItem::Entry(label) => {
|
|
||||||
ListEntry::new(label).variant(ListItemVariant::Inset).into()
|
|
||||||
}
|
|
||||||
ContextMenuItem::Separator => ListSeparator::new().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn header(label: &'static str) -> Self {
|
|
||||||
Self::Header(label)
|
|
||||||
}
|
|
||||||
pub fn separator() -> Self {
|
|
||||||
Self::Separator
|
|
||||||
}
|
|
||||||
pub fn entry(label: Label) -> Self {
|
|
||||||
Self::Entry(label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct ContextMenu {
|
|
||||||
items: Vec<ContextMenuItem>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContextMenu {
|
|
||||||
pub fn new(items: impl IntoIterator<Item = ContextMenuItem>) -> Self {
|
|
||||||
Self {
|
|
||||||
items: items.into_iter().collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn render<V: 'static>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
v_stack()
|
|
||||||
.flex()
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.border()
|
|
||||||
.border_color(theme.lowest.base.default.border)
|
|
||||||
.child(
|
|
||||||
List::new(
|
|
||||||
self.items
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(ContextMenuItem::to_list_item)
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
)
|
|
||||||
//div().p_1().children(self.items.clone())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{v_stack, Breadcrumb, Buffer, Icon, IconButton, Symbol, Tab, TabBar, Toolbar};
|
|
||||||
|
|
||||||
pub struct Editor {
|
|
||||||
pub tabs: Vec<Tab>,
|
|
||||||
pub path: PathBuf,
|
|
||||||
pub symbols: Vec<Symbol>,
|
|
||||||
pub buffer: Buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct EditorPane<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
editor: Editor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> EditorPane<V> {
|
|
||||||
pub fn new(editor: Editor) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
editor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
struct LeftItemsPayload {
|
|
||||||
path: PathBuf,
|
|
||||||
symbols: Vec<Symbol>,
|
|
||||||
}
|
|
||||||
|
|
||||||
v_stack()
|
|
||||||
.w_full()
|
|
||||||
.h_full()
|
|
||||||
.flex_1()
|
|
||||||
.child(TabBar::new(self.editor.tabs.clone()))
|
|
||||||
.child(Toolbar::new(
|
|
||||||
|_, payload| {
|
|
||||||
let payload = payload.downcast_ref::<LeftItemsPayload>().unwrap();
|
|
||||||
|
|
||||||
vec![Breadcrumb::new(payload.path.clone(), payload.symbols.clone()).into_any()]
|
|
||||||
},
|
|
||||||
Box::new(LeftItemsPayload {
|
|
||||||
path: self.editor.path.clone(),
|
|
||||||
symbols: self.editor.symbols.clone(),
|
|
||||||
}),
|
|
||||||
|_, _| {
|
|
||||||
vec![
|
|
||||||
IconButton::new(Icon::InlayHint).into_any(),
|
|
||||||
IconButton::new(Icon::MagnifyingGlass).into_any(),
|
|
||||||
IconButton::new(Icon::MagicWand).into_any(),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
Box::new(()),
|
|
||||||
))
|
|
||||||
.child(self.editor.buffer.clone())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{theme, Avatar, Player};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Facepile {
|
|
||||||
players: Vec<Player>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Facepile {
|
|
||||||
pub fn new<P: Iterator<Item = Player>>(players: P) -> Self {
|
|
||||||
Self {
|
|
||||||
players: players.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let player_count = self.players.len();
|
|
||||||
let player_list = self.players.iter().enumerate().map(|(ix, player)| {
|
|
||||||
let isnt_last = ix < player_count - 1;
|
|
||||||
|
|
||||||
div()
|
|
||||||
.when(isnt_last, |div| div.neg_mr_1())
|
|
||||||
.child(Avatar::new(player.avatar_src().to_string()))
|
|
||||||
});
|
|
||||||
div().p_1().flex().items_center().children(player_list)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{theme, Icon, IconColor, IconElement};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct IconButton {
|
|
||||||
icon: Icon,
|
|
||||||
color: IconColor,
|
|
||||||
variant: ButtonVariant,
|
|
||||||
state: InteractionState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IconButton {
|
|
||||||
pub fn new(icon: Icon) -> Self {
|
|
||||||
Self {
|
|
||||||
icon,
|
|
||||||
color: IconColor::default(),
|
|
||||||
variant: ButtonVariant::default(),
|
|
||||||
state: InteractionState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon(mut self, icon: Icon) -> Self {
|
|
||||||
self.icon = icon;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn color(mut self, color: IconColor) -> Self {
|
|
||||||
self.color = color;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variant(mut self, variant: ButtonVariant) -> Self {
|
|
||||||
self.variant = variant;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn state(mut self, state: InteractionState) -> Self {
|
|
||||||
self.state = state;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
let icon_color = match (self.state, self.color) {
|
|
||||||
(InteractionState::Disabled, _) => IconColor::Disabled,
|
|
||||||
_ => self.color,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut div = div();
|
|
||||||
if self.variant == ButtonVariant::Filled {
|
|
||||||
div = div.fill(theme.highest.on.default.background);
|
|
||||||
}
|
|
||||||
|
|
||||||
div.w_7()
|
|
||||||
.h_6()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_center()
|
|
||||||
.rounded_md()
|
|
||||||
.hover()
|
|
||||||
.fill(theme.highest.base.hovered.background)
|
|
||||||
.active()
|
|
||||||
.fill(theme.highest.base.pressed.background)
|
|
||||||
.child(IconElement::new(self.icon).color(icon_color))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use strum::{EnumIter, IntoEnumIterator};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct Keybinding {
|
|
||||||
/// A keybinding consists of a key and a set of modifier keys.
|
|
||||||
/// More then one keybinding produces a chord.
|
|
||||||
///
|
|
||||||
/// This should always contain at least one element.
|
|
||||||
keybinding: Vec<(String, ModifierKeys)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Keybinding {
|
|
||||||
pub fn new(key: String, modifiers: ModifierKeys) -> Self {
|
|
||||||
Self {
|
|
||||||
keybinding: vec![(key, modifiers)],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_chord(
|
|
||||||
first_note: (String, ModifierKeys),
|
|
||||||
second_note: (String, ModifierKeys),
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
keybinding: vec![first_note, second_note],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_2()
|
|
||||||
.children(self.keybinding.iter().map(|(key, modifiers)| {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_1()
|
|
||||||
.children(ModifierKey::iter().filter_map(|modifier| {
|
|
||||||
if modifiers.0.contains(&modifier) {
|
|
||||||
Some(Key::new(modifier.glyph()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.child(Key::new(key.clone()))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Key {
|
|
||||||
key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Key {
|
|
||||||
pub fn new<K>(key: K) -> Self
|
|
||||||
where
|
|
||||||
K: Into<String>,
|
|
||||||
{
|
|
||||||
Self { key: key.into() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.px_2()
|
|
||||||
.py_0()
|
|
||||||
.rounded_md()
|
|
||||||
.text_sm()
|
|
||||||
.text_color(theme.lowest.on.default.foreground)
|
|
||||||
.fill(theme.lowest.on.default.background)
|
|
||||||
.child(self.key.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: The order the modifier keys appear in this enum impacts the order in
|
|
||||||
// which they are rendered in the UI.
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum ModifierKey {
|
|
||||||
Control,
|
|
||||||
Alt,
|
|
||||||
Command,
|
|
||||||
Shift,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModifierKey {
|
|
||||||
/// Returns the glyph for the [`ModifierKey`].
|
|
||||||
pub fn glyph(&self) -> char {
|
|
||||||
match self {
|
|
||||||
Self::Control => '^',
|
|
||||||
Self::Alt => '⌥',
|
|
||||||
Self::Command => '⌘',
|
|
||||||
Self::Shift => '⇧',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ModifierKeys(HashSet<ModifierKey>);
|
|
||||||
|
|
||||||
impl ModifierKeys {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(HashSet::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all() -> Self {
|
|
||||||
Self(HashSet::from_iter(ModifierKey::iter()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(mut self, modifier: ModifierKey) -> Self {
|
|
||||||
self.0.insert(modifier);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn control(mut self, control: bool) -> Self {
|
|
||||||
if control {
|
|
||||||
self.0.insert(ModifierKey::Control);
|
|
||||||
} else {
|
|
||||||
self.0.remove(&ModifierKey::Control);
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alt(mut self, alt: bool) -> Self {
|
|
||||||
if alt {
|
|
||||||
self.0.insert(ModifierKey::Alt);
|
|
||||||
} else {
|
|
||||||
self.0.remove(&ModifierKey::Alt);
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn command(mut self, command: bool) -> Self {
|
|
||||||
if command {
|
|
||||||
self.0.insert(ModifierKey::Command);
|
|
||||||
} else {
|
|
||||||
self.0.remove(&ModifierKey::Command);
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shift(mut self, shift: bool) -> Self {
|
|
||||||
if shift {
|
|
||||||
self.0.insert(ModifierKey::Shift);
|
|
||||||
} else {
|
|
||||||
self.0.remove(&ModifierKey::Shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{OrderMethod, Palette, PaletteItem};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct LanguageSelector {
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LanguageSelector {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
scroll_state: ScrollState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
div().child(
|
|
||||||
Palette::new(self.scroll_state.clone())
|
|
||||||
.items(vec![
|
|
||||||
PaletteItem::new("C"),
|
|
||||||
PaletteItem::new("C++"),
|
|
||||||
PaletteItem::new("CSS"),
|
|
||||||
PaletteItem::new("Elixir"),
|
|
||||||
PaletteItem::new("Elm"),
|
|
||||||
PaletteItem::new("ERB"),
|
|
||||||
PaletteItem::new("Rust (current)"),
|
|
||||||
PaletteItem::new("Scheme"),
|
|
||||||
PaletteItem::new("TOML"),
|
|
||||||
PaletteItem::new("TypeScript"),
|
|
||||||
])
|
|
||||||
.placeholder("Select a language...")
|
|
||||||
.empty_string("No matches")
|
|
||||||
.default_order(OrderMethod::Ascending),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,512 +0,0 @@
|
||||||
use gpui2::elements::div::Div;
|
|
||||||
use gpui2::{Hsla, WindowContext};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{
|
|
||||||
h_stack, theme, token, v_stack, Avatar, DisclosureControlVisibility, Icon, IconColor,
|
|
||||||
IconElement, IconSize, InteractionState, Label, LabelColor, LabelSize, SystemColor,
|
|
||||||
ToggleState,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Debug, PartialEq)]
|
|
||||||
pub enum ListItemVariant {
|
|
||||||
/// The list item extends to the far left and right of the list.
|
|
||||||
#[default]
|
|
||||||
FullWidth,
|
|
||||||
Inset,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element, Clone, Copy)]
|
|
||||||
pub struct ListHeader {
|
|
||||||
label: &'static str,
|
|
||||||
left_icon: Option<Icon>,
|
|
||||||
variant: ListItemVariant,
|
|
||||||
state: InteractionState,
|
|
||||||
toggleable: Toggleable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListHeader {
|
|
||||||
pub fn new(label: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
label,
|
|
||||||
left_icon: None,
|
|
||||||
variant: ListItemVariant::default(),
|
|
||||||
state: InteractionState::default(),
|
|
||||||
toggleable: Toggleable::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
|
||||||
self.toggleable = toggle.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_toggleable(mut self, toggleable: Toggleable) -> Self {
|
|
||||||
self.toggleable = toggleable;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
|
|
||||||
self.left_icon = left_icon;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn state(mut self, state: InteractionState) -> Self {
|
|
||||||
self.state = state;
|
|
||||||
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::ChevronRight).color(IconColor::Muted)),
|
|
||||||
(_, false) => div().child(IconElement::new(Icon::ChevronDown).size(IconSize::Small)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn background_color(&self, cx: &WindowContext) -> Hsla {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
match self.state {
|
|
||||||
InteractionState::Hovered => theme.lowest.base.hovered.background,
|
|
||||||
InteractionState::Active => theme.lowest.base.pressed.background,
|
|
||||||
InteractionState::Enabled => theme.lowest.on.default.background,
|
|
||||||
_ => system_color.transparent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn label_color(&self) -> LabelColor {
|
|
||||||
match self.state {
|
|
||||||
InteractionState::Disabled => LabelColor::Disabled,
|
|
||||||
_ => Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn icon_color(&self) -> IconColor {
|
|
||||||
match self.state {
|
|
||||||
InteractionState::Disabled => IconColor::Disabled,
|
|
||||||
_ => Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let token = token();
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
let background_color = self.background_color(cx);
|
|
||||||
|
|
||||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
|
||||||
let is_toggled = Toggleable::is_toggled(&self.toggleable);
|
|
||||||
|
|
||||||
let disclosure_control = self.disclosure_control();
|
|
||||||
|
|
||||||
h_stack()
|
|
||||||
.flex_1()
|
|
||||||
.w_full()
|
|
||||||
.fill(background_color)
|
|
||||||
.when(self.state == InteractionState::Focused, |this| {
|
|
||||||
this.border()
|
|
||||||
.border_color(theme.lowest.accent.default.border)
|
|
||||||
})
|
|
||||||
.relative()
|
|
||||||
.py_1()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.h_6()
|
|
||||||
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
|
||||||
.flex()
|
|
||||||
.flex_1()
|
|
||||||
.w_full()
|
|
||||||
.gap_1()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_1()
|
|
||||||
.items_center()
|
|
||||||
.children(self.left_icon.map(|i| {
|
|
||||||
IconElement::new(i)
|
|
||||||
.color(IconColor::Muted)
|
|
||||||
.size(IconSize::Small)
|
|
||||||
}))
|
|
||||||
.child(
|
|
||||||
Label::new(self.label)
|
|
||||||
.color(LabelColor::Muted)
|
|
||||||
.size(LabelSize::Small),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(disclosure_control),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element, Clone, Copy)]
|
|
||||||
pub struct ListSubHeader {
|
|
||||||
label: &'static str,
|
|
||||||
left_icon: Option<Icon>,
|
|
||||||
variant: ListItemVariant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListSubHeader {
|
|
||||||
pub fn new(label: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
label,
|
|
||||||
left_icon: None,
|
|
||||||
variant: ListItemVariant::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
|
|
||||||
self.left_icon = left_icon;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let token = token();
|
|
||||||
|
|
||||||
h_stack().flex_1().w_full().relative().py_1().child(
|
|
||||||
div()
|
|
||||||
.h_6()
|
|
||||||
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
|
||||||
.flex()
|
|
||||||
.flex_1()
|
|
||||||
.w_full()
|
|
||||||
.gap_1()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.gap_1()
|
|
||||||
.items_center()
|
|
||||||
.children(self.left_icon.map(|i| {
|
|
||||||
IconElement::new(i)
|
|
||||||
.color(IconColor::Muted)
|
|
||||||
.size(IconSize::Small)
|
|
||||||
}))
|
|
||||||
.child(
|
|
||||||
Label::new(self.label)
|
|
||||||
.color(LabelColor::Muted)
|
|
||||||
.size(LabelSize::Small),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum LeftContent {
|
|
||||||
Icon(Icon),
|
|
||||||
Avatar(&'static str),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
|
||||||
pub enum ListEntrySize {
|
|
||||||
#[default]
|
|
||||||
Small,
|
|
||||||
Medium,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Element)]
|
|
||||||
pub enum ListItem {
|
|
||||||
Entry(ListEntry),
|
|
||||||
Separator(ListSeparator),
|
|
||||||
Header(ListSubHeader),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ListEntry> for ListItem {
|
|
||||||
fn from(entry: ListEntry) -> Self {
|
|
||||||
Self::Entry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ListSeparator> for ListItem {
|
|
||||||
fn from(entry: ListSeparator) -> Self {
|
|
||||||
Self::Separator(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ListSubHeader> for ListItem {
|
|
||||||
fn from(entry: ListSubHeader) -> Self {
|
|
||||||
Self::Header(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListItem {
|
|
||||||
fn render<V: 'static>(&mut self, v: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
match self {
|
|
||||||
ListItem::Entry(entry) => div().child(entry.render(v, cx)),
|
|
||||||
ListItem::Separator(separator) => div().child(separator.render(v, cx)),
|
|
||||||
ListItem::Header(header) => div().child(header.render(v, cx)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn new(label: Label) -> Self {
|
|
||||||
Self::Entry(ListEntry::new(label))
|
|
||||||
}
|
|
||||||
pub fn as_entry(&mut self) -> Option<&mut ListEntry> {
|
|
||||||
if let Self::Entry(entry) = self {
|
|
||||||
Some(entry)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct ListEntry {
|
|
||||||
disclosure_control_style: DisclosureControlVisibility,
|
|
||||||
indent_level: u32,
|
|
||||||
label: Label,
|
|
||||||
left_content: Option<LeftContent>,
|
|
||||||
variant: ListItemVariant,
|
|
||||||
size: ListEntrySize,
|
|
||||||
state: InteractionState,
|
|
||||||
toggle: Option<ToggleState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListEntry {
|
|
||||||
pub fn new(label: Label) -> Self {
|
|
||||||
Self {
|
|
||||||
disclosure_control_style: DisclosureControlVisibility::default(),
|
|
||||||
indent_level: 0,
|
|
||||||
label,
|
|
||||||
variant: ListItemVariant::default(),
|
|
||||||
left_content: None,
|
|
||||||
size: ListEntrySize::default(),
|
|
||||||
state: InteractionState::default(),
|
|
||||||
toggle: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn variant(mut self, variant: ListItemVariant) -> Self {
|
|
||||||
self.variant = variant;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn indent_level(mut self, indent_level: u32) -> Self {
|
|
||||||
self.indent_level = indent_level;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
|
||||||
self.toggle = Some(toggle);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_content(mut self, left_content: LeftContent) -> Self {
|
|
||||||
self.left_content = Some(left_content);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_icon(mut self, left_icon: Icon) -> Self {
|
|
||||||
self.left_content = Some(LeftContent::Icon(left_icon));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_avatar(mut self, left_avatar: &'static str) -> Self {
|
|
||||||
self.left_content = Some(LeftContent::Avatar(left_avatar));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn state(mut self, state: InteractionState) -> Self {
|
|
||||||
self.state = state;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(mut self, size: ListEntrySize) -> Self {
|
|
||||||
self.size = size;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disclosure_control_style(
|
|
||||||
mut self,
|
|
||||||
disclosure_control_style: DisclosureControlVisibility,
|
|
||||||
) -> Self {
|
|
||||||
self.disclosure_control_style = disclosure_control_style;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn background_color(&self, cx: &WindowContext) -> Hsla {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
match self.state {
|
|
||||||
InteractionState::Hovered => theme.lowest.base.hovered.background,
|
|
||||||
InteractionState::Active => theme.lowest.base.pressed.background,
|
|
||||||
InteractionState::Enabled => theme.lowest.on.default.background,
|
|
||||||
_ => system_color.transparent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 IntoElement<V>> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let token = token();
|
|
||||||
|
|
||||||
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, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let token = token();
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
let background_color = self.background_color(cx);
|
|
||||||
|
|
||||||
let left_content = match self.left_content {
|
|
||||||
Some(LeftContent::Icon(i)) => {
|
|
||||||
Some(h_stack().child(IconElement::new(i).size(IconSize::Small)))
|
|
||||||
}
|
|
||||||
Some(LeftContent::Avatar(src)) => Some(h_stack().child(Avatar::new(src))),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let sized_item = match self.size {
|
|
||||||
ListEntrySize::Small => div().h_6(),
|
|
||||||
ListEntrySize::Medium => div().h_7(),
|
|
||||||
};
|
|
||||||
|
|
||||||
div()
|
|
||||||
.fill(background_color)
|
|
||||||
.when(self.state == InteractionState::Focused, |this| {
|
|
||||||
this.border()
|
|
||||||
.border_color(theme.lowest.accent.default.border)
|
|
||||||
})
|
|
||||||
.relative()
|
|
||||||
.py_1()
|
|
||||||
.child(
|
|
||||||
sized_item
|
|
||||||
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
|
||||||
// .ml(rems(0.75 * self.indent_level as f32))
|
|
||||||
.children((0..self.indent_level).map(|_| {
|
|
||||||
div()
|
|
||||||
.w(token.list_indent_depth)
|
|
||||||
.h_full()
|
|
||||||
.flex()
|
|
||||||
.justify_center()
|
|
||||||
.child(h_stack().child(div().w_px().h_full()).child(
|
|
||||||
div().w_px().h_full().fill(theme.middle.base.default.border),
|
|
||||||
))
|
|
||||||
}))
|
|
||||||
.flex()
|
|
||||||
.gap_1()
|
|
||||||
.items_center()
|
|
||||||
.relative()
|
|
||||||
.children(self.disclosure_control(cx))
|
|
||||||
.children(left_content)
|
|
||||||
.child(self.label.clone()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Element)]
|
|
||||||
pub struct ListSeparator;
|
|
||||||
|
|
||||||
impl ListSeparator {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div().h_px().w_full().fill(theme.lowest.base.default.border)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct List {
|
|
||||||
items: Vec<ListItem>,
|
|
||||||
empty_message: &'static str,
|
|
||||||
header: Option<ListHeader>,
|
|
||||||
toggleable: Toggleable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl List {
|
|
||||||
pub fn new(items: Vec<ListItem>) -> Self {
|
|
||||||
Self {
|
|
||||||
items,
|
|
||||||
empty_message: "No items",
|
|
||||||
header: None,
|
|
||||||
toggleable: Toggleable::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty_message(mut self, empty_message: &'static str) -> Self {
|
|
||||||
self.empty_message = empty_message;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn header(mut self, header: ListHeader) -> Self {
|
|
||||||
self.header = Some(header);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
|
||||||
self.toggleable = toggle.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let token = token();
|
|
||||||
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
|
|
||||||
let is_toggled = Toggleable::is_toggled(&self.toggleable);
|
|
||||||
|
|
||||||
let disclosure_control = if is_toggleable {
|
|
||||||
IconElement::new(Icon::ChevronRight)
|
|
||||||
} else {
|
|
||||||
IconElement::new(Icon::ChevronDown)
|
|
||||||
};
|
|
||||||
|
|
||||||
let list_content = match (self.items.is_empty(), is_toggled) {
|
|
||||||
(_, false) => div(),
|
|
||||||
(false, _) => div().children(self.items.iter().cloned()),
|
|
||||||
(true, _) => div().child(Label::new(self.empty_message).color(LabelColor::Muted)),
|
|
||||||
};
|
|
||||||
|
|
||||||
v_stack()
|
|
||||||
.py_1()
|
|
||||||
.children(
|
|
||||||
self.header
|
|
||||||
.clone()
|
|
||||||
.map(|header| header.set_toggleable(self.toggleable)),
|
|
||||||
)
|
|
||||||
.child(list_content)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{v_stack, Buffer, Icon, IconButton, Label, LabelSize};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct MultiBuffer<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
buffers: Vec<Buffer>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> MultiBuffer<V> {
|
|
||||||
pub fn new(buffers: Vec<Buffer>) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
buffers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
v_stack()
|
|
||||||
.w_full()
|
|
||||||
.h_full()
|
|
||||||
.flex_1()
|
|
||||||
.children(self.buffers.clone().into_iter().map(|buffer| {
|
|
||||||
v_stack()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.p_4()
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.child(Label::new("main.rs").size(LabelSize::Small))
|
|
||||||
.child(IconButton::new(Icon::ArrowUpRight)),
|
|
||||||
)
|
|
||||||
.child(buffer)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme::theme;
|
|
||||||
use crate::{h_stack, v_stack, Keybinding, Label, LabelColor};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Palette<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
input_placeholder: &'static str,
|
|
||||||
empty_string: &'static str,
|
|
||||||
items: Vec<PaletteItem>,
|
|
||||||
default_order: OrderMethod,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Palette<V> {
|
|
||||||
pub fn new(scroll_state: ScrollState) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state,
|
|
||||||
input_placeholder: "Find something...",
|
|
||||||
empty_string: "No items found.",
|
|
||||||
items: vec![],
|
|
||||||
default_order: OrderMethod::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn items(mut self, items: Vec<PaletteItem>) -> Self {
|
|
||||||
self.items = items;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn placeholder(mut self, input_placeholder: &'static str) -> Self {
|
|
||||||
self.input_placeholder = input_placeholder;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty_string(mut self, empty_string: &'static str) -> Self {
|
|
||||||
self.empty_string = empty_string;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Hook up sort order
|
|
||||||
pub fn default_order(mut self, default_order: OrderMethod) -> Self {
|
|
||||||
self.default_order = default_order;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
v_stack()
|
|
||||||
.w_96()
|
|
||||||
.rounded_lg()
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.border()
|
|
||||||
.border_color(theme.lowest.base.default.border)
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.gap_px()
|
|
||||||
.child(v_stack().py_0p5().px_1().child(
|
|
||||||
div().px_2().py_0p5().child(
|
|
||||||
Label::new(self.input_placeholder).color(LabelColor::Placeholder),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.child(div().h_px().w_full().fill(theme.lowest.base.default.border))
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.py_0p5()
|
|
||||||
.px_1()
|
|
||||||
.grow()
|
|
||||||
.max_h_96()
|
|
||||||
.overflow_y_scroll(self.scroll_state.clone())
|
|
||||||
.children(
|
|
||||||
vec![if self.items.is_empty() {
|
|
||||||
Some(h_stack().justify_between().px_2().py_1().child(
|
|
||||||
Label::new(self.empty_string).color(LabelColor::Muted),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}]
|
|
||||||
.into_iter()
|
|
||||||
.flatten(),
|
|
||||||
)
|
|
||||||
.children(self.items.iter().map(|item| {
|
|
||||||
h_stack()
|
|
||||||
.justify_between()
|
|
||||||
.px_2()
|
|
||||||
.py_0p5()
|
|
||||||
.rounded_lg()
|
|
||||||
.hover()
|
|
||||||
.fill(theme.lowest.base.hovered.background)
|
|
||||||
.active()
|
|
||||||
.fill(theme.lowest.base.pressed.background)
|
|
||||||
.child(item.clone())
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct PaletteItem {
|
|
||||||
pub label: &'static str,
|
|
||||||
pub sublabel: Option<&'static str>,
|
|
||||||
pub keybinding: Option<Keybinding>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PaletteItem {
|
|
||||||
pub fn new(label: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
label,
|
|
||||||
sublabel: None,
|
|
||||||
keybinding: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn label(mut self, label: &'static str) -> Self {
|
|
||||||
self.label = label;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sublabel<L: Into<Option<&'static str>>>(mut self, sublabel: L) -> Self {
|
|
||||||
self.sublabel = sublabel.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn keybinding<K>(mut self, keybinding: K) -> Self
|
|
||||||
where
|
|
||||||
K: Into<Option<Keybinding>>,
|
|
||||||
{
|
|
||||||
self.keybinding = keybinding.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_row()
|
|
||||||
.grow()
|
|
||||||
.justify_between()
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.child(Label::new(self.label))
|
|
||||||
.children(self.sublabel.map(|sublabel| Label::new(sublabel))),
|
|
||||||
)
|
|
||||||
.children(self.keybinding.clone())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use gpui2::geometry::AbsoluteLength;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{theme, token, v_stack};
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
||||||
pub enum PanelAllowedSides {
|
|
||||||
LeftOnly,
|
|
||||||
RightOnly,
|
|
||||||
BottomOnly,
|
|
||||||
#[default]
|
|
||||||
LeftAndRight,
|
|
||||||
All,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PanelAllowedSides {
|
|
||||||
/// Return a `HashSet` that contains the allowable `PanelSide`s.
|
|
||||||
pub fn allowed_sides(&self) -> HashSet<PanelSide> {
|
|
||||||
match self {
|
|
||||||
Self::LeftOnly => HashSet::from_iter([PanelSide::Left]),
|
|
||||||
Self::RightOnly => HashSet::from_iter([PanelSide::Right]),
|
|
||||||
Self::BottomOnly => HashSet::from_iter([PanelSide::Bottom]),
|
|
||||||
Self::LeftAndRight => HashSet::from_iter([PanelSide::Left, PanelSide::Right]),
|
|
||||||
Self::All => HashSet::from_iter([PanelSide::Left, PanelSide::Right, PanelSide::Bottom]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
||||||
pub enum PanelSide {
|
|
||||||
#[default]
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Bottom,
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Panel<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
current_side: PanelSide,
|
|
||||||
/// Defaults to PanelAllowedSides::LeftAndRight
|
|
||||||
allowed_sides: PanelAllowedSides,
|
|
||||||
initial_width: AbsoluteLength,
|
|
||||||
width: Option<AbsoluteLength>,
|
|
||||||
children: HackyChildren<V>,
|
|
||||||
payload: HackyChildrenPayload,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Panel<V> {
|
|
||||||
pub fn new(
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
children: HackyChildren<V>,
|
|
||||||
payload: HackyChildrenPayload,
|
|
||||||
) -> Self {
|
|
||||||
let token = token();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state,
|
|
||||||
current_side: PanelSide::default(),
|
|
||||||
allowed_sides: PanelAllowedSides::default(),
|
|
||||||
initial_width: token.default_panel_size,
|
|
||||||
width: None,
|
|
||||||
children,
|
|
||||||
payload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initial_width(mut self, initial_width: AbsoluteLength) -> Self {
|
|
||||||
self.initial_width = initial_width;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn width(mut self, width: AbsoluteLength) -> Self {
|
|
||||||
self.width = Some(width);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn allowed_sides(mut self, allowed_sides: PanelAllowedSides) -> Self {
|
|
||||||
self.allowed_sides = allowed_sides;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn side(mut self, side: PanelSide) -> Self {
|
|
||||||
let allowed_sides = self.allowed_sides.allowed_sides();
|
|
||||||
|
|
||||||
if allowed_sides.contains(&side) {
|
|
||||||
self.current_side = side;
|
|
||||||
} else {
|
|
||||||
panic!(
|
|
||||||
"The panel side {:?} was not added as allowed before it was set.",
|
|
||||||
side
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let token = token();
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
let panel_base;
|
|
||||||
let current_width = self.width.unwrap_or(self.initial_width);
|
|
||||||
|
|
||||||
match self.current_side {
|
|
||||||
PanelSide::Left => {
|
|
||||||
panel_base = v_stack()
|
|
||||||
.flex_initial()
|
|
||||||
.h_full()
|
|
||||||
.w(current_width)
|
|
||||||
.fill(theme.middle.base.default.background)
|
|
||||||
.border_r()
|
|
||||||
.border_color(theme.middle.base.default.border);
|
|
||||||
}
|
|
||||||
PanelSide::Right => {
|
|
||||||
panel_base = v_stack()
|
|
||||||
.flex_initial()
|
|
||||||
.h_full()
|
|
||||||
.w(current_width)
|
|
||||||
.fill(theme.middle.base.default.background)
|
|
||||||
.border_l()
|
|
||||||
.border_color(theme.middle.base.default.border);
|
|
||||||
}
|
|
||||||
PanelSide::Bottom => {
|
|
||||||
panel_base = v_stack()
|
|
||||||
.flex_initial()
|
|
||||||
.w_full()
|
|
||||||
.h(current_width)
|
|
||||||
.fill(theme.middle.base.default.background)
|
|
||||||
.border_t()
|
|
||||||
.border_color(theme.middle.base.default.border);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
panel_base.children_any((self.children)(cx, self.payload.as_ref()))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use gpui2::geometry::{Length, Size};
|
|
||||||
use gpui2::{hsla, Hsla};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
|
||||||
pub enum SplitDirection {
|
|
||||||
#[default]
|
|
||||||
Horizontal,
|
|
||||||
Vertical,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Pane<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
size: Size<Length>,
|
|
||||||
fill: Hsla,
|
|
||||||
children: HackyChildren<V>,
|
|
||||||
payload: HackyChildrenPayload,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Pane<V> {
|
|
||||||
pub fn new(
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
size: Size<Length>,
|
|
||||||
children: HackyChildren<V>,
|
|
||||||
payload: HackyChildrenPayload,
|
|
||||||
) -> Self {
|
|
||||||
// Fill is only here for debugging purposes, remove before release
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state,
|
|
||||||
size,
|
|
||||||
fill: hsla(0.3, 0.3, 0.3, 1.),
|
|
||||||
// fill: system_color.transparent,
|
|
||||||
children,
|
|
||||||
payload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill(mut self, fill: Hsla) -> Self {
|
|
||||||
self.fill = fill;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_initial()
|
|
||||||
.fill(self.fill)
|
|
||||||
.w(self.size.width)
|
|
||||||
.h(self.size.height)
|
|
||||||
.overflow_y_scroll(self.scroll_state.clone())
|
|
||||||
.children_any((self.children)(cx, self.payload.as_ref()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct PaneGroup<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
groups: Vec<PaneGroup<V>>,
|
|
||||||
panes: Vec<Pane<V>>,
|
|
||||||
split_direction: SplitDirection,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> PaneGroup<V> {
|
|
||||||
pub fn new_groups(groups: Vec<PaneGroup<V>>, split_direction: SplitDirection) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
groups,
|
|
||||||
panes: Vec::new(),
|
|
||||||
split_direction,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_panes(panes: Vec<Pane<V>>, split_direction: SplitDirection) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
groups: Vec::new(),
|
|
||||||
panes,
|
|
||||||
split_direction,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
if !self.panes.is_empty() {
|
|
||||||
let el = div()
|
|
||||||
.flex()
|
|
||||||
.flex_1()
|
|
||||||
.gap_px()
|
|
||||||
.w_full()
|
|
||||||
.h_full()
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.children(self.panes.iter_mut().map(|pane| pane.render(view, cx)));
|
|
||||||
|
|
||||||
if self.split_direction == SplitDirection::Horizontal {
|
|
||||||
return el;
|
|
||||||
} else {
|
|
||||||
return el.flex_col();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.groups.is_empty() {
|
|
||||||
let el = div()
|
|
||||||
.flex()
|
|
||||||
.flex_1()
|
|
||||||
.gap_px()
|
|
||||||
.w_full()
|
|
||||||
.h_full()
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.children(self.groups.iter_mut().map(|group| group.render(view, cx)));
|
|
||||||
|
|
||||||
if self.split_direction == SplitDirection::Horizontal {
|
|
||||||
return el;
|
|
||||||
} else {
|
|
||||||
return el.flex_col();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{Avatar, Facepile, PlayerWithCallStatus};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct PlayerStack {
|
|
||||||
player_with_call_status: PlayerWithCallStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlayerStack {
|
|
||||||
pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self {
|
|
||||||
Self {
|
|
||||||
player_with_call_status,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
let player = self.player_with_call_status.get_player();
|
|
||||||
self.player_with_call_status.get_call_status();
|
|
||||||
|
|
||||||
let followers = self
|
|
||||||
.player_with_call_status
|
|
||||||
.get_call_status()
|
|
||||||
.followers
|
|
||||||
.as_ref()
|
|
||||||
.map(|followers| followers.clone());
|
|
||||||
|
|
||||||
// if we have no followers return a slightly different element
|
|
||||||
// if mic_status == muted add a red ring to avatar
|
|
||||||
|
|
||||||
div()
|
|
||||||
.h_full()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_px()
|
|
||||||
.justify_center()
|
|
||||||
.child(
|
|
||||||
div().flex().justify_center().w_full().child(
|
|
||||||
div()
|
|
||||||
.w_4()
|
|
||||||
.h_0p5()
|
|
||||||
.rounded_sm()
|
|
||||||
.fill(player.cursor_color(cx)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_center()
|
|
||||||
.h_6()
|
|
||||||
.pl_1()
|
|
||||||
.rounded_lg()
|
|
||||||
.fill(if followers.is_none() {
|
|
||||||
system_color.transparent
|
|
||||||
} else {
|
|
||||||
player.selection_color(cx)
|
|
||||||
})
|
|
||||||
.child(Avatar::new(player.avatar_src().to_string()))
|
|
||||||
.children(followers.map(|followers| {
|
|
||||||
div().neg_ml_2().child(Facepile::new(followers.into_iter()))
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{
|
|
||||||
static_project_panel_project_items, static_project_panel_single_items, theme, Input, List,
|
|
||||||
ListHeader,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct ProjectPanel<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> ProjectPanel<V> {
|
|
||||||
pub fn new(scroll_state: ScrollState) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.w_full()
|
|
||||||
.h_full()
|
|
||||||
.px_2()
|
|
||||||
.fill(theme.middle.base.default.background)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.w_56()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.overflow_y_scroll(ScrollState::default())
|
|
||||||
.child(
|
|
||||||
List::new(static_project_panel_single_items())
|
|
||||||
.header(ListHeader::new("FILES").set_toggle(ToggleState::Toggled))
|
|
||||||
.empty_message("No files in directory")
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
List::new(static_project_panel_project_items())
|
|
||||||
.header(ListHeader::new("PROJECT").set_toggle(ToggleState::Toggled))
|
|
||||||
.empty_message("No folders in directory")
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Input::new("Find something...")
|
|
||||||
.value("buffe".to_string())
|
|
||||||
.state(InteractionState::Focused),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{OrderMethod, Palette, PaletteItem};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct RecentProjects {
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecentProjects {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
scroll_state: ScrollState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
div().child(
|
|
||||||
Palette::new(self.scroll_state.clone())
|
|
||||||
.items(vec![
|
|
||||||
PaletteItem::new("zed").sublabel("~/projects/zed"),
|
|
||||||
PaletteItem::new("saga").sublabel("~/projects/saga"),
|
|
||||||
PaletteItem::new("journal").sublabel("~/journal"),
|
|
||||||
PaletteItem::new("dotfiles").sublabel("~/dotfiles"),
|
|
||||||
PaletteItem::new("zed.dev").sublabel("~/projects/zed.dev"),
|
|
||||||
PaletteItem::new("laminar").sublabel("~/projects/laminar"),
|
|
||||||
])
|
|
||||||
.placeholder("Recent Projects...")
|
|
||||||
.empty_string("No matches")
|
|
||||||
.default_order(OrderMethod::Ascending),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme::{theme, Theme};
|
|
||||||
use crate::{Button, Icon, IconButton, IconColor, ToolDivider};
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
|
||||||
pub enum Tool {
|
|
||||||
#[default]
|
|
||||||
ProjectPanel,
|
|
||||||
CollaborationPanel,
|
|
||||||
Terminal,
|
|
||||||
Assistant,
|
|
||||||
Feedback,
|
|
||||||
Diagnostics,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ToolGroup {
|
|
||||||
active_index: Option<usize>,
|
|
||||||
tools: Vec<Tool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ToolGroup {
|
|
||||||
fn default() -> Self {
|
|
||||||
ToolGroup {
|
|
||||||
active_index: None,
|
|
||||||
tools: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct StatusBar<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
left_tools: Option<ToolGroup>,
|
|
||||||
right_tools: Option<ToolGroup>,
|
|
||||||
bottom_tools: Option<ToolGroup>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> StatusBar<V> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
left_tools: None,
|
|
||||||
right_tools: None,
|
|
||||||
bottom_tools: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn left_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
|
|
||||||
self.left_tools = {
|
|
||||||
let mut tools = vec![tool];
|
|
||||||
tools.extend(self.left_tools.take().unwrap_or_default().tools);
|
|
||||||
Some(ToolGroup {
|
|
||||||
active_index,
|
|
||||||
tools,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn right_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
|
|
||||||
self.right_tools = {
|
|
||||||
let mut tools = vec![tool];
|
|
||||||
tools.extend(self.left_tools.take().unwrap_or_default().tools);
|
|
||||||
Some(ToolGroup {
|
|
||||||
active_index,
|
|
||||||
tools,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bottom_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
|
|
||||||
self.bottom_tools = {
|
|
||||||
let mut tools = vec![tool];
|
|
||||||
tools.extend(self.left_tools.take().unwrap_or_default().tools);
|
|
||||||
Some(ToolGroup {
|
|
||||||
active_index,
|
|
||||||
tools,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.py_0p5()
|
|
||||||
.px_1()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.w_full()
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.child(self.left_tools(&theme))
|
|
||||||
.child(self.right_tools(&theme))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn left_tools(&self, theme: &Theme) -> impl Element<V> {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.child(IconButton::new(Icon::FileTree).color(IconColor::Accent))
|
|
||||||
.child(IconButton::new(Icon::Hash))
|
|
||||||
.child(ToolDivider::new())
|
|
||||||
.child(IconButton::new(Icon::XCircle))
|
|
||||||
}
|
|
||||||
fn right_tools(&self, theme: &Theme) -> impl Element<V> {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_2()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.child(Button::new("116:25"))
|
|
||||||
.child(Button::new("Rust")),
|
|
||||||
)
|
|
||||||
.child(ToolDivider::new())
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.child(IconButton::new(Icon::Copilot))
|
|
||||||
.child(IconButton::new(Icon::Envelope)),
|
|
||||||
)
|
|
||||||
.child(ToolDivider::new())
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.child(IconButton::new(Icon::Terminal))
|
|
||||||
.child(IconButton::new(Icon::MessageBubbles))
|
|
||||||
.child(IconButton::new(Icon::Ai)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{theme, Icon, IconColor, IconElement, Label, LabelColor};
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct Tab {
|
|
||||||
title: String,
|
|
||||||
icon: Option<Icon>,
|
|
||||||
current: bool,
|
|
||||||
dirty: bool,
|
|
||||||
fs_status: FileSystemStatus,
|
|
||||||
git_status: GitStatus,
|
|
||||||
diagnostic_status: DiagnosticStatus,
|
|
||||||
close_side: IconSide,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tab {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
title: "untitled".to_string(),
|
|
||||||
icon: None,
|
|
||||||
current: false,
|
|
||||||
dirty: false,
|
|
||||||
fs_status: FileSystemStatus::None,
|
|
||||||
git_status: GitStatus::None,
|
|
||||||
diagnostic_status: DiagnosticStatus::None,
|
|
||||||
close_side: IconSide::Right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current(mut self, current: bool) -> Self {
|
|
||||||
self.current = current;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn title(mut self, title: String) -> Self {
|
|
||||||
self.title = title;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon<I>(mut self, icon: I) -> Self
|
|
||||||
where
|
|
||||||
I: Into<Option<Icon>>,
|
|
||||||
{
|
|
||||||
self.icon = icon.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dirty(mut self, dirty: bool) -> Self {
|
|
||||||
self.dirty = dirty;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
|
|
||||||
self.fs_status = fs_status;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn git_status(mut self, git_status: GitStatus) -> Self {
|
|
||||||
self.git_status = git_status;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
|
|
||||||
self.diagnostic_status = diagnostic_status;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close_side(mut self, close_side: IconSide) -> Self {
|
|
||||||
self.close_side = close_side;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
|
|
||||||
let is_deleted = self.fs_status == FileSystemStatus::Deleted;
|
|
||||||
|
|
||||||
let label = match (self.git_status, is_deleted) {
|
|
||||||
(_, true) | (GitStatus::Deleted, false) => Label::new(self.title.clone())
|
|
||||||
.color(LabelColor::Hidden)
|
|
||||||
.set_strikethrough(true),
|
|
||||||
(GitStatus::None, false) => Label::new(self.title.clone()),
|
|
||||||
(GitStatus::Created, false) => {
|
|
||||||
Label::new(self.title.clone()).color(LabelColor::Created)
|
|
||||||
}
|
|
||||||
(GitStatus::Modified, false) => {
|
|
||||||
Label::new(self.title.clone()).color(LabelColor::Modified)
|
|
||||||
}
|
|
||||||
(GitStatus::Renamed, false) => Label::new(self.title.clone()).color(LabelColor::Accent),
|
|
||||||
(GitStatus::Conflict, false) => Label::new(self.title.clone()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let close_icon = IconElement::new(Icon::Close).color(IconColor::Muted);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.px_2()
|
|
||||||
.py_0p5()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_center()
|
|
||||||
.fill(if self.current {
|
|
||||||
theme.highest.base.default.background
|
|
||||||
} else {
|
|
||||||
theme.middle.base.default.background
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.px_1()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.children(has_fs_conflict.then(|| {
|
|
||||||
IconElement::new(Icon::ExclamationTriangle)
|
|
||||||
.size(crate::IconSize::Small)
|
|
||||||
.color(IconColor::Warning)
|
|
||||||
}))
|
|
||||||
.children(self.icon.map(IconElement::new))
|
|
||||||
.children(if self.close_side == IconSide::Left {
|
|
||||||
Some(close_icon.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.child(label)
|
|
||||||
.children(if self.close_side == IconSide::Right {
|
|
||||||
Some(close_icon)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{theme, Icon, IconButton, Tab};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct TabBar<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
tabs: Vec<Tab>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> TabBar<V> {
|
|
||||||
pub fn new(tabs: Vec<Tab>) -> Self {
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state: ScrollState::default(),
|
|
||||||
tabs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) {
|
|
||||||
self.scroll_state = scroll_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let can_navigate_back = true;
|
|
||||||
let can_navigate_forward = false;
|
|
||||||
|
|
||||||
div()
|
|
||||||
.w_full()
|
|
||||||
.flex()
|
|
||||||
.fill(theme.middle.base.default.background)
|
|
||||||
// Left Side
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.px_1()
|
|
||||||
.flex()
|
|
||||||
.flex_none()
|
|
||||||
.gap_2()
|
|
||||||
// Nav Buttons
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_px()
|
|
||||||
.child(
|
|
||||||
IconButton::new(Icon::ArrowLeft)
|
|
||||||
.state(InteractionState::Enabled.if_enabled(can_navigate_back)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
IconButton::new(Icon::ArrowRight).state(
|
|
||||||
InteractionState::Enabled.if_enabled(can_navigate_forward),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div().w_0().flex_1().h_full().child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.overflow_x_scroll(self.scroll_state.clone())
|
|
||||||
.children(self.tabs.clone()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
// Right Side
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.px_1()
|
|
||||||
.flex()
|
|
||||||
.flex_none()
|
|
||||||
.gap_2()
|
|
||||||
// Nav Buttons
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_px()
|
|
||||||
.child(IconButton::new(Icon::Plus))
|
|
||||||
.child(IconButton::new(Icon::Split)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use gpui2::geometry::{relative, rems, Size};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{theme, Icon, IconButton, Pane, Tab};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Terminal {}
|
|
||||||
|
|
||||||
impl Terminal {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
let can_navigate_back = true;
|
|
||||||
let can_navigate_forward = false;
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.w_full()
|
|
||||||
.child(
|
|
||||||
// Terminal Tabs.
|
|
||||||
div()
|
|
||||||
.w_full()
|
|
||||||
.flex()
|
|
||||||
.fill(theme.middle.base.default.background)
|
|
||||||
.child(
|
|
||||||
div().px_1().flex().flex_none().gap_2().child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_px()
|
|
||||||
.child(
|
|
||||||
IconButton::new(Icon::ArrowLeft).state(
|
|
||||||
InteractionState::Enabled.if_enabled(can_navigate_back),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(IconButton::new(Icon::ArrowRight).state(
|
|
||||||
InteractionState::Enabled.if_enabled(can_navigate_forward),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div().w_0().flex_1().h_full().child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.child(
|
|
||||||
Tab::new()
|
|
||||||
.title("zed — fish".to_string())
|
|
||||||
.icon(Icon::Terminal)
|
|
||||||
.close_side(IconSide::Right)
|
|
||||||
.current(true),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Tab::new()
|
|
||||||
.title("zed — fish".to_string())
|
|
||||||
.icon(Icon::Terminal)
|
|
||||||
.close_side(IconSide::Right)
|
|
||||||
.current(false),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
// Terminal Pane.
|
|
||||||
.child(Pane::new(
|
|
||||||
ScrollState::default(),
|
|
||||||
Size {
|
|
||||||
width: relative(1.).into(),
|
|
||||||
height: rems(36.).into(),
|
|
||||||
},
|
|
||||||
|_, payload| {
|
|
||||||
let theme = payload.downcast_ref::<Arc<Theme>>().unwrap();
|
|
||||||
|
|
||||||
vec![crate::static_data::terminal_buffer(&theme).into_any()]
|
|
||||||
},
|
|
||||||
Box::new(theme),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{OrderMethod, Palette, PaletteItem};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct ThemeSelector {
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThemeSelector {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
scroll_state: ScrollState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
div().child(
|
|
||||||
Palette::new(self.scroll_state.clone())
|
|
||||||
.items(vec![
|
|
||||||
PaletteItem::new("One Dark"),
|
|
||||||
PaletteItem::new("Rosé Pine"),
|
|
||||||
PaletteItem::new("Rosé Pine Moon"),
|
|
||||||
PaletteItem::new("Sandcastle"),
|
|
||||||
PaletteItem::new("Solarized Dark"),
|
|
||||||
PaletteItem::new("Summercamp"),
|
|
||||||
PaletteItem::new("Atelier Cave Light"),
|
|
||||||
PaletteItem::new("Atelier Dune Light"),
|
|
||||||
PaletteItem::new("Atelier Estuary Light"),
|
|
||||||
PaletteItem::new("Atelier Forest Light"),
|
|
||||||
PaletteItem::new("Atelier Heath Light"),
|
|
||||||
])
|
|
||||||
.placeholder("Select Theme...")
|
|
||||||
.empty_string("No matches")
|
|
||||||
.default_order(OrderMethod::Ascending),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::{prelude::*, PlayerWithCallStatus};
|
|
||||||
use crate::{
|
|
||||||
theme, Avatar, Button, Icon, IconButton, IconColor, PlayerStack, ToolDivider, TrafficLights,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Livestream {
|
|
||||||
pub players: Vec<PlayerWithCallStatus>,
|
|
||||||
pub channel: Option<String>, // projects
|
|
||||||
// windows
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct TitleBar<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
/// If the window is active from the OS's perspective.
|
|
||||||
is_active: Arc<AtomicBool>,
|
|
||||||
livestream: Option<Livestream>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> TitleBar<V> {
|
|
||||||
pub fn new(cx: &mut ViewContext<V>) -> Self {
|
|
||||||
let is_active = Arc::new(AtomicBool::new(true));
|
|
||||||
let active = is_active.clone();
|
|
||||||
|
|
||||||
cx.observe_window_activation(move |_, is_active, cx| {
|
|
||||||
active.store(is_active, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
cx.notify();
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
view_type: PhantomData,
|
|
||||||
is_active,
|
|
||||||
livestream: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_livestream(mut self, livestream: Option<Livestream>) -> Self {
|
|
||||||
self.livestream = livestream;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let has_focus = cx.window_is_active();
|
|
||||||
|
|
||||||
let player_list = if let Some(livestream) = &self.livestream {
|
|
||||||
livestream.players.clone().into_iter()
|
|
||||||
} else {
|
|
||||||
vec![].into_iter()
|
|
||||||
};
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.w_full()
|
|
||||||
.h_8()
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.h_full()
|
|
||||||
.gap_4()
|
|
||||||
.px_2()
|
|
||||||
.child(TrafficLights::new().window_has_focus(has_focus))
|
|
||||||
// === Project Info === //
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.child(Button::new("zed"))
|
|
||||||
.child(Button::new("nate/gpui2-ui-components")),
|
|
||||||
)
|
|
||||||
.children(player_list.map(|p| PlayerStack::new(p)))
|
|
||||||
.child(IconButton::new(Icon::Plus)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.px_2()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.child(IconButton::new(Icon::FolderX))
|
|
||||||
.child(IconButton::new(Icon::Close)),
|
|
||||||
)
|
|
||||||
.child(ToolDivider::new())
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.px_2()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_1()
|
|
||||||
.child(IconButton::new(Icon::Mic))
|
|
||||||
.child(IconButton::new(Icon::AudioOn))
|
|
||||||
.child(IconButton::new(Icon::Screen).color(IconColor::Accent)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div().px_2().flex().items_center().child(
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
|
|
||||||
.shape(Shape::RoundedRectangle),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum ToastOrigin {
|
|
||||||
#[default]
|
|
||||||
Bottom,
|
|
||||||
BottomRight,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum ToastVariant {
|
|
||||||
#[default]
|
|
||||||
Toast,
|
|
||||||
Status,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A toast is a small, temporary window that appears to show a message to the user
|
|
||||||
/// or indicate a required action.
|
|
||||||
///
|
|
||||||
/// Toasts should not persist on the screen for more than a few seconds unless
|
|
||||||
/// they are actively showing the a process in progress.
|
|
||||||
///
|
|
||||||
/// Only one toast may be visible at a time.
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Toast<V: 'static> {
|
|
||||||
origin: ToastOrigin,
|
|
||||||
children: HackyChildren<V>,
|
|
||||||
payload: HackyChildrenPayload,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Toast<V> {
|
|
||||||
pub fn new(
|
|
||||||
origin: ToastOrigin,
|
|
||||||
children: HackyChildren<V>,
|
|
||||||
payload: HackyChildrenPayload,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
origin,
|
|
||||||
children,
|
|
||||||
payload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let color = ThemeColor::new(cx);
|
|
||||||
|
|
||||||
let mut div = div();
|
|
||||||
|
|
||||||
if self.origin == ToastOrigin::Bottom {
|
|
||||||
div = div.right_1_2();
|
|
||||||
} else {
|
|
||||||
div = div.right_4();
|
|
||||||
}
|
|
||||||
|
|
||||||
div.absolute()
|
|
||||||
.bottom_4()
|
|
||||||
.flex()
|
|
||||||
.py_2()
|
|
||||||
.px_1p5()
|
|
||||||
.min_w_40()
|
|
||||||
.rounded_md()
|
|
||||||
.fill(color.elevated_surface)
|
|
||||||
.max_w_64()
|
|
||||||
.children_any((self.children)(cx, self.payload.as_ref()))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ToolbarItem {}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Toolbar<V: 'static> {
|
|
||||||
left_items: HackyChildren<V>,
|
|
||||||
left_items_payload: HackyChildrenPayload,
|
|
||||||
right_items: HackyChildren<V>,
|
|
||||||
right_items_payload: HackyChildrenPayload,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Toolbar<V> {
|
|
||||||
pub fn new(
|
|
||||||
left_items: HackyChildren<V>,
|
|
||||||
left_items_payload: HackyChildrenPayload,
|
|
||||||
right_items: HackyChildren<V>,
|
|
||||||
right_items_payload: HackyChildrenPayload,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
left_items,
|
|
||||||
left_items_payload,
|
|
||||||
right_items,
|
|
||||||
right_items_payload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.fill(theme.highest.base.default.background)
|
|
||||||
.p_2()
|
|
||||||
.flex()
|
|
||||||
.justify_between()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.children_any((self.left_items)(cx, self.left_items_payload.as_ref())),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.children_any((self.right_items)(cx, self.right_items_payload.as_ref())),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{theme, token, SystemColor};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum TrafficLightColor {
|
|
||||||
Red,
|
|
||||||
Yellow,
|
|
||||||
Green,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
struct TrafficLight {
|
|
||||||
color: TrafficLightColor,
|
|
||||||
window_has_focus: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrafficLight {
|
|
||||||
fn new(color: TrafficLightColor, window_has_focus: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
color,
|
|
||||||
window_has_focus,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
let fill = match (self.window_has_focus, self.color) {
|
|
||||||
(true, TrafficLightColor::Red) => system_color.mac_os_traffic_light_red,
|
|
||||||
(true, TrafficLightColor::Yellow) => system_color.mac_os_traffic_light_yellow,
|
|
||||||
(true, TrafficLightColor::Green) => system_color.mac_os_traffic_light_green,
|
|
||||||
(false, _) => theme.lowest.base.active.background,
|
|
||||||
};
|
|
||||||
|
|
||||||
div().w_3().h_3().rounded_full().fill(fill)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct TrafficLights {
|
|
||||||
window_has_focus: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrafficLights {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
window_has_focus: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn window_has_focus(mut self, window_has_focus: bool) -> Self {
|
|
||||||
self.window_has_focus = window_has_focus;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let token = token();
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_2()
|
|
||||||
.child(TrafficLight::new(
|
|
||||||
TrafficLightColor::Red,
|
|
||||||
self.window_has_focus,
|
|
||||||
))
|
|
||||||
.child(TrafficLight::new(
|
|
||||||
TrafficLightColor::Yellow,
|
|
||||||
self.window_has_focus,
|
|
||||||
))
|
|
||||||
.child(TrafficLight::new(
|
|
||||||
TrafficLightColor::Green,
|
|
||||||
self.window_has_focus,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,186 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use chrono::DateTime;
|
|
||||||
use gpui2::geometry::{relative, rems, Size};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
hello_world_rust_editor_with_status_example, prelude::*, random_players_with_call_status,
|
|
||||||
Livestream,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
theme, v_stack, ChatMessage, ChatPanel, EditorPane, Pane, PaneGroup, Panel, PanelAllowedSides,
|
|
||||||
PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Element, Default)]
|
|
||||||
pub struct WorkspaceElement {
|
|
||||||
left_panel_scroll_state: ScrollState,
|
|
||||||
right_panel_scroll_state: ScrollState,
|
|
||||||
tab_bar_scroll_state: ScrollState,
|
|
||||||
bottom_panel_scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkspaceElement {
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx).clone();
|
|
||||||
|
|
||||||
let temp_size = rems(36.).into();
|
|
||||||
|
|
||||||
let root_group = PaneGroup::new_groups(
|
|
||||||
vec![
|
|
||||||
PaneGroup::new_panes(
|
|
||||||
vec![
|
|
||||||
Pane::new(
|
|
||||||
ScrollState::default(),
|
|
||||||
Size {
|
|
||||||
width: relative(1.).into(),
|
|
||||||
height: temp_size,
|
|
||||||
},
|
|
||||||
|_, payload| {
|
|
||||||
let theme = payload.downcast_ref::<Arc<Theme>>().unwrap();
|
|
||||||
|
|
||||||
vec![EditorPane::new(hello_world_rust_editor_with_status_example(
|
|
||||||
&theme,
|
|
||||||
))
|
|
||||||
.into_any()]
|
|
||||||
},
|
|
||||||
Box::new(theme.clone()),
|
|
||||||
),
|
|
||||||
Pane::new(
|
|
||||||
ScrollState::default(),
|
|
||||||
Size {
|
|
||||||
width: relative(1.).into(),
|
|
||||||
height: temp_size,
|
|
||||||
},
|
|
||||||
|_, _| vec![Terminal::new().into_any()],
|
|
||||||
Box::new(()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
SplitDirection::Vertical,
|
|
||||||
),
|
|
||||||
PaneGroup::new_panes(
|
|
||||||
vec![Pane::new(
|
|
||||||
ScrollState::default(),
|
|
||||||
Size {
|
|
||||||
width: relative(1.).into(),
|
|
||||||
height: relative(1.).into(),
|
|
||||||
},
|
|
||||||
|_, payload| {
|
|
||||||
let theme = payload.downcast_ref::<Arc<Theme>>().unwrap();
|
|
||||||
|
|
||||||
vec![EditorPane::new(hello_world_rust_editor_with_status_example(
|
|
||||||
&theme,
|
|
||||||
))
|
|
||||||
.into_any()]
|
|
||||||
},
|
|
||||||
Box::new(theme.clone()),
|
|
||||||
)],
|
|
||||||
SplitDirection::Vertical,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
SplitDirection::Horizontal,
|
|
||||||
);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.relative()
|
|
||||||
.size_full()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.font("Zed Sans Extended")
|
|
||||||
.gap_0()
|
|
||||||
.justify_start()
|
|
||||||
.items_start()
|
|
||||||
.text_color(theme.lowest.base.default.foreground)
|
|
||||||
.fill(theme.lowest.base.default.background)
|
|
||||||
.child(TitleBar::new(cx).set_livestream(Some(Livestream {
|
|
||||||
players: random_players_with_call_status(7),
|
|
||||||
channel: Some("gpui2-ui".to_string()),
|
|
||||||
})))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.w_full()
|
|
||||||
.flex()
|
|
||||||
.flex_row()
|
|
||||||
.overflow_hidden()
|
|
||||||
.border_t()
|
|
||||||
.border_b()
|
|
||||||
.border_color(theme.lowest.base.default.border)
|
|
||||||
.child(
|
|
||||||
Panel::new(
|
|
||||||
self.left_panel_scroll_state.clone(),
|
|
||||||
|_, payload| vec![ProjectPanel::new(ScrollState::default()).into_any()],
|
|
||||||
Box::new(()),
|
|
||||||
)
|
|
||||||
.side(PanelSide::Left),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_stack()
|
|
||||||
.flex_1()
|
|
||||||
.h_full()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_1()
|
|
||||||
// CSS Hack: Flex 1 has to have a set height to properly fill the space
|
|
||||||
// Or it will give you a height of 0
|
|
||||||
.h_px()
|
|
||||||
.child(root_group),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Panel::new(
|
|
||||||
self.bottom_panel_scroll_state.clone(),
|
|
||||||
|_, _| vec![Terminal::new().into_any()],
|
|
||||||
Box::new(()),
|
|
||||||
)
|
|
||||||
.allowed_sides(PanelAllowedSides::BottomOnly)
|
|
||||||
.side(PanelSide::Bottom),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Panel::new(
|
|
||||||
self.right_panel_scroll_state.clone(),
|
|
||||||
|_, payload| {
|
|
||||||
vec![ChatPanel::new(ScrollState::default())
|
|
||||||
.with_messages(vec![
|
|
||||||
ChatMessage::new(
|
|
||||||
"osiewicz".to_string(),
|
|
||||||
"is this thing on?".to_string(),
|
|
||||||
DateTime::parse_from_rfc3339(
|
|
||||||
"2023-09-27T15:40:52.707Z",
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.naive_local(),
|
|
||||||
),
|
|
||||||
ChatMessage::new(
|
|
||||||
"maxdeviant".to_string(),
|
|
||||||
"Reading you loud and clear!".to_string(),
|
|
||||||
DateTime::parse_from_rfc3339(
|
|
||||||
"2023-09-28T15:40:52.707Z",
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.naive_local(),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.into_any()]
|
|
||||||
},
|
|
||||||
Box::new(()),
|
|
||||||
)
|
|
||||||
.side(PanelSide::Right),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(StatusBar::new())
|
|
||||||
// An example of a toast is below
|
|
||||||
// Currently because of stacking order this gets obscured by other elements
|
|
||||||
|
|
||||||
// .child(Toast::new(
|
|
||||||
// ToastOrigin::Bottom,
|
|
||||||
// |_, payload| {
|
|
||||||
// let theme = payload.downcast_ref::<Arc<Theme>>().unwrap();
|
|
||||||
|
|
||||||
// vec![Label::new("label").into_any()]
|
|
||||||
// },
|
|
||||||
// Box::new(theme.clone()),
|
|
||||||
// ))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use gpui2::Element;
|
|
||||||
|
|
||||||
use crate::theme::{Theme, Themed};
|
|
||||||
|
|
||||||
pub trait ElementExt<V: 'static>: Element<V> {
|
|
||||||
fn themed(self, theme: Theme) -> Themed<V, Self>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V>> ElementExt<V> for E {
|
|
||||||
fn themed(self, theme: Theme) -> Themed<V, Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Themed {
|
|
||||||
child: self,
|
|
||||||
theme,
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
mod avatar;
|
|
||||||
mod button;
|
|
||||||
mod details;
|
|
||||||
mod icon;
|
|
||||||
mod input;
|
|
||||||
mod label;
|
|
||||||
mod player;
|
|
||||||
mod stack;
|
|
||||||
mod tool_divider;
|
|
||||||
|
|
||||||
pub use avatar::*;
|
|
||||||
pub use button::*;
|
|
||||||
pub use details::*;
|
|
||||||
pub use icon::*;
|
|
||||||
pub use input::*;
|
|
||||||
pub use label::*;
|
|
||||||
pub use player::*;
|
|
||||||
pub use stack::*;
|
|
||||||
pub use tool_divider::*;
|
|
|
@ -1,41 +0,0 @@
|
||||||
use gpui2::elements::img;
|
|
||||||
use gpui2::ArcCow;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct Avatar {
|
|
||||||
src: ArcCow<'static, str>,
|
|
||||||
shape: Shape,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Avatar {
|
|
||||||
pub fn new(src: impl Into<ArcCow<'static, str>>) -> Self {
|
|
||||||
Self {
|
|
||||||
src: src.into(),
|
|
||||||
shape: Shape::Circle,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shape(mut self, shape: Shape) -> Self {
|
|
||||||
self.shape = shape;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
let mut img = img();
|
|
||||||
|
|
||||||
if self.shape == Shape::Circle {
|
|
||||||
img = img.rounded_full();
|
|
||||||
} else {
|
|
||||||
img = img.rounded_md();
|
|
||||||
}
|
|
||||||
|
|
||||||
img.uri(self.src.clone())
|
|
||||||
.size_4()
|
|
||||||
.fill(theme.middle.warning.default.foreground)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,203 +0,0 @@
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use gpui2::geometry::DefiniteLength;
|
|
||||||
use gpui2::platform::MouseButton;
|
|
||||||
use gpui2::{EventContext, Hsla, Interactive, WindowContext};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{h_stack, theme, Icon, IconColor, IconElement, Label, LabelColor, LabelSize};
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone, Copy)]
|
|
||||||
pub enum IconPosition {
|
|
||||||
#[default]
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, PartialEq)]
|
|
||||||
pub enum ButtonVariant {
|
|
||||||
#[default]
|
|
||||||
Ghost,
|
|
||||||
Filled,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ButtonHandlers<V> {
|
|
||||||
click: Option<Rc<dyn Fn(&mut V, &mut EventContext<V>)>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Default for ButtonHandlers<V> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { click: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Button<V: 'static> {
|
|
||||||
label: String,
|
|
||||||
variant: ButtonVariant,
|
|
||||||
state: InteractionState,
|
|
||||||
icon: Option<Icon>,
|
|
||||||
icon_position: Option<IconPosition>,
|
|
||||||
width: Option<DefiniteLength>,
|
|
||||||
handlers: ButtonHandlers<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Button<V> {
|
|
||||||
pub fn new<L>(label: L) -> Self
|
|
||||||
where
|
|
||||||
L: Into<String>,
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
label: label.into(),
|
|
||||||
variant: Default::default(),
|
|
||||||
state: Default::default(),
|
|
||||||
icon: None,
|
|
||||||
icon_position: None,
|
|
||||||
width: Default::default(),
|
|
||||||
handlers: ButtonHandlers::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ghost<L>(label: L) -> Self
|
|
||||||
where
|
|
||||||
L: Into<String>,
|
|
||||||
{
|
|
||||||
Self::new(label).variant(ButtonVariant::Ghost)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variant(mut self, variant: ButtonVariant) -> Self {
|
|
||||||
self.variant = variant;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn state(mut self, state: InteractionState) -> Self {
|
|
||||||
self.state = state;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon(mut self, icon: Icon) -> Self {
|
|
||||||
self.icon = Some(icon);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon_position(mut self, icon_position: IconPosition) -> Self {
|
|
||||||
if self.icon.is_none() {
|
|
||||||
panic!("An icon must be present if an icon_position is provided.");
|
|
||||||
}
|
|
||||||
self.icon_position = Some(icon_position);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn width(mut self, width: Option<DefiniteLength>) -> Self {
|
|
||||||
self.width = width;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_click(mut self, handler: impl Fn(&mut V, &mut EventContext<V>) + 'static) -> Self {
|
|
||||||
self.handlers.click = Some(Rc::new(handler));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn background_color(&self, cx: &mut ViewContext<V>) -> Hsla {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
match (self.variant, self.state) {
|
|
||||||
(ButtonVariant::Ghost, InteractionState::Hovered) => {
|
|
||||||
theme.lowest.base.hovered.background
|
|
||||||
}
|
|
||||||
(ButtonVariant::Ghost, InteractionState::Active) => {
|
|
||||||
theme.lowest.base.pressed.background
|
|
||||||
}
|
|
||||||
(ButtonVariant::Filled, InteractionState::Enabled) => {
|
|
||||||
theme.lowest.on.default.background
|
|
||||||
}
|
|
||||||
(ButtonVariant::Filled, InteractionState::Hovered) => {
|
|
||||||
theme.lowest.on.hovered.background
|
|
||||||
}
|
|
||||||
(ButtonVariant::Filled, InteractionState::Active) => theme.lowest.on.pressed.background,
|
|
||||||
(ButtonVariant::Filled, InteractionState::Disabled) => {
|
|
||||||
theme.lowest.on.disabled.background
|
|
||||||
}
|
|
||||||
_ => system_color.transparent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 border_color(&self, cx: &WindowContext) -> Hsla {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
match self.state {
|
|
||||||
InteractionState::Focused => theme.lowest.accent.default.border,
|
|
||||||
_ => system_color.transparent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_label(&self) -> Label {
|
|
||||||
Label::new(self.label.clone())
|
|
||||||
.size(LabelSize::Small)
|
|
||||||
.color(self.label_color())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_icon(&self, icon_color: IconColor) -> Option<IconElement> {
|
|
||||||
self.icon.map(|i| IconElement::new(i).color(icon_color))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let icon_color = self.icon_color();
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
let border_color = self.border_color(cx);
|
|
||||||
|
|
||||||
let mut el = h_stack()
|
|
||||||
.h_6()
|
|
||||||
.px_1()
|
|
||||||
.items_center()
|
|
||||||
.rounded_md()
|
|
||||||
.border()
|
|
||||||
.border_color(border_color)
|
|
||||||
.fill(self.background_color(cx));
|
|
||||||
|
|
||||||
match (self.icon, self.icon_position) {
|
|
||||||
(Some(_), Some(IconPosition::Left)) => {
|
|
||||||
el = el
|
|
||||||
.gap_1()
|
|
||||||
.child(self.render_label())
|
|
||||||
.children(self.render_icon(icon_color))
|
|
||||||
}
|
|
||||||
(Some(_), Some(IconPosition::Right)) => {
|
|
||||||
el = el
|
|
||||||
.gap_1()
|
|
||||||
.children(self.render_icon(icon_color))
|
|
||||||
.child(self.render_label())
|
|
||||||
}
|
|
||||||
(_, _) => el = el.child(self.render_label()),
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(width) = self.width {
|
|
||||||
el = el.w(width).justify_center();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(click_handler) = self.handlers.click.clone() {
|
|
||||||
el = el.on_mouse_down(MouseButton::Left, move |view, event, cx| {
|
|
||||||
click_handler(view, cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
el
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct Details {
|
|
||||||
text: &'static str,
|
|
||||||
meta: Option<&'static str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Details {
|
|
||||||
pub fn new(text: &'static str) -> Self {
|
|
||||||
Self { text, meta: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn meta_text(mut self, meta: &'static str) -> Self {
|
|
||||||
self.meta = Some(meta);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
// .flex()
|
|
||||||
// .w_full()
|
|
||||||
.p_1()
|
|
||||||
.gap_0p5()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(theme.lowest.base.default.foreground)
|
|
||||||
.child(self.text)
|
|
||||||
.children(self.meta.map(|m| m))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use gpui2::elements::svg;
|
|
||||||
use gpui2::Hsla;
|
|
||||||
use strum::EnumIter;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme::theme;
|
|
||||||
use crate::Theme;
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
|
||||||
pub enum IconSize {
|
|
||||||
Small,
|
|
||||||
#[default]
|
|
||||||
Large,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
|
||||||
pub enum IconColor {
|
|
||||||
#[default]
|
|
||||||
Default,
|
|
||||||
Muted,
|
|
||||||
Disabled,
|
|
||||||
Placeholder,
|
|
||||||
Accent,
|
|
||||||
Error,
|
|
||||||
Warning,
|
|
||||||
Success,
|
|
||||||
Info,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IconColor {
|
|
||||||
pub fn color(self, theme: Arc<Theme>) -> Hsla {
|
|
||||||
match self {
|
|
||||||
IconColor::Default => theme.lowest.base.default.foreground,
|
|
||||||
IconColor::Muted => theme.lowest.variant.default.foreground,
|
|
||||||
IconColor::Disabled => theme.lowest.base.disabled.foreground,
|
|
||||||
IconColor::Placeholder => theme.lowest.base.disabled.foreground,
|
|
||||||
IconColor::Accent => theme.lowest.accent.default.foreground,
|
|
||||||
IconColor::Error => theme.lowest.negative.default.foreground,
|
|
||||||
IconColor::Warning => theme.lowest.warning.default.foreground,
|
|
||||||
IconColor::Success => theme.lowest.positive.default.foreground,
|
|
||||||
IconColor::Info => theme.lowest.accent.default.foreground,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone, EnumIter)]
|
|
||||||
pub enum Icon {
|
|
||||||
Ai,
|
|
||||||
ArrowLeft,
|
|
||||||
ArrowRight,
|
|
||||||
ArrowUpRight,
|
|
||||||
AudioOff,
|
|
||||||
AudioOn,
|
|
||||||
Bolt,
|
|
||||||
ChevronDown,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
ChevronUp,
|
|
||||||
Close,
|
|
||||||
ExclamationTriangle,
|
|
||||||
ExternalLink,
|
|
||||||
File,
|
|
||||||
FileGeneric,
|
|
||||||
FileDoc,
|
|
||||||
FileGit,
|
|
||||||
FileLock,
|
|
||||||
FileRust,
|
|
||||||
FileToml,
|
|
||||||
FileTree,
|
|
||||||
Folder,
|
|
||||||
FolderOpen,
|
|
||||||
FolderX,
|
|
||||||
#[default]
|
|
||||||
Hash,
|
|
||||||
InlayHint,
|
|
||||||
MagicWand,
|
|
||||||
MagnifyingGlass,
|
|
||||||
Maximize,
|
|
||||||
Menu,
|
|
||||||
MessageBubbles,
|
|
||||||
Mic,
|
|
||||||
MicMute,
|
|
||||||
Plus,
|
|
||||||
Quote,
|
|
||||||
Screen,
|
|
||||||
SelectAll,
|
|
||||||
Split,
|
|
||||||
SplitMessage,
|
|
||||||
Terminal,
|
|
||||||
XCircle,
|
|
||||||
Copilot,
|
|
||||||
Envelope,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Icon {
|
|
||||||
pub fn path(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Icon::Ai => "icons/ai.svg",
|
|
||||||
Icon::ArrowLeft => "icons/arrow_left.svg",
|
|
||||||
Icon::ArrowRight => "icons/arrow_right.svg",
|
|
||||||
Icon::ArrowUpRight => "icons/arrow_up_right.svg",
|
|
||||||
Icon::AudioOff => "icons/speaker-off.svg",
|
|
||||||
Icon::AudioOn => "icons/speaker-loud.svg",
|
|
||||||
Icon::Bolt => "icons/bolt.svg",
|
|
||||||
Icon::ChevronDown => "icons/chevron_down.svg",
|
|
||||||
Icon::ChevronLeft => "icons/chevron_left.svg",
|
|
||||||
Icon::ChevronRight => "icons/chevron_right.svg",
|
|
||||||
Icon::ChevronUp => "icons/chevron_up.svg",
|
|
||||||
Icon::Close => "icons/x.svg",
|
|
||||||
Icon::ExclamationTriangle => "icons/warning.svg",
|
|
||||||
Icon::ExternalLink => "icons/external_link.svg",
|
|
||||||
Icon::File => "icons/file.svg",
|
|
||||||
Icon::FileGeneric => "icons/file_icons/file.svg",
|
|
||||||
Icon::FileDoc => "icons/file_icons/book.svg",
|
|
||||||
Icon::FileGit => "icons/file_icons/git.svg",
|
|
||||||
Icon::FileLock => "icons/file_icons/lock.svg",
|
|
||||||
Icon::FileRust => "icons/file_icons/rust.svg",
|
|
||||||
Icon::FileToml => "icons/file_icons/toml.svg",
|
|
||||||
Icon::FileTree => "icons/project.svg",
|
|
||||||
Icon::Folder => "icons/file_icons/folder.svg",
|
|
||||||
Icon::FolderOpen => "icons/file_icons/folder_open.svg",
|
|
||||||
Icon::FolderX => "icons/stop_sharing.svg",
|
|
||||||
Icon::Hash => "icons/hash.svg",
|
|
||||||
Icon::InlayHint => "icons/inlay_hint.svg",
|
|
||||||
Icon::MagicWand => "icons/magic-wand.svg",
|
|
||||||
Icon::MagnifyingGlass => "icons/magnifying_glass.svg",
|
|
||||||
Icon::Maximize => "icons/maximize.svg",
|
|
||||||
Icon::Menu => "icons/menu.svg",
|
|
||||||
Icon::MessageBubbles => "icons/conversations.svg",
|
|
||||||
Icon::Mic => "icons/mic.svg",
|
|
||||||
Icon::MicMute => "icons/mic-mute.svg",
|
|
||||||
Icon::Plus => "icons/plus.svg",
|
|
||||||
Icon::Quote => "icons/quote.svg",
|
|
||||||
Icon::Screen => "icons/desktop.svg",
|
|
||||||
Icon::SelectAll => "icons/select-all.svg",
|
|
||||||
Icon::Split => "icons/split.svg",
|
|
||||||
Icon::SplitMessage => "icons/split_message.svg",
|
|
||||||
Icon::Terminal => "icons/terminal.svg",
|
|
||||||
Icon::XCircle => "icons/error.svg",
|
|
||||||
Icon::Copilot => "icons/copilot.svg",
|
|
||||||
Icon::Envelope => "icons/feedback.svg",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct IconElement {
|
|
||||||
icon: Icon,
|
|
||||||
color: IconColor,
|
|
||||||
size: IconSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IconElement {
|
|
||||||
pub fn new(icon: Icon) -> Self {
|
|
||||||
Self {
|
|
||||||
icon,
|
|
||||||
color: IconColor::default(),
|
|
||||||
size: IconSize::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn color(mut self, color: IconColor) -> Self {
|
|
||||||
self.color = color;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(mut self, size: IconSize) -> Self {
|
|
||||||
self.size = size;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let fill = self.color.color(theme);
|
|
||||||
|
|
||||||
let sized_svg = match self.size {
|
|
||||||
IconSize::Small => svg().size_3p5(),
|
|
||||||
IconSize::Large => svg().size_4(),
|
|
||||||
};
|
|
||||||
|
|
||||||
sized_svg.flex_none().path(self.icon.path()).fill(fill)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
|
||||||
pub enum InputVariant {
|
|
||||||
#[default]
|
|
||||||
Ghost,
|
|
||||||
Filled,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Input {
|
|
||||||
placeholder: &'static str,
|
|
||||||
value: String,
|
|
||||||
state: InteractionState,
|
|
||||||
variant: InputVariant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Input {
|
|
||||||
pub fn new(placeholder: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
placeholder,
|
|
||||||
value: "".to_string(),
|
|
||||||
state: InteractionState::default(),
|
|
||||||
variant: InputVariant::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value(mut self, value: String) -> Self {
|
|
||||||
self.value = value;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn state(mut self, state: InteractionState) -> Self {
|
|
||||||
self.state = state;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variant(mut self, variant: InputVariant) -> Self {
|
|
||||||
self.variant = variant;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
let text_el;
|
|
||||||
let text_color;
|
|
||||||
let background_color_default;
|
|
||||||
let background_color_active;
|
|
||||||
|
|
||||||
let mut border_color_default = theme.middle.base.default.border;
|
|
||||||
let mut border_color_hover = theme.middle.base.hovered.border;
|
|
||||||
let mut border_color_active = theme.middle.base.pressed.border;
|
|
||||||
let border_color_focus = theme.middle.base.pressed.background;
|
|
||||||
|
|
||||||
match self.variant {
|
|
||||||
InputVariant::Ghost => {
|
|
||||||
background_color_default = theme.middle.base.default.background;
|
|
||||||
background_color_active = theme.middle.base.active.background;
|
|
||||||
}
|
|
||||||
InputVariant::Filled => {
|
|
||||||
background_color_default = theme.middle.on.default.background;
|
|
||||||
background_color_active = theme.middle.on.active.background;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.state == InteractionState::Focused {
|
|
||||||
border_color_default = theme.players[0].cursor;
|
|
||||||
border_color_hover = theme.players[0].cursor;
|
|
||||||
border_color_active = theme.players[0].cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.state == InteractionState::Focused || self.state == InteractionState::Active {
|
|
||||||
text_el = self.value.clone();
|
|
||||||
text_color = theme.lowest.base.default.foreground;
|
|
||||||
} else {
|
|
||||||
text_el = self.placeholder.to_string().clone();
|
|
||||||
text_color = theme.lowest.base.disabled.foreground;
|
|
||||||
}
|
|
||||||
|
|
||||||
div()
|
|
||||||
.h_7()
|
|
||||||
.w_full()
|
|
||||||
.px_2()
|
|
||||||
.border()
|
|
||||||
.border_color(border_color_default)
|
|
||||||
.fill(background_color_default)
|
|
||||||
.hover()
|
|
||||||
.border_color(border_color_hover)
|
|
||||||
.active()
|
|
||||||
.border_color(border_color_active)
|
|
||||||
.fill(background_color_active)
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.text_sm()
|
|
||||||
.text_color(text_color)
|
|
||||||
.child(text_el)
|
|
||||||
.child(div().text_color(theme.players[0].cursor).child("|")),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
use gpui2::{Hsla, WindowContext};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme::theme;
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
|
||||||
pub enum LabelColor {
|
|
||||||
#[default]
|
|
||||||
Default,
|
|
||||||
Muted,
|
|
||||||
Created,
|
|
||||||
Modified,
|
|
||||||
Deleted,
|
|
||||||
Disabled,
|
|
||||||
Hidden,
|
|
||||||
Placeholder,
|
|
||||||
Accent,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LabelColor {
|
|
||||||
pub fn hsla(&self, cx: &WindowContext) -> Hsla {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::Default => theme.middle.base.default.foreground,
|
|
||||||
Self::Muted => theme.middle.variant.default.foreground,
|
|
||||||
Self::Created => theme.middle.positive.default.foreground,
|
|
||||||
Self::Modified => theme.middle.warning.default.foreground,
|
|
||||||
Self::Deleted => theme.middle.negative.default.foreground,
|
|
||||||
Self::Disabled => theme.middle.base.disabled.foreground,
|
|
||||||
Self::Hidden => theme.middle.variant.default.foreground,
|
|
||||||
Self::Placeholder => theme.middle.base.disabled.foreground,
|
|
||||||
Self::Accent => theme.middle.accent.default.foreground,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
|
||||||
pub enum LabelSize {
|
|
||||||
#[default]
|
|
||||||
Default,
|
|
||||||
Small,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
|
||||||
pub struct Label {
|
|
||||||
label: String,
|
|
||||||
color: LabelColor,
|
|
||||||
size: LabelSize,
|
|
||||||
highlight_indices: Vec<usize>,
|
|
||||||
strikethrough: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Label {
|
|
||||||
pub fn new<L>(label: L) -> Self
|
|
||||||
where
|
|
||||||
L: Into<String>,
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
label: label.into(),
|
|
||||||
color: LabelColor::Default,
|
|
||||||
size: LabelSize::Default,
|
|
||||||
highlight_indices: Vec::new(),
|
|
||||||
strikethrough: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn color(mut self, color: LabelColor) -> Self {
|
|
||||||
self.color = color;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(mut self, size: LabelSize) -> Self {
|
|
||||||
self.size = size;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
|
|
||||||
self.highlight_indices = indices;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
|
|
||||||
self.strikethrough = strikethrough;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
let highlight_color = theme.lowest.accent.default.foreground;
|
|
||||||
|
|
||||||
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
|
|
||||||
|
|
||||||
let mut runs: SmallVec<[Run; 8]> = SmallVec::new();
|
|
||||||
|
|
||||||
for (char_ix, char) in self.label.char_indices() {
|
|
||||||
let mut color = self.color.hsla(cx);
|
|
||||||
|
|
||||||
if let Some(highlight_ix) = highlight_indices.peek() {
|
|
||||||
if char_ix == *highlight_ix {
|
|
||||||
color = highlight_color;
|
|
||||||
|
|
||||||
highlight_indices.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let last_run = runs.last_mut();
|
|
||||||
|
|
||||||
let start_new_run = if let Some(last_run) = last_run {
|
|
||||||
if color == last_run.color {
|
|
||||||
last_run.text.push(char);
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
if start_new_run {
|
|
||||||
runs.push(Run {
|
|
||||||
text: char.to_string(),
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.when(self.strikethrough, |this| {
|
|
||||||
this.relative().child(
|
|
||||||
div()
|
|
||||||
.absolute()
|
|
||||||
.top_px()
|
|
||||||
.my_auto()
|
|
||||||
.w_full()
|
|
||||||
.h_px()
|
|
||||||
.fill(LabelColor::Hidden.hsla(cx)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.children(runs.into_iter().map(|run| {
|
|
||||||
let mut div = div();
|
|
||||||
|
|
||||||
if self.size == LabelSize::Small {
|
|
||||||
div = div.text_xs();
|
|
||||||
} else {
|
|
||||||
div = div.text_sm();
|
|
||||||
}
|
|
||||||
|
|
||||||
div.text_color(run.color).child(run.text)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A run of text that receives the same style.
|
|
||||||
struct Run {
|
|
||||||
pub text: String,
|
|
||||||
pub color: Hsla,
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
use gpui2::{Hsla, ViewContext};
|
|
||||||
|
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
||||||
pub enum PlayerStatus {
|
|
||||||
#[default]
|
|
||||||
Offline,
|
|
||||||
Online,
|
|
||||||
InCall,
|
|
||||||
Away,
|
|
||||||
DoNotDisturb,
|
|
||||||
Invisible,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
||||||
pub enum MicStatus {
|
|
||||||
Muted,
|
|
||||||
#[default]
|
|
||||||
Unmuted,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
||||||
pub enum VideoStatus {
|
|
||||||
On,
|
|
||||||
#[default]
|
|
||||||
Off,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
||||||
pub enum ScreenShareStatus {
|
|
||||||
Shared,
|
|
||||||
#[default]
|
|
||||||
NotShared,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PlayerCallStatus {
|
|
||||||
pub mic_status: MicStatus,
|
|
||||||
/// Indicates if the player is currently speaking
|
|
||||||
/// And the intensity of the volume coming through
|
|
||||||
///
|
|
||||||
/// 0.0 - 1.0
|
|
||||||
pub voice_activity: f32,
|
|
||||||
pub video_status: VideoStatus,
|
|
||||||
pub screen_share_status: ScreenShareStatus,
|
|
||||||
pub in_current_project: bool,
|
|
||||||
pub disconnected: bool,
|
|
||||||
pub following: Option<Vec<Player>>,
|
|
||||||
pub followers: Option<Vec<Player>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlayerCallStatus {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
mic_status: MicStatus::default(),
|
|
||||||
voice_activity: 0.,
|
|
||||||
video_status: VideoStatus::default(),
|
|
||||||
screen_share_status: ScreenShareStatus::default(),
|
|
||||||
in_current_project: true,
|
|
||||||
disconnected: false,
|
|
||||||
following: None,
|
|
||||||
followers: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Clone)]
|
|
||||||
pub struct Player {
|
|
||||||
index: usize,
|
|
||||||
avatar_src: String,
|
|
||||||
username: String,
|
|
||||||
status: PlayerStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PlayerWithCallStatus {
|
|
||||||
player: Player,
|
|
||||||
call_status: PlayerCallStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlayerWithCallStatus {
|
|
||||||
pub fn new(player: Player, call_status: PlayerCallStatus) -> Self {
|
|
||||||
Self {
|
|
||||||
player,
|
|
||||||
call_status,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_player(&self) -> &Player {
|
|
||||||
&self.player
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_call_status(&self) -> &PlayerCallStatus {
|
|
||||||
&self.call_status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Player {
|
|
||||||
pub fn new(index: usize, avatar_src: String, username: String) -> Self {
|
|
||||||
Self {
|
|
||||||
index,
|
|
||||||
avatar_src,
|
|
||||||
username,
|
|
||||||
status: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_status(mut self, status: PlayerStatus) -> Self {
|
|
||||||
self.status = status;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cursor_color<V>(&self, cx: &mut ViewContext<V>) -> Hsla {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let index = self.index % 8;
|
|
||||||
theme.players[self.index].cursor
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selection_color<V>(&self, cx: &mut ViewContext<V>) -> Hsla {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let index = self.index % 8;
|
|
||||||
theme.players[self.index].selection
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn avatar_src(&self) -> &str {
|
|
||||||
&self.avatar_src
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn index(&self) -> usize {
|
|
||||||
self.index
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
use gpui2::elements::div::Div;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
pub trait Stack: StyleHelpers {
|
|
||||||
/// Horizontally stacks elements.
|
|
||||||
fn h_stack(self) -> Self {
|
|
||||||
self.flex().flex_row().items_center()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Vertically stacks elements.
|
|
||||||
fn v_stack(self) -> Self {
|
|
||||||
self.flex().flex_col()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Stack for Div<V> {}
|
|
||||||
|
|
||||||
/// Horizontally stacks elements.
|
|
||||||
///
|
|
||||||
/// Sets `flex()`, `flex_row()`, `items_center()`
|
|
||||||
pub fn h_stack<V: 'static>() -> Div<V> {
|
|
||||||
div().h_stack()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Vertically stacks elements.
|
|
||||||
///
|
|
||||||
/// Sets `flex()`, `flex_col()`
|
|
||||||
pub fn v_stack<V: 'static>() -> Div<V> {
|
|
||||||
div().v_stack()
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct ToolDivider {}
|
|
||||||
|
|
||||||
impl ToolDivider {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div().w_px().h_3().fill(theme.lowest.base.default.border)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#![allow(dead_code, unused_variables)]
|
|
||||||
|
|
||||||
mod children;
|
|
||||||
mod components;
|
|
||||||
mod element_ext;
|
|
||||||
mod elements;
|
|
||||||
pub mod prelude;
|
|
||||||
mod static_data;
|
|
||||||
mod theme;
|
|
||||||
mod tokens;
|
|
||||||
|
|
||||||
pub use children::*;
|
|
||||||
pub use components::*;
|
|
||||||
pub use element_ext::*;
|
|
||||||
pub use elements::*;
|
|
||||||
pub use prelude::*;
|
|
||||||
pub use static_data::*;
|
|
||||||
pub use tokens::*;
|
|
||||||
|
|
||||||
pub use crate::theme::*;
|
|
|
@ -1,274 +0,0 @@
|
||||||
pub use gpui2::elements::div::{div, ScrollState};
|
|
||||||
pub use gpui2::style::{StyleHelpers, Styleable};
|
|
||||||
pub use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
|
||||||
|
|
||||||
pub use crate::{theme, ButtonVariant, HackyChildren, HackyChildrenPayload, InputVariant, Theme};
|
|
||||||
|
|
||||||
use gpui2::{hsla, rgb, Hsla, WindowContext};
|
|
||||||
use strum::EnumIter;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SystemColor {
|
|
||||||
pub transparent: Hsla,
|
|
||||||
pub mac_os_traffic_light_red: Hsla,
|
|
||||||
pub mac_os_traffic_light_yellow: Hsla,
|
|
||||||
pub mac_os_traffic_light_green: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SystemColor {
|
|
||||||
pub fn new() -> SystemColor {
|
|
||||||
SystemColor {
|
|
||||||
transparent: hsla(0.0, 0.0, 0.0, 0.0),
|
|
||||||
mac_os_traffic_light_red: rgb::<Hsla>(0xEC695E),
|
|
||||||
mac_os_traffic_light_yellow: rgb::<Hsla>(0xF4BF4F),
|
|
||||||
mac_os_traffic_light_green: rgb::<Hsla>(0x62C554),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn color(&self) -> Hsla {
|
|
||||||
self.transparent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct ThemeColor {
|
|
||||||
pub border: Hsla,
|
|
||||||
pub border_variant: Hsla,
|
|
||||||
/// The background color of an elevated surface, like a modal, tooltip or toast.
|
|
||||||
pub elevated_surface: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThemeColor {
|
|
||||||
pub fn new(cx: &WindowContext) -> Self {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
border: theme.lowest.base.default.border,
|
|
||||||
border_variant: theme.lowest.variant.default.border,
|
|
||||||
elevated_surface: theme.middle.base.default.background,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, EnumIter, Clone, Copy)]
|
|
||||||
pub enum HighlightColor {
|
|
||||||
#[default]
|
|
||||||
Default,
|
|
||||||
Comment,
|
|
||||||
String,
|
|
||||||
Function,
|
|
||||||
Keyword,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HighlightColor {
|
|
||||||
pub fn hsla(&self, theme: &Theme) -> Hsla {
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::Default => theme
|
|
||||||
.syntax
|
|
||||||
.get("primary")
|
|
||||||
.expect("no theme.syntax.primary")
|
|
||||||
.clone(),
|
|
||||||
Self::Comment => theme
|
|
||||||
.syntax
|
|
||||||
.get("comment")
|
|
||||||
.expect("no theme.syntax.comment")
|
|
||||||
.clone(),
|
|
||||||
Self::String => theme
|
|
||||||
.syntax
|
|
||||||
.get("string")
|
|
||||||
.expect("no theme.syntax.string")
|
|
||||||
.clone(),
|
|
||||||
Self::Function => theme
|
|
||||||
.syntax
|
|
||||||
.get("function")
|
|
||||||
.expect("no theme.syntax.function")
|
|
||||||
.clone(),
|
|
||||||
Self::Keyword => theme
|
|
||||||
.syntax
|
|
||||||
.get("keyword")
|
|
||||||
.expect("no theme.syntax.keyword")
|
|
||||||
.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum FileSystemStatus {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Conflict,
|
|
||||||
Deleted,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileSystemStatus {
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::None => "None".to_string(),
|
|
||||||
Self::Conflict => "Conflict".to_string(),
|
|
||||||
Self::Deleted => "Deleted".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum GitStatus {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Created,
|
|
||||||
Modified,
|
|
||||||
Deleted,
|
|
||||||
Conflict,
|
|
||||||
Renamed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GitStatus {
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::None => "None".to_string(),
|
|
||||||
Self::Created => "Created".to_string(),
|
|
||||||
Self::Modified => "Modified".to_string(),
|
|
||||||
Self::Deleted => "Deleted".to_string(),
|
|
||||||
Self::Conflict => "Conflict".to_string(),
|
|
||||||
Self::Renamed => "Renamed".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hsla(&self, cx: &WindowContext) -> Hsla {
|
|
||||||
let theme = theme(cx);
|
|
||||||
let system_color = SystemColor::new();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::None => system_color.transparent,
|
|
||||||
Self::Created => theme.lowest.positive.default.foreground,
|
|
||||||
Self::Modified => theme.lowest.warning.default.foreground,
|
|
||||||
Self::Deleted => theme.lowest.negative.default.foreground,
|
|
||||||
Self::Conflict => theme.lowest.warning.default.foreground,
|
|
||||||
Self::Renamed => theme.lowest.accent.default.foreground,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum DiagnosticStatus {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Error,
|
|
||||||
Warning,
|
|
||||||
Info,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum IconSide {
|
|
||||||
#[default]
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum OrderMethod {
|
|
||||||
#[default]
|
|
||||||
Ascending,
|
|
||||||
Descending,
|
|
||||||
MostRecent,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum Shape {
|
|
||||||
#[default]
|
|
||||||
Circle,
|
|
||||||
RoundedRectangle,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
|
||||||
pub enum DisclosureControlVisibility {
|
|
||||||
#[default]
|
|
||||||
OnHover,
|
|
||||||
Always,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone, EnumIter, strum::Display)]
|
|
||||||
pub enum InteractionState {
|
|
||||||
#[default]
|
|
||||||
Enabled,
|
|
||||||
Hovered,
|
|
||||||
Active,
|
|
||||||
Focused,
|
|
||||||
Disabled,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InteractionState {
|
|
||||||
pub fn if_enabled(&self, enabled: bool) -> Self {
|
|
||||||
if enabled {
|
|
||||||
*self
|
|
||||||
} else {
|
|
||||||
InteractionState::Disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
|
||||||
pub enum SelectedState {
|
|
||||||
#[default]
|
|
||||||
Unselected,
|
|
||||||
PartiallySelected,
|
|
||||||
Selected,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum Toggleable {
|
|
||||||
Toggleable(ToggleState),
|
|
||||||
#[default]
|
|
||||||
NotToggleable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Toggleable {
|
|
||||||
pub fn is_toggled(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Toggleable(ToggleState::Toggled) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<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,966 +0,0 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Buffer, BufferRow, BufferRows, Editor, FileSystemStatus, GitStatus, HighlightColor,
|
|
||||||
HighlightedLine, HighlightedText, Icon, Keybinding, Label, LabelColor, ListEntry,
|
|
||||||
ListEntrySize, ListItem, Livestream, MicStatus, ModifierKeys, PaletteItem, Player,
|
|
||||||
PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, Theme, ToggleState,
|
|
||||||
VideoStatus,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn static_tabs_example() -> Vec<Tab> {
|
|
||||||
vec![
|
|
||||||
Tab::new()
|
|
||||||
.title("wip.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false)
|
|
||||||
.fs_status(FileSystemStatus::Deleted),
|
|
||||||
Tab::new()
|
|
||||||
.title("Cargo.toml".to_string())
|
|
||||||
.icon(Icon::FileToml)
|
|
||||||
.current(false)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
Tab::new()
|
|
||||||
.title("Channels Panel".to_string())
|
|
||||||
.icon(Icon::Hash)
|
|
||||||
.current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("channels_panel.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(true)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
Tab::new()
|
|
||||||
.title("workspace.rs".to_string())
|
|
||||||
.current(false)
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
Tab::new()
|
|
||||||
.title("icon_button.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("storybook.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false)
|
|
||||||
.git_status(GitStatus::Created),
|
|
||||||
Tab::new()
|
|
||||||
.title("theme.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("theme_registry.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("styleable_helpers.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_tabs_1() -> Vec<Tab> {
|
|
||||||
vec![
|
|
||||||
Tab::new()
|
|
||||||
.title("project_panel.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false)
|
|
||||||
.fs_status(FileSystemStatus::Deleted),
|
|
||||||
Tab::new()
|
|
||||||
.title("tab_bar.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
Tab::new()
|
|
||||||
.title("workspace.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false),
|
|
||||||
Tab::new()
|
|
||||||
.title("tab.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(true)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_tabs_2() -> Vec<Tab> {
|
|
||||||
vec![
|
|
||||||
Tab::new()
|
|
||||||
.title("tab_bar.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(false)
|
|
||||||
.fs_status(FileSystemStatus::Deleted),
|
|
||||||
Tab::new()
|
|
||||||
.title("static_data.rs".to_string())
|
|
||||||
.icon(Icon::FileRust)
|
|
||||||
.current(true)
|
|
||||||
.git_status(GitStatus::Modified),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_tabs_3() -> Vec<Tab> {
|
|
||||||
vec![Tab::new().git_status(GitStatus::Created).current(true)]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_players() -> Vec<Player> {
|
|
||||||
vec![
|
|
||||||
Player::new(
|
|
||||||
0,
|
|
||||||
"https://avatars.githubusercontent.com/u/1714999?v=4".into(),
|
|
||||||
"nathansobo".into(),
|
|
||||||
),
|
|
||||||
Player::new(
|
|
||||||
1,
|
|
||||||
"https://avatars.githubusercontent.com/u/326587?v=4".into(),
|
|
||||||
"maxbrunsfeld".into(),
|
|
||||||
),
|
|
||||||
Player::new(
|
|
||||||
2,
|
|
||||||
"https://avatars.githubusercontent.com/u/482957?v=4".into(),
|
|
||||||
"as-cii".into(),
|
|
||||||
),
|
|
||||||
Player::new(
|
|
||||||
3,
|
|
||||||
"https://avatars.githubusercontent.com/u/1714999?v=4".into(),
|
|
||||||
"iamnbutler".into(),
|
|
||||||
),
|
|
||||||
Player::new(
|
|
||||||
4,
|
|
||||||
"https://avatars.githubusercontent.com/u/1486634?v=4".into(),
|
|
||||||
"maxdeviant".into(),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PlayerData {
|
|
||||||
pub url: String,
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
pub fn static_player_data() -> Vec<PlayerData> {
|
|
||||||
vec![
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/1714999?v=4".into(),
|
|
||||||
name: "iamnbutler".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/326587?v=4".into(),
|
|
||||||
name: "maxbrunsfeld".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/482957?v=4".into(),
|
|
||||||
name: "as-cii".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/1789?v=4".into(),
|
|
||||||
name: "nathansobo".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/1486634?v=4".into(),
|
|
||||||
name: "ForLoveOfCats".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/2690773?v=4".into(),
|
|
||||||
name: "SomeoneToIgnore".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/19867440?v=4".into(),
|
|
||||||
name: "JosephTLyons".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/24362066?v=4".into(),
|
|
||||||
name: "osiewicz".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/22121886?v=4".into(),
|
|
||||||
name: "KCaverly".into(),
|
|
||||||
},
|
|
||||||
PlayerData {
|
|
||||||
url: "https://avatars.githubusercontent.com/u/1486634?v=4".into(),
|
|
||||||
name: "maxdeviant".into(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
pub fn create_static_players(player_data: Vec<PlayerData>) -> Vec<Player> {
|
|
||||||
let mut players = Vec::new();
|
|
||||||
for data in player_data {
|
|
||||||
players.push(Player::new(players.len(), data.url, data.name));
|
|
||||||
}
|
|
||||||
players
|
|
||||||
}
|
|
||||||
pub fn static_player_1(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(1, data[0].url.clone(), data[0].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_2(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(2, data[1].url.clone(), data[1].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_3(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(3, data[2].url.clone(), data[2].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_4(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(4, data[3].url.clone(), data[3].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_5(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(5, data[4].url.clone(), data[4].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_6(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(6, data[5].url.clone(), data[5].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_7(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(7, data[6].url.clone(), data[6].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_8(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(8, data[7].url.clone(), data[7].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_9(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(9, data[8].url.clone(), data[8].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_player_10(data: &Vec<PlayerData>) -> Player {
|
|
||||||
Player::new(10, data[9].url.clone(), data[9].name.clone())
|
|
||||||
}
|
|
||||||
pub fn static_livestream() -> Livestream {
|
|
||||||
Livestream {
|
|
||||||
players: random_players_with_call_status(7),
|
|
||||||
channel: Some("gpui2-ui".to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn populate_player_call_status(
|
|
||||||
player: Player,
|
|
||||||
followers: Option<Vec<Player>>,
|
|
||||||
) -> PlayerCallStatus {
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let in_current_project: bool = rng.gen();
|
|
||||||
let disconnected: bool = rng.gen();
|
|
||||||
let voice_activity: f32 = rng.gen();
|
|
||||||
let mic_status = if rng.gen_bool(0.5) {
|
|
||||||
MicStatus::Muted
|
|
||||||
} else {
|
|
||||||
MicStatus::Unmuted
|
|
||||||
};
|
|
||||||
let video_status = if rng.gen_bool(0.5) {
|
|
||||||
VideoStatus::On
|
|
||||||
} else {
|
|
||||||
VideoStatus::Off
|
|
||||||
};
|
|
||||||
let screen_share_status = if rng.gen_bool(0.5) {
|
|
||||||
ScreenShareStatus::Shared
|
|
||||||
} else {
|
|
||||||
ScreenShareStatus::NotShared
|
|
||||||
};
|
|
||||||
PlayerCallStatus {
|
|
||||||
mic_status,
|
|
||||||
voice_activity,
|
|
||||||
video_status,
|
|
||||||
screen_share_status,
|
|
||||||
in_current_project,
|
|
||||||
disconnected,
|
|
||||||
following: None,
|
|
||||||
followers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn random_players_with_call_status(number_of_players: usize) -> Vec<PlayerWithCallStatus> {
|
|
||||||
let players = create_static_players(static_player_data());
|
|
||||||
let mut player_status = vec![];
|
|
||||||
for i in 0..number_of_players {
|
|
||||||
let followers = if i == 0 {
|
|
||||||
Some(vec![
|
|
||||||
players[1].clone(),
|
|
||||||
players[3].clone(),
|
|
||||||
players[5].clone(),
|
|
||||||
players[6].clone(),
|
|
||||||
])
|
|
||||||
} else if i == 1 {
|
|
||||||
Some(vec![players[2].clone(), players[6].clone()])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let call_status = populate_player_call_status(players[i].clone(), followers);
|
|
||||||
player_status.push(PlayerWithCallStatus::new(players[i].clone(), call_status));
|
|
||||||
}
|
|
||||||
player_status
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_players_with_call_status() -> Vec<PlayerWithCallStatus> {
|
|
||||||
let players = static_players();
|
|
||||||
let mut player_0_status = PlayerCallStatus::new();
|
|
||||||
let player_1_status = PlayerCallStatus::new();
|
|
||||||
let player_2_status = PlayerCallStatus::new();
|
|
||||||
let mut player_3_status = PlayerCallStatus::new();
|
|
||||||
let mut player_4_status = PlayerCallStatus::new();
|
|
||||||
|
|
||||||
player_0_status.screen_share_status = ScreenShareStatus::Shared;
|
|
||||||
player_0_status.followers = Some(vec![players[1].clone(), players[3].clone()]);
|
|
||||||
|
|
||||||
player_3_status.voice_activity = 0.5;
|
|
||||||
player_4_status.mic_status = MicStatus::Muted;
|
|
||||||
player_4_status.in_current_project = false;
|
|
||||||
|
|
||||||
vec![
|
|
||||||
PlayerWithCallStatus::new(players[0].clone(), player_0_status),
|
|
||||||
PlayerWithCallStatus::new(players[1].clone(), player_1_status),
|
|
||||||
PlayerWithCallStatus::new(players[2].clone(), player_2_status),
|
|
||||||
PlayerWithCallStatus::new(players[3].clone(), player_3_status),
|
|
||||||
PlayerWithCallStatus::new(players[4].clone(), player_4_status),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_project_panel_project_items() -> Vec<ListItem> {
|
|
||||||
vec![
|
|
||||||
ListEntry::new(Label::new("zed"))
|
|
||||||
.left_icon(Icon::FolderOpen.into())
|
|
||||||
.indent_level(0)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new(".cargo"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new(".config"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new(".git").color(LabelColor::Hidden))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new(".cargo"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new(".idea").color(LabelColor::Hidden))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("assets"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(1)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("crates"))
|
|
||||||
.left_icon(Icon::FolderOpen.into())
|
|
||||||
.indent_level(1)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("activity_indicator"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("ai"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("audio"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("auto_update"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("breadcrumbs"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("call"))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("sqlez").color(LabelColor::Modified))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(2)
|
|
||||||
.set_toggle(ToggleState::NotToggled),
|
|
||||||
ListEntry::new(Label::new("gpui2"))
|
|
||||||
.left_icon(Icon::FolderOpen.into())
|
|
||||||
.indent_level(2)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("src"))
|
|
||||||
.left_icon(Icon::FolderOpen.into())
|
|
||||||
.indent_level(3)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("derive_element.rs"))
|
|
||||||
.left_icon(Icon::FileRust.into())
|
|
||||||
.indent_level(4),
|
|
||||||
ListEntry::new(Label::new("storybook").color(LabelColor::Modified))
|
|
||||||
.left_icon(Icon::FolderOpen.into())
|
|
||||||
.indent_level(1)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("docs").color(LabelColor::Default))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(2)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("src").color(LabelColor::Modified))
|
|
||||||
.left_icon(Icon::FolderOpen.into())
|
|
||||||
.indent_level(3)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("ui").color(LabelColor::Modified))
|
|
||||||
.left_icon(Icon::FolderOpen.into())
|
|
||||||
.indent_level(4)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("component").color(LabelColor::Created))
|
|
||||||
.left_icon(Icon::FolderOpen.into())
|
|
||||||
.indent_level(5)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default))
|
|
||||||
.left_icon(Icon::FileRust.into())
|
|
||||||
.indent_level(6),
|
|
||||||
ListEntry::new(Label::new("follow_group.rs").color(LabelColor::Default))
|
|
||||||
.left_icon(Icon::FileRust.into())
|
|
||||||
.indent_level(6),
|
|
||||||
ListEntry::new(Label::new("list_item.rs").color(LabelColor::Created))
|
|
||||||
.left_icon(Icon::FileRust.into())
|
|
||||||
.indent_level(6),
|
|
||||||
ListEntry::new(Label::new("tab.rs").color(LabelColor::Default))
|
|
||||||
.left_icon(Icon::FileRust.into())
|
|
||||||
.indent_level(6),
|
|
||||||
ListEntry::new(Label::new("target").color(LabelColor::Hidden))
|
|
||||||
.left_icon(Icon::Folder.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new(".dockerignore"))
|
|
||||||
.left_icon(Icon::FileGeneric.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new(".DS_Store").color(LabelColor::Hidden))
|
|
||||||
.left_icon(Icon::FileGeneric.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("Cargo.lock"))
|
|
||||||
.left_icon(Icon::FileLock.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("Cargo.toml"))
|
|
||||||
.left_icon(Icon::FileToml.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("Dockerfile"))
|
|
||||||
.left_icon(Icon::FileGeneric.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("Procfile"))
|
|
||||||
.left_icon(Icon::FileGeneric.into())
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("README.md"))
|
|
||||||
.left_icon(Icon::FileDoc.into())
|
|
||||||
.indent_level(1),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.map(From::from)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_project_panel_single_items() -> Vec<ListItem> {
|
|
||||||
vec![
|
|
||||||
ListEntry::new(Label::new("todo.md"))
|
|
||||||
.left_icon(Icon::FileDoc.into())
|
|
||||||
.indent_level(0),
|
|
||||||
ListEntry::new(Label::new("README.md"))
|
|
||||||
.left_icon(Icon::FileDoc.into())
|
|
||||||
.indent_level(0),
|
|
||||||
ListEntry::new(Label::new("config.json"))
|
|
||||||
.left_icon(Icon::FileGeneric.into())
|
|
||||||
.indent_level(0),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.map(From::from)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_collab_panel_current_call() -> Vec<ListItem> {
|
|
||||||
vec![
|
|
||||||
ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"),
|
|
||||||
ListEntry::new(Label::new("nathansobo"))
|
|
||||||
.left_avatar("http://github.com/nathansobo.png?s=50"),
|
|
||||||
ListEntry::new(Label::new("maxbrunsfeld"))
|
|
||||||
.left_avatar("http://github.com/maxbrunsfeld.png?s=50"),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.map(From::from)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_collab_panel_channels() -> Vec<ListItem> {
|
|
||||||
vec![
|
|
||||||
ListEntry::new(Label::new("zed"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(0),
|
|
||||||
ListEntry::new(Label::new("community"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("dashboards"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("feedback"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("teams-in-channels-alpha"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("current-projects"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(1),
|
|
||||||
ListEntry::new(Label::new("codegen"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("gpui2"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("livestreaming"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("open-source"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("replace"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("semantic-index"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("vim"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
ListEntry::new(Label::new("web-tech"))
|
|
||||||
.left_icon(Icon::Hash.into())
|
|
||||||
.size(ListEntrySize::Medium)
|
|
||||||
.indent_level(2),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.map(From::from)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn example_editor_actions() -> Vec<PaletteItem> {
|
|
||||||
vec![
|
|
||||||
PaletteItem::new("New File").keybinding(Keybinding::new(
|
|
||||||
"N".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Open File").keybinding(Keybinding::new(
|
|
||||||
"O".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Save File").keybinding(Keybinding::new(
|
|
||||||
"S".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Cut").keybinding(Keybinding::new(
|
|
||||||
"X".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Copy").keybinding(Keybinding::new(
|
|
||||||
"C".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Paste").keybinding(Keybinding::new(
|
|
||||||
"V".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Undo").keybinding(Keybinding::new(
|
|
||||||
"Z".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Redo").keybinding(Keybinding::new(
|
|
||||||
"Z".to_string(),
|
|
||||||
ModifierKeys::new().control(true).shift(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Find").keybinding(Keybinding::new(
|
|
||||||
"F".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Replace").keybinding(Keybinding::new(
|
|
||||||
"R".to_string(),
|
|
||||||
ModifierKeys::new().control(true),
|
|
||||||
)),
|
|
||||||
PaletteItem::new("Jump to Line"),
|
|
||||||
PaletteItem::new("Select All"),
|
|
||||||
PaletteItem::new("Deselect All"),
|
|
||||||
PaletteItem::new("Switch Document"),
|
|
||||||
PaletteItem::new("Insert Line Below"),
|
|
||||||
PaletteItem::new("Insert Line Above"),
|
|
||||||
PaletteItem::new("Move Line Up"),
|
|
||||||
PaletteItem::new("Move Line Down"),
|
|
||||||
PaletteItem::new("Toggle Comment"),
|
|
||||||
PaletteItem::new("Delete Line"),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty_editor_example() -> Editor {
|
|
||||||
Editor {
|
|
||||||
tabs: static_tabs_example(),
|
|
||||||
path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(),
|
|
||||||
symbols: vec![],
|
|
||||||
buffer: empty_buffer_example(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty_buffer_example() -> Buffer {
|
|
||||||
Buffer::new().set_rows(Some(BufferRows::default()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hello_world_rust_editor_example(theme: &Theme) -> Editor {
|
|
||||||
Editor {
|
|
||||||
tabs: static_tabs_example(),
|
|
||||||
path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(),
|
|
||||||
symbols: vec![Symbol(vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "fn ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "main".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
])],
|
|
||||||
buffer: hello_world_rust_buffer_example(theme),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hello_world_rust_buffer_example(theme: &Theme) -> Buffer {
|
|
||||||
Buffer::new()
|
|
||||||
.set_title("hello_world.rs".to_string())
|
|
||||||
.set_path("src/hello_world.rs".to_string())
|
|
||||||
.set_language("rust".to_string())
|
|
||||||
.set_rows(Some(BufferRows {
|
|
||||||
show_line_numbers: true,
|
|
||||||
rows: hello_world_rust_buffer_rows(theme),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
|
|
||||||
let show_line_number = true;
|
|
||||||
|
|
||||||
vec![
|
|
||||||
BufferRow {
|
|
||||||
line_number: 1,
|
|
||||||
code_action: false,
|
|
||||||
current: true,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "fn ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "main".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "() {".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 2,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: " // Statements here are executed when the compiled binary is called."
|
|
||||||
.to_string(),
|
|
||||||
color: HighlightColor::Comment.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 3,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: None,
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 4,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: " // Print text to the console.".to_string(),
|
|
||||||
color: HighlightColor::Comment.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 5,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: " println!(".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "\"Hello, world!\"".to_string(),
|
|
||||||
color: HighlightColor::String.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: ");".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 6,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: "}".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hello_world_rust_editor_with_status_example(theme: &Theme) -> Editor {
|
|
||||||
Editor {
|
|
||||||
tabs: static_tabs_example(),
|
|
||||||
path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(),
|
|
||||||
symbols: vec![Symbol(vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "fn ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "main".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
])],
|
|
||||||
buffer: hello_world_rust_buffer_with_status_example(theme),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hello_world_rust_buffer_with_status_example(theme: &Theme) -> Buffer {
|
|
||||||
Buffer::new()
|
|
||||||
.set_title("hello_world.rs".to_string())
|
|
||||||
.set_path("src/hello_world.rs".to_string())
|
|
||||||
.set_language("rust".to_string())
|
|
||||||
.set_rows(Some(BufferRows {
|
|
||||||
show_line_numbers: true,
|
|
||||||
rows: hello_world_rust_with_status_buffer_rows(theme),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
|
|
||||||
let show_line_number = true;
|
|
||||||
|
|
||||||
vec![
|
|
||||||
BufferRow {
|
|
||||||
line_number: 1,
|
|
||||||
code_action: false,
|
|
||||||
current: true,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "fn ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "main".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "() {".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 2,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: "// Statements here are executed when the compiled binary is called."
|
|
||||||
.to_string(),
|
|
||||||
color: HighlightColor::Comment.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::Modified,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 3,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: None,
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 4,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: " // Print text to the console.".to_string(),
|
|
||||||
color: HighlightColor::Comment.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 5,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: " println!(".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "\"Hello, world!\"".to_string(),
|
|
||||||
color: HighlightColor::String.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: ");".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 6,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: "}".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 7,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: "".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::Created,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 8,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: "// Marshall and Nate were here".to_string(),
|
|
||||||
color: HighlightColor::Comment.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::Created,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn terminal_buffer(theme: &Theme) -> Buffer {
|
|
||||||
Buffer::new()
|
|
||||||
.set_title("zed — fish".to_string())
|
|
||||||
.set_rows(Some(BufferRows {
|
|
||||||
show_line_numbers: false,
|
|
||||||
rows: terminal_buffer_rows(theme),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn terminal_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
|
|
||||||
let show_line_number = false;
|
|
||||||
|
|
||||||
vec![
|
|
||||||
BufferRow {
|
|
||||||
line_number: 1,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![
|
|
||||||
HighlightedText {
|
|
||||||
text: "maxdeviant ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "in ".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "profaned-capital ".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "in ".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "~/p/zed ".to_string(),
|
|
||||||
color: HighlightColor::Function.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: "on ".to_string(),
|
|
||||||
color: HighlightColor::Default.hsla(&theme),
|
|
||||||
},
|
|
||||||
HighlightedText {
|
|
||||||
text: " gpui2-ui ".to_string(),
|
|
||||||
color: HighlightColor::Keyword.hsla(&theme),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
BufferRow {
|
|
||||||
line_number: 2,
|
|
||||||
code_action: false,
|
|
||||||
current: false,
|
|
||||||
line: Some(HighlightedLine {
|
|
||||||
highlighted_texts: vec![HighlightedText {
|
|
||||||
text: "λ ".to_string(),
|
|
||||||
color: HighlightColor::String.hsla(&theme),
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
cursors: None,
|
|
||||||
status: GitStatus::None,
|
|
||||||
show_line_number,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use gpui2::color::Hsla;
|
|
||||||
use gpui2::element::Element;
|
|
||||||
use gpui2::{serde_json, AppContext, IntoElement, Vector2F, ViewContext, WindowContext};
|
|
||||||
use serde::de::Visitor;
|
|
||||||
use serde::{Deserialize, Deserializer};
|
|
||||||
use theme::ThemeSettings;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct Theme {
|
|
||||||
pub name: String,
|
|
||||||
pub is_light: bool,
|
|
||||||
pub lowest: Layer,
|
|
||||||
pub middle: Layer,
|
|
||||||
pub highest: Layer,
|
|
||||||
pub popover_shadow: Shadow,
|
|
||||||
pub modal_shadow: Shadow,
|
|
||||||
#[serde(deserialize_with = "deserialize_player_colors")]
|
|
||||||
pub players: Vec<PlayerColors>,
|
|
||||||
#[serde(deserialize_with = "deserialize_syntax_colors")]
|
|
||||||
pub syntax: HashMap<String, Hsla>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct Layer {
|
|
||||||
pub base: StyleSet,
|
|
||||||
pub variant: StyleSet,
|
|
||||||
pub on: StyleSet,
|
|
||||||
pub accent: StyleSet,
|
|
||||||
pub positive: StyleSet,
|
|
||||||
pub warning: StyleSet,
|
|
||||||
pub negative: StyleSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct StyleSet {
|
|
||||||
#[serde(rename = "default")]
|
|
||||||
pub default: ContainerColors,
|
|
||||||
pub hovered: ContainerColors,
|
|
||||||
pub pressed: ContainerColors,
|
|
||||||
pub active: ContainerColors,
|
|
||||||
pub disabled: ContainerColors,
|
|
||||||
pub inverted: ContainerColors,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct ContainerColors {
|
|
||||||
pub background: Hsla,
|
|
||||||
pub foreground: Hsla,
|
|
||||||
pub border: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct PlayerColors {
|
|
||||||
pub selection: Hsla,
|
|
||||||
pub cursor: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct Shadow {
|
|
||||||
pub blur: u8,
|
|
||||||
pub color: Hsla,
|
|
||||||
pub offset: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_player_colors<'de, D>(deserializer: D) -> Result<Vec<PlayerColors>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct PlayerArrayVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for PlayerArrayVisitor {
|
|
||||||
type Value = Vec<PlayerColors>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("an object with integer keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_map<A: serde::de::MapAccess<'de>>(
|
|
||||||
self,
|
|
||||||
mut map: A,
|
|
||||||
) -> Result<Self::Value, A::Error> {
|
|
||||||
let mut players = Vec::with_capacity(8);
|
|
||||||
while let Some((key, value)) = map.next_entry::<usize, PlayerColors>()? {
|
|
||||||
if key < 8 {
|
|
||||||
players.push(value);
|
|
||||||
} else {
|
|
||||||
return Err(serde::de::Error::invalid_value(
|
|
||||||
serde::de::Unexpected::Unsigned(key as u64),
|
|
||||||
&"a key in range 0..7",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(players)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_map(PlayerArrayVisitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result<HashMap<String, Hsla>, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ColorWrapper {
|
|
||||||
color: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SyntaxVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for SyntaxVisitor {
|
|
||||||
type Value = HashMap<String, Hsla>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a map with keys and objects with a single color field as values")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_map<M>(self, mut map: M) -> Result<HashMap<String, Hsla>, M::Error>
|
|
||||||
where
|
|
||||||
M: serde::de::MapAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
while let Some(key) = map.next_key()? {
|
|
||||||
let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla
|
|
||||||
result.insert(key, wrapper.color);
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deserializer.deserialize_map(SyntaxVisitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct Themed<V: 'static, E: Element<V>> {
|
|
||||||
pub(crate) theme: Theme,
|
|
||||||
pub(crate) child: E,
|
|
||||||
pub(crate) view_type: PhantomData<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V>> Element<V> for Themed<V, E> {
|
|
||||||
type PaintState = E::PaintState;
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) -> anyhow::Result<(gpui2::LayoutId, Self::PaintState)>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
cx.push_theme(self.theme.clone());
|
|
||||||
let result = self.child.layout(view, cx);
|
|
||||||
cx.pop_theme();
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
parent_origin: Vector2F,
|
|
||||||
layout: &gpui2::Layout,
|
|
||||||
state: &mut Self::PaintState,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
cx.push_theme(self.theme.clone());
|
|
||||||
self.child.paint(view, parent_origin, layout, state, cx);
|
|
||||||
cx.pop_theme();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn preferred_theme<V: 'static>(cx: &AppContext) -> Theme {
|
|
||||||
settings::get::<ThemeSettings>(cx)
|
|
||||||
.theme
|
|
||||||
.deserialized_base_theme
|
|
||||||
.lock()
|
|
||||||
.get_or_insert_with(|| {
|
|
||||||
let theme: Theme =
|
|
||||||
serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
|
|
||||||
.unwrap();
|
|
||||||
Box::new(theme)
|
|
||||||
})
|
|
||||||
.downcast_ref::<Theme>()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn theme(cx: &WindowContext) -> Arc<Theme> {
|
|
||||||
cx.theme::<Theme>()
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
use gpui2::geometry::AbsoluteLength;
|
|
||||||
use gpui2::{hsla, Hsla};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Token {
|
|
||||||
pub list_indent_depth: AbsoluteLength,
|
|
||||||
pub default_panel_size: AbsoluteLength,
|
|
||||||
pub state_hover_background: Hsla,
|
|
||||||
pub state_active_background: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Token {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
list_indent_depth: AbsoluteLength::Rems(0.5),
|
|
||||||
default_panel_size: AbsoluteLength::Rems(16.),
|
|
||||||
state_hover_background: hsla(0.0, 0.0, 0.0, 0.08),
|
|
||||||
state_active_background: hsla(0.0, 0.0, 0.0, 0.16),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn token() -> Token {
|
|
||||||
Token::default()
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
* = Not in the app today
|
|
||||||
|
|
||||||
## Template
|
|
||||||
- [ ] Workspace
|
|
||||||
- [ ] Title Bar
|
|
||||||
- [ ] Project Panel
|
|
||||||
- [ ] Collab Panel
|
|
||||||
- [ ] Project Diagnosics
|
|
||||||
- [ ] Project Search
|
|
||||||
- [ ] Feedback Editor
|
|
||||||
- [ ] Terminal
|
|
||||||
- [ ] Assistant
|
|
||||||
- [ ] Chat*
|
|
||||||
- [ ] Notifications*
|
|
||||||
- [ ] Status Bar
|
|
||||||
- [ ] Panes
|
|
||||||
- [ ] Pane
|
|
||||||
- [ ] Editor
|
|
||||||
- [ ] Tab Bar
|
|
||||||
- [ ] Tool Bar
|
|
||||||
- [ ] Buffer
|
|
||||||
- [ ] Zoomed Editor (Modal)
|
|
||||||
|
|
||||||
### Palettes
|
|
||||||
- [ ] Project Files Palette (⌘-P)
|
|
||||||
- [ ] Command Palette (⌘-SHIFT-P)
|
|
||||||
- [ ] Recent Projects Palette (⌘-OPT-O)
|
|
||||||
- [ ] Recent Branches Palette (⌘-OPT-B)
|
|
||||||
- [ ] Project Symbols (⌘-T)
|
|
||||||
- [ ] Theme Palette (⌘-K, ⌘-T)
|
|
||||||
- [ ] Outline View (⌘-SHIFT-O)
|
|
||||||
|
|
||||||
### Debug Views
|
|
||||||
- [ ] LSP Tool
|
|
||||||
- [ ] Syntax Tree
|
|
||||||
|
|
||||||
## Modules
|
|
||||||
|
|
||||||
### Title Bar
|
|
||||||
- [ ] Traffic Lights
|
|
||||||
- [ ] Host Menu
|
|
||||||
- [ ] Project Menu
|
|
||||||
- [ ] Branch Menu
|
|
||||||
- [ ] Collaborators
|
|
||||||
- [ ] Add Collaborator*
|
|
||||||
- [ ] Project Controls
|
|
||||||
- [ ] Call Controls
|
|
||||||
- [ ] User Menu
|
|
||||||
|
|
||||||
### Project Panel
|
|
||||||
- [ ] Open Editors*
|
|
||||||
- [ ] Open Files (Non-project files)
|
|
||||||
- [ ] Project Files
|
|
||||||
- [ ] Root Folder - Context Menu
|
|
||||||
- [ ] Folder - Context Menu
|
|
||||||
- [ ] File - Context Menu
|
|
||||||
- [ ] Project Filter*
|
|
||||||
|
|
||||||
### Collab Panel
|
|
||||||
- [ ] Current Call
|
|
||||||
- [ ] Channels
|
|
||||||
- [ ] Channel - Context Menu
|
|
||||||
- [ ] Contacts
|
|
||||||
- [ ] Collab Filter
|
|
||||||
|
|
||||||
### Project Diagnosics
|
|
||||||
WIP
|
|
||||||
|
|
||||||
### Feedback Editor
|
|
||||||
- [ ] Feedback Header
|
|
||||||
- [ ] Editor
|
|
||||||
- [ ] Feedback Actions
|
|
||||||
|
|
||||||
### Terminal
|
|
||||||
- [ ] Terminal Toolbar*
|
|
||||||
- [ ] Terminal Line
|
|
||||||
- [ ] Terminal Input
|
|
||||||
|
|
||||||
### Assistant
|
|
||||||
- [ ] Toolbar
|
|
||||||
- [ ] History / Past Conversations
|
|
||||||
- [ ] Model Controls / Token Counter
|
|
||||||
- [ ] Chat Editor
|
|
||||||
|
|
||||||
### Chat
|
|
||||||
WIP
|
|
||||||
|
|
||||||
### Notifications
|
|
||||||
WIP
|
|
||||||
|
|
||||||
### Status Bar
|
|
||||||
- [ ] Status Bar Tool (Icon)
|
|
||||||
- [ ] Status Bar Tool (Text)
|
|
||||||
- [ ] Status Bar Tool - Context Menu
|
|
||||||
- [ ] Status Bar Tool - Popover Palette
|
|
||||||
- [ ] Status Bar Tool - Popover Menu
|
|
||||||
- [ ] Diagnostic Message
|
|
||||||
- [ ] LSP Message
|
|
||||||
- [ ] Update message (New version available, downloading, etc)
|
|
||||||
|
|
||||||
### Panes/Pane
|
|
||||||
|
|
||||||
- [ ] Editor
|
|
||||||
- [ ] Split Divider/Control
|
|
||||||
|
|
||||||
### Editor
|
|
||||||
- [ ] Editor
|
|
||||||
- [ ] Read-only Editor
|
|
||||||
- [ ] Rendered Markdown View*
|
|
||||||
|
|
||||||
### Tab Bar
|
|
||||||
- [ ] Navigation History / Control
|
|
||||||
- [ ] Tabs
|
|
||||||
- [ ] Editor Controls (New, Split, Zoom)
|
|
||||||
|
|
||||||
### Tool Bar
|
|
||||||
- [ ] Breadcrumb
|
|
||||||
- [ ] Editor Tool (Togglable)
|
|
||||||
- [ ] Buffer Search
|
|
||||||
|
|
||||||
### Buffer
|
|
||||||
|
|
||||||
### Zoomed Editor (Modal)
|
|
||||||
- [ ] Modal View
|
|
||||||
|
|
||||||
### Palette
|
|
||||||
- [ ] Input
|
|
||||||
- [ ] Section Title
|
|
||||||
- [ ] List
|
|
||||||
|
|
||||||
## Components
|
|
||||||
|
|
||||||
- [ ] Context Menu
|
|
Loading…
Add table
Add a link
Reference in a new issue