├── .github
└── workflows
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── CNAME
├── LICENSE
├── book.toml
├── src
├── README.md
├── SUMMARY.md
├── acknowledgements.md
├── additional-resources.md
├── architecture.md
├── concurrency.md
├── faq.md
├── first-steps.md
├── layout.md
├── renderers.md
├── resources
│ ├── counter-interface-annotated.svg
│ ├── counter-interface.svg
│ ├── diagrams.excalidraw
│ ├── gui-displays-user-interacts.svg
│ ├── logo.svg
│ ├── the-elm-architecture.svg
│ ├── the-gui-trinity-focused.svg
│ ├── the-gui-trinity.svg
│ └── the-runtime.svg
├── shells.md
├── styling.md
├── subscriptions.md
├── the-runtime.md
├── themes.md
└── widgets.md
└── theme
└── favicon.svg
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 | book:
8 | runs-on: ubuntu-20.04
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref }}
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: hecrj/setup-rust-action@v2
14 | - name: Install `mdbook`, `mdbook-iced`, and `wasm-bindgen-cli`
15 | run: cargo install mdbook mdbook-iced wasm-bindgen-cli
16 | - name: Add `wasm32-unknown-unknown` target to Rust toolchain
17 | run: rustup target add wasm32-unknown-unknown
18 | - name: Build book
19 | run: mdbook build
20 | - name: Copy CNAME
21 | run: cp CNAME ./book/.
22 | - name: Publish book
23 | uses: peaceiris/actions-gh-pages@v3
24 | with:
25 | github_token: ${{ secrets.GITHUB_TOKEN }}
26 | publish_dir: ./book
27 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 | on:
3 | push:
4 | branches-ignore:
5 | - gh-pages
6 | pull_request:
7 | jobs:
8 | book:
9 | runs-on: ubuntu-20.04
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Install mdBook
13 | uses: peaceiris/actions-mdbook@v1
14 | with:
15 | mdbook-version: 'latest'
16 | - name: Build book
17 | run: mdbook build
18 | - name: Test book
19 | run: mdbook test
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | book
2 | target
3 | .icebergs
4 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | book.iced.rs
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2024 Héctor Ramón Jiménez
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so,
8 | subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["Héctor Ramón Jiménez"]
3 | language = "en"
4 | multilingual = false
5 | src = "src"
6 | title = "iced — A Cross-Platform GUI Library for Rust"
7 |
8 | [output.html]
9 | mathjax-support = false
10 | git-repository-url = "https://github.com/iced-rs/book/tree/master"
11 | edit-url-template = "https://github.com/iced-rs/book/edit/master/{path}"
12 |
13 | [output.html.playground]
14 | runnable = false
15 |
16 | [preprocessor.iced]
17 | tag = "0.13.0"
18 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Introduction
6 | [iced] is a cross-platform GUI library for [Rust]. It is inspired by [Elm], a delightful functional language for building web applications.
7 |
8 | As a GUI library, iced helps you build *[graphical user interfaces]* for your Rust applications.
9 |
10 | iced is strongly focused on **simplicity** and **type-safety**. As a result, iced tries to provide simple building blocks that can be put together with strong typing to reduce the chance of **runtime errors**.
11 |
12 | This book will:
13 |
14 | - Introduce you to the fundamental ideas of iced.
15 | - Teach you how to build interactive applications with iced.
16 | - Emphasize principles to scale and grow iced applications.
17 |
18 | Before proceeding, you should have some basic familiarity with Rust. If you are new to Rust or feel lost at some point, I recommend you to read [the official Rust book].
19 |
20 | [iced]: https://iced.rs
21 | [Rust]: https://rust-lang.org
22 | [Elm]: https://elm-lang.org
23 | [graphical user interfaces]: https://en.wikipedia.org/wiki/Graphical_user_interface
24 | [the official Rust book]: https://doc.rust-lang.org/book/
25 |
--------------------------------------------------------------------------------
/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 | [Introduction](README.md)
3 |
4 | # Learning the Basics
5 | - [Architecture](architecture.md)
6 | - [First Steps](first-steps.md)
7 | - [The Runtime](the-runtime.md)
8 |
9 | - [More to come!]()
10 |
11 |
28 |
29 | # Appendix
30 | - [Additional Resources](additional-resources.md)
31 | - [Frequently Asked Questions](faq.md)
32 |
--------------------------------------------------------------------------------
/src/acknowledgements.md:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 |
--------------------------------------------------------------------------------
/src/additional-resources.md:
--------------------------------------------------------------------------------
1 | # Additional Resources
2 | Here are some further resources you can use to learn more about iced while I am still working on this book:
3 |
4 | > Keep in mind that some of these resources may be using an older version of iced. However, while the specifics
5 | > of the APIs used may change, the fundamental ideas of iced tend to be quite stable.
6 |
7 | - A [step-by-step video guide to building a simple text editor](https://www.youtube.com/watch?v=gcBJ7cPSALo)
8 | - The [official examples](https://github.com/iced-rs/iced/tree/master/examples)
9 | - The [API Reference](https://docs.iced.rs/iced/)
10 | - The [official list of awesome iced projects](https://github.com/iced-rs/awesome-iced)
11 | - The [unofficial guides](https://github.com/iced-rs/awesome-iced#Resources)
12 |
13 | We also have a very welcoming and active community! Feel free to ask any questions in [our Discord server] or [our Discourse forum].
14 |
15 | [](https://discord.gg/3xZJ65GAhd)
16 | [](https://discourse.iced.rs/)
17 |
18 | [our Discord server]: https://discord.gg/3xZJ65GAhd
19 | [our Discourse forum]: https://discourse.iced.rs/
20 |
--------------------------------------------------------------------------------
/src/architecture.md:
--------------------------------------------------------------------------------
1 | # Architecture
2 | Let's start from the basics! You are probably very familiar with graphical user interfaces already.
3 | You can find them on your phone, computer, and most interactive electronic devices. In fact, you are
4 | most likely reading this book using one!
5 |
6 | At their essence, graphical user interfaces are applications that __display__ some information graphically
7 | to a user. This user can then choose to __interact__ with the application—normally using some kind of device;
8 | like a keyboard, mouse, or touchscreen.
9 |
10 |
11 |
12 |
13 |
14 | The user interactions may cause the application to update and display new information as a result, which in turn
15 | may cause further user interactions, which in turn cause further updates... And so on. This quick feedback loop
16 | is what causes the feeling of _interactivity_.
17 |
18 | > Note: In this book, we will refer to graphical user interfaces as __GUIs__, __UIs__, __user interfaces__, or simply
19 | __interfaces__. Technically, not all interfaces are graphical nor user-oriented; but, given the context of this
20 | book, we will use all of these terms interchangeably.
21 |
22 | ## Dissecting an Interface
23 | Since we are interested in creating user interfaces, let's take a closer look at them. We will start with a very
24 | simple one: the classical counter interface. What is it made of?
25 |
26 |
27 |
28 |
29 |
30 | As we can clearly see, this interface has three visibly distinct elements: two buttons with a number in between.
31 | We refer to these visibly distinct elements of a user interface as __widgets__ or __elements__.
32 |
33 | Some __widgets__ may be interactive, like a button. In the counter interface, the buttons can be used to trigger
34 | certain __interactions__. Specifically, the button at the top can be used to increment the counter value, while the
35 | button at the bottom can be used to decrement it.
36 |
37 | We can also say that user interfaces are _stateful_—there is some __state__ that persists between interactions.
38 | The counter interface displays a number representing the counter value. The number displayed will change depending on
39 | the amount of times we press the buttons. Pressing the increment button once will result in a different displayed value
40 | compared to pressing it twice.
41 |
42 |
43 |
44 |
45 |
46 | ## The GUI Trinity
47 | Our quick dissection has successfully identified three foundational ideas in a user interface:
48 |
49 | - __Widgets__ — the distinct visual elements of an interface.
50 | - __Interactions__ — the actions that may be triggered by some widgets.
51 | - __State__ — the underlying condition or information of an interface.
52 |
53 | These ideas are connected to each other, forming another feedback loop!
54 |
55 | __Widgets__ produce __interactions__ when a user interacts with them. These __interactions__ then change the __state__
56 | of the interface. The changed __state__ propagates and dictates the new __widgets__ that must be displayed. These new
57 | __widgets__ may then produce new __interactions__, which can change the __state__ again... And so on.
58 |
59 |
60 |
61 |
62 |
63 | These ideas and their connections make up the fundamental architecture of a user interface. Therefore, creating a user
64 | interface must inevitably consist in defining these __widgets__, __interactions__, and __state__; as well as the connections
65 | between them.
66 |
67 | ## Different Ideas, Different Nature
68 | The three foundational ideas of an interface differ quite a bit when it comes to reusability.
69 |
70 | The state and the interactions of an interface are very specific to the application and its purpose. If I tell you that
71 | I have an interface with a numeric value and increment and decrement interactions, you will very easily
72 | guess I am talking about a counter interface.
73 |
74 | However, if I tell you I have an interface with two buttons and a number... It's quite trickier for you to guess the kind
75 | of interface I am talking about. It could be anything!
76 |
77 | This is because widgets are generally very generic and, therefore, more reusable. Most interfaces display a combination of
78 | familiar widgets—like buttons and numbers. In fact, users expect familiar widgets to always behave a certain way. If they
79 | don't behave properly, the interface will be unintuitive and have poor [user experience].
80 |
81 | While widgets are generally very reusable; the specific widget configuration dictated by the application state and its
82 | interactions is very application-specific. A button is generic; but a button that has a "+" label and causes a value
83 | increment when pressed is very specific.
84 |
85 | All of this means that, when we are creating a specific user interface, we don't want to focus on implementing every
86 | familiar widget and its behavior. Instead, we want to leverage widgets as reusable building blocks—independent of our
87 | application and provided by some library—while placing our focus on the application-specific parts of the fundamental
88 | architecture: state, interactions, how the interactions change the state, and how the state dictates the widgets.
89 |
90 |
91 |
92 |
93 |
94 | [user experience]: https://en.wikipedia.org/wiki/User_experience
95 |
96 | ## The Elm Architecture
97 | It turns out that the four application-specific parts of the architecture of an interface are also the four foundational
98 | ideas of [The Elm Architecture].
99 |
100 | > The Elm Architecture is a pattern for architecting interactive programs that emerges naturally in [Elm], a delightful
101 | > purely functional programming language for reliable web applications.
102 | >
103 | > Patterns and ideas that emerge in purely functional programming languages tend to work very well in Rust
104 | > because they leverage immutability and [referential transparency]—both very desirable properties that not only
105 | > make code easy to reason about, but also play nicely with the borrow checker.
106 | >
107 | > Furthermore, The Elm Architecture not only emerges naturally in Elm, but also when simply dissecting user
108 | > interfaces and formalizing their inner workings; like we just did in this chapter.
109 |
110 | The Elm Architecture uses a different—if not more precise—nomenclature for its fundamental parts:
111 |
112 | - __Model__ — the state of the application.
113 | - __Messages__ — the interactions of the application.
114 | - __Update logic__ — how the messages change the state.
115 | - __View logic__ — how the state dictates the widgets.
116 |
117 | These are different names, but they point to the same exact fundamental ideas we have already discovered and,
118 | therefore, can be used interchangeably.
119 |
120 |
121 |
122 |
123 |
124 |
125 | > Note: In iced, the names __state__ and __messages__ are used more often than __model__ and
126 | __interactions__, respectively.
127 |
128 |
129 | [The Elm Architecture]: https://guide.elm-lang.org/architecture/
130 | [Elm]: https://elm-lang.org/
131 | [referential transparency]: https://en.wikipedia.org/wiki/Referential_transparency
--------------------------------------------------------------------------------
/src/concurrency.md:
--------------------------------------------------------------------------------
1 | # Concurrency
2 |
--------------------------------------------------------------------------------
/src/faq.md:
--------------------------------------------------------------------------------
1 | # Frequently Asked Questions
2 |
3 | ## When Will the Book Be Finished?
4 | Soon™. Open source is a gift; so whenever I feel like it.
5 |
6 | ## How Do I Scale a Large Application?
7 | You split your application into multiple screens, and then use simple composition.
8 |
9 | [The Pocket Guide] has [a specific section that showcases this approach](https://docs.rs/iced/0.13.1/iced/#scaling-applications).
10 |
11 | ## How Can My Application Receive Updates From a Channel?
12 | You can use [`Task::run`] to generate messages from an asynchronous [`Stream`].
13 |
14 | Alternatively, if you control the creation of the channel; you can use [`Subscription::run`].
15 |
16 | [The Pocket Guide]: https://docs.rs/iced/0.13.1/iced/index.html#the-pocket-guide
17 | [`Task::run`]: https://docs.rs/iced/0.13.1/iced/task/struct.Task.html#method.run
18 | [`Subscription::run`]: https://docs.rs/iced/0.13.1/iced/struct.Subscription.html#method.run
19 | [`Stream`]: https://docs.rs/futures/latest/futures/stream/trait.Stream.html
20 |
21 | ## Does Iced Support Right-To-Left Text and/or CJK scripts?
22 | Not very well yet!
23 |
24 | You may be able to render some scripts using [`Text::shaping`] with [`Shaping::Advanced`],
25 | but text editing for these scripts is not yet supported; and neither are [Input Method Editors].
26 |
27 | These features are in the [`ROADMAP`], however!
28 |
29 | [`Text::shaping`]: https://docs.rs/iced/0.13.1/iced/widget/text/type.Text.html#method.shaping
30 | [`Shaping::Advanced`]: https://docs.rs/iced/0.13.1/iced/widget/text/enum.Shaping.html#variant.Advanced
31 | [Input Method Editors]: https://en.wikipedia.org/wiki/Input_method
32 | [`ROADMAP`]: https://whimsical.com/roadmap-iced-7vhq6R35Lp3TmYH4WeYwLM
33 |
34 | ## When Are the `view` and `subscription` Functions Called?
35 | After every batch of messages and `update` calls. But this is an implementation detail;
36 | and should never rely on this.
37 |
38 | Try to treat these functions as declarative, stateless functions.
39 |
40 | ## Does Iced Redraw All the Time?!
41 | Yes! iced currently redraws after every runtime event; including tiny mouse movements.
42 |
43 | There are plans to redraw less frequently by detecting widget state changes, but performance has not
44 | been a priority so far.
45 |
46 | The renderers do perform quite a lot of caching; so redrawing is quite cheap. As a result,
47 | this is rarely an issue for most use cases!
48 |
49 | ## I Am Getting A Panic Saying There Is No Reactor Running. What Is Going On?
50 | You are probably using `Task` to execute a `Future` that needs the `tokio` executor:
51 |
52 | ```text
53 | there is no reactor running, must be called from the context of a Tokio 1.x runtime
54 | ```
55 |
56 | You should be able to fix this issue by enabling [the `tokio` feature flag] in the `iced` crate:
57 |
58 | ```toml
59 | iced = { version = "0.13", features = ["tokio"] }
60 | ```
61 |
62 | [the `tokio` feature flag]: https://docs.rs/crate/iced/latest/features#tokio
63 |
--------------------------------------------------------------------------------
/src/first-steps.md:
--------------------------------------------------------------------------------
1 | # First Steps
2 | But enough with the theory. It's about time we start writing some code!
3 |
4 | iced embraces The Elm Architecture as the most natural approach for architecting interactive applications.
5 | Therefore, when using iced, we will be dealing with the four main ideas we introduced in the previous chapter:
6 | __state__, __messages__, __update logic__, and __view logic__.
7 |
8 | In the previous chapter, we dissected and studied the classical counter interface. Let's try to
9 | build it in Rust while leveraging The Elm Architecture.
10 |
11 |
12 |
13 |
14 |
15 | ## State
16 | Let's start with the __state__—the underlying data of the application.
17 |
18 | In Rust, given the ownership and borrowing rules, it is extremely important to think carefully about the data model
19 | of your application.
20 |
21 | > I encourage you to always start by pondering about the data of your application and
22 | its different states—not only those that are possible, but also those that must be impossible. Then try to leverage
23 | the type system as much as you can to _[Make Impossible States Impossible]_.
24 |
25 | For our counter interface, all we need is a counter value. Since we have both increment and decrement interactions,
26 | the number could potentially be negative. This means we need a signed integer.
27 |
28 | Also, we know some users are crazy and they may want to count a lot of things. Let's give them 64 bits to play with:
29 |
30 | ```rust
31 | struct Counter {
32 | value: i64,
33 | }
34 | ```
35 |
36 | If a crazy user counted 1000 things every second, it would take them ~300 million years to run out of numbers.
37 | Let's hope that's enough.
38 |
39 | [Make Impossible States Impossible]: https://www.youtube.com/watch?v=IcgmSRJHu_8
40 |
41 | ## Messages
42 | Next, we need to define our __messages__—the interactions of the application.
43 |
44 | Our counter interface has two interactions: __increment__ and __decrement__. Technically, we could use a simple boolean to
45 | encode these interactions: `true` for increment and `false` for decrement, for instance.
46 |
47 | But... we can do better in Rust! Interactions are mutually exclusive—when we have an interaction, what we really have is one
48 | value of a possible set of values. It turns out that Rust has the perfect data type for modeling this kind of idea: the _enum_.
49 |
50 | Thus, we can define our messages like this:
51 |
52 | ```rust
53 | enum Message {
54 | Increment,
55 | Decrement,
56 | }
57 | ```
58 |
59 | Simple enough! This also sets us up for the long-term. If we ever wanted to add additional interactions to our application—like a
60 | `Reset` interaction, for instance—we could just introduce additional variants to this type. Enums are very powerful and convenient.
61 |
62 | ## Update Logic
63 | Now, it's time for our __update logic__—how messages change the state of the application.
64 |
65 | Basically, we need to write some logic that given any message can update any state of the application accordingly. The simplest
66 | and most idiomatic way to express this logic in Rust is by defining a method named `update` in our application state.
67 |
68 | For our counter interface, we only need to properly increment or decrement the `value` of our `Counter` struct based on the `Message`
69 | we just defined:
70 |
71 | ```rust,ignore
72 | impl Counter {
73 | fn update(&mut self, message: Message) {
74 | match message {
75 | Message::Increment => {
76 | self.value += 1;
77 | }
78 | Message::Decrement => {
79 | self.value -= 1;
80 | }
81 | }
82 | }
83 | }
84 | ```
85 |
86 | Great! Now we are ready to process user interactions. For instance, imagine we initialized our counter like this:
87 |
88 | ```rust,ignore
89 | let mut counter = Counter { value: 0 };
90 | ```
91 |
92 | And let's say we wanted to simulate a user playing with our interface for a bit—pressing the increment button twice
93 | and then the decrement button once. We could easily compute the final state of our counter with our __update logic__:
94 |
95 | ```rust,ignore
96 | counter.update(Message::Increment);
97 | counter.update(Message::Increment);
98 | counter.update(Message::Decrement);
99 | ```
100 |
101 | This would cause our `Counter` to end up with a `value` of `1`:
102 |
103 | ```rust,ignore
104 | assert_eq!(counter.value, 1);
105 | ```
106 |
107 | In fact, we have just written a simple test for our application logic:
108 |
109 | ```rust,ignore
110 | #[test]
111 | fn it_counts_properly() {
112 | let mut counter = Counter { value: 0 };
113 |
114 | counter.update(Message::Increment);
115 | counter.update(Message::Increment);
116 | counter.update(Message::Decrement);
117 |
118 | assert_eq!(counter.value, 1);
119 | }
120 | ```
121 |
122 | Notice how easy this was to write! So far, we are just leveraging very simple Rust concepts. No dependencies in sight!
123 | You may even be wondering... "Where is the GUI code?!"
124 |
125 | This is one of the main advantages of The Elm Architecture. As we discovered in the previous chapter, widgets are the
126 | only fundamental idea of an interface that is reusable in nature. All the parts we have defined so far are application-specific
127 | and, therefore, do not need to know about the UI library at all!
128 |
129 | The Elm Architecture properly embraces the different nature of each part of a user interface—decoupling __state__,
130 | __messages__, and __update logic__ from __widgets__ and __view logic__.
131 |
132 | ## View Logic
133 | Finally, the only part left for us to define is our __view logic__—how state dictates the widgets of the application.
134 |
135 | Here is where the magic happens! In view logic, we bring together the state of the application and its possible interactions
136 | to produce a visual representation of the user interface that must be displayed to the user.
137 |
138 |
139 |
140 |
141 |
142 | As we have already learned, this visual representation is made of widgets—the visibly distinct units of an interface. Most
143 | widgets are not application-specific and they can be abstracted and packaged into reusable libraries. These libraries are
144 | normally called _widget toolkits_, _GUI frameworks_, or simply _GUI libraries_.
145 |
146 | And this is where __iced__ comes in—finally! iced is a cross-platform GUI library for Rust. It packages a fair collection of
147 | ready-to-use widgets; buttons and numbers included. Exactly what we need for our counter.
148 |
149 | ### The Buttons
150 | Our counter interface has two __buttons__. Let's see how we can define them using iced.
151 |
152 | In iced, widgets are independent values. The same way you can have an integer in a variable, you can have a widget as well.
153 | These values are normally created using a _helper function_ from the `widget` module.
154 |
155 | For our buttons, we can use the `button` helper:
156 |
157 | ```rust,ignore
158 | use iced::widget::button;
159 |
160 | let increment = button("+");
161 | let decrement = button("-");
162 | ```
163 |
164 | That's quite simple, isn't it? For now, we have just defined a couple of variables for our buttons.
165 |
166 | As we can see, widget helpers may take arguments for configuring parts of the widgets to our liking.
167 | In this case, the `button` function takes a single argument used to describe the contents of the button.
168 |
169 |
170 | ### The Number
171 | We have our buttons sitting nicely in our `increment` and `decrement` variables. How about we do the same
172 | for our counter value?
173 |
174 | While iced does not really have a `number` widget, it does have a more generic `text` widget that can be used
175 | to display any kind of text—numbers included:
176 |
177 | ```rust,ignore
178 | use iced::widget::text;
179 |
180 | let counter = text(15);
181 | ```
182 |
183 | Sweet! Like `button`, `text` also takes an argument used to describe its contents. Since we are just getting started, let's
184 | simply hardcode `15` for now.
185 |
186 | ### The Layout
187 | Alright! We have our two buttons in `increment` and `decrement`, and our counter value in `counter`. That should be everything, right?
188 |
189 | Not so fast! The widgets in our counter interface are displayed in a specific __order__. Given our three widgets, there is a total of
190 | __six__ different ways to order them. However, the order we want is: `increment`, `counter`, and `decrement`.
191 |
192 | A very simple way of describing this order is to create a list with our widgets:
193 |
194 | ```rust,ignore
195 | let interface = vec![increment, counter, decrement];
196 | ```
197 |
198 | But we are still missing something! It's not only the order that is specific, our interface also has a specific visual __layout__.
199 |
200 | The widgets are positioned on top of each other, but they could very well be positioned from left to right instead. There is nothing
201 | in our description so far that talks about the __layout__ of our widgets.
202 |
203 | In iced, layout is described using... well, more widgets! That's right. Not all widgets produce visual results directly; some may simply
204 | manage the position of existing widgets. And since widgets are just values, they can be nested and composed nicely.
205 |
206 | The kind of vertical layout that we need for our counter can be achieved with the `column` widget:
207 |
208 | ```rust,ignore
209 | use iced::widget::column;
210 |
211 | let interface = column![increment, counter, decrement];
212 | ```
213 |
214 | This is very similar to our previous snippet. iced provides a `column!` macro for creating a `column` out of some widgets in a particular
215 | __order__—analogous to `vec!`.
216 |
217 | ### The Interactions
218 | At this point, we have in our `interface` variable a `column` representing our counter interface. But if we actually tried to run it,
219 | we would quickly find out that something is wrong.
220 |
221 | Our buttons would be completely disabled. Of course! We have not defined any __interactions__ for them. Notice that we have yet
222 | to use our `Message` enum in our view logic. How is our user interface supposed to produce __messages__ if we don't specify
223 | them? Let's do that now.
224 |
225 | In iced, every widget has a specific type that enables further configuration using simple builder methods. The `button`
226 | helper returns an instance of [the `Button` type], which has an `on_press` method we can use to define the message it must
227 | __produce__ when a user presses the button:
228 |
229 | ```rust,ignore
230 | use iced::widget::button;
231 |
232 | let increment = button("+").on_press(Message::Increment);
233 | let decrement = button("-").on_press(Message::Decrement);
234 | ```
235 |
236 | Awesome! Our interactions are wired up. But there is still a small detail left. A button can be pressed multiple times. Therefore,
237 | the same button may need to produce multiple instances of the same `Message`. As a result, we need our `Message` type to be cloneable.
238 |
239 | We can easily _derive_ the `Clone` trait—as well as `Debug` and `Copy` for good measure:
240 |
241 | ```rust
242 | #[derive(Debug, Clone, Copy)]
243 | enum Message {
244 | Increment,
245 | Decrement,
246 | }
247 | ```
248 |
249 | In The Elm Architecture, messages represent __events__ that have occurred—made of pure data. As a consequence, it should always be easy
250 | to derive `Debug` and `Clone` for our `Message` type.
251 |
252 |
253 | [the `Button` type]: https://docs.rs/iced/0.12.1/iced/widget/struct.Button.html
254 |
255 | ### The View
256 | We are almost there! There is only one thing left to do: connecting our application __state__ to the view logic.
257 |
258 | Let's bring together all the view logic we have written so far:
259 |
260 | ```rust,ignore
261 | use iced::widget::{button, column, text};
262 |
263 | // The buttons
264 | let increment = button("+").on_press(Message::Increment);
265 | let decrement = button("-").on_press(Message::Decrement);
266 |
267 | // The number
268 | let counter = text(15);
269 |
270 | // The layout
271 | let interface = column![increment, counter, decrement];
272 | ```
273 |
274 | If we ran this view logic, we would now be able to press the buttons. However, nothing would happen as a result. The
275 | counter would be stuck—always showing the number `15`. Our interface is completely stateless!
276 |
277 | Obviously, the issue here is that our `counter` variable contains a text widget with a hardcoded `15`. Instead, what
278 | we want is to actually display the `value` field of our `Counter` state. This way, when a button is pressed and
279 | our update logic is triggered, the text widget will display the new `value`.
280 |
281 | We can easily do this by running our view logic in a method of our `Counter`—just like we did with our update logic:
282 |
283 | ```rust,ignore
284 | use iced::widget::{button, column, text};
285 |
286 | impl Counter {
287 | fn view(&self) {
288 | // The buttons
289 | let increment = button("+").on_press(Message::Increment);
290 | let decrement = button("-").on_press(Message::Decrement);
291 |
292 | // The number
293 | let counter = text(self.value);
294 |
295 | // The layout
296 | let interface = column![increment, counter, decrement];
297 | }
298 | }
299 | ```
300 |
301 | Our `counter` variable now will always have a `text` widget with the current `value` of our `Counter`. Great!
302 |
303 | However, and as you may have noticed, this `view` method is completely useless—it constructs an
304 | `interface`, but then... It does nothing with it and throws it away!
305 |
306 | > In iced, constructing and configuring widgets has no side effects. There is no "global context" you need to
307 | worry about in your view code.
308 |
309 | Instead of throwing the `interface` away, we need to return it. Remember, the purpose of our __view logic__ is
310 | to dictate the widgets of our user interface; and the content of the `interface` variable is precisely the
311 | description of the interface we want:
312 |
313 | ```rust,ignore
314 | use iced::widget::{button, column, text, Column};
315 |
316 | impl Counter {
317 | fn view(&self) -> Column {
318 | // The buttons
319 | let increment = button("+").on_press(Message::Increment);
320 | let decrement = button("-").on_press(Message::Decrement);
321 |
322 | // The number
323 | let counter = text(self.value);
324 |
325 | // The layout
326 | let interface = column![increment, counter, decrement];
327 |
328 | interface
329 | }
330 | }
331 | ```
332 |
333 | Tada! Notice how the `view` method needs a return type now. The returned type is `Column` because the `column!` macro produces
334 | a widget of this type—just like `button` produces a widget of the `Button` type.
335 |
336 | You may also have noticed that this `Column` type has a generic type parameter. This type parameter simply specifies the type
337 | of messages the widget may produce. In this case, it takes our `Message` because the `increment` and `decrement` buttons inside
338 | the column produce messages of this type.
339 |
340 | > iced has a strong focus on type safety—leveraging the type system and compile-time guarantees to minimize runtime errors
341 | as much as possible.
342 |
343 | And well... That's it! Our view logic is done! But wait... It's a bit verbose right now. Since it's such a simple interface,
344 | let's just inline everything:
345 |
346 |
347 |
348 |
349 |
350 | ```rust,ignore
351 | use iced::widget::{button, column, text, Column};
352 |
353 | impl Counter {
354 | fn view(&self) -> Column {
355 | column![
356 | button("+").on_press(Message::Increment),
357 | text(self.value),
358 | button("-").on_press(Message::Decrement),
359 | ]
360 | }
361 | }
362 | ```
363 |
364 | That's much more concise. It even resembles the actual interface! Since creating widgets just yields values with no
365 | side effects; we can move things around in our view logic without worrying about breaking other stuff. No spooky
366 | action at a distance!
367 |
368 | And that's all there is to our counter interface. I am sure you can't wait to __run__ it. Shall we?
369 |
--------------------------------------------------------------------------------
/src/layout.md:
--------------------------------------------------------------------------------
1 | # Layout
2 |
--------------------------------------------------------------------------------
/src/renderers.md:
--------------------------------------------------------------------------------
1 | # Renderers
2 |
--------------------------------------------------------------------------------
/src/resources/counter-interface-annotated.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/resources/counter-interface.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/resources/gui-displays-user-interacts.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/resources/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/resources/the-elm-architecture.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/resources/the-gui-trinity-focused.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/resources/the-gui-trinity.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/resources/the-runtime.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/shells.md:
--------------------------------------------------------------------------------
1 | # Shells
2 |
--------------------------------------------------------------------------------
/src/styling.md:
--------------------------------------------------------------------------------
1 | # Styling
2 |
--------------------------------------------------------------------------------
/src/subscriptions.md:
--------------------------------------------------------------------------------
1 | # Subscriptions
2 |
--------------------------------------------------------------------------------
/src/the-runtime.md:
--------------------------------------------------------------------------------
1 | # The Runtime
2 | In the previous chapter we built the classical counter interface using iced and The Elm Architecture. We focused on each
3 | fundamental part—one at a time: __state__, __messages__, __update logic__, and __view logic__.
4 |
5 | But now what? Yes, we have all the fundamental parts of a user interface—as we learned during
6 | [our dissection](architecture.md)—but it is unclear how we are supposed to bring it to life.
7 |
8 | It seems we are missing _something_ that can put all the parts together and _run_ them in unison. _Something_ that
9 | creates and runs the fundamental loop of a user interface—displaying widgets to a user and reacting to any interactions.
10 |
11 | This _something_ is called the __runtime__. You can think of it as the environment where the feedback loop of a user
12 | interface takes place. The runtime is in charge of every part of the loop: initializing the __state__,
13 | producing __messages__, executing the __update logic__, and running our __view logic__.
14 |
15 |
16 |
17 |
18 |
19 | > Another way to picture the runtime is by imagining a huge engine with four fundamental parts missing. Our job is
20 | > to fill in these parts—and then the engine can run!
21 |
22 | ## A Magical Runtime
23 | Let's try to get a better understanding of the lifetime of an interface by exploring the internals of a basic (although very magical!) runtime.
24 |
25 | In fact, we have actually started writing a runtime already! When [we implemented the update logic of our counter](first-steps.md#update-logic),
26 | we wrote a very small test that simulated a user:
27 |
28 | ```rust,ignore
29 | #[test]
30 | fn it_counts_properly() {
31 | let mut counter = Counter { value: 0 };
32 |
33 | counter.update(Message::Increment);
34 | counter.update(Message::Increment);
35 | counter.update(Message::Decrement);
36 |
37 | assert_eq!(counter.value, 1);
38 | }
39 | ```
40 |
41 | This is technically a very bare-bones runtime. It initializes the __state__, produces some __interactions__,
42 | and executes the __update logic__.
43 |
44 | Of course, the interactions are made up, it is very short-lived, and there is no __view logic__
45 | involved—far from what we actually want. Still, it's a great start! Let's try to extend it, step by step.
46 |
47 | ### Initializing the State
48 | Our small runtime is already initializing the application state properly:
49 |
50 | ```rust,ignore
51 | // Initialize the state
52 | let mut counter = Counter { value: 0 };
53 | ```
54 |
55 | However, we can avoid hardcoding the initial state by leveraging the `Default` trait. Let's just derive it:
56 |
57 | ```rust
58 | #[derive(Default)]
59 | struct Counter {
60 | value: i64
61 | }
62 | ```
63 |
64 | And then, we simply use `Counter::default` in our runtime:
65 |
66 | ```rust,ignore
67 | // Initialize the state
68 | let mut counter = Counter::default();
69 | ```
70 |
71 | The difference may be subtle, but we are separating concerns—we keep the initial state of our application close to
72 | the state definition and separated from the runtime. This way, we may eventually be able to make our runtime work with
73 | _any_ application!
74 |
75 | ### Displaying the Interface
76 | Alright! We have our __state__ initialized. What's next? Well, before a user can __interact__ with our interface, we
77 | need to __display__ it to them.
78 |
79 | That's easy! We just need to open a window in whatever OS the user is running, initialize a proper graphics backend,
80 | and then render the widgets returned by our __view logic__—properly laid out, of course!
81 |
82 | What? You have no clue of how to do that? Don't worry, I have this magical function: `display`. It takes a reference to
83 | any interface and displays it to the user. It totally works!
84 |
85 | ```rust,ignore
86 | use magic::display;
87 |
88 | # // Initialize the state
89 | # let mut counter = Counter::default();
90 | #
91 | // Run our view logic to obtain our interface
92 | let interface = counter.view();
93 |
94 | // Display the interface to the user
95 | display(&interface);
96 | ```
97 |
98 | See? Easy! Jokes aside, the purpose of this chapter is not for us to learn graphics programming; but for us
99 | to get a better understanding of how a runtime works. A little bit of magic doesn't hurt!
100 |
101 | ### Gathering the Interactions
102 | The user is seeing our interface and is now interacting with it. We need to pay very good attention to all
103 | the interactions and produce all the relevant __messages__ that our widgets specify.
104 |
105 | How? With some more magic, of course! I just found this `interact` function inside of my top hat—it takes an
106 | interface and produces the __messages__ that correspond to the latest interactions of the user.
107 |
108 | ```rust,ignore
109 | use magic::{display, interact};
110 |
111 | # // Initialize the state
112 | # let mut counter = Counter::default();
113 | #
114 | # // Run our view logic to obtain our interface
115 | # let interface = counter.view();
116 | #
117 | # // Display the interface to the user
118 | # display(&interface);
119 | #
120 | // Process the user interactions and obtain our messages
121 | let messages = interact(&interface);
122 | ```
123 |
124 | Great! `interact` returns a list of __messages__ for us—ready to be iterated.
125 |
126 |
127 | ### Reacting to the Interactions
128 | At this point, we have gathered the user interactions and we have turned them into a bunch of __messages__. In order to
129 | react properly to the user, we need to update our __state__ accordingly for each message.
130 |
131 | Luckily, there are no more magic tricks involved in this step—we can just use our __update logic__:
132 |
133 | ```rust,ignore
134 | # use magic::{display, interact};
135 | #
136 | # // Initialize the state
137 | # let mut counter = Counter::default();
138 | #
139 | # // Run our view logic to obtain our interface
140 | # let interface = counter.view();
141 | #
142 | # // Display the interface to the user
143 | # display(&interface);
144 | #
145 | # // Process the user interactions and obtain our messages
146 | # let messages = interact(&interface);
147 | #
148 | // Update our state by processing each message
149 | for message in messages {
150 | counter.update(message);
151 | }
152 | ```
153 |
154 | That should keep our state completely up-to-date with the latest user interactions.
155 |
156 | ### Looping Around
157 | Okay! Our state has been updated to reflect the user interactions. Now, we need to display the resulting interface again
158 | to the user. And after that, we must process any further interactions... And then, update our state once more.
159 | And then... Do it all over once again!
160 |
161 | This is a loop! And no, loops aren't very magical—not when we write Rust, at least:
162 |
163 | ```rust,ignore
164 | use magic::{display, interact};
165 |
166 | // Initialize the state
167 | let mut counter = Counter::default();
168 |
169 | // Be interactive. All the time!
170 | loop {
171 | // Run our view logic to obtain our interface
172 | let interface = counter.view();
173 |
174 | // Display the interface to the user
175 | display(&interface);
176 |
177 | // Process the user interactions and obtain our messages
178 | let messages = interact(&interface);
179 |
180 | // Update our state by processing each message
181 | for message in messages {
182 | counter.update(message);
183 | }
184 | }
185 | ```
186 |
187 | Congratulations! We just wrote a perfectly functional runtime—magical properties aside. We can clearly understand here how
188 | each fundamental part of The Elm Architecture fits in the lifetime of an application.
189 |
190 | Specifically,
191 |
192 | - __state__ is initialized once,
193 | - __view logic__ runs once at startup and then after every batch of interactions,
194 | - and __update logic__ runs for every interaction that created a __message__.
195 |
196 | ## The Ice Wizard
197 | "That's cool and all", you say, "but I am not a wizard and I still have no clue of how to run the counter interface I wrote.
198 | I have things to count!"
199 |
200 | Fair enough! iced implements a very similar runtime to the one we just built. It comes bundled with
201 | its own magic[^magic]—so you don't need to worry about learning the dark arts yourself.
202 |
203 | If we want to run our `Counter`, all we have to do is call [`run`]:
204 |
205 | ```rust,ignore,iced(height=100px)
206 | # use iced::widget::{button, column, text, Column};
207 | #
208 | pub fn main() -> iced::Result {
209 | iced::run("A cool counter", Counter::update, Counter::view)
210 | }
211 | #
212 | # #[derive(Default)]
213 | # struct Counter {
214 | # value: i64,
215 | # }
216 | #
217 | # #[derive(Debug, Clone, Copy)]
218 | # enum Message {
219 | # Increment,
220 | # Decrement,
221 | # }
222 | #
223 | # impl Counter {
224 | # fn update(&mut self, message: Message) {
225 | # match message {
226 | # Message::Increment => {
227 | # self.value += 1;
228 | # }
229 | # Message::Decrement => {
230 | # self.value -= 1;
231 | # }
232 | # }
233 | # }
234 | #
235 | # fn view(&self) -> Column {
236 | # column![
237 | # button("+").on_press(Message::Increment),
238 | # text(self.value),
239 | # button("-").on_press(Message::Decrement),
240 | # ]
241 | # }
242 | # }
243 | ```
244 |
245 | We just give our application a _cool_ title and then provide the __update logic__ and __view logic__ to
246 | the __runtime__—which then figures out the rest!
247 |
248 | The runtime is capable of inferring the types for the __state__ and __messages__ out of the type signatures of
249 | our __update logic__ and __view logic__. The __state__ is initialized leveraging `Default`, as we described earlier.
250 |
251 | Notice also that [`run`] can fail and, therefore, it returns an [`iced::Result`]. If all we are doing is run the
252 | application, we can return this result directly in `main`.
253 |
254 | And that should be it! Have fun counting things for 300 million years—at least!
255 |
256 | [`run`]: https://docs.iced.rs/iced/fn.run.html
257 | [`iced::Result`]: https://docs.iced.rs/iced/type.Result.html
258 | [^magic]: Mainly [`winit`], [`softbuffer`], [`wgpu`], [`tiny-skia`], and [`cosmic-text`].
259 |
260 | [`winit`]: https://github.com/rust-windowing/winit
261 | [`softbuffer`]: https://github.com/rust-windowing/softbuffer
262 | [`wgpu`]: https://github.com/gfx-rs/wgpu
263 | [`tiny-skia`]: https://github.com/RazrFalcon/tiny-skia
264 | [`cosmic-text`]: https://github.com/pop-os/cosmic-text
265 |
266 | > #### Note From the Author
267 | > You reached the end of the book, for now!
268 | >
269 | > I think it should already serve as a quick introduction to the basics of the library.
270 | > There is a lot more to unravel—but hopefully you are now at a point where you can start
271 | > playing around, having fun, and experimenting further.
272 | >
273 | > The book is far from finished—there are a lot more topics I want to cover here, namely:
274 | >
275 | > - Layout
276 | > - Styling
277 | > - Concurrency
278 | > - Scaling Applications
279 | > - Extending the Runtime
280 | > - And More!
281 | >
282 | > Until I get to write them, check out the [Additional Resources](additional-resources.md)
283 | > chapter if you want to explore and learn further.
284 | >
285 | > I hope that you enjoyed the read so far. Stay tuned!
286 | >
287 | > — Héctor
288 |
--------------------------------------------------------------------------------
/src/themes.md:
--------------------------------------------------------------------------------
1 | # Themes
2 |
--------------------------------------------------------------------------------
/src/widgets.md:
--------------------------------------------------------------------------------
1 | # Widgets
2 |
--------------------------------------------------------------------------------
/theme/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------