Expand description
iced is a cross-platform GUI library focused on simplicity and type-safety. Inspired by Elm.
§Disclaimer
iced is experimental software. If you expect the documentation to hold your hand as you learn the ropes, you are in for a frustrating experience.
The library leverages Rust to its full extent: ownership, borrowing, lifetimes, futures, streams, first-class functions, trait bounds, closures, and more. This documentation is not meant to teach you any of these. Far from it, it will assume you have mastered all of them.
Furthermore—just like Rust—iced is very unforgiving. It will not let you easily cut corners. The type signatures alone can be used to learn how to use most of the library. Everything is connected.
Therefore, iced is easy to learn for advanced Rust programmers; but plenty of patient beginners have learned it and had a good time with it. Since it leverages a lot of what Rust has to offer in a type-safe way, it can be a great way to discover Rust itself.
If you don’t like the sound of that, you expect to be spoonfed, or you feel frustrated and struggle to use the library; then I recommend you to wait patiently until the book is finished.
§The Pocket Guide
Start by calling run
:
pub fn main() -> iced::Result {
iced::run("A cool counter", update, view)
}
Define an update
function to change your state:
fn update(counter: &mut u64, message: Message) {
match message {
Message::Increment => *counter += 1,
}
}
Define a view
function to display your state:
use iced::widget::{button, text};
use iced::Element;
fn view(counter: &u64) -> Element<Message> {
button(text(counter)).on_press(Message::Increment).into()
}
And create a Message
enum to connect view
and update
together:
#[derive(Debug, Clone)]
enum Message {
Increment,
}
§Custom State
You can define your own struct for your state:
#[derive(Default)]
struct Counter {
value: u64,
}
But you have to change update
and view
accordingly:
fn update(counter: &mut Counter, message: Message) {
match message {
Message::Increment => counter.value += 1,
}
}
fn view(counter: &Counter) -> Element<Message> {
button(text(counter.value)).on_press(Message::Increment).into()
}
§Widgets and Elements
The view
function must return an Element
. An Element
is just a generic widget
.
The widget
module contains a bunch of functions to help you build
and use widgets.
Widgets are configured using the builder pattern:
use iced::widget::{button, column, text};
use iced::Element;
fn view(counter: &Counter) -> Element<Message> {
column![
text(counter.value).size(20),
button("Increment").on_press(Message::Increment),
]
.spacing(10)
.into()
}
A widget can be turned into an Element
by calling into
.
Widgets and elements are generic over the message type they produce. The
Element
returned by view
must have the same Message
type as
your update
.
§Layout
There is no unified layout system in iced. Instead, each widget implements its own layout strategy.
Building your layout will often consist in using a combination of rows, columns, and containers:
use iced::widget::{column, container, row};
use iced::{Fill, Element};
fn view(state: &State) -> Element<Message> {
container(
column![
"Top",
row!["Left", "Right"].spacing(10),
"Bottom"
]
.spacing(10)
)
.padding(10)
.center_x(Fill)
.center_y(Fill)
.into()
}
Rows and columns lay out their children horizontally and vertically, respectively. Spacing can be easily added between elements.
Containers position or align a single widget inside their bounds.
§Sizing
The width and height of widgets can generally be defined using a Length
.
Fill
will make the widget take all the available space in a given axis.Shrink
will make the widget use its intrinsic size.
Most widgets use a Shrink
sizing strategy by default, but will inherit
a Fill
strategy from their children.
A fixed numeric Length
in Pixels
can also be used:
use iced::widget::container;
use iced::Element;
fn view(state: &State) -> Element<Message> {
container("I am 300px tall!").height(300).into()
}
§Theming
The default Theme
of an application can be changed by defining a theme
function and leveraging the Application
builder, instead of directly
calling run
:
use iced::Theme;
pub fn main() -> iced::Result {
iced::application("A cool application", update, view)
.theme(theme)
.run()
}
fn theme(state: &State) -> Theme {
Theme::TokyoNight
}
The theme
function takes the current state of the application, allowing the
returned Theme
to be completely dynamic—just like view
.
There are a bunch of built-in Theme
variants at your disposal, but you can
also create your own.
§Styling
As with layout, iced does not have a unified styling system. However, all of the built-in widgets follow the same styling approach.
The appearance of a widget can be changed by calling its style
method:
use iced::widget::container;
use iced::Element;
fn view(state: &State) -> Element<Message> {
container("I am a rounded box!").style(container::rounded_box).into()
}
The style
method of a widget takes a closure that, given the current active
Theme
, returns the widget style:
use iced::widget::button;
use iced::{Element, Theme};
fn view(state: &State) -> Element<Message> {
button("I am a styled button!").style(|theme: &Theme, status| {
let palette = theme.extended_palette();
match status {
button::Status::Active => {
button::Style::default()
.with_background(palette.success.strong.color)
}
_ => button::primary(theme, status),
}
})
.into()
}
Widgets that can be in multiple different states will also provide the closure
with some Status
, allowing you to use a different style for each state.
You can extract the Palette
colors of a Theme
with the palette
or
extended_palette
methods.
Most widgets provide styling functions for your convenience in their respective modules;
like container::rounded_box
, button::primary
, or text::danger
.
§Concurrent Tasks
The update
function can optionally return a Task
.
A Task
can be leveraged to perform asynchronous work, like running a
future or a stream:
use iced::Task;
struct State {
weather: Option<Weather>,
}
enum Message {
FetchWeather,
WeatherFetched(Weather),
}
fn update(state: &mut State, message: Message) -> Task<Message> {
match message {
Message::FetchWeather => Task::perform(
fetch_weather(),
Message::WeatherFetched,
),
Message::WeatherFetched(weather) => {
state.weather = Some(weather);
Task::none()
}
}
}
async fn fetch_weather() -> Weather {
// ...
}
Tasks can also be used to interact with the iced runtime. Some modules expose functions that create tasks for different purposes—like changing window settings, focusing a widget, or querying its visible bounds.
Like futures and streams, tasks expose a monadic interface—but they can also be mapped, chained, batched, canceled, and more.
§Passive Subscriptions
Applications can subscribe to passive sources of data—like time ticks or runtime events.
You will need to define a subscription
function and use the Application
builder:
use iced::window;
use iced::{Size, Subscription};
#[derive(Debug)]
enum Message {
WindowResized(Size),
}
pub fn main() -> iced::Result {
iced::application("A cool application", update, view)
.subscription(subscription)
.run()
}
fn subscription(state: &State) -> Subscription<Message> {
window::resize_events().map(|(_id, size)| Message::WindowResized(size))
}
A Subscription
is a declarative builder of streams
that are not allowed to end on their own. Only the subscription
function
dictates the active subscriptions—just like view
fully dictates the
visible widgets of your user interface, at every moment.
As with tasks, some modules expose convenient functions that build a Subscription
for you—like
time::every
which can be used to listen to time, or keyboard::on_key_press
which will notify you
of any key presses. But you can also create your own with Subscription::run
and run_with_id
.
§Scaling Applications
The update
, view
, and Message
triplet composes very nicely.
A common pattern is to leverage this composability to split an application into different screens:
use contacts::Contacts;
use conversation::Conversation;
use iced::{Element, Task};
struct State {
screen: Screen,
}
enum Screen {
Contacts(Contacts),
Conversation(Conversation),
}
enum Message {
Contacts(contacts::Message),
Conversation(conversation::Message)
}
fn update(state: &mut State, message: Message) -> Task<Message> {
match message {
Message::Contacts(message) => {
if let Screen::Contacts(contacts) = &mut state.screen {
let action = contacts.update(message);
match action {
contacts::Action::None => Task::none(),
contacts::Action::Run(task) => task.map(Message::Contacts),
contacts::Action::Chat(contact) => {
let (conversation, task) = Conversation::new(contact);
state.screen = Screen::Conversation(conversation);
task.map(Message::Conversation)
}
}
} else {
Task::none()
}
}
Message::Conversation(message) => {
if let Screen::Conversation(conversation) = &mut state.screen {
conversation.update(message).map(Message::Conversation)
} else {
Task::none()
}
}
}
}
fn view(state: &State) -> Element<Message> {
match &state.screen {
Screen::Contacts(contacts) => contacts.view().map(Message::Contacts),
Screen::Conversation(conversation) => conversation.view().map(Message::Conversation),
}
}
The update
method of a screen can return an Action
enum that can be leveraged by the parent to
execute a task or transition to a completely different screen altogether. The variants of Action
can
have associated data. For instance, in the example above, the Conversation
screen is created when
Contacts::update
returns an Action::Chat
with the selected contact.
Effectively, this approach lets you “tell a story” to connect different screens together in a type safe way.
Furthermore, functor methods like Task::map
, Element::map
, and Subscription::map
make composition
seamless.
Re-exports§
pub use application::Application;
pub use daemon::Daemon;
pub use settings::Settings;
pub use iced_futures::futures;
pub use iced_highlighter as highlighter;
highlighter
pub use alignment::Horizontal::Left;
pub use alignment::Horizontal::Right;
pub use alignment::Vertical::Bottom;
pub use alignment::Vertical::Top;
pub use Alignment::Center;
pub use Length::Fill;
pub use Length::FillPortion;
pub use Length::Shrink;
Modules§
- advanced
advanced
Leverage advanced concepts like custom widgets. - Align and position widgets.
- Create and run iced applications step by step.
- Draw lines around containers.
- Access the clipboard.
- Create and run daemons that run in the background.
- Handle events of a user interface.
- Choose your preferred executor to power your application.
- Load and use fonts.
- Colors that transition progressively.
- Listen and react to keyboard events.
- Listen and react to mouse events.
- Display interactive elements on top of other widgets.
- Space stuff around the perimeter.
- Configure your application.
- Create asynchronous streams of data.
- system
system
Retrieve system information. - Create runtime tasks.
- Use the built-in theme and styles.
- Listen and react to time.
- Listen and react to touch events.
- Use the built-in widgets or create your own.
- Configure the window of your application in native platforms.
Macros§
- Creates a
Color
with shorter and cleaner syntax.
Structs§
- A border.
- A color in the
sRGB
color space. - Degrees
- A font.
- An amount of space to pad for each side of a box
- An amount of logical pixels.
- A 2D point.
- Radians
- An axis-aligned rectangle.
- A shadow.
- An amount of space in 2 dimensions.
- A request to listen to external events.
- A set of concurrent actions to be performed by the iced runtime.
- A 2D transformation matrix.
- A 2D vector.
Enums§
- Alignment on the axis of a container.
- The background of some element.
- The strategy used to fit the contents of a widget to its bounding box.
- An error that occurred while running an application.
- A user interface event.
- A fill which transitions colors progressively along a direction, either linearly, radially (TBD), or conically (TBD).
- The strategy used to fill space in a specific dimension.
- The strategy used to rotate the content.
- A built-in theme.
Traits§
- A type that can run futures.
Functions§
- Creates an iced
Application
given its title, update, and view logic. - Creates an iced
Daemon
given its title, update, and view logic. - Creates a
Task
that exits the iced runtime. - Runs a basic iced application with default
Settings
given its title, update, and view logic.
Type Aliases§
- A generic widget.
- The default graphics renderer for
iced
. - The result of running an iced program.