ui: More component previews, UI component cleanup (#25302)
- Don't require ui component docs (this isn't really working) - Add more component previews - Update component preview style & navigation Release Notes: - N/A
This commit is contained in:
parent
c9235ff916
commit
5397ca23a1
52 changed files with 813 additions and 856 deletions
|
@ -2458,8 +2458,8 @@ impl CollabPanel {
|
||||||
Avatar::new(contact.user.avatar_uri.clone())
|
Avatar::new(contact.user.avatar_uri.clone())
|
||||||
.indicator::<AvatarAvailabilityIndicator>(if online {
|
.indicator::<AvatarAvailabilityIndicator>(if online {
|
||||||
Some(AvatarAvailabilityIndicator::new(match busy {
|
Some(AvatarAvailabilityIndicator::new(match busy {
|
||||||
true => ui::Availability::Busy,
|
true => ui::CollaboratorAvailability::Busy,
|
||||||
false => ui::Availability::Free,
|
false => ui::CollaboratorAvailability::Free,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -173,9 +173,9 @@ pub enum ExampleLabelSide {
|
||||||
Left,
|
Left,
|
||||||
/// Right side
|
/// Right side
|
||||||
Right,
|
Right,
|
||||||
#[default]
|
|
||||||
/// Top side
|
/// Top side
|
||||||
Top,
|
Top,
|
||||||
|
#[default]
|
||||||
/// Bottom side
|
/// Bottom side
|
||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
|
@ -200,10 +200,10 @@ impl RenderOnce for ComponentExample {
|
||||||
ExampleLabelSide::Top => base.flex_col_reverse(),
|
ExampleLabelSide::Top => base.flex_col_reverse(),
|
||||||
};
|
};
|
||||||
|
|
||||||
base.gap_1()
|
base.gap_2()
|
||||||
.p_2()
|
.p_2()
|
||||||
.text_sm()
|
.text_size(px(10.))
|
||||||
.text_color(cx.theme().colors().text)
|
.text_color(cx.theme().colors().text_muted)
|
||||||
.when(self.grow, |this| this.flex_1())
|
.when(self.grow, |this| this.flex_1())
|
||||||
.child(self.element)
|
.child(self.element)
|
||||||
.child(self.variant_name)
|
.child(self.variant_name)
|
||||||
|
@ -245,12 +245,13 @@ impl RenderOnce for ComponentExampleGroup {
|
||||||
.text_color(cx.theme().colors().text_muted)
|
.text_color(cx.theme().colors().text_muted)
|
||||||
.when(self.grow, |this| this.w_full().flex_1())
|
.when(self.grow, |this| this.w_full().flex_1())
|
||||||
.when_some(self.title, |this, title| {
|
.when_some(self.title, |this, title| {
|
||||||
this.gap_4().pb_5().child(
|
this.gap_4().child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap_3()
|
.gap_3()
|
||||||
.child(div().h_px().w_4().bg(cx.theme().colors().border_variant))
|
.pb_1()
|
||||||
|
.child(div().h_px().w_4().bg(cx.theme().colors().border))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
|
@ -271,7 +272,7 @@ impl RenderOnce for ComponentExampleGroup {
|
||||||
.flex()
|
.flex()
|
||||||
.items_start()
|
.items_start()
|
||||||
.w_full()
|
.w_full()
|
||||||
.gap_8()
|
.gap_6()
|
||||||
.children(self.examples)
|
.children(self.examples)
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
//! A view for exploring Zed components.
|
//! A view for exploring Zed components.
|
||||||
|
|
||||||
use component::{components, ComponentMetadata};
|
use component::{components, ComponentMetadata};
|
||||||
use gpui::{prelude::*, App, EventEmitter, FocusHandle, Focusable, Window};
|
use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window};
|
||||||
use ui::prelude::*;
|
use gpui::{ListState, ScrollHandle, UniformListScrollHandle};
|
||||||
|
use ui::{prelude::*, ListItem};
|
||||||
|
|
||||||
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ pub fn init(cx: &mut App) {
|
||||||
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
|
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
|
||||||
workspace.register_action(
|
workspace.register_action(
|
||||||
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
||||||
let component_preview = cx.new(ComponentPreview::new);
|
let component_preview = cx.new(|cx| ComponentPreview::new(window, cx));
|
||||||
workspace.add_item_to_active_pane(
|
workspace.add_item_to_active_pane(
|
||||||
Box::new(component_preview),
|
Box::new(component_preview),
|
||||||
None,
|
None,
|
||||||
|
@ -28,124 +29,161 @@ pub fn init(cx: &mut App) {
|
||||||
|
|
||||||
struct ComponentPreview {
|
struct ComponentPreview {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
|
_view_scroll_handle: ScrollHandle,
|
||||||
|
nav_scroll_handle: UniformListScrollHandle,
|
||||||
|
components: Vec<ComponentMetadata>,
|
||||||
|
component_list: ListState,
|
||||||
|
selected_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComponentPreview {
|
impl ComponentPreview {
|
||||||
pub fn new(cx: &mut Context<Self>) -> Self {
|
pub fn new(_window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
|
let components = components().all_sorted();
|
||||||
|
let initial_length = components.len();
|
||||||
|
|
||||||
|
let component_list = ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), {
|
||||||
|
let this = cx.entity().downgrade();
|
||||||
|
move |ix, window: &mut Window, cx: &mut App| {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.render_preview(ix, window, cx).into_any_element()
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
|
_view_scroll_handle: ScrollHandle::new(),
|
||||||
|
nav_scroll_handle: UniformListScrollHandle::new(),
|
||||||
|
components,
|
||||||
|
component_list,
|
||||||
|
selected_index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_sidebar(&self, _window: &Window, _cx: &Context<Self>) -> impl IntoElement {
|
fn scroll_to_preview(&mut self, ix: usize, cx: &mut Context<Self>) {
|
||||||
let components = components().all_sorted();
|
self.component_list.scroll_to_reveal_item(ix);
|
||||||
let sorted_components = components.clone();
|
self.selected_index = ix;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
v_flex()
|
fn get_component(&self, ix: usize) -> ComponentMetadata {
|
||||||
.max_w_48()
|
self.components[ix].clone()
|
||||||
.gap_px()
|
|
||||||
.p_1()
|
|
||||||
.children(
|
|
||||||
sorted_components
|
|
||||||
.into_iter()
|
|
||||||
.map(|component| self.render_sidebar_entry(&component, _cx)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Label::new("These will be clickable once the layout is moved to a gpui::List.")
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(LabelSize::XSmall)
|
|
||||||
.italic(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_sidebar_entry(
|
fn render_sidebar_entry(
|
||||||
&self,
|
&self,
|
||||||
component: &ComponentMetadata,
|
ix: usize,
|
||||||
_cx: &Context<Self>,
|
selected: bool,
|
||||||
|
cx: &Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
h_flex()
|
let component = self.get_component(ix);
|
||||||
.w_40()
|
|
||||||
.px_1p5()
|
ListItem::new(ix)
|
||||||
.py_0p5()
|
.child(Label::new(component.name().clone()).color(Color::Default))
|
||||||
.text_sm()
|
.selectable(true)
|
||||||
.child(component.name().clone())
|
.toggle_state(selected)
|
||||||
|
.inset(true)
|
||||||
|
.on_click(cx.listener(move |this, _, _, cx| {
|
||||||
|
this.scroll_to_preview(ix, cx);
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_preview(
|
fn render_preview(
|
||||||
&self,
|
&self,
|
||||||
component: &ComponentMetadata,
|
ix: usize,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
|
let component = self.get_component(ix);
|
||||||
|
|
||||||
let name = component.name();
|
let name = component.name();
|
||||||
let scope = component.scope();
|
let scope = component.scope();
|
||||||
|
|
||||||
let description = component.description();
|
let description = component.description();
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.border_b_1()
|
.py_2()
|
||||||
.border_color(cx.theme().colors().border)
|
|
||||||
.w_full()
|
|
||||||
.gap_3()
|
|
||||||
.py_6()
|
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_1()
|
.border_1()
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
|
.rounded_md()
|
||||||
|
.w_full()
|
||||||
|
.gap_4()
|
||||||
|
.py_4()
|
||||||
|
.px_6()
|
||||||
|
.flex_none()
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
v_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.text_2xl()
|
.child(
|
||||||
.child(div().child(name))
|
h_flex()
|
||||||
.when_some(scope, |this, scope| {
|
.gap_1()
|
||||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
.text_xl()
|
||||||
|
.child(div().child(name))
|
||||||
|
.when_some(scope, |this, scope| {
|
||||||
|
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.when_some(description, |this, description| {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.text_ui_sm(cx)
|
||||||
|
.text_color(cx.theme().colors().text_muted)
|
||||||
|
.max_w(px(600.0))
|
||||||
|
.child(description),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.when_some(description, |this, description| {
|
.when_some(component.preview(), |this, preview| {
|
||||||
this.child(
|
this.child(preview(window, cx))
|
||||||
div()
|
|
||||||
.text_ui_sm(cx)
|
|
||||||
.text_color(cx.theme().colors().text_muted)
|
|
||||||
.max_w(px(600.0))
|
|
||||||
.child(description),
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.when_some(component.preview(), |this, preview| {
|
|
||||||
this.child(preview(window, cx))
|
|
||||||
})
|
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_previews(&self, window: &mut Window, cx: &Context<Self>) -> impl IntoElement {
|
|
||||||
v_flex()
|
|
||||||
.id("component-previews")
|
|
||||||
.size_full()
|
|
||||||
.overflow_y_scroll()
|
|
||||||
.p_4()
|
|
||||||
.gap_4()
|
|
||||||
.children(
|
|
||||||
components()
|
|
||||||
.all_previews_sorted()
|
|
||||||
.iter()
|
|
||||||
.map(|component| self.render_preview(component, window, cx)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for ComponentPreview {
|
impl Render for ComponentPreview {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("component-preview")
|
.id("component-preview")
|
||||||
.key_context("ComponentPreview")
|
.key_context("ComponentPreview")
|
||||||
.items_start()
|
.items_start()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.size_full()
|
.size_full()
|
||||||
.max_h_full()
|
|
||||||
.track_focus(&self.focus_handle)
|
.track_focus(&self.focus_handle)
|
||||||
.px_2()
|
.px_2()
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.child(self.render_sidebar(window, cx))
|
.child(
|
||||||
.child(self.render_previews(window, cx))
|
uniform_list(
|
||||||
|
cx.entity().clone(),
|
||||||
|
"component-nav",
|
||||||
|
self.components.len(),
|
||||||
|
move |this, range, _window, cx| {
|
||||||
|
range
|
||||||
|
.map(|ix| this.render_sidebar_entry(ix, ix == this.selected_index, cx))
|
||||||
|
.collect()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.track_scroll(self.nav_scroll_handle.clone())
|
||||||
|
.pt_4()
|
||||||
|
.w(px(240.))
|
||||||
|
.h_full()
|
||||||
|
.flex_grow(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.id("component-list")
|
||||||
|
.px_8()
|
||||||
|
.pt_4()
|
||||||
|
.size_full()
|
||||||
|
.child(
|
||||||
|
list(self.component_list.clone())
|
||||||
|
.flex_grow()
|
||||||
|
.with_sizing_behavior(gpui::ListSizingBehavior::Auto),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,13 +213,13 @@ impl Item for ComponentPreview {
|
||||||
fn clone_on_split(
|
fn clone_on_split(
|
||||||
&self,
|
&self,
|
||||||
_workspace_id: Option<WorkspaceId>,
|
_workspace_id: Option<WorkspaceId>,
|
||||||
_window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Option<gpui::Entity<Self>>
|
) -> Option<gpui::Entity<Self>>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
Some(cx.new(Self::new))
|
Some(cx.new(|cx| Self::new(window, cx)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
||||||
|
|
|
@ -14,8 +14,6 @@ use ui::prelude::*;
|
||||||
pub enum ComponentStory {
|
pub enum ComponentStory {
|
||||||
ApplicationMenu,
|
ApplicationMenu,
|
||||||
AutoHeightEditor,
|
AutoHeightEditor,
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
CollabNotification,
|
CollabNotification,
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
Cursor,
|
Cursor,
|
||||||
|
@ -47,8 +45,6 @@ impl ComponentStory {
|
||||||
.new(|cx| title_bar::ApplicationMenuStory::new(window, cx))
|
.new(|cx| title_bar::ApplicationMenuStory::new(window, cx))
|
||||||
.into(),
|
.into(),
|
||||||
Self::AutoHeightEditor => AutoHeightEditorStory::new(window, cx).into(),
|
Self::AutoHeightEditor => AutoHeightEditorStory::new(window, cx).into(),
|
||||||
Self::Avatar => cx.new(|_| ui::AvatarStory).into(),
|
|
||||||
Self::Button => cx.new(|_| ui::ButtonStory).into(),
|
|
||||||
Self::CollabNotification => cx
|
Self::CollabNotification => cx
|
||||||
.new(|_| collab_ui::notifications::CollabNotificationStory)
|
.new(|_| collab_ui::notifications::CollabNotificationStory)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
# Building UI with GPUI
|
|
||||||
|
|
||||||
## Common patterns
|
|
||||||
|
|
||||||
### Method ordering
|
|
||||||
|
|
||||||
- id
|
|
||||||
- Flex properties
|
|
||||||
- Position properties
|
|
||||||
- Size properties
|
|
||||||
- Style properties
|
|
||||||
- Handlers
|
|
||||||
- State properties
|
|
||||||
|
|
||||||
### Using the Label Component to Create UI Text
|
|
||||||
|
|
||||||
The `Label` component helps in displaying text on user interfaces. It creates an interface where specific parameters such as label color, line height style, and strikethrough can be set.
|
|
||||||
|
|
||||||
Firstly, to create a `Label` instance, use the `Label::new()` function. This function takes a string that will be displayed as text in the interface.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
Label::new("Hello, world!");
|
|
||||||
```
|
|
||||||
|
|
||||||
Now let's dive a bit deeper into how to customize `Label` instances:
|
|
||||||
|
|
||||||
- **Setting Color:** To set the color of the label using various predefined color options such as `Default`, `Muted`, `Created`, `Modified`, `Deleted`, etc, the `color()` function is called on the `Label` instance:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
Label::new("Hello, world!").color(LabelColor::Default);
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Setting Line Height Style:** To set the line height style, the `line_height_style()` function is utilized:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
Label::new("Hello, world!").line_height_style(LineHeightStyle::TextLabel);
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Adding a Strikethrough:** To add a strikethrough in a `Label`, the `set_strikethrough()` function is used:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
Label::new("Hello, world!").set_strikethrough(true);
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it! Now you can use the `Label` component to create and customize text on your application's interface.
|
|
||||||
|
|
||||||
## Building a new component
|
|
||||||
|
|
||||||
TODO
|
|
|
@ -1,160 +0,0 @@
|
||||||
# Hello World
|
|
||||||
|
|
||||||
Let's work through the prototypical "Build a todo app" example to showcase how we might build a simple component from scratch.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
We'll create a headline, a list of todo items, and a form to add new items.
|
|
||||||
|
|
||||||
~~~rust
|
|
||||||
struct TodoList<V: 'static> {
|
|
||||||
headline: SharedString,
|
|
||||||
items: Vec<TodoItem>,
|
|
||||||
submit_form: ClickHandler<V>
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TodoItem<V: 'static> {
|
|
||||||
text: SharedString,
|
|
||||||
completed: bool,
|
|
||||||
delete: ClickHandler<V>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> TodoList<V> {
|
|
||||||
pub fn new(
|
|
||||||
// Here we impl Into<SharedString>
|
|
||||||
headline: impl Into<SharedString>,
|
|
||||||
items: Vec<TodoItem>,
|
|
||||||
submit_form: ClickHandler<V>
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
// and here we call .into() so we can simply pass a string
|
|
||||||
// when creating the headline. This pattern is used throughout
|
|
||||||
// outr components
|
|
||||||
headline: headline.into(),
|
|
||||||
items: Vec::new(),
|
|
||||||
submit_form,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
All of this is relatively straightforward.
|
|
||||||
|
|
||||||
We use [gpui::SharedString] in components instead of [std::string::String]. This allows us to efficiently handle shared string data across multiple components and threads without the performance overhead of copying strings.
|
|
||||||
|
|
||||||
When we want to pass an action we pass a `ClickHandler`. Whenever we want to add an action, the struct it belongs to needs to be generic over the view type `V`.
|
|
||||||
|
|
||||||
~~~rust
|
|
||||||
use gpui::hsla
|
|
||||||
|
|
||||||
impl<V: 'static> TodoList<V> {
|
|
||||||
// ...
|
|
||||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
|
||||||
div().size_4().bg(hsla(50.0/360.0, 1.0, 0.5, 1.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
Every component needs a render method, and it should return `impl Element<V>`. This basic component will render a 16x16px yellow square on the screen.
|
|
||||||
|
|
||||||
A couple of questions might come to mind:
|
|
||||||
|
|
||||||
**Why is `size_4()` 16px, not 4px?**
|
|
||||||
|
|
||||||
gpui's style system is based on conventions created by [Tailwind CSS](https://tailwindcss.com/). Here is an example of the list of sizes for `width`: [Width - TailwindCSS Docs](https://tailwindcss.com/docs/width).
|
|
||||||
|
|
||||||
I'll quote from the Tailwind [Core Concepts](https://tailwindcss.com/docs/utility-first) docs here:
|
|
||||||
|
|
||||||
> Now I know what you’re thinking, “this is an atrocity, what a horrible mess!”
|
|
||||||
> and you’re right, it’s kind of ugly. In fact it’s just about impossible to
|
|
||||||
> think this is a good idea the first time you see it —
|
|
||||||
> you have to actually try it.
|
|
||||||
|
|
||||||
As you start using the Tailwind-style conventions you will be surprised how quick it makes it to build out UIs.
|
|
||||||
|
|
||||||
**Why `50.0/360.0` in `hsla()`?**
|
|
||||||
|
|
||||||
gpui [gpui::Hsla] use `0.0-1.0` for all its values, but it is common for tools to use `0-360` for hue.
|
|
||||||
|
|
||||||
This may change in the future, but this is a little trick that let's you use familiar looking values.
|
|
||||||
|
|
||||||
## Building out the container
|
|
||||||
|
|
||||||
Let's grab our [theme::colors::ThemeColors] from the theme and start building out a basic container.
|
|
||||||
|
|
||||||
We can access the current theme's colors like this:
|
|
||||||
|
|
||||||
~~~rust
|
|
||||||
impl<V: 'static> TodoList<V> {
|
|
||||||
// ...
|
|
||||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
|
||||||
let color = cx.theme().colors()
|
|
||||||
|
|
||||||
div().size_4().hsla(50.0/360.0, 1.0, 0.5, 1.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
Now we have access to the complete set of colors defined in the theme.
|
|
||||||
|
|
||||||
~~~rust
|
|
||||||
use gpui::hsla
|
|
||||||
|
|
||||||
impl<V: 'static> TodoList<V> {
|
|
||||||
// ...
|
|
||||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
|
||||||
let color = cx.theme().colors()
|
|
||||||
|
|
||||||
div().size_4().bg(color.surface)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
Let's finish up some basic styles for the container then move on to adding the other elements.
|
|
||||||
|
|
||||||
~~~rust
|
|
||||||
use gpui::hsla
|
|
||||||
|
|
||||||
impl<V: 'static> TodoList<V> {
|
|
||||||
// ...
|
|
||||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
|
||||||
let color = cx.theme().colors()
|
|
||||||
|
|
||||||
div()
|
|
||||||
// Flex properties
|
|
||||||
.flex()
|
|
||||||
.flex_col() // Stack elements vertically
|
|
||||||
.gap_2() // Add 8px of space between elements
|
|
||||||
// Size properties
|
|
||||||
.w_96() // Set width to 384px
|
|
||||||
.p_4() // Add 16px of padding on all sides
|
|
||||||
// Color properties
|
|
||||||
.bg(color.surface) // Set background color
|
|
||||||
.text_color(color.text) // Set text color
|
|
||||||
// Border properties
|
|
||||||
.rounded_md() // Add 4px of border radius
|
|
||||||
.border_1() // Add a 1px border
|
|
||||||
.border_color(color.border)
|
|
||||||
.child(
|
|
||||||
"Hello, world!"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
### Headline
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
### List of todo items
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
### Input
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
|
|
||||||
### End result
|
|
||||||
|
|
||||||
TODO
|
|
|
@ -1,7 +1,302 @@
|
||||||
mod avatar;
|
use crate::prelude::*;
|
||||||
mod avatar_audio_status_indicator;
|
|
||||||
mod avatar_availability_indicator;
|
|
||||||
|
|
||||||
pub use avatar::*;
|
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
||||||
pub use avatar_audio_status_indicator::*;
|
|
||||||
pub use avatar_availability_indicator::*;
|
/// An element that renders a user avatar with customizable appearance options.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ui::{Avatar, AvatarShape};
|
||||||
|
///
|
||||||
|
/// Avatar::new("path/to/image.png")
|
||||||
|
/// .shape(AvatarShape::Circle)
|
||||||
|
/// .grayscale(true)
|
||||||
|
/// .border_color(gpui::red());
|
||||||
|
/// ```
|
||||||
|
#[derive(IntoElement, IntoComponent)]
|
||||||
|
pub struct Avatar {
|
||||||
|
image: Img,
|
||||||
|
size: Option<AbsoluteLength>,
|
||||||
|
border_color: Option<Hsla>,
|
||||||
|
indicator: Option<AnyElement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Avatar {
|
||||||
|
/// Creates a new avatar element with the specified image source.
|
||||||
|
pub fn new(src: impl Into<ImageSource>) -> Self {
|
||||||
|
Avatar {
|
||||||
|
image: img(src),
|
||||||
|
size: None,
|
||||||
|
border_color: None,
|
||||||
|
indicator: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies a grayscale filter to the avatar image.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ui::{Avatar, AvatarShape};
|
||||||
|
///
|
||||||
|
/// let avatar = Avatar::new("path/to/image.png").grayscale(true);
|
||||||
|
/// ```
|
||||||
|
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
||||||
|
self.image = self.image.grayscale(grayscale);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the border color of the avatar.
|
||||||
|
///
|
||||||
|
/// This might be used to match the border to the background color of
|
||||||
|
/// the parent element to create the illusion of cropping another
|
||||||
|
/// shape underneath (for example in face piles.)
|
||||||
|
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
||||||
|
self.border_color = Some(color.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Size overrides the avatar size. By default they are 1rem.
|
||||||
|
pub fn size<L: Into<AbsoluteLength>>(mut self, size: impl Into<Option<L>>) -> Self {
|
||||||
|
self.size = size.into().map(Into::into);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the current indicator to be displayed on the avatar, if any.
|
||||||
|
pub fn indicator<E: IntoElement>(mut self, indicator: impl Into<Option<E>>) -> Self {
|
||||||
|
self.indicator = indicator.into().map(IntoElement::into_any_element);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for Avatar {
|
||||||
|
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
|
let border_width = if self.border_color.is_some() {
|
||||||
|
px(2.)
|
||||||
|
} else {
|
||||||
|
px(0.)
|
||||||
|
};
|
||||||
|
|
||||||
|
let image_size = self.size.unwrap_or_else(|| rems(1.).into());
|
||||||
|
let container_size = image_size.to_pixels(window.rem_size()) + border_width * 2.;
|
||||||
|
|
||||||
|
div()
|
||||||
|
.size(container_size)
|
||||||
|
.rounded_full()
|
||||||
|
.when_some(self.border_color, |this, color| {
|
||||||
|
this.border(border_width).border_color(color)
|
||||||
|
})
|
||||||
|
.child(
|
||||||
|
self.image
|
||||||
|
.size(image_size)
|
||||||
|
.rounded_full()
|
||||||
|
.bg(cx.theme().colors().ghost_element_background),
|
||||||
|
)
|
||||||
|
.children(self.indicator.map(|indicator| div().child(indicator)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use gpui::AnyView;
|
||||||
|
|
||||||
|
/// The audio status of an player, for use in representing
|
||||||
|
/// their status visually on their avatar.
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
|
pub enum AudioStatus {
|
||||||
|
/// The player's microphone is muted.
|
||||||
|
Muted,
|
||||||
|
/// The player's microphone is muted, and collaboration audio is disabled.
|
||||||
|
Deafened,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An indicator that shows the audio status of a player.
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct AvatarAudioStatusIndicator {
|
||||||
|
audio_status: AudioStatus,
|
||||||
|
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AvatarAudioStatusIndicator {
|
||||||
|
/// Creates a new `AvatarAudioStatusIndicator`
|
||||||
|
pub fn new(audio_status: AudioStatus) -> Self {
|
||||||
|
Self {
|
||||||
|
audio_status,
|
||||||
|
tooltip: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the tooltip for the indicator.
|
||||||
|
pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
|
||||||
|
self.tooltip = Some(Box::new(tooltip));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for AvatarAudioStatusIndicator {
|
||||||
|
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
|
let icon_size = IconSize::Indicator;
|
||||||
|
|
||||||
|
let width_in_px = icon_size.rems() * window.rem_size();
|
||||||
|
let padding_x = px(4.);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.bottom(rems_from_px(-3.))
|
||||||
|
.right(rems_from_px(-6.))
|
||||||
|
.w(width_in_px + padding_x)
|
||||||
|
.h(icon_size.rems())
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.id("muted-indicator")
|
||||||
|
.justify_center()
|
||||||
|
.px(padding_x)
|
||||||
|
.py(px(2.))
|
||||||
|
.bg(cx.theme().status().error_background)
|
||||||
|
.rounded_md()
|
||||||
|
.child(
|
||||||
|
Icon::new(match self.audio_status {
|
||||||
|
AudioStatus::Muted => IconName::MicMute,
|
||||||
|
AudioStatus::Deafened => IconName::AudioOff,
|
||||||
|
})
|
||||||
|
.size(icon_size)
|
||||||
|
.color(Color::Error),
|
||||||
|
)
|
||||||
|
.when_some(self.tooltip, |this, tooltip| {
|
||||||
|
this.tooltip(move |window, cx| tooltip(window, cx))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the availability status of a collaborator.
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
|
pub enum CollaboratorAvailability {
|
||||||
|
Free,
|
||||||
|
Busy,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the availability and presence status of a collaborator.
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct AvatarAvailabilityIndicator {
|
||||||
|
availability: CollaboratorAvailability,
|
||||||
|
avatar_size: Option<Pixels>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AvatarAvailabilityIndicator {
|
||||||
|
/// Creates a new indicator
|
||||||
|
pub fn new(availability: CollaboratorAvailability) -> Self {
|
||||||
|
Self {
|
||||||
|
availability,
|
||||||
|
avatar_size: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the size of the [`Avatar`](crate::Avatar) this indicator appears on.
|
||||||
|
pub fn avatar_size(mut self, size: impl Into<Option<Pixels>>) -> Self {
|
||||||
|
self.avatar_size = size.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for AvatarAvailabilityIndicator {
|
||||||
|
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
|
let avatar_size = self.avatar_size.unwrap_or_else(|| window.rem_size());
|
||||||
|
|
||||||
|
// HACK: non-integer sizes result in oval indicators.
|
||||||
|
let indicator_size = (avatar_size * 0.4).round();
|
||||||
|
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.bottom_0()
|
||||||
|
.right_0()
|
||||||
|
.size(indicator_size)
|
||||||
|
.rounded(indicator_size)
|
||||||
|
.bg(match self.availability {
|
||||||
|
CollaboratorAvailability::Free => cx.theme().status().created,
|
||||||
|
CollaboratorAvailability::Busy => cx.theme().status().deleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// View this component preview using `workspace: open component-preview`
|
||||||
|
impl ComponentPreview for Avatar {
|
||||||
|
fn preview(_window: &mut Window, cx: &App) -> AnyElement {
|
||||||
|
let example_avatar = "https://avatars.githubusercontent.com/u/1714999?v=4";
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.gap_6()
|
||||||
|
.children(vec![
|
||||||
|
example_group_with_title(
|
||||||
|
"Sizes",
|
||||||
|
vec![
|
||||||
|
single_example("Default", Avatar::new(example_avatar).into_any_element()),
|
||||||
|
single_example(
|
||||||
|
"Small",
|
||||||
|
Avatar::new(example_avatar).size(px(24.)).into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Large",
|
||||||
|
Avatar::new(example_avatar).size(px(48.)).into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Styles",
|
||||||
|
vec![
|
||||||
|
single_example("Default", Avatar::new(example_avatar).into_any_element()),
|
||||||
|
single_example(
|
||||||
|
"Grayscale",
|
||||||
|
Avatar::new(example_avatar)
|
||||||
|
.grayscale(true)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"With Border",
|
||||||
|
Avatar::new(example_avatar)
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Audio Status",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Muted",
|
||||||
|
Avatar::new(example_avatar)
|
||||||
|
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted))
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Deafened",
|
||||||
|
Avatar::new(example_avatar)
|
||||||
|
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened))
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Availability",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Free",
|
||||||
|
Avatar::new(example_avatar)
|
||||||
|
.indicator(AvatarAvailabilityIndicator::new(
|
||||||
|
CollaboratorAvailability::Free,
|
||||||
|
))
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Busy",
|
||||||
|
Avatar::new(example_avatar)
|
||||||
|
.indicator(AvatarAvailabilityIndicator::new(
|
||||||
|
CollaboratorAvailability::Busy,
|
||||||
|
))
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
])
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
use crate::{prelude::*, Indicator};
|
|
||||||
|
|
||||||
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
|
||||||
|
|
||||||
/// An element that renders a user avatar with customizable appearance options.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ui::{Avatar, AvatarShape};
|
|
||||||
///
|
|
||||||
/// Avatar::new("path/to/image.png")
|
|
||||||
/// .shape(AvatarShape::Circle)
|
|
||||||
/// .grayscale(true)
|
|
||||||
/// .border_color(gpui::red());
|
|
||||||
/// ```
|
|
||||||
#[derive(IntoElement, IntoComponent)]
|
|
||||||
pub struct Avatar {
|
|
||||||
image: Img,
|
|
||||||
size: Option<AbsoluteLength>,
|
|
||||||
border_color: Option<Hsla>,
|
|
||||||
indicator: Option<AnyElement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Avatar {
|
|
||||||
/// Creates a new avatar element with the specified image source.
|
|
||||||
pub fn new(src: impl Into<ImageSource>) -> Self {
|
|
||||||
Avatar {
|
|
||||||
image: img(src),
|
|
||||||
size: None,
|
|
||||||
border_color: None,
|
|
||||||
indicator: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Applies a grayscale filter to the avatar image.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ui::{Avatar, AvatarShape};
|
|
||||||
///
|
|
||||||
/// let avatar = Avatar::new("path/to/image.png").grayscale(true);
|
|
||||||
/// ```
|
|
||||||
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
|
||||||
self.image = self.image.grayscale(grayscale);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the border color of the avatar.
|
|
||||||
///
|
|
||||||
/// This might be used to match the border to the background color of
|
|
||||||
/// the parent element to create the illusion of cropping another
|
|
||||||
/// shape underneath (for example in face piles.)
|
|
||||||
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
|
||||||
self.border_color = Some(color.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Size overrides the avatar size. By default they are 1rem.
|
|
||||||
pub fn size<L: Into<AbsoluteLength>>(mut self, size: impl Into<Option<L>>) -> Self {
|
|
||||||
self.size = size.into().map(Into::into);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the current indicator to be displayed on the avatar, if any.
|
|
||||||
pub fn indicator<E: IntoElement>(mut self, indicator: impl Into<Option<E>>) -> Self {
|
|
||||||
self.indicator = indicator.into().map(IntoElement::into_any_element);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for Avatar {
|
|
||||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
||||||
let border_width = if self.border_color.is_some() {
|
|
||||||
px(2.)
|
|
||||||
} else {
|
|
||||||
px(0.)
|
|
||||||
};
|
|
||||||
|
|
||||||
let image_size = self.size.unwrap_or_else(|| rems(1.).into());
|
|
||||||
let container_size = image_size.to_pixels(window.rem_size()) + border_width * 2.;
|
|
||||||
|
|
||||||
div()
|
|
||||||
.size(container_size)
|
|
||||||
.rounded_full()
|
|
||||||
.when_some(self.border_color, |this, color| {
|
|
||||||
this.border(border_width).border_color(color)
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
self.image
|
|
||||||
.size(image_size)
|
|
||||||
.rounded_full()
|
|
||||||
.bg(cx.theme().colors().ghost_element_background),
|
|
||||||
)
|
|
||||||
.children(self.indicator.map(|indicator| div().child(indicator)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// View this component preview using `workspace: open component-preview`
|
|
||||||
impl ComponentPreview for Avatar {
|
|
||||||
fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
|
|
||||||
let example_avatar = "https://avatars.githubusercontent.com/u/1714999?v=4";
|
|
||||||
|
|
||||||
v_flex()
|
|
||||||
.gap_6()
|
|
||||||
.children(vec![
|
|
||||||
example_group_with_title(
|
|
||||||
"Sizes",
|
|
||||||
vec![
|
|
||||||
single_example(
|
|
||||||
"Default",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
|
|
||||||
.into_any_element(),
|
|
||||||
),
|
|
||||||
single_example(
|
|
||||||
"Small",
|
|
||||||
Avatar::new(example_avatar).size(px(24.)).into_any_element(),
|
|
||||||
),
|
|
||||||
single_example(
|
|
||||||
"Large",
|
|
||||||
Avatar::new(example_avatar).size(px(48.)).into_any_element(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
example_group_with_title(
|
|
||||||
"Styles",
|
|
||||||
vec![
|
|
||||||
single_example("Default", Avatar::new(example_avatar).into_any_element()),
|
|
||||||
single_example(
|
|
||||||
"Grayscale",
|
|
||||||
Avatar::new(example_avatar)
|
|
||||||
.grayscale(true)
|
|
||||||
.into_any_element(),
|
|
||||||
),
|
|
||||||
single_example(
|
|
||||||
"With Border",
|
|
||||||
Avatar::new(example_avatar)
|
|
||||||
.border_color(gpui::red())
|
|
||||||
.into_any_element(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
example_group_with_title(
|
|
||||||
"With Indicator",
|
|
||||||
vec![single_example(
|
|
||||||
"Dot",
|
|
||||||
Avatar::new(example_avatar)
|
|
||||||
.indicator(Indicator::dot().color(Color::Success))
|
|
||||||
.into_any_element(),
|
|
||||||
)],
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
use gpui::AnyView;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
/// The audio status of an player, for use in representing
|
|
||||||
/// their status visually on their avatar.
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
||||||
pub enum AudioStatus {
|
|
||||||
/// The player's microphone is muted.
|
|
||||||
Muted,
|
|
||||||
/// The player's microphone is muted, and collaboration audio is disabled.
|
|
||||||
Deafened,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An indicator that shows the audio status of a player.
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct AvatarAudioStatusIndicator {
|
|
||||||
audio_status: AudioStatus,
|
|
||||||
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AvatarAudioStatusIndicator {
|
|
||||||
/// Creates a new `AvatarAudioStatusIndicator`
|
|
||||||
pub fn new(audio_status: AudioStatus) -> Self {
|
|
||||||
Self {
|
|
||||||
audio_status,
|
|
||||||
tooltip: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the tooltip for the indicator.
|
|
||||||
pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
|
|
||||||
self.tooltip = Some(Box::new(tooltip));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for AvatarAudioStatusIndicator {
|
|
||||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
||||||
let icon_size = IconSize::Indicator;
|
|
||||||
|
|
||||||
let width_in_px = icon_size.rems() * window.rem_size();
|
|
||||||
let padding_x = px(4.);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.absolute()
|
|
||||||
.bottom(rems_from_px(-3.))
|
|
||||||
.right(rems_from_px(-6.))
|
|
||||||
.w(width_in_px + padding_x)
|
|
||||||
.h(icon_size.rems())
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.id("muted-indicator")
|
|
||||||
.justify_center()
|
|
||||||
.px(padding_x)
|
|
||||||
.py(px(2.))
|
|
||||||
.bg(cx.theme().status().error_background)
|
|
||||||
.rounded_md()
|
|
||||||
.child(
|
|
||||||
Icon::new(match self.audio_status {
|
|
||||||
AudioStatus::Muted => IconName::MicMute,
|
|
||||||
AudioStatus::Deafened => IconName::AudioOff,
|
|
||||||
})
|
|
||||||
.size(icon_size)
|
|
||||||
.color(Color::Error),
|
|
||||||
)
|
|
||||||
.when_some(self.tooltip, |this, tooltip| {
|
|
||||||
this.tooltip(move |window, cx| tooltip(window, cx))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
||||||
pub enum Availability {
|
|
||||||
Free,
|
|
||||||
Busy,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct AvatarAvailabilityIndicator {
|
|
||||||
availability: Availability,
|
|
||||||
avatar_size: Option<Pixels>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AvatarAvailabilityIndicator {
|
|
||||||
pub fn new(availability: Availability) -> Self {
|
|
||||||
Self {
|
|
||||||
availability,
|
|
||||||
avatar_size: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the size of the [`Avatar`](crate::Avatar) this indicator appears on.
|
|
||||||
pub fn avatar_size(mut self, size: impl Into<Option<Pixels>>) -> Self {
|
|
||||||
self.avatar_size = size.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for AvatarAvailabilityIndicator {
|
|
||||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
||||||
let avatar_size = self.avatar_size.unwrap_or_else(|| window.rem_size());
|
|
||||||
|
|
||||||
// HACK: non-integer sizes result in oval indicators.
|
|
||||||
let indicator_size = (avatar_size * 0.4).round();
|
|
||||||
|
|
||||||
div()
|
|
||||||
.absolute()
|
|
||||||
.bottom_0()
|
|
||||||
.right_0()
|
|
||||||
.size(indicator_size)
|
|
||||||
.rounded(indicator_size)
|
|
||||||
.bg(match self.availability {
|
|
||||||
Availability::Free => cx.theme().status().created,
|
|
||||||
Availability::Busy => cx.theme().status().deleted,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use component::{example_group_with_title, single_example, ComponentPreview};
|
use component::{example_group_with_title, single_example, ComponentPreview};
|
||||||
use gpui::{AnyElement, AnyView, DefiniteLength};
|
use gpui::{AnyElement, AnyView, DefiniteLength};
|
||||||
use ui_macros::IntoComponent;
|
use ui_macros::IntoComponent;
|
||||||
|
@ -81,6 +80,7 @@ use super::button_icon::ButtonIcon;
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
#[derive(IntoElement, IntoComponent)]
|
#[derive(IntoElement, IntoComponent)]
|
||||||
|
#[component(scope = "input")]
|
||||||
pub struct Button {
|
pub struct Button {
|
||||||
base: ButtonLike,
|
base: ButtonLike,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
|
@ -463,7 +463,7 @@ impl ComponentPreview for Button {
|
||||||
.gap_6()
|
.gap_6()
|
||||||
.children(vec![
|
.children(vec![
|
||||||
example_group_with_title(
|
example_group_with_title(
|
||||||
"Styles",
|
"Button Styles",
|
||||||
vec![
|
vec![
|
||||||
single_example(
|
single_example(
|
||||||
"Default",
|
"Default",
|
||||||
|
@ -481,6 +481,12 @@ impl ComponentPreview for Button {
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle)
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
),
|
),
|
||||||
|
single_example(
|
||||||
|
"Tinted",
|
||||||
|
Button::new("tinted_accent_style", "Accent")
|
||||||
|
.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
single_example(
|
single_example(
|
||||||
"Transparent",
|
"Transparent",
|
||||||
Button::new("transparent", "Transparent")
|
Button::new("transparent", "Transparent")
|
||||||
|
@ -490,7 +496,7 @@ impl ComponentPreview for Button {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
example_group_with_title(
|
example_group_with_title(
|
||||||
"Tinted",
|
"Tint Styles",
|
||||||
vec![
|
vec![
|
||||||
single_example(
|
single_example(
|
||||||
"Accent",
|
"Accent",
|
||||||
|
@ -519,7 +525,7 @@ impl ComponentPreview for Button {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
example_group_with_title(
|
example_group_with_title(
|
||||||
"States",
|
"Special States",
|
||||||
vec![
|
vec![
|
||||||
single_example(
|
single_example(
|
||||||
"Default",
|
"Default",
|
||||||
|
@ -540,7 +546,7 @@ impl ComponentPreview for Button {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
example_group_with_title(
|
example_group_with_title(
|
||||||
"With Icons",
|
"Buttons with Icons",
|
||||||
vec![
|
vec![
|
||||||
single_example(
|
single_example(
|
||||||
"Icon Start",
|
"Icon Start",
|
||||||
|
@ -563,16 +569,6 @@ impl ComponentPreview for Button {
|
||||||
.icon_color(Color::Accent)
|
.icon_color(Color::Accent)
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
),
|
),
|
||||||
single_example(
|
|
||||||
"Tinted Icons",
|
|
||||||
Button::new("tinted_icons", "Error")
|
|
||||||
.style(ButtonStyle::Tinted(TintColor::Error))
|
|
||||||
.color(Color::Error)
|
|
||||||
.icon_color(Color::Error)
|
|
||||||
.icon(IconName::Trash)
|
|
||||||
.icon_position(IconPosition::Start)
|
|
||||||
.into_any_element(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use crate::{prelude::*, Icon, IconName, IconSize, IconWithIndicator, Indicator};
|
use crate::{prelude::*, Icon, IconName, IconSize, IconWithIndicator, Indicator};
|
||||||
use gpui::Hsla;
|
use gpui::Hsla;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use gpui::{relative, CursorStyle, DefiniteLength, MouseButton};
|
use gpui::{relative, CursorStyle, DefiniteLength, MouseButton};
|
||||||
use gpui::{transparent_black, AnyElement, AnyView, ClickEvent, Hsla, Rems};
|
use gpui::{transparent_black, AnyElement, AnyView, ClickEvent, Hsla, Rems};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use gpui::{AnyView, DefiniteLength, Hsla};
|
use gpui::{AnyView, DefiniteLength, Hsla};
|
||||||
|
|
||||||
use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
|
use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
|
||||||
use crate::{prelude::*, ElevationIndex, Indicator, SelectableButton};
|
use crate::{prelude::*, ElevationIndex, Indicator, SelectableButton, TintColor};
|
||||||
use crate::{IconName, IconSize};
|
use crate::{IconName, IconSize};
|
||||||
|
|
||||||
use super::button_icon::ButtonIcon;
|
use super::button_icon::ButtonIcon;
|
||||||
|
@ -14,7 +13,8 @@ pub enum IconButtonShape {
|
||||||
Wide,
|
Wide,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement, IntoComponent)]
|
||||||
|
#[component(scope = "input")]
|
||||||
pub struct IconButton {
|
pub struct IconButton {
|
||||||
base: ButtonLike,
|
base: ButtonLike,
|
||||||
shape: IconButtonShape,
|
shape: IconButtonShape,
|
||||||
|
@ -200,3 +200,160 @@ impl RenderOnce for IconButton {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComponentPreview for IconButton {
|
||||||
|
fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
|
||||||
|
v_flex()
|
||||||
|
.gap_6()
|
||||||
|
.children(vec![
|
||||||
|
example_group_with_title(
|
||||||
|
"Icon Button Styles",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Default",
|
||||||
|
IconButton::new("default", IconName::Check)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Filled",
|
||||||
|
IconButton::new("filled", IconName::Check)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Subtle",
|
||||||
|
IconButton::new("subtle", IconName::Check)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Subtle)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Tinted",
|
||||||
|
IconButton::new("tinted", IconName::Check)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Transparent",
|
||||||
|
IconButton::new("transparent", IconName::Check)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Transparent)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Icon Button Shapes",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Square",
|
||||||
|
IconButton::new("square", IconName::Check)
|
||||||
|
.shape(IconButtonShape::Square)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Wide",
|
||||||
|
IconButton::new("wide", IconName::Check)
|
||||||
|
.shape(IconButtonShape::Wide)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Icon Button Sizes",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Small",
|
||||||
|
IconButton::new("small", IconName::Check)
|
||||||
|
.icon_size(IconSize::XSmall)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Small",
|
||||||
|
IconButton::new("small", IconName::Check)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Medium",
|
||||||
|
IconButton::new("medium", IconName::Check)
|
||||||
|
.icon_size(IconSize::Medium)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"XLarge",
|
||||||
|
IconButton::new("xlarge", IconName::Check)
|
||||||
|
.icon_size(IconSize::XLarge)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Special States",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Disabled",
|
||||||
|
IconButton::new("disabled", IconName::Check)
|
||||||
|
.disabled(true)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Selected",
|
||||||
|
IconButton::new("selected", IconName::Check)
|
||||||
|
.toggle_state(true)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"With Indicator",
|
||||||
|
IconButton::new("indicator", IconName::Check)
|
||||||
|
.indicator(Indicator::dot().color(Color::Success))
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Custom Colors",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Custom Icon Color",
|
||||||
|
IconButton::new("custom_color", IconName::Check)
|
||||||
|
.icon_color(Color::Accent)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"With Alpha",
|
||||||
|
IconButton::new("alpha", IconName::Check)
|
||||||
|
.alpha(0.5)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
])
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use gpui::{AnyView, ClickEvent};
|
use gpui::{AnyView, ClickEvent};
|
||||||
|
|
||||||
use crate::{prelude::*, ButtonLike, ButtonLikeRounding, ElevationIndex};
|
use crate::{prelude::*, ButtonLike, ButtonLikeRounding, ElevationIndex};
|
||||||
|
@ -16,7 +15,8 @@ pub enum ToggleButtonPosition {
|
||||||
Last,
|
Last,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement, IntoComponent)]
|
||||||
|
#[component(scope = "input")]
|
||||||
pub struct ToggleButton {
|
pub struct ToggleButton {
|
||||||
base: ButtonLike,
|
base: ButtonLike,
|
||||||
position_in_group: Option<ToggleButtonPosition>,
|
position_in_group: Option<ToggleButtonPosition>,
|
||||||
|
@ -142,3 +142,130 @@ impl RenderOnce for ToggleButton {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComponentPreview for ToggleButton {
|
||||||
|
fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
|
||||||
|
v_flex()
|
||||||
|
.gap_6()
|
||||||
|
.children(vec![
|
||||||
|
example_group_with_title(
|
||||||
|
"Button Styles",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Off",
|
||||||
|
ToggleButton::new("off", "Off")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"On",
|
||||||
|
ToggleButton::new("on", "On")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.toggle_state(true)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Off – Disabled",
|
||||||
|
ToggleButton::new("disabled_off", "Disabled Off")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.disabled(true)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"On – Disabled",
|
||||||
|
ToggleButton::new("disabled_on", "Disabled On")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.disabled(true)
|
||||||
|
.toggle_state(true)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Button Group",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Three Buttons",
|
||||||
|
h_flex()
|
||||||
|
.child(
|
||||||
|
ToggleButton::new("three_btn_first", "First")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.first()
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
ToggleButton::new("three_btn_middle", "Middle")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.middle()
|
||||||
|
.toggle_state(true)
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
ToggleButton::new("three_btn_last", "Last")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.last()
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Two Buttons",
|
||||||
|
h_flex()
|
||||||
|
.child(
|
||||||
|
ToggleButton::new("two_btn_first", "First")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.first()
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
ToggleButton::new("two_btn_last", "Last")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.last()
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Alternate Sizes",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"None",
|
||||||
|
ToggleButton::new("none", "None")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::None)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Compact",
|
||||||
|
ToggleButton::new("compact", "Compact")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::Compact)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Large",
|
||||||
|
ToggleButton::new("large", "Large")
|
||||||
|
.layer(ElevationIndex::Background)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::Large)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
])
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use crate::{
|
use crate::{
|
||||||
h_flex, prelude::*, utils::WithRemSize, v_flex, Icon, IconName, IconSize, KeyBinding, Label,
|
h_flex, prelude::*, utils::WithRemSize, v_flex, Icon, IconName, IconSize, KeyBinding, Label,
|
||||||
List, ListItem, ListSeparator, ListSubHeader,
|
List, ListItem, ListSeparator, ListSubHeader,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use gpui::{ClickEvent, CursorStyle};
|
use gpui::{ClickEvent, CursorStyle};
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use gpui::{Hsla, IntoElement};
|
use gpui::{Hsla, IntoElement};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use gpui::{ClickEvent, Corner, CursorStyle, Entity, MouseButton};
|
use gpui::{ClickEvent, Corner, CursorStyle, Entity, MouseButton};
|
||||||
|
|
||||||
use crate::{prelude::*, ContextMenu, PopoverMenu};
|
use crate::{prelude::*, ContextMenu, PopoverMenu};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::prelude::*;
|
use crate::{prelude::*, Avatar};
|
||||||
use gpui::{AnyElement, StyleRefinement};
|
use gpui::{AnyElement, StyleRefinement};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use smallvec::SmallVec;
|
||||||
///
|
///
|
||||||
/// Facepiles are used to display a group of people or things,
|
/// Facepiles are used to display a group of people or things,
|
||||||
/// such as a list of participants in a collaboration session.
|
/// such as a list of participants in a collaboration session.
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement, IntoComponent)]
|
||||||
pub struct Facepile {
|
pub struct Facepile {
|
||||||
base: Div,
|
base: Div,
|
||||||
faces: SmallVec<[AnyElement; 2]>,
|
faces: SmallVec<[AnyElement; 2]>,
|
||||||
|
@ -60,60 +60,57 @@ impl RenderOnce for Facepile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl ComponentPreview for Facepile {
|
impl ComponentPreview for Facepile {
|
||||||
// fn description() -> impl Into<Option<&'static str>> {
|
fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
|
||||||
// "A facepile is a collection of faces stacked horizontally–\
|
let faces: [&'static str; 6] = [
|
||||||
// always with the leftmost face on top and descending in z-index.\
|
"https://avatars.githubusercontent.com/u/326587?s=60&v=4",
|
||||||
// \n\nFacepiles are used to display a group of people or things,\
|
"https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
|
||||||
// such as a list of participants in a collaboration session."
|
"https://avatars.githubusercontent.com/u/1789?s=60&v=4",
|
||||||
// }
|
"https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
|
||||||
// fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
|
"https://avatars.githubusercontent.com/u/482957?s=60&v=4",
|
||||||
// let few_faces: [&'static str; 3] = [
|
"https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
|
||||||
// "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
|
];
|
||||||
// "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
|
|
||||||
// "https://avatars.githubusercontent.com/u/482957?s=60&v=4",
|
|
||||||
// ];
|
|
||||||
|
|
||||||
// let many_faces: [&'static str; 6] = [
|
v_flex()
|
||||||
// "https://avatars.githubusercontent.com/u/326587?s=60&v=4",
|
.gap_6()
|
||||||
// "https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
|
.children(vec![
|
||||||
// "https://avatars.githubusercontent.com/u/1789?s=60&v=4",
|
example_group_with_title(
|
||||||
// "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
|
"Facepile Examples",
|
||||||
// "https://avatars.githubusercontent.com/u/482957?s=60&v=4",
|
vec![
|
||||||
// "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
|
single_example(
|
||||||
// ];
|
"Default",
|
||||||
|
Facepile::new(
|
||||||
// vec![example_group_with_title(
|
faces
|
||||||
// "Examples",
|
.iter()
|
||||||
// vec![
|
.map(|&url| Avatar::new(url).into_any_element())
|
||||||
// single_example(
|
.collect(),
|
||||||
// "Few Faces",
|
)
|
||||||
// Facepile::new(
|
.into_any_element(),
|
||||||
// few_faces
|
),
|
||||||
// .iter()
|
single_example(
|
||||||
// .map(|&url| Avatar::new(url).into_any_element())
|
"Custom Size",
|
||||||
// .collect(),
|
Facepile::new(
|
||||||
// ),
|
faces
|
||||||
// ),
|
.iter()
|
||||||
// single_example(
|
.map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
|
||||||
// "Many Faces",
|
.collect(),
|
||||||
// Facepile::new(
|
)
|
||||||
// many_faces
|
.into_any_element(),
|
||||||
// .iter()
|
),
|
||||||
// .map(|&url| Avatar::new(url).into_any_element())
|
],
|
||||||
// .collect(),
|
),
|
||||||
// ),
|
example_group_with_title(
|
||||||
// ),
|
"Special Cases",
|
||||||
// single_example(
|
vec![
|
||||||
// "Custom Size",
|
single_example("Empty Facepile", Facepile::empty().into_any_element()),
|
||||||
// Facepile::new(
|
single_example(
|
||||||
// few_faces
|
"Single Face",
|
||||||
// .iter()
|
Facepile::new(vec![Avatar::new(faces[0]).into_any_element()].into())
|
||||||
// .map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
|
.into_any_element(),
|
||||||
// .collect(),
|
),
|
||||||
// ),
|
],
|
||||||
// ),
|
),
|
||||||
// ],
|
])
|
||||||
// )]
|
.into_any_element()
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
mod decorated_icon;
|
mod decorated_icon;
|
||||||
mod icon_decoration;
|
mod icon_decoration;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use gpui::{svg, App, IntoElement, Rems, RenderOnce, Size, Styled, Window};
|
use gpui::{svg, App, IntoElement, Rems, RenderOnce, Size, Styled, Window};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::{EnumIter, EnumString, IntoStaticStr};
|
use strum::{EnumIter, EnumString, IntoStaticStr};
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use std::{cmp::Ordering, ops::Range, rc::Rc};
|
use std::{cmp::Ordering, ops::Range, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use crate::{prelude::*, AnyIcon};
|
use crate::{prelude::*, AnyIcon};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use crate::PlatformStyle;
|
use crate::PlatformStyle;
|
||||||
use crate::{h_flex, prelude::*, Icon, IconName, IconSize};
|
use crate::{h_flex, prelude::*, Icon, IconName, IconSize};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use gpui::{FontWeight, HighlightStyle, StyledText};
|
use gpui::{FontWeight, HighlightStyle, StyledText};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use gpui::AnyElement;
|
use gpui::AnyElement;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{h_flex, prelude::*, Disclosure, Label};
|
use crate::{h_flex, prelude::*, Disclosure, Label};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use gpui::{px, AnyElement, AnyView, ClickEvent, MouseButton, MouseDownEvent, Pixels};
|
use gpui::{px, AnyElement, AnyView, ClickEvent, MouseButton, MouseDownEvent, Pixels};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{h_flex, Icon, IconName, IconSize, Label};
|
use crate::{h_flex, Icon, IconName, IconSize, Label};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
h_flex, v_flex, Clickable, Color, DynamicSpacing, Headline, HeadlineSize, IconButton,
|
h_flex, v_flex, Clickable, Color, DynamicSpacing, Headline, HeadlineSize, IconButton,
|
||||||
IconButtonShape, IconName, Label, LabelCommon, LabelSize,
|
IconButtonShape, IconName, Label, LabelCommon, LabelSize,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use gpui::ClickEvent;
|
use gpui::ClickEvent;
|
||||||
|
|
||||||
use crate::{prelude::*, IconButtonShape};
|
use crate::{prelude::*, IconButtonShape};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::v_flex;
|
use crate::v_flex;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use std::{any::Any, cell::Cell, fmt::Debug, ops::Range, rc::Rc, sync::Arc};
|
use std::{any::Any, cell::Cell, fmt::Debug, ops::Range, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use crate::{prelude::*, px, relative, IntoElement};
|
use crate::{prelude::*, px, relative, IntoElement};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use gpui::AnyElement;
|
use gpui::AnyElement;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use gpui::AnyElement;
|
use gpui::AnyElement;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use gpui::{div, Div};
|
use gpui::{div, Div};
|
||||||
|
|
||||||
use crate::StyledExt;
|
use crate::StyledExt;
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
// We allow missing docs for stories as the docs will more or less be
|
|
||||||
// "This is the ___ story", which is not very useful.
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
mod avatar;
|
|
||||||
mod button;
|
|
||||||
mod context_menu;
|
mod context_menu;
|
||||||
mod disclosure;
|
mod disclosure;
|
||||||
mod icon;
|
mod icon;
|
||||||
|
@ -15,8 +10,6 @@ mod tab;
|
||||||
mod tab_bar;
|
mod tab_bar;
|
||||||
mod toggle_button;
|
mod toggle_button;
|
||||||
|
|
||||||
pub use avatar::*;
|
|
||||||
pub use button::*;
|
|
||||||
pub use context_menu::*;
|
pub use context_menu::*;
|
||||||
pub use disclosure::*;
|
pub use disclosure::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
use gpui::Render;
|
|
||||||
use story::{Story, StoryItem, StorySection};
|
|
||||||
|
|
||||||
use crate::{prelude::*, AudioStatus, Availability, AvatarAvailabilityIndicator};
|
|
||||||
use crate::{Avatar, AvatarAudioStatusIndicator};
|
|
||||||
|
|
||||||
pub struct AvatarStory;
|
|
||||||
|
|
||||||
impl Render for AvatarStory {
|
|
||||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
||||||
Story::container()
|
|
||||||
.child(Story::title_for::<Avatar>())
|
|
||||||
.child(
|
|
||||||
StorySection::new()
|
|
||||||
.child(StoryItem::new(
|
|
||||||
"Default",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4"),
|
|
||||||
))
|
|
||||||
.child(StoryItem::new(
|
|
||||||
"Default",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4"),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
StorySection::new()
|
|
||||||
.child(StoryItem::new(
|
|
||||||
"With free availability indicator",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
|
||||||
.indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
|
|
||||||
))
|
|
||||||
.child(StoryItem::new(
|
|
||||||
"With busy availability indicator",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
|
||||||
.indicator(AvatarAvailabilityIndicator::new(Availability::Busy)),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
StorySection::new()
|
|
||||||
.child(StoryItem::new(
|
|
||||||
"With info border",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
|
||||||
.border_color(cx.theme().status().info_border),
|
|
||||||
))
|
|
||||||
.child(StoryItem::new(
|
|
||||||
"With error border",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
|
||||||
.border_color(cx.theme().status().error_border),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
StorySection::new()
|
|
||||||
.child(StoryItem::new(
|
|
||||||
"With muted audio indicator",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
|
||||||
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)),
|
|
||||||
))
|
|
||||||
.child(StoryItem::new(
|
|
||||||
"With deafened audio indicator",
|
|
||||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
|
||||||
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
use gpui::Render;
|
|
||||||
use story::Story;
|
|
||||||
|
|
||||||
use crate::{prelude::*, IconName};
|
|
||||||
use crate::{Button, ButtonStyle};
|
|
||||||
|
|
||||||
pub struct ButtonStory;
|
|
||||||
|
|
||||||
impl Render for ButtonStory {
|
|
||||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
|
||||||
Story::container()
|
|
||||||
.child(Story::title_for::<Button>())
|
|
||||||
.child(Story::label("Default"))
|
|
||||||
.child(Button::new("default_filled", "Click me"))
|
|
||||||
.child(Story::label("Selected"))
|
|
||||||
.child(Button::new("selected_filled", "Click me").toggle_state(true))
|
|
||||||
.child(Story::label("Selected with `selected_label`"))
|
|
||||||
.child(
|
|
||||||
Button::new("selected_label_filled", "Click me")
|
|
||||||
.toggle_state(true)
|
|
||||||
.selected_label("I have been selected"),
|
|
||||||
)
|
|
||||||
.child(Story::label("With `label_color`"))
|
|
||||||
.child(Button::new("filled_with_label_color", "Click me").color(Color::Created))
|
|
||||||
.child(Story::label("With `icon`"))
|
|
||||||
.child(Button::new("filled_with_icon", "Click me").icon(IconName::FileGit))
|
|
||||||
.child(Story::label("Selected with `icon`"))
|
|
||||||
.child(
|
|
||||||
Button::new("filled_and_selected_with_icon", "Click me")
|
|
||||||
.toggle_state(true)
|
|
||||||
.icon(IconName::FileGit),
|
|
||||||
)
|
|
||||||
.child(Story::label("Default (Subtle)"))
|
|
||||||
.child(Button::new("default_subtle", "Click me").style(ButtonStyle::Subtle))
|
|
||||||
.child(Story::label("Default (Transparent)"))
|
|
||||||
.child(Button::new("default_transparent", "Click me").style(ButtonStyle::Transparent))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use gpui::{AnyElement, IntoElement, Stateful};
|
use gpui::{AnyElement, IntoElement, Stateful};
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
use gpui::{AnyElement, ScrollHandle};
|
use gpui::{AnyElement, ScrollHandle};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use gpui::{Action, AnyElement, AnyView, AppContext as _, FocusHandle, IntoElement, Render};
|
use gpui::{Action, AnyElement, AnyView, AppContext as _, FocusHandle, IntoElement, Render};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
|
|
|
@ -237,40 +237,37 @@ impl Headline {
|
||||||
impl ComponentPreview for Headline {
|
impl ComponentPreview for Headline {
|
||||||
fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
|
fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_6()
|
.gap_1()
|
||||||
.children(vec![example_group_with_title(
|
.children(vec![
|
||||||
"Headline Sizes",
|
single_example(
|
||||||
vec![
|
"XLarge",
|
||||||
single_example(
|
Headline::new("XLarge Headline")
|
||||||
"XLarge",
|
.size(HeadlineSize::XLarge)
|
||||||
Headline::new("XLarge Headline")
|
.into_any_element(),
|
||||||
.size(HeadlineSize::XLarge)
|
),
|
||||||
.into_any_element(),
|
single_example(
|
||||||
),
|
"Large",
|
||||||
single_example(
|
Headline::new("Large Headline")
|
||||||
"Large",
|
.size(HeadlineSize::Large)
|
||||||
Headline::new("Large Headline")
|
.into_any_element(),
|
||||||
.size(HeadlineSize::Large)
|
),
|
||||||
.into_any_element(),
|
single_example(
|
||||||
),
|
"Medium (Default)",
|
||||||
single_example(
|
Headline::new("Medium Headline").into_any_element(),
|
||||||
"Medium (Default)",
|
),
|
||||||
Headline::new("Medium Headline").into_any_element(),
|
single_example(
|
||||||
),
|
"Small",
|
||||||
single_example(
|
Headline::new("Small Headline")
|
||||||
"Small",
|
.size(HeadlineSize::Small)
|
||||||
Headline::new("Small Headline")
|
.into_any_element(),
|
||||||
.size(HeadlineSize::Small)
|
),
|
||||||
.into_any_element(),
|
single_example(
|
||||||
),
|
"XSmall",
|
||||||
single_example(
|
Headline::new("XSmall Headline")
|
||||||
"XSmall",
|
.size(HeadlineSize::XSmall)
|
||||||
Headline::new("XSmall Headline")
|
.into_any_element(),
|
||||||
.size(HeadlineSize::XSmall)
|
),
|
||||||
.into_any_element(),
|
])
|
||||||
),
|
|
||||||
],
|
|
||||||
)])
|
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![deny(missing_docs)]
|
|
||||||
|
|
||||||
//! # UI – Zed UI Primitives & Components
|
//! # UI – Zed UI Primitives & Components
|
||||||
//!
|
//!
|
||||||
//! This crate provides a set of UI primitives and components that are used to build all of the elements in Zed's UI.
|
//! This crate provides a set of UI primitives and components that are used to build all of the elements in Zed's UI.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This won't be documented further as it is intended to be removed, or merged with the `time_format` crate.
|
// This won't be documented further as it is intended to be removed, or merged with the `time_format` crate.
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use chrono::{DateTime, Local, NaiveDateTime};
|
use chrono::{DateTime, Local, NaiveDateTime};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(missing_docs)]
|
|
||||||
|
|
||||||
use gpui::Pixels;
|
use gpui::Pixels;
|
||||||
|
|
||||||
pub struct SearchInputWidth;
|
pub struct SearchInputWidth;
|
||||||
|
|
|
@ -3,10 +3,10 @@ use gpui::{actions, hsla, AnyElement, App, Entity, EventEmitter, FocusHandle, Fo
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use theme::all_theme_colors;
|
use theme::all_theme_colors;
|
||||||
use ui::{
|
use ui::{
|
||||||
element_cell, prelude::*, string_cell, utils::calculate_contrast_ratio, AudioStatus,
|
element_cell, prelude::*, string_cell, utils::calculate_contrast_ratio, AudioStatus, Avatar,
|
||||||
Availability, Avatar, AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike,
|
AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike, Checkbox,
|
||||||
Checkbox, CheckboxWithLabel, ContentGroup, DecoratedIcon, ElevationIndex, Facepile,
|
CheckboxWithLabel, CollaboratorAvailability, ContentGroup, DecoratedIcon, ElevationIndex,
|
||||||
IconDecoration, Indicator, KeybindingHint, Switch, Table, TintColor, Tooltip,
|
Facepile, IconDecoration, Indicator, KeybindingHint, Switch, Table, TintColor, Tooltip,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Item, Workspace};
|
use crate::{Item, Workspace};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue