mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-31 01:24:31 +03:00
Bring UI crate up to date
This commit is contained in:
parent
66358f2900
commit
f54634aeb2
57
crates/ui/doc/elevation.md
Normal file
57
crates/ui/doc/elevation.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# 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,11 +1,15 @@
|
|||||||
mod facepile;
|
mod facepile;
|
||||||
mod follow_group;
|
mod follow_group;
|
||||||
mod list_item;
|
mod list_item;
|
||||||
|
mod list_section_header;
|
||||||
|
mod palette_item;
|
||||||
mod tab;
|
mod tab;
|
||||||
|
|
||||||
pub use facepile::*;
|
pub use facepile::*;
|
||||||
pub use follow_group::*;
|
pub use follow_group::*;
|
||||||
pub use list_item::*;
|
pub use list_item::*;
|
||||||
|
pub use list_section_header::*;
|
||||||
|
pub use palette_item::*;
|
||||||
pub use tab::*;
|
pub use tab::*;
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
use gpui2::elements::div;
|
use crate::prelude::{DisclosureControlVisibility, InteractionState, ToggleState};
|
||||||
use gpui2::geometry::rems;
|
use crate::theme::theme;
|
||||||
|
use crate::tokens::token;
|
||||||
|
use crate::{icon, IconAsset, Label};
|
||||||
use gpui2::style::{StyleHelpers, Styleable};
|
use gpui2::style::{StyleHelpers, Styleable};
|
||||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
use gpui2::{elements::div, IntoElement};
|
||||||
|
use gpui2::{Element, ParentElement, ViewContext};
|
||||||
|
|
||||||
use crate::prelude::*;
|
#[derive(Element, Clone)]
|
||||||
use crate::{icon, theme, IconAsset, Label};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct ListItem {
|
pub struct ListItem {
|
||||||
label: Label,
|
label: Label,
|
||||||
left_icon: Option<IconAsset>,
|
left_icon: Option<IconAsset>,
|
||||||
indent_level: u32,
|
indent_level: u32,
|
||||||
state: InteractionState,
|
state: InteractionState,
|
||||||
|
disclosure_control_style: DisclosureControlVisibility,
|
||||||
toggle: Option<ToggleState>,
|
toggle: Option<ToggleState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ pub fn list_item(label: Label) -> ListItem {
|
|||||||
label,
|
label,
|
||||||
indent_level: 0,
|
indent_level: 0,
|
||||||
left_icon: None,
|
left_icon: None,
|
||||||
|
disclosure_control_style: DisclosureControlVisibility::default(),
|
||||||
state: InteractionState::default(),
|
state: InteractionState::default(),
|
||||||
toggle: None,
|
toggle: None,
|
||||||
}
|
}
|
||||||
@ -46,8 +48,30 @@ impl ListItem {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn disclosure_control_style(
|
||||||
|
mut self,
|
||||||
|
disclosure_control_style: DisclosureControlVisibility,
|
||||||
|
) -> Self {
|
||||||
|
self.disclosure_control_style = disclosure_control_style;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
let theme = theme(cx);
|
let theme = theme(cx);
|
||||||
|
let token = token();
|
||||||
|
let mut disclosure_control = match self.toggle {
|
||||||
|
Some(ToggleState::NotToggled) => Some(div().child(icon(IconAsset::ChevronRight))),
|
||||||
|
Some(ToggleState::Toggled) => Some(div().child(icon(IconAsset::ChevronDown))),
|
||||||
|
None => Some(div()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.disclosure_control_style {
|
||||||
|
DisclosureControlVisibility::OnHover => {
|
||||||
|
disclosure_control =
|
||||||
|
disclosure_control.map(|c| div().absolute().neg_left_5().child(c));
|
||||||
|
}
|
||||||
|
DisclosureControlVisibility::Always => {}
|
||||||
|
}
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.fill(theme.middle.base.default.background)
|
.fill(theme.middle.base.default.background)
|
||||||
@ -56,31 +80,31 @@ impl ListItem {
|
|||||||
.active()
|
.active()
|
||||||
.fill(theme.middle.base.pressed.background)
|
.fill(theme.middle.base.pressed.background)
|
||||||
.relative()
|
.relative()
|
||||||
|
.py_1()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.h_7()
|
.h_6()
|
||||||
.px_2()
|
.px_2()
|
||||||
// .ml(rems(0.75 * self.indent_level as f32))
|
// .ml(rems(0.75 * self.indent_level as f32))
|
||||||
.children((0..self.indent_level).map(|_| {
|
.children((0..self.indent_level).map(|_| {
|
||||||
div().w(rems(0.75)).h_full().flex().justify_center().child(
|
|
||||||
div()
|
div()
|
||||||
|
.w(token.list_indent_depth)
|
||||||
|
.h_full()
|
||||||
|
.flex()
|
||||||
|
.justify_center()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.ml_px()
|
||||||
.w_px()
|
.w_px()
|
||||||
.h_full()
|
.h_full()
|
||||||
.fill(theme.middle.base.default.border)
|
.fill(theme.middle.base.default.border),
|
||||||
.hover()
|
|
||||||
.fill(theme.middle.warning.default.border)
|
|
||||||
.active()
|
|
||||||
.fill(theme.middle.negative.default.border),
|
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
.flex()
|
.flex()
|
||||||
.gap_2()
|
.gap_1()
|
||||||
.items_center()
|
.items_center()
|
||||||
.children(match self.toggle {
|
.relative()
|
||||||
Some(ToggleState::NotToggled) => Some(icon(IconAsset::ChevronRight)),
|
.children(disclosure_control)
|
||||||
Some(ToggleState::Toggled) => Some(icon(IconAsset::ChevronDown)),
|
|
||||||
None => None,
|
|
||||||
})
|
|
||||||
.children(self.left_icon.map(|i| icon(i)))
|
.children(self.left_icon.map(|i| icon(i)))
|
||||||
.child(self.label.clone()),
|
.child(self.label.clone()),
|
||||||
)
|
)
|
||||||
|
88
crates/ui/src/components/list_section_header.rs
Normal file
88
crates/ui/src/components/list_section_header.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use crate::prelude::{InteractionState, ToggleState};
|
||||||
|
use crate::theme::theme;
|
||||||
|
use crate::tokens::token;
|
||||||
|
use crate::{icon, label, IconAsset, LabelColor, LabelSize};
|
||||||
|
use gpui2::style::{StyleHelpers, Styleable};
|
||||||
|
use gpui2::{elements::div, IntoElement};
|
||||||
|
use gpui2::{Element, ParentElement, ViewContext};
|
||||||
|
|
||||||
|
#[derive(Element, Clone, Copy)]
|
||||||
|
pub struct ListSectionHeader {
|
||||||
|
label: &'static str,
|
||||||
|
left_icon: Option<IconAsset>,
|
||||||
|
state: InteractionState,
|
||||||
|
toggle: Option<ToggleState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_section_header(label: &'static str) -> ListSectionHeader {
|
||||||
|
ListSectionHeader {
|
||||||
|
label,
|
||||||
|
left_icon: None,
|
||||||
|
state: InteractionState::default(),
|
||||||
|
toggle: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListSectionHeader {
|
||||||
|
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
||||||
|
self.toggle = Some(toggle);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn left_icon(mut self, left_icon: Option<IconAsset>) -> Self {
|
||||||
|
self.left_icon = left_icon;
|
||||||
|
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 token = token();
|
||||||
|
|
||||||
|
let disclosure_control = match self.toggle {
|
||||||
|
Some(ToggleState::NotToggled) => Some(div().child(icon(IconAsset::ChevronRight))),
|
||||||
|
Some(ToggleState::Toggled) => Some(div().child(icon(IconAsset::ChevronDown))),
|
||||||
|
None => Some(div()),
|
||||||
|
};
|
||||||
|
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_1()
|
||||||
|
.w_full()
|
||||||
|
.fill(theme.middle.base.default.background)
|
||||||
|
.hover()
|
||||||
|
.fill(theme.middle.base.hovered.background)
|
||||||
|
.active()
|
||||||
|
.fill(theme.middle.base.pressed.background)
|
||||||
|
.relative()
|
||||||
|
.py_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.h_6()
|
||||||
|
.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| icon(i)))
|
||||||
|
.child(
|
||||||
|
label(self.label.clone())
|
||||||
|
.color(LabelColor::Muted)
|
||||||
|
.size(LabelSize::Small),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.children(disclosure_control),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
63
crates/ui/src/components/palette_item.rs
Normal file
63
crates/ui/src/components/palette_item.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use crate::theme::theme;
|
||||||
|
use crate::{label, LabelColor, LabelSize};
|
||||||
|
use gpui2::elements::div;
|
||||||
|
use gpui2::style::StyleHelpers;
|
||||||
|
use gpui2::{Element, IntoElement};
|
||||||
|
use gpui2::{ParentElement, ViewContext};
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct PaletteItem {
|
||||||
|
pub label: &'static str,
|
||||||
|
pub keybinding: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn palette_item(label: &'static str, keybinding: Option<&'static str>) -> PaletteItem {
|
||||||
|
PaletteItem { label, keybinding }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaletteItem {
|
||||||
|
pub fn label(mut self, label: &'static str) -> Self {
|
||||||
|
self.label = label;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keybinding(mut self, keybinding: Option<&'static str>) -> Self {
|
||||||
|
self.keybinding = keybinding;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
let keybinding_label = match self.keybinding {
|
||||||
|
Some(keybind) => label(keybind)
|
||||||
|
.color(LabelColor::Muted)
|
||||||
|
.size(LabelSize::Small),
|
||||||
|
None => label(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.grow()
|
||||||
|
.justify_between()
|
||||||
|
.child(label(self.label))
|
||||||
|
.child(
|
||||||
|
self.keybinding
|
||||||
|
.map(|_| {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.px_1()
|
||||||
|
.py_0()
|
||||||
|
.my_0p5()
|
||||||
|
.rounded_md()
|
||||||
|
.text_sm()
|
||||||
|
.fill(theme.lowest.on.default.background)
|
||||||
|
.child(keybinding_label)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| div()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,30 @@
|
|||||||
|
use crate::theme::theme;
|
||||||
use gpui2::elements::svg;
|
use gpui2::elements::svg;
|
||||||
use gpui2::style::StyleHelpers;
|
use gpui2::style::StyleHelpers;
|
||||||
use gpui2::{Element, IntoElement, ViewContext};
|
use gpui2::IntoElement;
|
||||||
|
use gpui2::{Element, ViewContext};
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
// Icon::Hash
|
|
||||||
// icon(IconAsset::Hash).color(IconColor::Warning)
|
|
||||||
// Icon::new(IconAsset::Hash).color(IconColor::Warning)
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
#[derive(Default, PartialEq, Copy, Clone)]
|
||||||
pub enum IconAsset {
|
pub enum IconAsset {
|
||||||
Ai,
|
Ai,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
#[default]
|
|
||||||
ArrowUpRight,
|
ArrowUpRight,
|
||||||
Bolt,
|
Bolt,
|
||||||
Hash,
|
|
||||||
File,
|
|
||||||
Folder,
|
|
||||||
FolderOpen,
|
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronUp,
|
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
|
ChevronUp,
|
||||||
|
#[default]
|
||||||
|
File,
|
||||||
|
FileDoc,
|
||||||
|
FileGit,
|
||||||
|
FileLock,
|
||||||
|
FileRust,
|
||||||
|
FileToml,
|
||||||
|
Folder,
|
||||||
|
FolderOpen,
|
||||||
|
Hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IconAsset {
|
impl IconAsset {
|
||||||
@ -34,14 +35,19 @@ impl IconAsset {
|
|||||||
IconAsset::ArrowRight => "icons/arrow_right.svg",
|
IconAsset::ArrowRight => "icons/arrow_right.svg",
|
||||||
IconAsset::ArrowUpRight => "icons/arrow_up_right.svg",
|
IconAsset::ArrowUpRight => "icons/arrow_up_right.svg",
|
||||||
IconAsset::Bolt => "icons/bolt.svg",
|
IconAsset::Bolt => "icons/bolt.svg",
|
||||||
IconAsset::Hash => "icons/hash.svg",
|
|
||||||
IconAsset::ChevronDown => "icons/chevron_down.svg",
|
IconAsset::ChevronDown => "icons/chevron_down.svg",
|
||||||
IconAsset::ChevronUp => "icons/chevron_up.svg",
|
|
||||||
IconAsset::ChevronLeft => "icons/chevron_left.svg",
|
IconAsset::ChevronLeft => "icons/chevron_left.svg",
|
||||||
IconAsset::ChevronRight => "icons/chevron_right.svg",
|
IconAsset::ChevronRight => "icons/chevron_right.svg",
|
||||||
|
IconAsset::ChevronUp => "icons/chevron_up.svg",
|
||||||
IconAsset::File => "icons/file_icons/file.svg",
|
IconAsset::File => "icons/file_icons/file.svg",
|
||||||
|
IconAsset::FileDoc => "icons/file_icons/book.svg",
|
||||||
|
IconAsset::FileGit => "icons/file_icons/git.svg",
|
||||||
|
IconAsset::FileLock => "icons/file_icons/lock.svg",
|
||||||
|
IconAsset::FileRust => "icons/file_icons/rust.svg",
|
||||||
|
IconAsset::FileToml => "icons/file_icons/toml.svg",
|
||||||
IconAsset::Folder => "icons/file_icons/folder.svg",
|
IconAsset::Folder => "icons/file_icons/folder.svg",
|
||||||
IconAsset::FolderOpen => "icons/file_icons/folder_open.svg",
|
IconAsset::FolderOpen => "icons/file_icons/folder_open.svg",
|
||||||
|
IconAsset::Hash => "icons/hash.svg",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,19 +61,14 @@ pub fn icon(asset: IconAsset) -> Icon {
|
|||||||
Icon { asset }
|
Icon { asset }
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Icon {
|
|
||||||
// pub fn new(asset: IconAsset) -> Icon {
|
|
||||||
// Icon { asset }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl Icon {
|
impl Icon {
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
let theme = theme(cx);
|
let theme = theme(cx);
|
||||||
|
|
||||||
svg()
|
svg()
|
||||||
|
.flex_none()
|
||||||
.path(self.asset.path())
|
.path(self.asset.path())
|
||||||
.size_4()
|
.size_4()
|
||||||
.fill(theme.lowest.base.default.foreground)
|
.fill(theme.lowest.variant.default.foreground)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,40 @@
|
|||||||
|
use crate::theme::theme;
|
||||||
use gpui2::elements::div;
|
use gpui2::elements::div;
|
||||||
use gpui2::style::StyleHelpers;
|
use gpui2::style::StyleHelpers;
|
||||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
use gpui2::{Element, ViewContext};
|
||||||
|
use gpui2::{IntoElement, ParentElement};
|
||||||
use crate::theme;
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Copy, Clone)]
|
#[derive(Default, PartialEq, Copy, Clone)]
|
||||||
pub enum LabelColor {
|
pub enum LabelColor {
|
||||||
#[default]
|
#[default]
|
||||||
Default,
|
Default,
|
||||||
|
Muted,
|
||||||
Created,
|
Created,
|
||||||
Modified,
|
Modified,
|
||||||
Deleted,
|
Deleted,
|
||||||
Hidden,
|
Hidden,
|
||||||
|
Placeholder,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Copy, Clone)]
|
||||||
|
pub enum LabelSize {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
Small,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Element, Clone)]
|
#[derive(Element, Clone)]
|
||||||
pub struct Label {
|
pub struct Label {
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
color: LabelColor,
|
color: LabelColor,
|
||||||
|
size: LabelSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label(label: &'static str) -> Label {
|
pub fn label(label: &'static str) -> Label {
|
||||||
Label {
|
Label {
|
||||||
label,
|
label,
|
||||||
color: LabelColor::Default,
|
color: LabelColor::Default,
|
||||||
|
size: LabelSize::Default,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,17 +44,32 @@ impl Label {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn size(mut self, size: LabelSize) -> Self {
|
||||||
|
self.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
let theme = theme(cx);
|
let theme = theme(cx);
|
||||||
|
|
||||||
let color = match self.color {
|
let color = match self.color {
|
||||||
LabelColor::Default => theme.lowest.base.default.foreground,
|
LabelColor::Default => theme.lowest.base.default.foreground,
|
||||||
|
LabelColor::Muted => theme.lowest.variant.default.foreground,
|
||||||
LabelColor::Created => theme.lowest.positive.default.foreground,
|
LabelColor::Created => theme.lowest.positive.default.foreground,
|
||||||
LabelColor::Modified => theme.lowest.warning.default.foreground,
|
LabelColor::Modified => theme.lowest.warning.default.foreground,
|
||||||
LabelColor::Deleted => theme.lowest.negative.default.foreground,
|
LabelColor::Deleted => theme.lowest.negative.default.foreground,
|
||||||
LabelColor::Hidden => theme.lowest.variant.default.foreground,
|
LabelColor::Hidden => theme.lowest.variant.default.foreground,
|
||||||
|
LabelColor::Placeholder => theme.lowest.base.disabled.foreground,
|
||||||
};
|
};
|
||||||
|
|
||||||
div().text_sm().text_color(color).child(self.label.clone())
|
let mut div = div();
|
||||||
|
|
||||||
|
if self.size == LabelSize::Small {
|
||||||
|
div = div.text_xs();
|
||||||
|
} else {
|
||||||
|
div = div.text_sm();
|
||||||
|
}
|
||||||
|
|
||||||
|
div.text_color(color).child(self.label.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,17 @@ mod element_ext;
|
|||||||
mod elements;
|
mod elements;
|
||||||
mod modules;
|
mod modules;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
mod static_data;
|
||||||
|
mod templates;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
mod tokens;
|
||||||
|
|
||||||
pub use crate::theme::*;
|
pub use crate::theme::*;
|
||||||
pub use components::*;
|
pub use components::*;
|
||||||
pub use element_ext::*;
|
pub use element_ext::*;
|
||||||
pub use elements::*;
|
pub use elements::*;
|
||||||
pub use modules::*;
|
pub use modules::*;
|
||||||
|
pub use prelude::*;
|
||||||
|
pub use static_data::*;
|
||||||
|
pub use templates::*;
|
||||||
|
pub use tokens::*;
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
mod chat_panel;
|
mod list;
|
||||||
mod project_panel;
|
mod palette;
|
||||||
mod status_bar;
|
|
||||||
mod tab_bar;
|
|
||||||
mod title_bar;
|
|
||||||
|
|
||||||
pub use chat_panel::*;
|
pub use list::*;
|
||||||
pub use project_panel::*;
|
pub use palette::*;
|
||||||
pub use status_bar::*;
|
|
||||||
pub use tab_bar::*;
|
|
||||||
pub use title_bar::*;
|
|
||||||
|
64
crates/ui/src/modules/list.rs
Normal file
64
crates/ui/src/modules/list.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use crate::theme::theme;
|
||||||
|
use crate::tokens::token;
|
||||||
|
use crate::{icon, label, prelude::*, IconAsset, LabelColor, ListItem, ListSectionHeader};
|
||||||
|
use gpui2::style::StyleHelpers;
|
||||||
|
use gpui2::{elements::div, IntoElement};
|
||||||
|
use gpui2::{Element, ParentElement, ViewContext};
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct List {
|
||||||
|
header: Option<ListSectionHeader>,
|
||||||
|
items: Vec<ListItem>,
|
||||||
|
empty_message: &'static str,
|
||||||
|
toggle: Option<ToggleState>,
|
||||||
|
// footer: Option<ListSectionFooter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list(items: Vec<ListItem>) -> List {
|
||||||
|
List {
|
||||||
|
header: None,
|
||||||
|
items,
|
||||||
|
empty_message: "No items",
|
||||||
|
toggle: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl List {
|
||||||
|
pub fn header(mut self, header: ListSectionHeader) -> Self {
|
||||||
|
self.header = Some(header);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty_message(mut self, empty_message: &'static str) -> Self {
|
||||||
|
self.empty_message = empty_message;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
||||||
|
self.toggle = Some(toggle);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
let token = token();
|
||||||
|
|
||||||
|
let disclosure_control = match self.toggle {
|
||||||
|
Some(ToggleState::NotToggled) => Some(icon(IconAsset::ChevronRight)),
|
||||||
|
Some(ToggleState::Toggled) => Some(icon(IconAsset::ChevronDown)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
div()
|
||||||
|
.py_1()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.children(self.header.map(|h| h))
|
||||||
|
.children(
|
||||||
|
self.items
|
||||||
|
.is_empty()
|
||||||
|
.then(|| label(self.empty_message).color(LabelColor::Muted)),
|
||||||
|
)
|
||||||
|
.children(self.items.iter().cloned())
|
||||||
|
}
|
||||||
|
}
|
124
crates/ui/src/modules/palette.rs
Normal file
124
crates/ui/src/modules/palette.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::prelude::OrderMethod;
|
||||||
|
use crate::theme::theme;
|
||||||
|
use crate::{label, palette_item, LabelColor, PaletteItem};
|
||||||
|
use gpui2::elements::div::ScrollState;
|
||||||
|
use gpui2::style::{StyleHelpers, Styleable};
|
||||||
|
use gpui2::{elements::div, IntoElement};
|
||||||
|
use gpui2::{Element, ParentElement, ViewContext};
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn palette<V: 'static>(scroll_state: ScrollState) -> Palette<V> {
|
||||||
|
Palette {
|
||||||
|
view_type: PhantomData,
|
||||||
|
scroll_state,
|
||||||
|
input_placeholder: "Find something...",
|
||||||
|
empty_string: "No items found.",
|
||||||
|
items: vec![],
|
||||||
|
default_order: OrderMethod::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> Palette<V> {
|
||||||
|
pub fn items(mut self, mut items: Vec<PaletteItem>) -> Self {
|
||||||
|
items.sort_by_key(|item| item.label);
|
||||||
|
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);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.w_96()
|
||||||
|
.rounded_lg()
|
||||||
|
.fill(theme.lowest.base.default.background)
|
||||||
|
.border()
|
||||||
|
.border_color(theme.lowest.base.default.border)
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_px()
|
||||||
|
.child(
|
||||||
|
div().py_0p5().px_1().flex().flex_col().child(
|
||||||
|
div().px_2().py_0p5().child(
|
||||||
|
label(self.input_placeholder).color(LabelColor::Placeholder),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(div().h_px().w_full().fill(theme.lowest.base.default.border))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.py_0p5()
|
||||||
|
.px_1()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.grow()
|
||||||
|
.max_h_96()
|
||||||
|
.overflow_y_scroll(self.scroll_state.clone())
|
||||||
|
.children(
|
||||||
|
vec![if self.items.is_empty() {
|
||||||
|
Some(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.justify_between()
|
||||||
|
.px_2()
|
||||||
|
.py_1()
|
||||||
|
.child(
|
||||||
|
label(self.empty_string).color(LabelColor::Muted),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}]
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
)
|
||||||
|
.children(self.items.iter().map(|item| {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.justify_between()
|
||||||
|
.px_2()
|
||||||
|
.py_0p5()
|
||||||
|
.rounded_lg()
|
||||||
|
.hover()
|
||||||
|
.fill(theme.lowest.base.hovered.background)
|
||||||
|
.active()
|
||||||
|
.fill(theme.lowest.base.pressed.background)
|
||||||
|
.child(palette_item(item.label, item.keybinding))
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,94 +0,0 @@
|
|||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use gpui2::elements::div;
|
|
||||||
use gpui2::elements::div::ScrollState;
|
|
||||||
use gpui2::style::StyleHelpers;
|
|
||||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{details, input, label, list_item, theme, IconAsset, LabelColor};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct ProjectPanel<V: 'static> {
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
scroll_state: ScrollState,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn project_panel<V: 'static>(scroll_state: ScrollState) -> ProjectPanel<V> {
|
|
||||||
ProjectPanel {
|
|
||||||
view_type: PhantomData,
|
|
||||||
scroll_state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> ProjectPanel<V> {
|
|
||||||
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
|
||||||
let theme = theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.w_56()
|
|
||||||
.h_full()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.fill(theme.middle.base.default.background)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.w_56()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.overflow_y_scroll(self.scroll_state.clone())
|
|
||||||
.child(details("This is a long string that should wrap when it keeps going for a long time.").meta_text("6 h ago)"))
|
|
||||||
.child(
|
|
||||||
div().flex().flex_col().children(
|
|
||||||
std::iter::repeat_with(|| {
|
|
||||||
vec![
|
|
||||||
list_item(label("sqlez").color(LabelColor::Modified))
|
|
||||||
.left_icon(IconAsset::FolderOpen.into())
|
|
||||||
.indent_level(0)
|
|
||||||
.set_toggle(ToggleState::NotToggled),
|
|
||||||
list_item(label("storybook").color(LabelColor::Modified))
|
|
||||||
.left_icon(IconAsset::FolderOpen.into())
|
|
||||||
.indent_level(0)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
list_item(label("docs").color(LabelColor::Default))
|
|
||||||
.left_icon(IconAsset::Folder.into())
|
|
||||||
.indent_level(1)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
list_item(label("src").color(LabelColor::Modified))
|
|
||||||
.left_icon(IconAsset::FolderOpen.into())
|
|
||||||
.indent_level(2)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
list_item(label("ui").color(LabelColor::Modified))
|
|
||||||
.left_icon(IconAsset::FolderOpen.into())
|
|
||||||
.indent_level(3)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
list_item(label("component").color(LabelColor::Created))
|
|
||||||
.left_icon(IconAsset::FolderOpen.into())
|
|
||||||
.indent_level(4)
|
|
||||||
.set_toggle(ToggleState::Toggled),
|
|
||||||
list_item(label("facepile.rs").color(LabelColor::Default))
|
|
||||||
.left_icon(IconAsset::File.into())
|
|
||||||
.indent_level(5),
|
|
||||||
list_item(label("follow_group.rs").color(LabelColor::Default))
|
|
||||||
.left_icon(IconAsset::File.into())
|
|
||||||
.indent_level(5),
|
|
||||||
list_item(label("list_item.rs").color(LabelColor::Created))
|
|
||||||
.left_icon(IconAsset::File.into())
|
|
||||||
.indent_level(5),
|
|
||||||
list_item(label("tab.rs").color(LabelColor::Default))
|
|
||||||
.left_icon(IconAsset::File.into())
|
|
||||||
.indent_level(5),
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.take(10)
|
|
||||||
.flatten(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
input("Find something...")
|
|
||||||
.value("buffe".to_string())
|
|
||||||
.state(InteractionState::Focused),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,11 @@
|
|||||||
|
#[derive(Default, PartialEq)]
|
||||||
|
pub enum OrderMethod {
|
||||||
|
#[default]
|
||||||
|
Ascending,
|
||||||
|
Descending,
|
||||||
|
MostRecent,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
#[derive(Default, PartialEq)]
|
||||||
pub enum ButtonVariant {
|
pub enum ButtonVariant {
|
||||||
#[default]
|
#[default]
|
||||||
@ -19,6 +27,13 @@ pub enum Shape {
|
|||||||
RoundedRectangle,
|
RoundedRectangle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Clone, Copy)]
|
||||||
|
pub enum DisclosureControlVisibility {
|
||||||
|
#[default]
|
||||||
|
OnHover,
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone, Copy)]
|
#[derive(Default, PartialEq, Clone, Copy)]
|
||||||
pub enum InteractionState {
|
pub enum InteractionState {
|
||||||
#[default]
|
#[default]
|
||||||
|
166
crates/ui/src/static_data.rs
Normal file
166
crates/ui/src/static_data.rs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
use crate::{
|
||||||
|
label, list_item, palette_item, IconAsset, LabelColor, ListItem, PaletteItem, ToggleState,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn static_project_panel_project_items() -> Vec<ListItem> {
|
||||||
|
vec![
|
||||||
|
list_item(label("zed"))
|
||||||
|
.left_icon(IconAsset::FolderOpen.into())
|
||||||
|
.indent_level(0)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label(".cargo"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label(".config"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label(".git").color(LabelColor::Hidden))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label(".cargo"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label(".idea").color(LabelColor::Hidden))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label("assets"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(1)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("cargo-target").color(LabelColor::Hidden))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label("crates"))
|
||||||
|
.left_icon(IconAsset::FolderOpen.into())
|
||||||
|
.indent_level(1)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("activity_indicator"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(2),
|
||||||
|
list_item(label("ai"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(2),
|
||||||
|
list_item(label("audio"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(2),
|
||||||
|
list_item(label("auto_update"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(2),
|
||||||
|
list_item(label("breadcrumbs"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(2),
|
||||||
|
list_item(label("call"))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(2),
|
||||||
|
list_item(label("sqlez").color(LabelColor::Modified))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(2)
|
||||||
|
.set_toggle(ToggleState::NotToggled),
|
||||||
|
list_item(label("gpui2"))
|
||||||
|
.left_icon(IconAsset::FolderOpen.into())
|
||||||
|
.indent_level(2)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("src"))
|
||||||
|
.left_icon(IconAsset::FolderOpen.into())
|
||||||
|
.indent_level(3)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("derrive_element.rs"))
|
||||||
|
.left_icon(IconAsset::FileRust.into())
|
||||||
|
.indent_level(4),
|
||||||
|
list_item(label("storybook").color(LabelColor::Modified))
|
||||||
|
.left_icon(IconAsset::FolderOpen.into())
|
||||||
|
.indent_level(1)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("docs").color(LabelColor::Default))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(2)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("src").color(LabelColor::Modified))
|
||||||
|
.left_icon(IconAsset::FolderOpen.into())
|
||||||
|
.indent_level(3)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("ui").color(LabelColor::Modified))
|
||||||
|
.left_icon(IconAsset::FolderOpen.into())
|
||||||
|
.indent_level(4)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("component").color(LabelColor::Created))
|
||||||
|
.left_icon(IconAsset::FolderOpen.into())
|
||||||
|
.indent_level(5)
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
list_item(label("facepile.rs").color(LabelColor::Default))
|
||||||
|
.left_icon(IconAsset::FileRust.into())
|
||||||
|
.indent_level(6),
|
||||||
|
list_item(label("follow_group.rs").color(LabelColor::Default))
|
||||||
|
.left_icon(IconAsset::FileRust.into())
|
||||||
|
.indent_level(6),
|
||||||
|
list_item(label("list_item.rs").color(LabelColor::Created))
|
||||||
|
.left_icon(IconAsset::FileRust.into())
|
||||||
|
.indent_level(6),
|
||||||
|
list_item(label("tab.rs").color(LabelColor::Default))
|
||||||
|
.left_icon(IconAsset::FileRust.into())
|
||||||
|
.indent_level(6),
|
||||||
|
list_item(label("target").color(LabelColor::Hidden))
|
||||||
|
.left_icon(IconAsset::Folder.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label(".dockerignore"))
|
||||||
|
.left_icon(IconAsset::File.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label(".DS_Store").color(LabelColor::Hidden))
|
||||||
|
.left_icon(IconAsset::File.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label("Cargo.lock"))
|
||||||
|
.left_icon(IconAsset::FileLock.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label("Cargo.toml"))
|
||||||
|
.left_icon(IconAsset::FileToml.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label("Dockerfile"))
|
||||||
|
.left_icon(IconAsset::File.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label("Procfile"))
|
||||||
|
.left_icon(IconAsset::File.into())
|
||||||
|
.indent_level(1),
|
||||||
|
list_item(label("README.md"))
|
||||||
|
.left_icon(IconAsset::FileDoc.into())
|
||||||
|
.indent_level(1),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn static_project_panel_single_items() -> Vec<ListItem> {
|
||||||
|
vec![
|
||||||
|
list_item(label("todo.md"))
|
||||||
|
.left_icon(IconAsset::FileDoc.into())
|
||||||
|
.indent_level(0),
|
||||||
|
list_item(label("README.md"))
|
||||||
|
.left_icon(IconAsset::FileDoc.into())
|
||||||
|
.indent_level(0),
|
||||||
|
list_item(label("config.json"))
|
||||||
|
.left_icon(IconAsset::File.into())
|
||||||
|
.indent_level(0),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn example_editor_actions() -> Vec<PaletteItem> {
|
||||||
|
vec![
|
||||||
|
palette_item("New File", Some("Ctrl+N")),
|
||||||
|
palette_item("Open File", Some("Ctrl+O")),
|
||||||
|
palette_item("Save File", Some("Ctrl+S")),
|
||||||
|
palette_item("Cut", Some("Ctrl+X")),
|
||||||
|
palette_item("Copy", Some("Ctrl+C")),
|
||||||
|
palette_item("Paste", Some("Ctrl+V")),
|
||||||
|
palette_item("Undo", Some("Ctrl+Z")),
|
||||||
|
palette_item("Redo", Some("Ctrl+Shift+Z")),
|
||||||
|
palette_item("Find", Some("Ctrl+F")),
|
||||||
|
palette_item("Replace", Some("Ctrl+R")),
|
||||||
|
palette_item("Jump to Line", None),
|
||||||
|
palette_item("Select All", None),
|
||||||
|
palette_item("Deselect All", None),
|
||||||
|
palette_item("Switch Document", None),
|
||||||
|
palette_item("Insert Line Below", None),
|
||||||
|
palette_item("Insert Line Above", None),
|
||||||
|
palette_item("Move Line Up", None),
|
||||||
|
palette_item("Move Line Down", None),
|
||||||
|
palette_item("Toggle Comment", None),
|
||||||
|
palette_item("Delete Line", None),
|
||||||
|
]
|
||||||
|
}
|
17
crates/ui/src/templates.rs
Normal file
17
crates/ui/src/templates.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
mod chat_panel;
|
||||||
|
mod collab_panel;
|
||||||
|
mod command_palette;
|
||||||
|
mod project_panel;
|
||||||
|
mod status_bar;
|
||||||
|
mod tab_bar;
|
||||||
|
mod title_bar;
|
||||||
|
mod workspace;
|
||||||
|
|
||||||
|
pub use chat_panel::*;
|
||||||
|
pub use collab_panel::*;
|
||||||
|
pub use command_palette::*;
|
||||||
|
pub use project_panel::*;
|
||||||
|
pub use status_bar::*;
|
||||||
|
pub use tab_bar::*;
|
||||||
|
pub use title_bar::*;
|
||||||
|
pub use workspace::*;
|
@ -1,11 +1,11 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use gpui2::elements::div;
|
use crate::icon_button;
|
||||||
|
use crate::theme::theme;
|
||||||
use gpui2::elements::div::ScrollState;
|
use gpui2::elements::div::ScrollState;
|
||||||
use gpui2::style::StyleHelpers;
|
use gpui2::style::StyleHelpers;
|
||||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
use gpui2::{elements::div, IntoElement};
|
||||||
|
use gpui2::{Element, ParentElement, ViewContext};
|
||||||
use crate::{icon_button, theme};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
#[derive(Element)]
|
||||||
pub struct ChatPanel<V: 'static> {
|
pub struct ChatPanel<V: 'static> {
|
177
crates/ui/src/templates/collab_panel.rs
Normal file
177
crates/ui/src/templates/collab_panel.rs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
use crate::theme::{theme, Theme};
|
||||||
|
use gpui2::{
|
||||||
|
elements::{div, div::ScrollState, img, svg},
|
||||||
|
style::{StyleHelpers, Styleable},
|
||||||
|
ArcCow, Element, IntoElement, ParentElement, ViewContext,
|
||||||
|
};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct CollabPanelElement<V: 'static> {
|
||||||
|
view_type: PhantomData<V>,
|
||||||
|
scroll_state: ScrollState,
|
||||||
|
}
|
||||||
|
|
||||||
|
// When I improve child view rendering, I'd like to have V implement a trait that
|
||||||
|
// provides the scroll state, among other things.
|
||||||
|
pub fn collab_panel<V: 'static>(scroll_state: ScrollState) -> CollabPanelElement<V> {
|
||||||
|
CollabPanelElement {
|
||||||
|
view_type: PhantomData,
|
||||||
|
scroll_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> CollabPanelElement<V> {
|
||||||
|
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
// Panel
|
||||||
|
div()
|
||||||
|
.w_64()
|
||||||
|
.h_full()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.font("Zed Sans Extended")
|
||||||
|
.text_color(theme.middle.base.default.foreground)
|
||||||
|
.border_color(theme.middle.base.default.border)
|
||||||
|
.border()
|
||||||
|
.fill(theme.middle.base.default.background)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w_full()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.overflow_y_scroll(self.scroll_state.clone())
|
||||||
|
// List Container
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.fill(theme.lowest.base.default.background)
|
||||||
|
.pb_1()
|
||||||
|
.border_color(theme.lowest.base.default.border)
|
||||||
|
.border_b()
|
||||||
|
//:: https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state
|
||||||
|
// .group()
|
||||||
|
// List Section Header
|
||||||
|
.child(self.list_section_header("#CRDB", true, theme))
|
||||||
|
// List Item Large
|
||||||
|
.child(self.list_item(
|
||||||
|
"http://github.com/maxbrunsfeld.png?s=50",
|
||||||
|
"maxbrunsfeld",
|
||||||
|
theme,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.py_2()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.child(self.list_section_header("CHANNELS", true, theme)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.py_2()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.child(self.list_section_header("CONTACTS", true, theme))
|
||||||
|
.children(
|
||||||
|
std::iter::repeat_with(|| {
|
||||||
|
vec![
|
||||||
|
self.list_item(
|
||||||
|
"http://github.com/as-cii.png?s=50",
|
||||||
|
"as-cii",
|
||||||
|
theme,
|
||||||
|
),
|
||||||
|
self.list_item(
|
||||||
|
"http://github.com/nathansobo.png?s=50",
|
||||||
|
"nathansobo",
|
||||||
|
theme,
|
||||||
|
),
|
||||||
|
self.list_item(
|
||||||
|
"http://github.com/maxbrunsfeld.png?s=50",
|
||||||
|
"maxbrunsfeld",
|
||||||
|
theme,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.take(3)
|
||||||
|
.flatten(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
31
crates/ui/src/templates/command_palette.rs
Normal file
31
crates/ui/src/templates/command_palette.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use gpui2::elements::div;
|
||||||
|
use gpui2::{elements::div::ScrollState, ViewContext};
|
||||||
|
use gpui2::{Element, IntoElement, ParentElement};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::{example_editor_actions, palette, OrderMethod};
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct CommandPalette<V: 'static> {
|
||||||
|
view_type: PhantomData<V>,
|
||||||
|
scroll_state: ScrollState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn command_palette<V: 'static>(scroll_state: ScrollState) -> CommandPalette<V> {
|
||||||
|
CommandPalette {
|
||||||
|
view_type: PhantomData,
|
||||||
|
scroll_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> CommandPalette<V> {
|
||||||
|
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
|
div().child(
|
||||||
|
palette(self.scroll_state.clone())
|
||||||
|
.items(example_editor_actions())
|
||||||
|
.placeholder("Execute a command...")
|
||||||
|
.empty_string("No items found.")
|
||||||
|
.default_order(OrderMethod::Ascending),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
62
crates/ui/src/templates/project_panel.rs
Normal file
62
crates/ui/src/templates/project_panel.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use crate::{
|
||||||
|
input, list, list_section_header, prelude::*, static_project_panel_project_items,
|
||||||
|
static_project_panel_single_items, theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
use gpui2::{
|
||||||
|
elements::{div, div::ScrollState},
|
||||||
|
style::StyleHelpers,
|
||||||
|
ParentElement, ViewContext,
|
||||||
|
};
|
||||||
|
use gpui2::{Element, IntoElement};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
#[derive(Element)]
|
||||||
|
pub struct ProjectPanel<V: 'static> {
|
||||||
|
view_type: PhantomData<V>,
|
||||||
|
scroll_state: ScrollState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn project_panel<V: 'static>(scroll_state: ScrollState) -> ProjectPanel<V> {
|
||||||
|
ProjectPanel {
|
||||||
|
view_type: PhantomData,
|
||||||
|
scroll_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static> ProjectPanel<V> {
|
||||||
|
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.w_56()
|
||||||
|
.h_full()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.fill(theme.middle.base.default.background)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w_56()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.overflow_y_scroll(self.scroll_state.clone())
|
||||||
|
.child(
|
||||||
|
list(static_project_panel_single_items())
|
||||||
|
.header(list_section_header("FILES").set_toggle(ToggleState::Toggled))
|
||||||
|
.empty_message("No files in directory")
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
list(static_project_panel_project_items())
|
||||||
|
.header(list_section_header("PROJECT").set_toggle(ToggleState::Toggled))
|
||||||
|
.empty_message("No folders in directory")
|
||||||
|
.set_toggle(ToggleState::Toggled),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
input("Find something...")
|
||||||
|
.value("buffe".to_string())
|
||||||
|
.state(InteractionState::Focused),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use gpui2::elements::div;
|
|
||||||
use gpui2::style::StyleHelpers;
|
|
||||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
|
||||||
|
|
||||||
use crate::theme::{theme, Theme};
|
use crate::theme::{theme, Theme};
|
||||||
use crate::{icon_button, text_button, tool_divider};
|
use crate::{icon_button, text_button, tool_divider};
|
||||||
|
use gpui2::style::StyleHelpers;
|
||||||
|
use gpui2::{elements::div, IntoElement};
|
||||||
|
use gpui2::{Element, ParentElement, ViewContext};
|
||||||
|
|
||||||
#[derive(Default, PartialEq)]
|
#[derive(Default, PartialEq)]
|
||||||
pub enum Tool {
|
pub enum Tool {
|
@ -1,12 +1,12 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use gpui2::elements::div;
|
use crate::prelude::InteractionState;
|
||||||
|
use crate::theme::theme;
|
||||||
|
use crate::{icon_button, tab};
|
||||||
use gpui2::elements::div::ScrollState;
|
use gpui2::elements::div::ScrollState;
|
||||||
use gpui2::style::StyleHelpers;
|
use gpui2::style::StyleHelpers;
|
||||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
use gpui2::{elements::div, IntoElement};
|
||||||
|
use gpui2::{Element, ParentElement, ViewContext};
|
||||||
use crate::prelude::InteractionState;
|
|
||||||
use crate::{icon_button, tab, theme};
|
|
||||||
|
|
||||||
#[derive(Element)]
|
#[derive(Element)]
|
||||||
pub struct TabBar<V: 'static> {
|
pub struct TabBar<V: 'static> {
|
80
crates/ui/src/templates/workspace.rs
Normal file
80
crates/ui/src/templates/workspace.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use crate::{chat_panel, collab_panel, project_panel, status_bar, tab_bar, theme, title_bar};
|
||||||
|
|
||||||
|
use gpui2::{
|
||||||
|
elements::{div, div::ScrollState},
|
||||||
|
style::StyleHelpers,
|
||||||
|
Element, IntoElement, ParentElement, ViewContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Element, Default)]
|
||||||
|
struct WorkspaceElement {
|
||||||
|
project_panel_scroll_state: ScrollState,
|
||||||
|
collab_panel_scroll_state: ScrollState,
|
||||||
|
right_scroll_state: ScrollState,
|
||||||
|
tab_bar_scroll_state: ScrollState,
|
||||||
|
palette_scroll_state: ScrollState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn workspace<V: 'static>() -> impl Element<V> {
|
||||||
|
WorkspaceElement::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkspaceElement {
|
||||||
|
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||||
|
let theme = theme(cx);
|
||||||
|
|
||||||
|
div()
|
||||||
|
// Elevation Level 0
|
||||||
|
.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)
|
||||||
|
.relative()
|
||||||
|
// Elevation Level 1
|
||||||
|
.child(title_bar())
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.w_full()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.overflow_hidden()
|
||||||
|
.child(project_panel(self.project_panel_scroll_state.clone()))
|
||||||
|
.child(collab_panel(self.collab_panel_scroll_state.clone()))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.h_full()
|
||||||
|
.flex_1()
|
||||||
|
.fill(theme.highest.base.default.background)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.flex_1()
|
||||||
|
.child(tab_bar(self.tab_bar_scroll_state.clone())),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(chat_panel(self.right_scroll_state.clone())),
|
||||||
|
)
|
||||||
|
.child(status_bar())
|
||||||
|
// Elevation Level 3
|
||||||
|
// .child(
|
||||||
|
// div()
|
||||||
|
// .absolute()
|
||||||
|
// .top_0()
|
||||||
|
// .left_0()
|
||||||
|
// .size_full()
|
||||||
|
// .flex()
|
||||||
|
// .justify_center()
|
||||||
|
// .items_center()
|
||||||
|
// // .fill(theme.lowest.base.default.background)
|
||||||
|
// // Elevation Level 4
|
||||||
|
// .child(command_palette(self.palette_scroll_state.clone())),
|
||||||
|
// )
|
||||||
|
}
|
||||||
|
}
|
18
crates/ui/src/tokens.rs
Normal file
18
crates/ui/src/tokens.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use gpui2::geometry::AbsoluteLength;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Token {
|
||||||
|
pub list_indent_depth: AbsoluteLength,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Token {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
list_indent_depth: AbsoluteLength::Rems(0.5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn token() -> Token {
|
||||||
|
Token::default()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user