Detailed changes
@@ -1,21 +1,22 @@
-mod component;
mod element;
-mod module;
+pub use element::avatar::*;
+pub use element::icon::*;
+pub use element::icon_button::*;
+pub use element::indicator::*;
+pub use element::input::*;
+pub use element::label::*;
+pub use element::text_button::*;
+pub use element::tool_divider::*;
+mod component;
pub use component::facepile::*;
pub use component::follow_group::*;
+pub use component::list_item::*;
pub use component::tab::*;
+mod module;
pub use module::chat_panel::*;
pub use module::project_panel::*;
pub use module::status_bar::*;
pub use module::tab_bar::*;
pub use module::title_bar::*;
-
-pub use element::avatar::*;
-pub use element::icon_button::*;
-pub use element::indicator::*;
-pub use element::input::*;
-pub use element::label::*;
-pub use element::text_button::*;
-pub use element::tool_divider::*;
@@ -1,3 +1,4 @@
pub(crate) mod facepile;
pub(crate) mod follow_group;
+pub(crate) mod list_item;
pub(crate) mod tab;
@@ -0,0 +1,57 @@
+use crate::prelude::InteractionState;
+use crate::theme::theme;
+use crate::ui::{icon, IconAsset, Label};
+use gpui2::geometry::rems;
+use gpui2::style::StyleHelpers;
+use gpui2::{elements::div, IntoElement};
+use gpui2::{Element, ParentElement, ViewContext};
+
+#[derive(Element)]
+pub struct ListItem {
+ label: Label,
+ left_icon: Option<IconAsset>,
+ indent_level: f32,
+ state: InteractionState,
+}
+
+pub fn list_item(label: Label) -> ListItem {
+ ListItem {
+ label,
+ indent_level: 0.0,
+ left_icon: None,
+ state: InteractionState::default(),
+ }
+}
+
+impl ListItem {
+ pub fn indent_level(mut self, indent_level: f32) -> Self {
+ self.indent_level = indent_level;
+ 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 mut el = div()
+ .h_7()
+ .px_2()
+ .ml(rems(0.75 * self.indent_level.clone()))
+ .flex()
+ .gap_2()
+ .items_center();
+
+ if self.left_icon.is_some() {
+ el = el.child(icon(self.left_icon.clone().unwrap()))
+ }
+
+ el.child(self.label.clone())
+ }
+}
@@ -1,4 +1,5 @@
pub(crate) mod avatar;
+pub(crate) mod icon;
pub(crate) mod icon_button;
pub(crate) mod indicator;
pub(crate) mod input;
@@ -5,8 +5,6 @@ use gpui2::style::StyleHelpers;
use gpui2::{ArcCow, IntoElement};
use gpui2::{Element, ViewContext};
-pub type UnknownString = ArcCow<'static, str>;
-
#[derive(Element, Clone)]
pub struct Avatar {
src: ArcCow<'static, str>,
@@ -0,0 +1,55 @@
+use std::borrow::Cow;
+
+use crate::theme::theme;
+use gpui2::elements::svg;
+use gpui2::style::StyleHelpers;
+use gpui2::IntoElement;
+use gpui2::{Element, ViewContext};
+
+#[derive(Default, PartialEq, Copy, Clone)]
+pub enum IconAsset {
+ Ai,
+ ArrowLeft,
+ ArrowRight,
+ #[default]
+ ArrowUpRight,
+ Bolt,
+ Hash,
+ File,
+ Folder,
+ FolderOpen,
+}
+
+pub fn icon_asset(asset: IconAsset) -> impl Into<Cow<'static, str>> {
+ match asset {
+ IconAsset::Ai => "icons/ai.svg",
+ IconAsset::ArrowLeft => "icons/arrow_left.svg",
+ IconAsset::ArrowRight => "icons/arrow_right.svg",
+ IconAsset::ArrowUpRight => "icons/arrow_up_right.svg",
+ IconAsset::Bolt => "icons/bolt.svg",
+ IconAsset::Hash => "icons/hash.svg",
+ IconAsset::File => "icons/file_icons/file.svg",
+ IconAsset::Folder => "icons/file_icons/folder.svg",
+ IconAsset::FolderOpen => "icons/file_icons/folder_open.svg",
+ }
+}
+
+#[derive(Element, Clone)]
+pub struct Icon {
+ asset: IconAsset,
+}
+
+pub fn icon(asset: IconAsset) -> Icon {
+ Icon { asset }
+}
+
+impl Icon {
+ fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
+ let theme = theme(cx);
+
+ svg()
+ .path(icon_asset(self.asset))
+ .size_4()
+ .fill(theme.lowest.base.default.foreground)
+ }
+}
@@ -22,7 +22,7 @@ pub struct Label {
pub fn label(label: &'static str) -> Label {
Label {
- label: "Label",
+ label,
color: LabelColor::Default,
}
}
@@ -1,7 +1,7 @@
use crate::{
prelude::InteractionState,
theme::theme,
- ui::{input, label, LabelColor},
+ ui::{input, label, list_item, IconAsset, LabelColor},
};
use gpui2::{
elements::{div, div::ScrollState},
@@ -29,7 +29,7 @@ impl<V: 'static> ProjectPanel<V> {
let theme = theme(cx);
div()
- .w_64()
+ .w_56()
.h_full()
.flex()
.flex_col()
@@ -41,17 +41,39 @@ impl<V: 'static> ProjectPanel<V> {
.flex_col()
.overflow_y_scroll(self.scroll_state.clone())
.child(
- div().py_2().flex().flex_col().children(
+ div().flex().flex_col().children(
std::iter::repeat_with(|| {
vec![
- label("File"),
- label("Modified File").color(LabelColor::Modified),
- label("Created File").color(LabelColor::Created),
- label("Deleted File").color(LabelColor::Deleted),
- label("Hidden File").color(LabelColor::Hidden),
+ list_item(label("storybook").color(LabelColor::Modified))
+ .left_icon(IconAsset::FolderOpen.into())
+ .indent_level(0.0),
+ list_item(label("docs").color(LabelColor::Default))
+ .left_icon(IconAsset::Folder.into())
+ .indent_level(1.0),
+ list_item(label("src").color(LabelColor::Modified))
+ .left_icon(IconAsset::FolderOpen.into())
+ .indent_level(2.0),
+ list_item(label("ui").color(LabelColor::Modified))
+ .left_icon(IconAsset::FolderOpen.into())
+ .indent_level(3.0),
+ list_item(label("component").color(LabelColor::Created))
+ .left_icon(IconAsset::FolderOpen.into())
+ .indent_level(4.0),
+ list_item(label("facepile.rs").color(LabelColor::Default))
+ .left_icon(IconAsset::File.into())
+ .indent_level(5.0),
+ list_item(label("follow_group.rs").color(LabelColor::Default))
+ .left_icon(IconAsset::File.into())
+ .indent_level(5.0),
+ list_item(label("list_item.rs").color(LabelColor::Created))
+ .left_icon(IconAsset::File.into())
+ .indent_level(5.0),
+ list_item(label("tab.rs").color(LabelColor::Default))
+ .left_icon(IconAsset::File.into())
+ .indent_level(5.0),
]
})
- .take(60)
+ .take(10)
.flatten(),
),
),