hello-world.md

  1# Hello World
  2
  3Let's work through the prototypical "Build a todo app" example to showcase how we might build a simple component from scratch.
  4
  5## Setup
  6
  7We'll create a headline, a list of todo items, and a form to add new items.
  8
  9~~~rust
 10struct TodoList<V: 'static> {
 11    headline: SharedString,
 12    items: Vec<TodoItem>,
 13    submit_form: ClickHandler<V>
 14}
 15
 16struct TodoItem<V: 'static> {
 17    text: SharedString,
 18    completed: bool,
 19    delete: ClickHandler<V>
 20}
 21
 22impl<V: 'static> TodoList<V> {
 23    pub fn new(
 24        // Here we impl Into<SharedString>
 25        headline: impl Into<SharedString>,
 26        items: Vec<TodoItem>,
 27        submit_form: ClickHandler<V>
 28    ) -> Self {
 29        Self {
 30            // and here we call .into() so we can simply pass a string
 31            // when creating the headline. This pattern is used throughout
 32            // outr components
 33            headline: headline.into(),
 34            items: Vec::new(),
 35            submit_form,
 36        }
 37    }
 38}
 39~~~
 40
 41All of this is relatively straightforward.
 42
 43We 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.
 44
 45When 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`.
 46
 47~~~rust
 48use gpui::hsla
 49
 50impl<V: 'static> TodoList<V> {
 51    // ...
 52    fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
 53        div().size_4().bg(hsla(50.0/360.0, 1.0, 0.5, 1.0))
 54    }
 55}
 56~~~
 57
 58Every component needs a render method, and it should return `impl Element<V>`. This basic component will render a 16x16px yellow square on the screen.
 59
 60A couple of questions might come to mind:
 61
 62**Why is `size_4()` 16px, not 4px?**
 63
 64gpui'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).
 65
 66I'll quote from the Tailwind [Core Concepts](https://tailwindcss.com/docs/utility-first) docs here:
 67
 68> Now I know what you’re thinking, “this is an atrocity, what a horrible mess!”
 69> and you’re right, it’s kind of ugly. In fact it’s just about impossible to
 70> think this is a good idea the first time you see it —
 71> you have to actually try it.
 72
 73As you start using the Tailwind-style conventions you will be surprised how quick it makes it to build out UIs.
 74
 75**Why `50.0/360.0` in `hsla()`?**
 76
 77gpui [gpui::Hsla] use `0.0-1.0` for all its values, but it is common for tools to use `0-360` for hue.
 78
 79This may change in the future, but this is a little trick that let's you use familiar looking values.
 80
 81## Building out the container
 82
 83Let's grab our [theme::colors::ThemeColors] from the theme and start building out a basic container.
 84
 85We can access the current theme's colors like this:
 86
 87~~~rust
 88impl<V: 'static> TodoList<V> {
 89    // ...
 90    fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
 91        let color = cx.theme().colors()
 92
 93        div().size_4().hsla(50.0/360.0, 1.0, 0.5, 1.0)
 94    }
 95}
 96~~~
 97
 98Now we have access to the complete set of colors defined in the theme.
 99
100~~~rust
101use gpui::hsla
102
103impl<V: 'static> TodoList<V> {
104    // ...
105    fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
106        let color = cx.theme().colors()
107
108        div().size_4().bg(color.surface)
109    }
110}
111~~~
112
113Let's finish up some basic styles for the container then move on to adding the other elements.
114
115~~~rust
116use gpui::hsla
117
118impl<V: 'static> TodoList<V> {
119    // ...
120    fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
121        let color = cx.theme().colors()
122
123        div()
124            // Flex properties
125            .flex()
126            .flex_col()             // Stack elements vertically
127            .gap_2()                // Add 8px of space between elements
128            // Size properties
129            .w_96()                 // Set width to 384px
130            .p_4()                  // Add 16px of padding on all sides
131            // Color properties
132            .bg(color.surface)      // Set background color
133            .text_color(color.text) // Set text color
134            // Border properties
135            .rounded_md()           // Add 4px of border radius
136            .border_1()             // Add a 1px border
137            .border_color(color.border)
138            .child(
139                "Hello, world!"
140            )
141    }
142}
143~~~
144
145### Headline
146
147TODO
148
149### List of todo items
150
151TODO
152
153### Input
154
155TODO
156
157
158### End result
159
160TODO