Expand description
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.
Work in Progress
This crate is still a work in progress. The initial primitives and components are built for getting all the UI on the screen, much of the state and functionality is mocked or hard codeded, and performance has not been a focus.
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.
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 [TODO: someone who actually knows please explain why we use SharedString].
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
.
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. Here is an example of the list of sizes for width
: Width - TailwindCSS Docs.
I’ll quote from the Tailwind Core Concepts 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 it’s 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 [theme2::colors::ThemeColors] from the theme and start building out a basic container.
We can access the current theme’s colors like this:
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.
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.
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() // Add a 1px border
.border_color(color.border)
.child(
"Hello, world!"
)
}
}
Headline
TODO
List of todo items
TODO
Input
TODO
End result
TODO
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.
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, thecolor()
function is called on theLabel
instance:Label::new("Hello, world!").color(LabelColor::Default);
-
Setting Line Height Style: To set the line height style, the
line_height_style()
function is utilized:Label::new("Hello, world!").line_height_style(LineHeightStyle::TextLabel);
-
Adding a Strikethrough: To add a strikethrough in a
Label
, theset_strikethrough()
function is used: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
Documentation priorities:
These are the priorities to get documented, in a rough stack rank order:
- label
- button
- icon_button
- icon
- list
- avatar
- panel
- modal
- palette
- input
- facepile
- player
- stacks
- context menu
- input
- textarea/multiline input (not built - not an editor)
- indicator
- public actor
- keybinding
- tab
- toast
Re-exports
pub use prelude::*;
Modules
Structs
- Checkbox
- An iterator over the variants of Icon
Enums
- Provides the flexibility to use either a standard button or an icon button in a given context.
- Elevation
- A slot utility that provides a way to to pass either an icon or an image to a component.
- Whether the entry is toggleable, and if so, whether it is currently toggled.
Traits
- Extends
Styled
with Zed specific styling methods.
Functions
- Horizontally stacks elements.
- Vertically stacks elements.