diff --git a/Cargo.lock b/Cargo.lock index 3c0edc6f..d00ef821 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4171,6 +4171,7 @@ dependencies = [ name = "ui" version = "0.1.0" dependencies = [ + "anyhow", "catppuccin", "gpui", "log", @@ -4180,6 +4181,14 @@ dependencies = [ "windows", ] +[[package]] +name = "ui-story" +version = "0.1.0" +dependencies = [ + "gpui", + "ui", +] + [[package]] name = "unicase" version = "2.7.0" @@ -4866,6 +4875,7 @@ dependencies = [ "anyhow", "gpui", "ui", + "ui-story", "util 0.1.0", ] diff --git a/Cargo.toml b/Cargo.toml index 693b9103..1c739852 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,9 @@ members = [ "crates/app", "crates/ui", + "crates/ui-story", "crates/workspace", - "crates/util" + "crates/util", ] default-members = ["crates/app"] @@ -12,6 +13,7 @@ resolver = "2" [workspace.dependencies] gpui = { git = "https://github.com/zed-industries/zed.git" } ui = { path = "crates/ui" } +ui-story = { path = "crates/ui-story" } workspace = { path = "crates/workspace" } util = { path = "crates/util" } anyhow = "1" diff --git a/crates/ui-story/Cargo.toml b/crates/ui-story/Cargo.toml new file mode 100644 index 00000000..a625a570 --- /dev/null +++ b/crates/ui-story/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ui-story" +version = "0.1.0" +edition = "2021" + +[dependencies] +ui.workspace = true +gpui.workspace = true diff --git a/crates/ui/src/story/button_story.rs b/crates/ui-story/src/button_story.rs similarity index 99% rename from crates/ui/src/story/button_story.rs rename to crates/ui-story/src/button_story.rs index 8d02c31e..7be2828a 100644 --- a/crates/ui/src/story/button_story.rs +++ b/crates/ui-story/src/button_story.rs @@ -3,7 +3,7 @@ use gpui::{ WindowContext, }; -use crate::{ +use ui::{ button::{Button, ButtonSize, ButtonStyle}, Clickable, Disableable as _, }; diff --git a/crates/ui/src/story/checkbox_story.rs b/crates/ui-story/src/checkbox_story.rs similarity index 96% rename from crates/ui/src/story/checkbox_story.rs rename to crates/ui-story/src/checkbox_story.rs index 91eae66f..f3a93075 100644 --- a/crates/ui/src/story/checkbox_story.rs +++ b/crates/ui-story/src/checkbox_story.rs @@ -3,12 +3,7 @@ use gpui::{ WindowContext, }; -use crate::{ - checkbox::Checkbox, - disableable::Disableable as _, - selectable::Selection, - stock::{h_flex, v_flex}, -}; +use ui::{checkbox::Checkbox, h_flex, v_flex, Disableable as _, Selection}; use super::story_case; diff --git a/crates/ui/src/story/input_story.rs b/crates/ui-story/src/input_story.rs similarity index 62% rename from crates/ui/src/story/input_story.rs rename to crates/ui-story/src/input_story.rs index e0af1d11..03eae973 100644 --- a/crates/ui/src/story/input_story.rs +++ b/crates/ui-story/src/input_story.rs @@ -3,7 +3,7 @@ use gpui::{ VisualContext, WindowContext, }; -use crate::text_field::TextField; +use ui::text_field::TextField; use super::story_case; @@ -16,22 +16,32 @@ pub struct InputStory { impl InputStory { pub(crate) fn new(cx: &mut WindowContext) -> Self { - let input1 = cx.new_view(|cx| TextField::new(cx).set_text("Hello 世界", cx)); + let input1 = cx.new_view(|cx| { + let mut input = TextField::new(cx); + input.set_text("Hello 世界", cx); + input + }); let mask_input = cx.new_view(|cx| { - TextField::new(cx) - .set_masked(true, cx) - .set_text("this-is-password", cx) + let mut input = TextField::new(cx); + input.set_masked(true, cx); + input.set_text("this-is-password", cx); + input }); Self { input1, - input2: cx.new_view(|cx| TextField::new(cx).set_placeholder("Enter text here...", cx)), + input2: cx.new_view(|cx| { + let mut input = TextField::new(cx); + input.set_placeholder("Enter text here...", cx); + input + }), mash_input: mask_input, disabled_input: cx.new_view(|cx| { - TextField::new(cx) - .set_text("This is disabled input", cx) - .set_disabled(true, cx) + let mut input = TextField::new(cx); + input.set_text("This is disabled input", cx); + input.set_disabled(true, cx); + input }), } } diff --git a/crates/ui/src/story/mod.rs b/crates/ui-story/src/lib.rs similarity index 97% rename from crates/ui/src/story/mod.rs rename to crates/ui-story/src/lib.rs index 986f8978..c0f8c564 100644 --- a/crates/ui/src/story/mod.rs +++ b/crates/ui-story/src/lib.rs @@ -1,8 +1,4 @@ -use core::fmt; -use std::{ - fmt::{Display, Formatter}, - sync::Arc, -}; +use std::fmt::{self, Display, Formatter}; use checkbox_story::CheckboxStory; use gpui::{ @@ -15,10 +11,10 @@ use switch_story::SwitchStory; mod button_story; mod checkbox_story; mod input_story; +mod picker_story; mod switch_story; -use crate::{ - button::Button, +use ui::{ label::Label, tab::{Tab, TabBar}, Selectable, diff --git a/crates/ui-story/src/picker_story.rs b/crates/ui-story/src/picker_story.rs new file mode 100644 index 00000000..e69de29b diff --git a/crates/ui/src/story/switch_story.rs b/crates/ui-story/src/switch_story.rs similarity index 91% rename from crates/ui/src/story/switch_story.rs rename to crates/ui-story/src/switch_story.rs index 42346207..e0f53099 100644 --- a/crates/ui/src/story/switch_story.rs +++ b/crates/ui-story/src/switch_story.rs @@ -1,17 +1,13 @@ use gpui::{ - div, ClickEvent, Div, IntoElement, ParentElement, Render, RenderOnce, SharedString, - StatefulInteractiveElement as _, Styled, ViewContext, VisualContext as _, WindowContext, + Div, IntoElement, ParentElement, Render, SharedString, Styled, ViewContext, WindowContext, }; -use crate::{ - checkbox::Checkbox, - disableable::Disableable as _, +use ui::{ + h_flex, label::Label, - selectable::Selection, - stock::{h_flex, v_flex}, switch::{LabelSide, Switch}, theme::ActiveTheme, - StyledExt, + v_flex, Disableable as _, StyledExt, }; use super::story_case; diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml index 4856f60b..05d1e3c6 100644 --- a/crates/ui/Cargo.toml +++ b/crates/ui/Cargo.toml @@ -11,3 +11,4 @@ serde.workspace = true serde_json.workspace = true smallvec = "1.13.2" windows = "0.57.0" +anyhow = "1" diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index ad7b13b3..08508f30 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -9,17 +9,21 @@ mod styled_ext; pub mod button; pub mod checkbox; +pub mod empty; pub mod label; -pub mod story; pub mod text_field; pub mod theme; pub mod title_bar; pub use styled_ext::StyledExt; +pub mod divider; +pub mod dropdown; +pub mod picker; pub mod switch; pub mod tab; pub use clickable::Clickable; pub use disableable::Disableable; -pub use selectable::Selectable; +pub use selectable::*; pub use icon::*; +pub use stock::*; diff --git a/crates/ui/src/styled_ext.rs b/crates/ui/src/styled_ext.rs index 82f668dc..9c3e0c4e 100644 --- a/crates/ui/src/styled_ext.rs +++ b/crates/ui/src/styled_ext.rs @@ -1,4 +1,63 @@ -use gpui::Styled; +use crate::theme::ActiveTheme; +use gpui::{hsla, point, px, BoxShadow, Styled, WindowContext}; +use smallvec::{smallvec, SmallVec}; + +pub enum ElevationIndex { + Background, + Surface, + ElevatedSurface, + Wash, + ModalSurface, + DraggedElement, +} + +impl ElevationIndex { + pub fn shadow(self) -> SmallVec<[BoxShadow; 2]> { + match self { + ElevationIndex::Surface => smallvec![], + + ElevationIndex::ElevatedSurface => smallvec![BoxShadow { + color: hsla(0., 0., 0., 0.12), + offset: point(px(0.), px(2.)), + blur_radius: px(3.), + spread_radius: px(0.), + }], + + ElevationIndex::ModalSurface => smallvec![ + BoxShadow { + color: hsla(0., 0., 0., 0.12), + offset: point(px(0.), px(2.)), + blur_radius: px(3.), + spread_radius: px(0.), + }, + BoxShadow { + color: hsla(0., 0., 0., 0.08), + offset: point(px(0.), px(3.)), + blur_radius: px(6.), + spread_radius: px(0.), + }, + BoxShadow { + color: hsla(0., 0., 0., 0.04), + offset: point(px(0.), px(6.)), + blur_radius: px(12.), + spread_radius: px(0.), + }, + ], + + _ => smallvec![], + } + } +} + +fn elevated(this: E, cx: &mut WindowContext, index: ElevationIndex) -> E { + let theme = cx.theme(); + + this.bg(theme.popover) + .rounded(px(8.)) + .border_1() + .border_color(theme.border) + .shadow(index.shadow()) +} /// Extends [`gpui::Styled`] with specific styling methods. pub trait StyledExt: Styled + Sized { @@ -15,6 +74,21 @@ pub trait StyledExt: Styled + Sized { fn v_flex(self) -> Self { self.flex().flex_col() } + + /// Located above the app background + fn elevation_1(self, cx: &mut WindowContext) -> Self { + elevated(self, cx, ElevationIndex::Surface) + } + + /// Appear above most UI elements + fn elevation_2(self, cx: &mut WindowContext) -> Self { + elevated(self, cx, ElevationIndex::ElevatedSurface) + } + + // Above all other UI elements and are located above the wash layer + fn elevation_3(self, cx: &mut WindowContext) -> Self { + elevated(self, cx, ElevationIndex::ModalSurface) + } } impl StyledExt for E {} diff --git a/crates/ui/src/text_field/mod.rs b/crates/ui/src/text_field/mod.rs index 4240a12e..8691fe97 100644 --- a/crates/ui/src/text_field/mod.rs +++ b/crates/ui/src/text_field/mod.rs @@ -4,9 +4,9 @@ mod text_view; use crate::theme::ActiveTheme; use gpui::{ - div, prelude::FluentBuilder as _, ClipboardItem, EventEmitter, FocusHandle, FocusableView, - InteractiveElement, IntoElement, KeyDownEvent, MouseButton, ParentElement, Render, RenderOnce, - Styled, View, WindowContext, + div, prelude::FluentBuilder as _, AppContext, ClipboardItem, EventEmitter, FocusHandle, + FocusableView, InteractiveElement, IntoElement, KeyDownEvent, MouseButton, ParentElement, + Render, RenderOnce, Styled, View, ViewContext, WindowContext, }; use std::time::Duration; use text_view::TextView; @@ -31,29 +31,29 @@ impl TextField { cx.focus(&self.focus_handle); } - pub fn set_placeholder(self, placeholder: &str, cx: &mut WindowContext) -> Self { + pub fn set_placeholder(&mut self, placeholder: &str, cx: &mut WindowContext) { self.view.update(cx, |text_view, cx| { text_view.set_placeholder(placeholder, cx) }); - self } - pub fn set_disabled(self, disabled: bool, cx: &mut WindowContext) -> Self { + pub fn set_disabled(&mut self, disabled: bool, cx: &mut WindowContext) { self.view .update(cx, |text_view, cx| text_view.set_disabled(disabled, cx)); - self } - pub fn set_text(self, text: &str, cx: &mut WindowContext) -> Self { + pub fn set_text(&mut self, text: &str, cx: &mut WindowContext) { self.view .update(cx, |text_view, cx| text_view.set_text(text, cx)); - self } - pub fn set_masked(self, masked: bool, cx: &mut WindowContext) -> Self { + pub fn text(&self, cx: &AppContext) -> String { + self.view.read(cx).text.clone() + } + + pub fn set_masked(&mut self, masked: bool, cx: &mut WindowContext) { self.view .update(cx, |text_view, cx| text_view.set_masked(masked, cx)); - self } } diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index d4759f69..4f2d2a6c 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" [dependencies] gpui.workspace = true ui.workspace = true +ui-story.workspace = true anyhow.workspace = true util.workspace = true diff --git a/crates/workspace/src/lib.rs b/crates/workspace/src/lib.rs index 98036bf8..9e14f858 100644 --- a/crates/workspace/src/lib.rs +++ b/crates/workspace/src/lib.rs @@ -5,11 +5,11 @@ use std::sync::Arc; use ui::{ button::Button, label::Label, - story::Stories, theme::{ActiveTheme, Theme}, title_bar::TitleBar, Clickable as _, StyledExt as _, }; +use ui_story::Stories; use util::ResultExt as _; mod app_state;