summaryrefslogtreecommitdiffstats
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs561
1 files changed, 423 insertions, 138 deletions
diff --git a/src/lib.rs b/src/lib.rs
index bc3fe6ab..022f8d6e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,170 +1,451 @@
-//! Iced is a cross-platform GUI library focused on simplicity and type-safety.
+//! iced is a cross-platform GUI library focused on simplicity and type-safety.
//! Inspired by [Elm].
//!
-//! # Features
-//! * Simple, easy-to-use, batteries-included API
-//! * Type-safe, reactive programming model
-//! * [Cross-platform support] (Windows, macOS, Linux, and the Web)
-//! * Responsive layout
-//! * Built-in widgets (including [text inputs], [scrollables], and more!)
-//! * Custom widget support (create your own!)
-//! * [Debug overlay with performance metrics]
-//! * First-class support for async actions (use futures!)
-//! * [Modular ecosystem] split into reusable parts:
-//! * A [renderer-agnostic native runtime] enabling integration with existing
-//! systems
-//! * A [built-in renderer] supporting Vulkan, Metal, DX11, and DX12
-//! * A [windowing shell]
-//! * A [web runtime] leveraging the DOM
-//!
-//! Check out the [repository] and the [examples] for more details!
-//!
-//! [Cross-platform support]: https://github.com/iced-rs/iced/blob/master/docs/images/todos_desktop.jpg?raw=true
-//! [text inputs]: https://iced.rs/examples/text_input.mp4
-//! [scrollables]: https://iced.rs/examples/scrollable.mp4
-//! [Debug overlay with performance metrics]: https://iced.rs/examples/debug.mp4
-//! [Modular ecosystem]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md
-//! [renderer-agnostic native runtime]: https://github.com/iced-rs/iced/tree/0.12/runtime
-//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
-//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.12/wgpu
-//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.12/winit
-//! [`dodrio`]: https://github.com/fitzgen/dodrio
-//! [web runtime]: https://github.com/iced-rs/iced_web
-//! [examples]: https://github.com/iced-rs/iced/tree/0.12/examples
-//! [repository]: https://github.com/iced-rs/iced
-//!
-//! # Overview
-//! Inspired by [The Elm Architecture], Iced expects you to split user
-//! interfaces into four different concepts:
-//!
-//! * __State__ — the state of your application
-//! * __Messages__ — user interactions or meaningful events that you care
-//! about
-//! * __View logic__ — a way to display your __state__ as widgets that
-//! may produce __messages__ on user interaction
-//! * __Update logic__ — a way to react to __messages__ and update your
-//! __state__
-//!
-//! We can build something to see how this works! Let's say we want a simple
-//! counter that can be incremented and decremented using two buttons.
-//!
-//! We start by modelling the __state__ of our application:
+//! [Elm]: https://elm-lang.org/
+//!
+//! # 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 book]: https://book.iced.rs
+//!
+//! # The Pocket Guide
+//! Start by calling [`run`]:
+//!
+//! ```rust,no_run
+//! pub fn main() -> iced::Result {
+//! iced::run("A cool counter", update, view)
+//! }
+//! # fn update(state: &mut (), message: ()) {}
+//! # fn view(state: &()) -> iced::Element<()> { iced::widget::text("").into() }
//! ```
-//! #[derive(Default)]
-//! struct Counter {
-//! // The counter value
-//! value: i32,
+//!
+//! Define an `update` function to __change__ your state:
+//!
+//! ```rust
+//! fn update(counter: &mut u64, message: Message) {
+//! match message {
+//! Message::Increment => *counter += 1,
+//! }
//! }
+//! # #[derive(Clone)]
+//! # enum Message { Increment }
//! ```
//!
-//! Next, we need to define the possible user interactions of our counter:
-//! the button presses. These interactions are our __messages__:
+//! Define a `view` function to __display__ your state:
+//!
+//! ```rust
+//! use iced::widget::{button, text};
+//! use iced::Element;
//!
+//! fn view(counter: &u64) -> Element<Message> {
+//! button(text(counter)).on_press(Message::Increment).into()
+//! }
+//! # #[derive(Clone)]
+//! # enum Message { Increment }
//! ```
-//! #[derive(Debug, Clone, Copy)]
-//! pub enum Message {
+//!
+//! And create a `Message` enum to __connect__ `view` and `update` together:
+//!
+//! ```rust
+//! #[derive(Debug, Clone)]
+//! enum Message {
//! Increment,
-//! Decrement,
//! }
//! ```
//!
-//! Now, let's show the actual counter by putting it all together in our
-//! __view logic__:
+//! ## Custom State
+//! You can define your own struct for your state:
//!
+//! ```rust
+//! #[derive(Default)]
+//! struct Counter {
+//! value: u64,
+//! }
//! ```
-//! # struct Counter {
-//! # // The counter value
-//! # value: i32,
-//! # }
-//! #
-//! # #[derive(Debug, Clone, Copy)]
-//! # pub enum Message {
-//! # Increment,
-//! # Decrement,
-//! # }
-//! #
-//! use iced::widget::{button, column, text, Column};
//!
-//! impl Counter {
-//! pub fn view(&self) -> Column<Message> {
-//! // We use a column: a simple vertical layout
-//! column![
-//! // The increment button. We tell it to produce an
-//! // `Increment` message when pressed
-//! button("+").on_press(Message::Increment),
+//! But you have to change `update` and `view` accordingly:
+//!
+//! ```rust
+//! # struct Counter { value: u64 }
+//! # #[derive(Clone)]
+//! # enum Message { Increment }
+//! # use iced::widget::{button, text};
+//! # use iced::Element;
+//! 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:
//!
-//! // We show the value of the counter here
-//! text(self.value).size(50),
+//! ```rust
+//! # struct Counter { value: u64 }
+//! # #[derive(Clone)]
+//! # enum Message { Increment }
+//! use iced::widget::{button, column, text};
+//! use iced::Element;
//!
-//! // The decrement button. We tell it to produce a
-//! // `Decrement` message when pressed
-//! button("-").on_press(Message::Decrement),
+//! 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]:
+//!
+//! ```rust
+//! # struct State;
+//! # enum Message {}
+//! 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()
//! }
//! ```
//!
-//! Finally, we need to be able to react to any produced __messages__ and change
-//! our __state__ accordingly in our __update logic__:
+//! 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.
+//!
+//! [rows]: widget::Row
+//! [columns]: widget::Column
+//! [containers]: widget::Container
+//! [Spacing]: widget::Column::spacing
+//!
+//! ## 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:
+//!
+//! ```rust
+//! # struct State;
+//! # enum Message {}
+//! use iced::widget::container;
+//! use iced::Element;
//!
+//! fn view(state: &State) -> Element<Message> {
+//! container("I am 300px tall!").height(300).into()
+//! }
//! ```
-//! # struct Counter {
-//! # // The counter value
-//! # value: i32,
-//! # }
-//! #
-//! # #[derive(Debug, Clone, Copy)]
-//! # pub enum Message {
-//! # Increment,
-//! # Decrement,
-//! # }
-//! impl Counter {
-//! // ...
//!
-//! pub fn update(&mut self, message: Message) {
-//! match message {
-//! Message::Increment => {
-//! self.value += 1;
-//! }
-//! Message::Decrement => {
-//! self.value -= 1;
+//! ## 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`]:
+//!
+//! ```rust,no_run
+//! # #[derive(Default)]
+//! # struct State;
+//! 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
+//! }
+//! # fn update(state: &mut State, message: ()) {}
+//! # fn view(state: &State) -> iced::Element<()> { iced::widget::text("").into() }
+//! ```
+//!
+//! 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](Theme::custom).
+//!
+//! ## 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:
+//!
+//! ```rust
+//! # struct State;
+//! # enum Message {}
+//! 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:
+//!
+//! ```rust
+//! # struct State;
+//! # #[derive(Clone)]
+//! # enum Message {}
+//! 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`].
+//!
+//! [`Status`]: widget::button::Status
+//! [`palette`]: Theme::palette
+//! [`extended_palette`]: Theme::extended_palette
+//! [`container::rounded_box`]: widget::container::rounded_box
+//! [`button::primary`]: widget::button::primary
+//! [`text::danger`]: widget::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:
+//!
+//! ```rust
+//! # #[derive(Clone)]
+//! # struct Weather;
+//! 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 {
+//! // ...
+//! # unimplemented!()
+//! }
//! ```
//!
-//! And that's everything! We just wrote a whole user interface. Let's run it:
+//! 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](window#functions), [focusing a widget](widget::focus_next), or
+//! [querying its visible bounds](widget::container::visible_bounds).
+//!
+//! Like futures and streams, tasks expose [a monadic interface](Task::then)—but they can also be
+//! [mapped](Task::map), [chained](Task::chain), [batched](Task::batch), [canceled](Task::abortable),
+//! 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:
//!
-//! ```no_run
+//! ```rust,no_run
//! # #[derive(Default)]
-//! # struct Counter;
-//! # impl Counter {
-//! # fn update(&mut self, _message: ()) {}
-//! # fn view(&self) -> iced::Element<()> { unimplemented!() }
-//! # }
-//! #
-//! fn main() -> iced::Result {
-//! iced::run("A cool counter", Counter::update, Counter::view)
+//! # struct State;
+//! 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))
+//! }
+//! # fn update(state: &mut State, message: Message) {}
+//! # fn view(state: &State) -> iced::Element<Message> { iced::widget::text("").into() }
//! ```
//!
-//! Iced will automatically:
+//! A [`Subscription`] is [a _declarative_ builder of streams](Subscription#the-lifetime-of-a-subscription)
+//! 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.
//!
-//! 1. Take the result of our __view logic__ and layout its widgets.
-//! 1. Process events from our system and produce __messages__ for our
-//! __update logic__.
-//! 1. Draw the resulting user interface.
+//! 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`].
//!
-//! # Usage
-//! Use [`run`] or the [`application`] builder.
+//! [`run_with_id`]: Subscription::run_with_id
//!
-//! [Elm]: https://elm-lang.org/
-//! [The Elm Architecture]: https://guide.elm-lang.org/architecture/
-//! [`application`]: application()
+//! ## 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:
+//!
+//! ```rust
+//! # mod contacts {
+//! # use iced::{Element, Task};
+//! # pub struct Contacts;
+//! # impl Contacts {
+//! # pub fn update(&mut self, message: Message) -> Task<Message> { unimplemented!() }
+//! # pub fn view(&self) -> Element<Message> { unimplemented!() }
+//! # }
+//! # #[derive(Debug)]
+//! # pub enum Message {}
+//! # }
+//! # mod conversation {
+//! # use iced::{Element, Task};
+//! # pub struct Conversation;
+//! # impl Conversation {
+//! # pub fn update(&mut self, message: Message) -> Task<Message> { unimplemented!() }
+//! # pub fn view(&self) -> Element<Message> { unimplemented!() }
+//! # }
+//! # #[derive(Debug)]
+//! # pub enum Message {}
+//! # }
+//! 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 {
+//! contacts.update(message).map(Message::Contacts)
+//! } 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),
+//! }
+//! }
+//! ```
+//!
+//! Functor methods like [`Task::map`], [`Element::map`], and [`Subscription::map`] make this
+//! approach seamless.
#![doc(
- html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
+ html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/bdf0430880f5c29443f5f0a0ae4895866dfef4c6/docs/logo.svg"
)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
@@ -175,6 +456,7 @@ use iced_winit::core;
use iced_winit::runtime;
pub use iced_futures::futures;
+pub use iced_futures::stream;
#[cfg(feature = "highlighter")]
pub use iced_highlighter as highlighter;
@@ -195,13 +477,25 @@ pub use crate::core::alignment;
pub use crate::core::border;
pub use crate::core::color;
pub use crate::core::gradient;
+pub use crate::core::padding;
pub use crate::core::theme;
pub use crate::core::{
Alignment, Background, Border, Color, ContentFit, Degrees, Gradient,
Length, Padding, Pixels, Point, Radians, Rectangle, Rotation, Shadow, Size,
Theme, Transformation, Vector,
};
-pub use crate::runtime::{exit, Task};
+pub use crate::runtime::exit;
+pub use iced_futures::Subscription;
+
+pub use alignment::Horizontal::{Left, Right};
+pub use alignment::Vertical::{Bottom, Top};
+pub use Alignment::Center;
+pub use Length::{Fill, FillPortion, Shrink};
+
+pub mod task {
+ //! Create runtime tasks.
+ pub use crate::runtime::task::{Handle, Task};
+}
pub mod clipboard {
//! Access the clipboard.
@@ -255,13 +549,6 @@ pub mod mouse {
};
}
-pub mod subscription {
- //! Listen to external events in your application.
- pub use iced_futures::subscription::{
- channel, run, run_with_id, unfold, Subscription,
- };
-}
-
#[cfg(feature = "system")]
pub mod system {
//! Retrieve system information.
@@ -314,7 +601,7 @@ pub use executor::Executor;
pub use font::Font;
pub use renderer::Renderer;
pub use settings::Settings;
-pub use subscription::Subscription;
+pub use task::Task;
#[doc(inline)]
pub use application::application;
@@ -339,8 +626,6 @@ pub type Result = std::result::Result<(), Error>;
///
/// This is equivalent to chaining [`application()`] with [`Application::run`].
///
-/// [`program`]: program()
-///
/// # Example
/// ```no_run
/// use iced::widget::{button, column, text, Column};