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 [TODO: someone who actually knows please explain why we use SharedString].
 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()               // 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