diff --git a/ECOSYSTEM.md b/ECOSYSTEM.md deleted file mode 100644 index da3066d8be..0000000000 --- a/ECOSYSTEM.md +++ /dev/null @@ -1,91 +0,0 @@ -# Ecosystem -This document describes the Iced ecosystem and explains how the different crates relate to each other. - -## Overview -Iced is meant to be used by 2 different types of users: - -- __End-users__. They should be able to: - - get started quickly, - - have many widgets available, - - keep things simple, - - and build applications that are __maintainable__ and __performant__. -- __GUI toolkit developers / Ecosystem contributors__. They should be able to: - - build new kinds of widgets, - - implement custom runtimes, - - integrate existing runtimes in their own system (like game engines), - - and create their own custom renderers. - -Iced consists of different crates which offer different layers of abstractions for our users. This modular architecture helps us keep implementation details hidden and decoupled, which should allow us to rewrite or change strategies in the future. - -

- The Iced Ecosystem -

- -## The foundations -There are a bunch of concepts that permeate the whole ecosystem. These concepts are considered __the foundations__, and they are provided by three different crates: - -- [`iced_core`] contains many lightweight, reusable primitives (e.g. `Point`, `Rectangle`, `Color`). -- [`iced_futures`] implements the concurrent concepts of [The Elm Architecture] on top of the [`futures`] ecosystem. -- [`iced_style`] defines the default styling capabilities of built-in widgets. - -

- The foundations -

- -## The native target -The native side of the ecosystem is split into two different groups: __renderers__ and __shells__. - -

- The native target -

- -### Renderers -The widgets of a _graphical_ user interface produce some primitives that eventually need to be drawn on screen. __Renderers__ take care of this task, potentially leveraging GPU acceleration. - -Currently, there are two different official renderers: - -- [`iced_wgpu`] is powered by [`wgpu`] and supports Vulkan, DirectX 12, and Metal. -- [`tiny-skia`] is used as a fallback software renderer when `wgpu` is not supported. - -Additionally, the [`iced_graphics`] subcrate contains a bunch of backend-agnostic types that can be leveraged to build renderers. Both of the renderers rely on the graphical foundations provided by this crate. - -### Shells -The widgets of a graphical user _interface_ are interactive. __Shells__ gather and process user interactions in an event loop. - -Normally, a shell will be responsible of creating a window and managing the lifecycle of a user interface, implementing a runtime of [The Elm Architecture]. - -As of now, there is one official shell: [`iced_winit`] implements a shell runtime on top of [`winit`]. - -## The web target -The Web platform provides all the abstractions necessary to draw widgets and gather users interactions. - -Therefore, unlike the native path, the web side of the ecosystem does not need to split renderers and shells. Instead, [`iced_web`] leverages [`dodrio`] to both render widgets and implement a proper runtime. - -## Iced -Finally, [`iced`] unifies everything into a simple abstraction to create cross-platform applications: - -- On native, it uses __[shells](#shells)__ and __[renderers](#renderers)__. -- On the web, it uses [`iced_web`]. - -

- Iced -

- -[`iced_core`]: core -[`iced_futures`]: futures -[`iced_style`]: style -[`iced_native`]: native -[`iced_web`]: https://github.com/iced-rs/iced_web -[`iced_graphics`]: graphics -[`iced_wgpu`]: wgpu -[`iced_glow`]: glow -[`iced_winit`]: winit -[`iced_glutin`]: glutin -[`iced`]: .. -[`futures`]: https://github.com/rust-lang/futures-rs -[`glow`]: https://github.com/grovesNL/glow -[`wgpu`]: https://github.com/gfx-rs/wgpu -[`winit`]: https://github.com/rust-windowing/winit -[`glutin`]: https://github.com/rust-windowing/glutin -[`dodrio`]: https://github.com/fitzgen/dodrio -[The Elm Architecture]: https://guide.elm-lang.org/architecture/ diff --git a/README.md b/README.md index a9c3797795..9cfa03de13 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ A cross-platform GUI library for Rust focused on simplicity and type-safety. Inspired by [Elm]. - - + + - - + + @@ -34,34 +34,28 @@ Inspired by [Elm]. * 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: +* Modular ecosystem split into reusable parts: * A [renderer-agnostic native runtime] enabling integration with existing systems - * Two [built-in renderers] leveraging [`wgpu`] and [`tiny-skia`] + * Two built-in renderers leveraging [`wgpu`] and [`tiny-skia`] * [`iced_wgpu`] supporting Vulkan, Metal and DX12 * [`iced_tiny_skia`] offering a software alternative as a fallback * A [windowing shell] - * A [web runtime] leveraging the DOM -__Iced is currently experimental software.__ [Take a look at the roadmap], -[check out the issues], and [feel free to contribute!] +__Iced is currently experimental software.__ [Take a look at the roadmap] and +[check out the issues]. [Cross-platform support]: https://raw.githubusercontent.com/iced-rs/iced/master/docs/images/todos_desktop.jpg [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]: ECOSYSTEM.md [renderer-agnostic native runtime]: runtime/ [`wgpu`]: https://github.com/gfx-rs/wgpu [`tiny-skia`]: https://github.com/RazrFalcon/tiny-skia [`iced_wgpu`]: wgpu/ [`iced_tiny_skia`]: tiny_skia/ -[built-in renderers]: ECOSYSTEM.md#Renderers [windowing shell]: winit/ -[`dodrio`]: https://github.com/fitzgen/dodrio -[web runtime]: https://github.com/iced-rs/iced_web [Take a look at the roadmap]: ROADMAP.md [check out the issues]: https://github.com/iced-rs/iced/issues -[feel free to contribute!]: #contributing--feedback ## Overview @@ -164,7 +158,7 @@ Read the [book], the [documentation], and the [examples] to learn more! ## Implementation details Iced was originally born as an attempt at bringing the simplicity of [Elm] and -[The Elm Architecture] into [Coffee], a 2D game engine I am working on. +[The Elm Architecture] into [Coffee], a 2D game library I am working on. The core of the library was implemented during May 2019 in [this pull request]. [The first alpha version] was eventually released as @@ -172,25 +166,17 @@ The core of the library was implemented during May 2019 in [this pull request]. implemented the current [tour example] on top of [`ggez`], a game library. Since then, the focus has shifted towards providing a batteries-included, -end-user-oriented GUI library, while keeping [the ecosystem] modular: - -

- - The Iced Ecosystem - -

+end-user-oriented GUI library, while keeping the ecosystem modular. [this pull request]: https://github.com/hecrj/coffee/pull/35 [The first alpha version]: https://github.com/iced-rs/iced/tree/0.1.0-alpha [a renderer-agnostic GUI library]: https://www.reddit.com/r/rust/comments/czzjnv/iced_a_rendereragnostic_gui_library_focused_on/ [tour example]: examples/README.md#tour [`ggez`]: https://github.com/ggez/ggez -[the ecosystem]: ECOSYSTEM.md ## Contributing / Feedback -Contributions are greatly appreciated! If you want to contribute, please -read our [contributing guidelines] for more details. +If you want to contribute, please read our [contributing guidelines] for more details. Feedback is also welcome! You can create a new topic in [our Discourse forum] or come chat to [our Discord server]. diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index d8d6e4c6ba..8b02f8c238 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -1,4 +1,25 @@ -//! Write some text for your users to read. +//! Text widgets display information through writing. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub fn text(t: T) -> iced_core::widget::Text<'static, iced_core::Theme, ()> { unimplemented!() } } +//! # pub use iced_core::color; } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, ()>; +//! use iced::widget::text; +//! use iced::color; +//! +//! enum Message { +//! // ... +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! text("Hello, this is iced!") +//! .size(20) +//! .color(color!(0x0000ff)) +//! .into() +//! } +//! ``` use crate::alignment; use crate::layout; use crate::mouse; @@ -13,7 +34,28 @@ use crate::{ pub use text::{LineHeight, Shaping, Wrapping}; -/// A paragraph of text. +/// A bunch of text. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub fn text(t: T) -> iced_core::widget::Text<'static, iced_core::Theme, ()> { unimplemented!() } } +/// # pub use iced_core::color; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, ()>; +/// use iced::widget::text; +/// use iced::color; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// text("Hello, this is iced!") +/// .size(20) +/// .color(color!(0x0000ff)) +/// .into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Text<'a, Theme, Renderer> where diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs index 946995d8c2..8067c25906 100644 --- a/futures/src/subscription.rs +++ b/futures/src/subscription.rs @@ -138,11 +138,16 @@ impl Subscription { /// and returning the `Sender` as a `Message` for the `Application`: /// /// ``` - /// use iced_futures::subscription::{self, Subscription}; - /// use iced_futures::stream; - /// use iced_futures::futures::channel::mpsc; - /// use iced_futures::futures::sink::SinkExt; - /// use iced_futures::futures::Stream; + /// # mod iced { + /// # pub use iced_futures::Subscription; + /// # pub use iced_futures::futures; + /// # pub use iced_futures::stream; + /// # } + /// use iced::futures::channel::mpsc; + /// use iced::futures::sink::SinkExt; + /// use iced::futures::Stream; + /// use iced::stream; + /// use iced::Subscription; /// /// pub enum Event { /// Ready(mpsc::Sender), diff --git a/src/lib.rs b/src/lib.rs index 022f8d6e13..91d8e53fe0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -380,16 +380,18 @@ //! # use iced::{Element, Task}; //! # pub struct Contacts; //! # impl Contacts { -//! # pub fn update(&mut self, message: Message) -> Task { unimplemented!() } +//! # pub fn update(&mut self, message: Message) -> Action { unimplemented!() } //! # pub fn view(&self) -> Element { unimplemented!() } //! # } //! # #[derive(Debug)] //! # pub enum Message {} +//! # pub enum Action { None, Run(Task), Chat(()) } //! # } //! # mod conversation { //! # use iced::{Element, Task}; //! # pub struct Conversation; //! # impl Conversation { +//! # pub fn new(contact: ()) -> (Self, Task) { unimplemented!() } //! # pub fn update(&mut self, message: Message) -> Task { unimplemented!() } //! # pub fn view(&self) -> Element { unimplemented!() } //! # } @@ -419,7 +421,19 @@ //! match message { //! Message::Contacts(message) => { //! if let Screen::Contacts(contacts) = &mut state.screen { -//! contacts.update(message).map(Message::Contacts) +//! 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() //! } @@ -442,8 +456,16 @@ //! } //! ``` //! -//! Functor methods like [`Task::map`], [`Element::map`], and [`Subscription::map`] make this -//! approach seamless. +//! 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. #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/bdf0430880f5c29443f5f0a0ae4895866dfef4c6/docs/logo.svg" )] diff --git a/widget/src/button.rs b/widget/src/button.rs index eafa71b993..3323c0d326 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -1,4 +1,21 @@ -//! Allow your users to perform actions by pressing a button. +//! Buttons allow your users to perform actions by pressing them. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! use iced::widget::button; +//! +//! #[derive(Clone)] +//! enum Message { +//! ButtonPressed, +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! button("Press me!").on_press(Message::ButtonPressed).into() +//! } +//! ``` use crate::core::border::{self, Border}; use crate::core::event::{self, Event}; use crate::core::layout; @@ -16,34 +33,39 @@ use crate::core::{ /// A generic widget that produces a message when pressed. /// +/// # Example /// ```no_run -/// # type Button<'a, Message> = iced_widget::Button<'a, Message>; -/// # +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::button; +/// /// #[derive(Clone)] /// enum Message { /// ButtonPressed, /// } /// -/// let button = Button::new("Press me!").on_press(Message::ButtonPressed); +/// fn view(state: &State) -> Element<'_, Message> { +/// button("Press me!").on_press(Message::ButtonPressed).into() +/// } /// ``` /// /// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will /// be disabled: /// -/// ``` -/// # type Button<'a, Message> = iced_widget::Button<'a, Message>; -/// # +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::button; +/// /// #[derive(Clone)] /// enum Message { /// ButtonPressed, /// } /// -/// fn disabled_button<'a>() -> Button<'a, Message> { -/// Button::new("I'm disabled!") -/// } -/// -/// fn enabled_button<'a>() -> Button<'a, Message> { -/// disabled_button().on_press(Message::ButtonPressed) +/// fn view(state: &State) -> Element<'_, Message> { +/// button("I am disabled!").into() /// } /// ``` #[allow(missing_debug_implementations)] diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 185fa0829d..9fbccf824e 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -1,4 +1,53 @@ -//! Draw 2D graphics for your users. +//! Canvases can be leveraged to draw interactive 2D graphics. +//! +//! # Example: Drawing a Simple Circle +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::mouse; +//! use iced::widget::canvas; +//! use iced::{Color, Rectangle, Renderer, Theme}; +//! +//! // First, we define the data we need for drawing +//! #[derive(Debug)] +//! struct Circle { +//! radius: f32, +//! } +//! +//! // Then, we implement the `Program` trait +//! impl canvas::Program for Circle { +//! // No internal state +//! type State = (); +//! +//! fn draw( +//! &self, +//! _state: &(), +//! renderer: &Renderer, +//! _theme: &Theme, +//! bounds: Rectangle, +//! _cursor: mouse::Cursor +//! ) -> Vec { +//! // We prepare a new `Frame` +//! let mut frame = canvas::Frame::new(renderer, bounds.size()); +//! +//! // We create a `Path` representing a simple circle +//! let circle = canvas::Path::circle(frame.center(), self.radius); +//! +//! // And fill it with some color +//! frame.fill(&circle, Color::BLACK); +//! +//! // Then, we produce the geometry +//! vec![frame.into_geometry()] +//! } +//! } +//! +//! // Finally, we simply use our `Circle` to create the `Canvas`! +//! fn view<'a, Message: 'a>(_state: &'a State) -> Element<'a, Message> { +//! canvas(Circle { radius: 50.0 }).into() +//! } +//! ``` pub mod event; mod program; @@ -39,15 +88,16 @@ pub type Frame = geometry::Frame; /// A widget capable of drawing 2D graphics. /// -/// ## Drawing a simple circle -/// If you want to get a quick overview, here's how we can draw a simple circle: -/// +/// # Example: Drawing a Simple Circle /// ```no_run -/// # use iced_widget::canvas::{self, Canvas, Fill, Frame, Geometry, Path, Program}; -/// # use iced_widget::core::{Color, Rectangle}; -/// # use iced_widget::core::mouse; -/// # use iced_widget::{Renderer, Theme}; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # +/// use iced::mouse; +/// use iced::widget::canvas; +/// use iced::{Color, Rectangle, Renderer, Theme}; +/// /// // First, we define the data we need for drawing /// #[derive(Debug)] /// struct Circle { @@ -55,26 +105,36 @@ pub type Frame = geometry::Frame; /// } /// /// // Then, we implement the `Program` trait -/// impl Program<()> for Circle { +/// impl canvas::Program for Circle { +/// // No internal state /// type State = (); /// -/// fn draw(&self, _state: &(), renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: mouse::Cursor) -> Vec { +/// fn draw( +/// &self, +/// _state: &(), +/// renderer: &Renderer, +/// _theme: &Theme, +/// bounds: Rectangle, +/// _cursor: mouse::Cursor +/// ) -> Vec { /// // We prepare a new `Frame` -/// let mut frame = Frame::new(renderer, bounds.size()); +/// let mut frame = canvas::Frame::new(renderer, bounds.size()); /// /// // We create a `Path` representing a simple circle -/// let circle = Path::circle(frame.center(), self.radius); +/// let circle = canvas::Path::circle(frame.center(), self.radius); /// /// // And fill it with some color /// frame.fill(&circle, Color::BLACK); /// -/// // Finally, we produce the geometry +/// // Then, we produce the geometry /// vec![frame.into_geometry()] /// } /// } /// /// // Finally, we simply use our `Circle` to create the `Canvas`! -/// let canvas = Canvas::new(Circle { radius: 50.0 }); +/// fn view<'a, Message: 'a>(_state: &'a State) -> Element<'a, Message> { +/// canvas(Circle { radius: 50.0 }).into() +/// } /// ``` #[derive(Debug)] pub struct Canvas diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 32db509085..4a3f35ed21 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -1,4 +1,35 @@ -//! Show toggle controls using checkboxes. +//! Checkboxes can be used to let users make binary choices. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::checkbox; +//! +//! struct State { +//! is_checked: bool, +//! } +//! +//! enum Message { +//! CheckboxToggled(bool), +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! checkbox("Toggle me!", state.is_checked) +//! .on_toggle(Message::CheckboxToggled) +//! .into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::CheckboxToggled(is_checked) => { +//! state.is_checked = is_checked; +//! } +//! } +//! } +//! ``` +//! ![Checkbox drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true) use crate::core::alignment; use crate::core::event::{self, Event}; use crate::core::layout; @@ -17,19 +48,34 @@ use crate::core::{ /// A box that can be checked. /// /// # Example -/// /// ```no_run -/// # type Checkbox<'a, Message> = iced_widget::Checkbox<'a, Message>; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # -/// pub enum Message { +/// use iced::widget::checkbox; +/// +/// struct State { +/// is_checked: bool, +/// } +/// +/// enum Message { /// CheckboxToggled(bool), /// } /// -/// let is_checked = true; +/// fn view(state: &State) -> Element<'_, Message> { +/// checkbox("Toggle me!", state.is_checked) +/// .on_toggle(Message::CheckboxToggled) +/// .into() +/// } /// -/// Checkbox::new("Toggle me!", is_checked).on_toggle(Message::CheckboxToggled); +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::CheckboxToggled(is_checked) => { +/// state.is_checked = is_checked; +/// } +/// } +/// } /// ``` -/// /// ![Checkbox drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true) #[allow(missing_debug_implementations)] pub struct Checkbox< diff --git a/widget/src/column.rs b/widget/src/column.rs index d3ea4cf7e3..fc4653b916 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -12,6 +12,27 @@ use crate::core::{ }; /// A container that distributes its contents vertically. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{button, column}; +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// column![ +/// "I am on top!", +/// button("I am in the center!"), +/// "I am below.", +/// ].into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Column<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> { diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index a51701ca61..fb661ad5cb 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -1,4 +1,59 @@ -//! Display a dropdown list of searchable and selectable options. +//! Combo boxes display a dropdown list of searchable and selectable options. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::combo_box; +//! +//! struct State { +//! fruits: combo_box::State, +//! favorite: Option, +//! } +//! +//! #[derive(Debug, Clone)] +//! enum Fruit { +//! Apple, +//! Orange, +//! Strawberry, +//! Tomato, +//! } +//! +//! #[derive(Debug, Clone)] +//! enum Message { +//! FruitSelected(Fruit), +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! combo_box( +//! &state.fruits, +//! "Select your favorite fruit...", +//! state.favorite.as_ref(), +//! Message::FruitSelected +//! ) +//! .into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::FruitSelected(fruit) => { +//! state.favorite = Some(fruit); +//! } +//! } +//! } +//! +//! impl std::fmt::Display for Fruit { +//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +//! f.write_str(match self { +//! Self::Apple => "Apple", +//! Self::Orange => "Orange", +//! Self::Strawberry => "Strawberry", +//! Self::Tomato => "Tomato", +//! }) +//! } +//! } +//! ``` use crate::core::event::{self, Event}; use crate::core::keyboard; use crate::core::keyboard::key; @@ -21,9 +76,60 @@ use std::fmt::Display; /// A widget for searching and selecting a single value from a list of options. /// -/// This widget is composed by a [`TextInput`] that can be filled with the text -/// to search for corresponding values from the list of options that are displayed -/// as a Menu. +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::combo_box; +/// +/// struct State { +/// fruits: combo_box::State, +/// favorite: Option, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Fruit { +/// Apple, +/// Orange, +/// Strawberry, +/// Tomato, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// FruitSelected(Fruit), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// combo_box( +/// &state.fruits, +/// "Select your favorite fruit...", +/// state.favorite.as_ref(), +/// Message::FruitSelected +/// ) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::FruitSelected(fruit) => { +/// state.favorite = Some(fruit); +/// } +/// } +/// } +/// +/// impl std::fmt::Display for Fruit { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// f.write_str(match self { +/// Self::Apple => "Apple", +/// Self::Orange => "Orange", +/// Self::Strawberry => "Strawberry", +/// Self::Tomato => "Tomato", +/// }) +/// } +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct ComboBox< 'a, diff --git a/widget/src/container.rs b/widget/src/container.rs index 3b794099fd..b256540c1c 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -1,4 +1,24 @@ -//! Decorate content and apply alignment. +//! Containers let you align a widget inside their boundaries. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! use iced::widget::container; +//! +//! enum Message { +//! // ... +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! container("This text is centered inside a rounded box!") +//! .padding(10) +//! .center(800) +//! .style(container::rounded_box) +//! .into() +//! } +//! ``` use crate::core::alignment::{self, Alignment}; use crate::core::border::{self, Border}; use crate::core::event::{self, Event}; @@ -16,9 +36,27 @@ use crate::core::{ }; use crate::runtime::task::{self, Task}; -/// An element decorating some content. +/// A widget that aligns its contents inside of its boundaries. /// -/// It is normally used for alignment purposes. +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::container; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// container("This text is centered inside a rounded box!") +/// .padding(10) +/// .center(800) +/// .style(container::rounded_box) +/// .into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Container< 'a, diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 51978823b3..52290a54d5 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -9,6 +9,7 @@ use crate::core::window; use crate::core::{Element, Length, Pixels, Widget}; use crate::keyed; use crate::overlay; +use crate::pane_grid::{self, PaneGrid}; use crate::pick_list::{self, PickList}; use crate::progress_bar::{self, ProgressBar}; use crate::radio::{self, Radio}; @@ -30,7 +31,28 @@ use std::ops::RangeInclusive; /// Creates a [`Column`] with the given children. /// -/// [`Column`]: crate::Column +/// Columns distribute their children vertically. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{button, column}; +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// column![ +/// "I am on top!", +/// button("I am in the center!"), +/// "I am below.", +/// ].into() +/// } +/// ``` #[macro_export] macro_rules! column { () => ( @@ -43,7 +65,28 @@ macro_rules! column { /// Creates a [`Row`] with the given children. /// -/// [`Row`]: crate::Row +/// Rows distribute their children horizontally. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{button, row}; +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// row![ +/// "I am to the left!", +/// button("I am in the middle!"), +/// "I am to the right!", +/// ].into() +/// } +/// ``` #[macro_export] macro_rules! row { () => ( @@ -80,7 +123,6 @@ macro_rules! stack { /// /// ```no_run /// # mod iced { -/// # pub struct Element(pub std::marker::PhantomData); /// # pub mod widget { /// # macro_rules! text { /// # ($($arg:tt)*) => {unimplemented!()} @@ -88,22 +130,23 @@ macro_rules! stack { /// # pub(crate) use text; /// # } /// # } -/// # struct Example; -/// # enum Message {} -/// use iced::Element; +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::core::Theme, ()>; /// use iced::widget::text; /// -/// impl Example { -/// fn view(&self) -> Element { -/// let simple = text!("Hello, world!"); +/// enum Message { +/// // ... +/// } /// -/// let keyword = text!("Hello, {}", "world!"); +/// fn view(_state: &State) -> Element { +/// let simple = text!("Hello, world!"); /// -/// let planet = "Earth"; -/// let local_variable = text!("Hello, {planet}!"); -/// // ... -/// # iced::Element(std::marker::PhantomData) -/// } +/// let keyword = text!("Hello, {}", "world!"); +/// +/// let planet = "Earth"; +/// let local_variable = text!("Hello, {planet}!"); +/// // ... +/// # unimplemented!() /// } /// ``` #[macro_export] @@ -116,6 +159,31 @@ macro_rules! text { /// Creates some [`Rich`] text with the given spans. /// /// [`Rich`]: text::Rich +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::*; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::font; +/// use iced::widget::{rich_text, span}; +/// use iced::{color, Font}; +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// rich_text![ +/// span("I am red!").color(color!(0xff0000)), +/// " ", +/// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }), +/// ] +/// .size(20) +/// .into() +/// } +/// ``` #[macro_export] macro_rules! rich_text { () => ( @@ -128,7 +196,27 @@ macro_rules! rich_text { /// Creates a new [`Container`] with the provided content. /// -/// [`Container`]: crate::Container +/// Containers let you align a widget inside their boundaries. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::container; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// container("This text is centered inside a rounded box!") +/// .padding(10) +/// .center(800) +/// .style(container::rounded_box) +/// .into() +/// } +/// ``` pub fn container<'a, Message, Theme, Renderer>( content: impl Into>, ) -> Container<'a, Message, Theme, Renderer> @@ -162,6 +250,24 @@ where } /// Creates a new [`Column`] with the given children. +/// +/// Columns distribute their children vertically. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{column, text}; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// column((0..5).map(|i| text!("Item {i}").into())).into() +/// } +/// ``` pub fn column<'a, Message, Theme, Renderer>( children: impl IntoIterator>, ) -> Column<'a, Message, Theme, Renderer> @@ -171,7 +277,27 @@ where Column::with_children(children) } -/// Creates a new [`keyed::Column`] with the given children. +/// Creates a new [`keyed::Column`] from an iterator of elements. +/// +/// Keyed columns distribute content vertically while keeping continuity. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{keyed_column, text}; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// keyed_column((0..=100).map(|i| { +/// (i, text!("Item {i}").into()) +/// })).into() +/// } +/// ``` pub fn keyed_column<'a, Key, Message, Theme, Renderer>( children: impl IntoIterator)>, ) -> keyed::Column<'a, Key, Message, Theme, Renderer> @@ -182,9 +308,25 @@ where keyed::Column::with_children(children) } -/// Creates a new [`Row`] with the given children. +/// Creates a new [`Row`] from an iterator. /// -/// [`Row`]: crate::Row +/// Rows distribute their children horizontally. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{row, text}; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// row((0..5).map(|i| text!("Item {i}").into())).into() +/// } +/// ``` pub fn row<'a, Message, Theme, Renderer>( children: impl IntoIterator>, ) -> Row<'a, Message, Theme, Renderer> @@ -640,7 +782,27 @@ where /// Creates a new [`Scrollable`] with the provided content. /// -/// [`Scrollable`]: crate::Scrollable +/// Scrollables let users navigate an endless amount of content with a scrollbar. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{column, scrollable, vertical_space}; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// scrollable(column![ +/// "Scroll me!", +/// vertical_space().height(3000), +/// "You did it!", +/// ]).into() +/// } +/// ``` pub fn scrollable<'a, Message, Theme, Renderer>( content: impl Into>, ) -> Scrollable<'a, Message, Theme, Renderer> @@ -653,7 +815,22 @@ where /// Creates a new [`Button`] with the provided content. /// -/// [`Button`]: crate::Button +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::button; +/// +/// #[derive(Clone)] +/// enum Message { +/// ButtonPressed, +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// button("Press me!").on_press(Message::ButtonPressed).into() +/// } +/// ``` pub fn button<'a, Message, Theme, Renderer>( content: impl Into>, ) -> Button<'a, Message, Theme, Renderer> @@ -667,8 +844,29 @@ where /// Creates a new [`Tooltip`] for the provided content with the given /// [`Element`] and [`tooltip::Position`]. /// -/// [`Tooltip`]: crate::Tooltip -/// [`tooltip::Position`]: crate::tooltip::Position +/// Tooltips display a hint of information over some element when hovered. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{container, tooltip}; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(_state: &State) -> Element<'_, Message> { +/// tooltip( +/// "Hover me to display the tooltip!", +/// container("This is the tooltip contents!") +/// .padding(10) +/// .style(container::rounded_box), +/// tooltip::Position::Bottom, +/// ).into() +/// } +/// ``` pub fn tooltip<'a, Message, Theme, Renderer>( content: impl Into>, tooltip: impl Into>, @@ -682,6 +880,26 @@ where } /// Creates a new [`Text`] widget with the provided content. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::core::Theme, ()>; +/// use iced::widget::text; +/// use iced::color; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// text("Hello, this is iced!") +/// .size(20) +/// .color(color!(0x0000ff)) +/// .into() +/// } +/// ``` pub fn text<'a, Theme, Renderer>( text: impl text::IntoFragment<'a>, ) -> Text<'a, Theme, Renderer> @@ -706,6 +924,31 @@ where /// Creates a new [`Rich`] text widget with the provided spans. /// /// [`Rich`]: text::Rich +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::*; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::font; +/// use iced::widget::{rich_text, span}; +/// use iced::{color, Font}; +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// rich_text([ +/// span("I am red!").color(color!(0xff0000)), +/// span(" "), +/// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }), +/// ]) +/// .size(20) +/// .into() +/// } +/// ``` pub fn rich_text<'a, Link, Theme, Renderer>( spans: impl AsRef<[text::Span<'a, Link, Renderer::Font>]> + 'a, ) -> text::Rich<'a, Link, Theme, Renderer> @@ -720,7 +963,35 @@ where /// Creates a new [`Span`] of text with the provided content. /// +/// A [`Span`] is a fragment of some [`Rich`] text. +/// /// [`Span`]: text::Span +/// [`Rich`]: text::Rich +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::*; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::font; +/// use iced::widget::{rich_text, span}; +/// use iced::{color, Font}; +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// rich_text![ +/// span("I am red!").color(color!(0xff0000)), +/// " ", +/// span("And I am bold!").font(Font { weight: font::Weight::Bold, ..Font::default() }), +/// ] +/// .size(20) +/// .into() +/// } +/// ``` pub fn span<'a, Link, Font>( text: impl text::IntoFragment<'a>, ) -> text::Span<'a, Link, Font> { @@ -733,7 +1004,36 @@ pub use crate::markdown::view as markdown; /// Creates a new [`Checkbox`]. /// -/// [`Checkbox`]: crate::Checkbox +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::checkbox; +/// +/// struct State { +/// is_checked: bool, +/// } +/// +/// enum Message { +/// CheckboxToggled(bool), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// checkbox("Toggle me!", state.is_checked) +/// .on_toggle(Message::CheckboxToggled) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::CheckboxToggled(is_checked) => { +/// state.is_checked = is_checked; +/// } +/// } +/// } +/// ``` +/// ![Checkbox drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true) pub fn checkbox<'a, Message, Theme, Renderer>( label: impl Into, is_checked: bool, @@ -747,7 +1047,64 @@ where /// Creates a new [`Radio`]. /// -/// [`Radio`]: crate::Radio +/// Radio buttons let users choose a single option from a bunch of options. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::{column, radio}; +/// +/// struct State { +/// selection: Option, +/// } +/// +/// #[derive(Debug, Clone, Copy)] +/// enum Message { +/// RadioSelected(Choice), +/// } +/// +/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// enum Choice { +/// A, +/// B, +/// C, +/// All, +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// let a = radio( +/// "A", +/// Choice::A, +/// state.selection, +/// Message::RadioSelected, +/// ); +/// +/// let b = radio( +/// "B", +/// Choice::B, +/// state.selection, +/// Message::RadioSelected, +/// ); +/// +/// let c = radio( +/// "C", +/// Choice::C, +/// state.selection, +/// Message::RadioSelected, +/// ); +/// +/// let all = radio( +/// "All of the above", +/// Choice::All, +/// state.selection, +/// Message::RadioSelected +/// ); +/// +/// column![a, b, c, all].into() +/// } +/// ``` pub fn radio<'a, Message, Theme, Renderer, V>( label: impl Into, value: V, @@ -765,7 +1122,38 @@ where /// Creates a new [`Toggler`]. /// -/// [`Toggler`]: crate::Toggler +/// Togglers let users make binary choices by toggling a switch. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::toggler; +/// +/// struct State { +/// is_checked: bool, +/// } +/// +/// enum Message { +/// TogglerToggled(bool), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// toggler(state.is_checked) +/// .label("Toggle me!") +/// .on_toggle(Message::TogglerToggled) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::TogglerToggled(is_checked) => { +/// state.is_checked = is_checked; +/// } +/// } +/// } +/// ``` pub fn toggler<'a, Message, Theme, Renderer>( is_checked: bool, ) -> Toggler<'a, Message, Theme, Renderer> @@ -778,7 +1166,38 @@ where /// Creates a new [`TextInput`]. /// -/// [`TextInput`]: crate::TextInput +/// Text inputs display fields that can be filled with text. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::text_input; +/// +/// struct State { +/// content: String, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// ContentChanged(String) +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// text_input("Type something here...", &state.content) +/// .on_input(Message::ContentChanged) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::ContentChanged(content) => { +/// state.content = content; +/// } +/// } +/// } +/// ``` pub fn text_input<'a, Message, Theme, Renderer>( placeholder: &str, value: &str, @@ -793,7 +1212,39 @@ where /// Creates a new [`TextEditor`]. /// -/// [`TextEditor`]: crate::TextEditor +/// Text editors display a multi-line text input for text editing. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::text_editor; +/// +/// struct State { +/// content: text_editor::Content, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// Edit(text_editor::Action) +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// text_editor(&state.content) +/// .placeholder("Type something here...") +/// .on_action(Message::Edit) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::Edit(action) => { +/// state.content.perform(action); +/// } +/// } +/// } +/// ``` pub fn text_editor<'a, Message, Theme, Renderer>( content: &'a text_editor::Content, ) -> TextEditor<'a, core::text::highlighter::PlainText, Message, Theme, Renderer> @@ -807,7 +1258,36 @@ where /// Creates a new [`Slider`]. /// -/// [`Slider`]: crate::Slider +/// Sliders let users set a value by moving an indicator. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::slider; +/// +/// struct State { +/// value: f32, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// ValueChanged(f32), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// slider(0.0..=100.0, state.value, Message::ValueChanged).into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::ValueChanged(value) => { +/// state.value = value; +/// } +/// } +/// } +/// ``` pub fn slider<'a, T, Message, Theme>( range: std::ops::RangeInclusive, value: T, @@ -823,7 +1303,36 @@ where /// Creates a new [`VerticalSlider`]. /// -/// [`VerticalSlider`]: crate::VerticalSlider +/// Sliders let users set a value by moving an indicator. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::vertical_slider; +/// +/// struct State { +/// value: f32, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// ValueChanged(f32), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// vertical_slider(0.0..=100.0, state.value, Message::ValueChanged).into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::ValueChanged(value) => { +/// state.value = value; +/// } +/// } +/// } +/// ``` pub fn vertical_slider<'a, T, Message, Theme>( range: std::ops::RangeInclusive, value: T, @@ -839,7 +1348,68 @@ where /// Creates a new [`PickList`]. /// -/// [`PickList`]: crate::PickList +/// Pick lists display a dropdown list of selectable options. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::pick_list; +/// +/// struct State { +/// favorite: Option, +/// } +/// +/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// enum Fruit { +/// Apple, +/// Orange, +/// Strawberry, +/// Tomato, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// FruitSelected(Fruit), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// let fruits = [ +/// Fruit::Apple, +/// Fruit::Orange, +/// Fruit::Strawberry, +/// Fruit::Tomato, +/// ]; +/// +/// pick_list( +/// fruits, +/// state.favorite, +/// Message::FruitSelected, +/// ) +/// .placeholder("Select your favorite fruit...") +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::FruitSelected(fruit) => { +/// state.favorite = Some(fruit); +/// } +/// } +/// } +/// +/// impl std::fmt::Display for Fruit { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// f.write_str(match self { +/// Self::Apple => "Apple", +/// Self::Orange => "Orange", +/// Self::Strawberry => "Strawberry", +/// Self::Tomato => "Tomato", +/// }) +/// } +/// } +/// ``` pub fn pick_list<'a, T, L, V, Message, Theme, Renderer>( options: L, selected: Option, @@ -858,7 +1428,62 @@ where /// Creates a new [`ComboBox`]. /// -/// [`ComboBox`]: crate::ComboBox +/// Combo boxes display a dropdown list of searchable and selectable options. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::combo_box; +/// +/// struct State { +/// fruits: combo_box::State, +/// favorite: Option, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Fruit { +/// Apple, +/// Orange, +/// Strawberry, +/// Tomato, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// FruitSelected(Fruit), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// combo_box( +/// &state.fruits, +/// "Select your favorite fruit...", +/// state.favorite.as_ref(), +/// Message::FruitSelected +/// ) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::FruitSelected(fruit) => { +/// state.favorite = Some(fruit); +/// } +/// } +/// } +/// +/// impl std::fmt::Display for Fruit { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// f.write_str(match self { +/// Self::Apple => "Apple", +/// Self::Orange => "Orange", +/// Self::Strawberry => "Strawberry", +/// Self::Tomato => "Tomato", +/// }) +/// } +/// } +/// ``` pub fn combo_box<'a, T, Message, Theme, Renderer>( state: &'a combo_box::State, placeholder: &str, @@ -891,7 +1516,22 @@ pub fn vertical_space() -> Space { /// Creates a horizontal [`Rule`] with the given height. /// -/// [`Rule`]: crate::Rule +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::horizontal_rule; +/// +/// #[derive(Clone)] +/// enum Message { +/// // ..., +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// horizontal_rule(2).into() +/// } +/// ``` pub fn horizontal_rule<'a, Theme>(height: impl Into) -> Rule<'a, Theme> where Theme: rule::Catalog + 'a, @@ -901,7 +1541,22 @@ where /// Creates a vertical [`Rule`] with the given width. /// -/// [`Rule`]: crate::Rule +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::vertical_rule; +/// +/// #[derive(Clone)] +/// enum Message { +/// // ..., +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// vertical_rule(2).into() +/// } +/// ``` pub fn vertical_rule<'a, Theme>(width: impl Into) -> Rule<'a, Theme> where Theme: rule::Catalog + 'a, @@ -911,11 +1566,31 @@ where /// Creates a new [`ProgressBar`]. /// +/// Progress bars visualize the progression of an extended computer operation, such as a download, file transfer, or installation. +/// /// It expects: /// * an inclusive range of possible values, and /// * the current value of the [`ProgressBar`]. /// -/// [`ProgressBar`]: crate::ProgressBar +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::progress_bar; +/// +/// struct State { +/// progress: f32, +/// } +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// progress_bar(0.0..=100.0, state.progress).into() +/// } +/// ``` pub fn progress_bar<'a, Theme>( range: RangeInclusive, value: f32, @@ -928,7 +1603,26 @@ where /// Creates a new [`Image`]. /// +/// Images display raster graphics in different formats (PNG, JPG, etc.). +/// /// [`Image`]: crate::Image +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::image; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// image("ferris.png").into() +/// } +/// ``` +/// #[cfg(feature = "image")] pub fn image(handle: impl Into) -> crate::Image { crate::Image::new(handle.into()) @@ -936,8 +1630,26 @@ pub fn image(handle: impl Into) -> crate::Image { /// Creates a new [`Svg`] widget from the given [`Handle`]. /// +/// Svg widgets display vector graphics in your application. +/// /// [`Svg`]: crate::Svg /// [`Handle`]: crate::svg::Handle +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::svg; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// svg("tiger.svg").into() +/// } +/// ``` #[cfg(feature = "svg")] pub fn svg<'a, Theme>( handle: impl Into, @@ -985,7 +1697,58 @@ where /// Creates a new [`Canvas`]. /// +/// Canvases can be leveraged to draw interactive 2D graphics. +/// /// [`Canvas`]: crate::Canvas +/// +/// # Example: Drawing a Simple Circle +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::mouse; +/// use iced::widget::canvas; +/// use iced::{Color, Rectangle, Renderer, Theme}; +/// +/// // First, we define the data we need for drawing +/// #[derive(Debug)] +/// struct Circle { +/// radius: f32, +/// } +/// +/// // Then, we implement the `Program` trait +/// impl canvas::Program for Circle { +/// // No internal state +/// type State = (); +/// +/// fn draw( +/// &self, +/// _state: &(), +/// renderer: &Renderer, +/// _theme: &Theme, +/// bounds: Rectangle, +/// _cursor: mouse::Cursor +/// ) -> Vec { +/// // We prepare a new `Frame` +/// let mut frame = canvas::Frame::new(renderer, bounds.size()); +/// +/// // We create a `Path` representing a simple circle +/// let circle = canvas::Path::circle(frame.center(), self.radius); +/// +/// // And fill it with some color +/// frame.fill(&circle, Color::BLACK); +/// +/// // Then, we produce the geometry +/// vec![frame.into_geometry()] +/// } +/// } +/// +/// // Finally, we simply use our `Circle` to create the `Canvas`! +/// fn view<'a, Message: 'a>(_state: &'a State) -> Element<'a, Message> { +/// canvas(Circle { radius: 50.0 }).into() +/// } +/// ``` #[cfg(feature = "canvas")] pub fn canvas( program: P, @@ -999,8 +1762,31 @@ where /// Creates a new [`QRCode`] widget from the given [`Data`]. /// +/// QR codes display information in a type of two-dimensional matrix barcode. +/// /// [`QRCode`]: crate::QRCode /// [`Data`]: crate::qr_code::Data +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::qr_code; +/// +/// struct State { +/// data: qr_code::Data, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// qr_code(&state.data).into() +/// } +/// ``` #[cfg(feature = "qr_code")] pub fn qr_code<'a, Theme>( data: &'a crate::qr_code::Data, @@ -1060,3 +1846,55 @@ where { Themer::new(move |_| new_theme.clone(), content) } + +/// Creates a [`PaneGrid`] with the given [`pane_grid::State`] and view function. +/// +/// Pane grids let your users split regions of your application and organize layout dynamically. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::{pane_grid, text}; +/// +/// struct State { +/// panes: pane_grid::State, +/// } +/// +/// enum Pane { +/// SomePane, +/// AnotherKindOfPane, +/// } +/// +/// enum Message { +/// PaneDragged(pane_grid::DragEvent), +/// PaneResized(pane_grid::ResizeEvent), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// pane_grid(&state.panes, |pane, state, is_maximized| { +/// pane_grid::Content::new(match state { +/// Pane::SomePane => text("This is some pane"), +/// Pane::AnotherKindOfPane => text("This is another kind of pane"), +/// }) +/// }) +/// .on_drag(Message::PaneDragged) +/// .on_resize(10, Message::PaneResized) +/// .into() +/// } +/// ``` +pub fn pane_grid<'a, T, Message, Theme, Renderer>( + state: &'a pane_grid::State, + view: impl Fn( + pane_grid::Pane, + &'a T, + bool, + ) -> pane_grid::Content<'a, Message, Theme, Renderer>, +) -> PaneGrid<'a, Message, Theme, Renderer> +where + Theme: pane_grid::Catalog, + Renderer: core::Renderer, +{ + PaneGrid::new(state, view) +} diff --git a/widget/src/image.rs b/widget/src/image.rs index e04f2d6f0f..c8f2a620f6 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -1,4 +1,21 @@ -//! Display images in your user interface. +//! Images display raster graphics in different formats (PNG, JPG, etc.). +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! use iced::widget::image; +//! +//! enum Message { +//! // ... +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! image("ferris.png").into() +//! } +//! ``` +//! pub mod viewer; pub use viewer::Viewer; @@ -22,16 +39,23 @@ pub fn viewer(handle: Handle) -> Viewer { /// A frame that displays an image while keeping aspect ratio. /// /// # Example -/// /// ```no_run -/// # use iced_widget::image::{self, Image}; -/// # -/// let image = Image::::new("resources/ferris.png"); -/// ``` +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::image; /// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// image("ferris.png").into() +/// } +/// ``` /// #[derive(Debug)] -pub struct Image { +pub struct Image { handle: Handle, width: Length, height: Length, diff --git a/widget/src/keyed.rs b/widget/src/keyed.rs index ad531e6642..923cb118ed 100644 --- a/widget/src/keyed.rs +++ b/widget/src/keyed.rs @@ -1,4 +1,4 @@ -//! Use widgets that can provide hints to ensure continuity. +//! Keyed widgets can provide hints to ensure continuity. //! //! # What is continuity? //! Continuity is the feeling of persistence of state. @@ -41,13 +41,35 @@ pub mod column; pub use column::Column; -/// Creates a [`Column`] with the given children. +/// Creates a keyed [`Column`] with the given children. +/// +/// Keyed columns distribute content vertically while keeping continuity. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::keyed_column; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// keyed_column![ +/// (0, "Item 0"), +/// (1, "Item 1"), +/// (2, "Item 2"), +/// ].into() +/// } +/// ``` #[macro_export] macro_rules! keyed_column { () => ( - $crate::Column::new() + $crate::keyed::Column::new() ); - ($($x:expr),+ $(,)?) => ( - $crate::keyed::Column::with_children(vec![$($crate::core::Element::from($x)),+]) + ($(($key:expr, $x:expr)),+ $(,)?) => ( + $crate::keyed::Column::with_children(vec![$(($key, $crate::core::Element::from($x))),+]) ); } diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 2c56c6051e..5852ede19d 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -1,4 +1,4 @@ -//! Distribute content vertically. +//! Keyed columns distribute content vertically while keeping continuity. use crate::core::event::{self, Event}; use crate::core::layout; use crate::core::mouse; @@ -11,7 +11,25 @@ use crate::core::{ Shell, Size, Vector, Widget, }; -/// A container that distributes its contents vertically. +/// A container that distributes its contents vertically while keeping continuity. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{keyed_column, text}; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// keyed_column((0..=100).map(|i| { +/// (i, text!("Item {i}").into()) +/// })).into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Column< 'a, diff --git a/widget/src/markdown.rs b/widget/src/markdown.rs index 4bcd33530e..81bea0c506 100644 --- a/widget/src/markdown.rs +++ b/widget/src/markdown.rs @@ -1,9 +1,52 @@ -//! Parse and display Markdown. +//! Markdown widgets can parse and display Markdown. //! //! You can enable the `highlighter` feature for syntax highligting //! in code blocks. //! //! Only the variants of [`Item`] are currently supported. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::markdown; +//! use iced::Theme; +//! +//! struct State { +//! markdown: Vec, +//! } +//! +//! enum Message { +//! LinkClicked(markdown::Url), +//! } +//! +//! impl State { +//! pub fn new() -> Self { +//! Self { +//! markdown: markdown::parse("This is some **Markdown**!").collect(), +//! } +//! } +//! +//! fn view(&self) -> Element<'_, Message> { +//! markdown::view( +//! &self.markdown, +//! markdown::Settings::default(), +//! markdown::Style::from_palette(Theme::TokyoNightStorm.palette()), +//! ) +//! .map(Message::LinkClicked) +//! .into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::LinkClicked(url) => { +//! println!("The following url was clicked: {url}"); +//! } +//! } +//! } +//! } +//! ``` use crate::core::border; use crate::core::font::{self, Font}; use crate::core::padding; @@ -145,6 +188,49 @@ impl Span { } /// Parse the given Markdown content. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::markdown; +/// use iced::Theme; +/// +/// struct State { +/// markdown: Vec, +/// } +/// +/// enum Message { +/// LinkClicked(markdown::Url), +/// } +/// +/// impl State { +/// pub fn new() -> Self { +/// Self { +/// markdown: markdown::parse("This is some **Markdown**!").collect(), +/// } +/// } +/// +/// fn view(&self) -> Element<'_, Message> { +/// markdown::view( +/// &self.markdown, +/// markdown::Settings::default(), +/// markdown::Style::from_palette(Theme::TokyoNightStorm.palette()), +/// ) +/// .map(Message::LinkClicked) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::LinkClicked(url) => { +/// println!("The following url was clicked: {url}"); +/// } +/// } +/// } +/// } +/// ``` pub fn parse(markdown: &str) -> impl Iterator + '_ { struct List { start: Option, @@ -484,6 +570,49 @@ impl Style { /// Display a bunch of Markdown items. /// /// You can obtain the items with [`parse`]. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::markdown; +/// use iced::Theme; +/// +/// struct State { +/// markdown: Vec, +/// } +/// +/// enum Message { +/// LinkClicked(markdown::Url), +/// } +/// +/// impl State { +/// pub fn new() -> Self { +/// Self { +/// markdown: markdown::parse("This is some **Markdown**!").collect(), +/// } +/// } +/// +/// fn view(&self) -> Element<'_, Message> { +/// markdown::view( +/// &self.markdown, +/// markdown::Settings::default(), +/// markdown::Style::from_palette(Theme::TokyoNightStorm.palette()), +/// ) +/// .map(Message::LinkClicked) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::LinkClicked(url) => { +/// println!("The following url was clicked: {url}"); +/// } +/// } +/// } +/// } +/// ``` pub fn view<'a, Theme, Renderer>( items: impl IntoIterator, settings: Settings, diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 4473119d41..9d4dda25f8 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -1,8 +1,54 @@ -//! Let your users split regions of your application and organize layout dynamically. +//! Pane grids let your users split regions of your application and organize layout dynamically. //! //! ![Pane grid - Iced](https://iced.rs/examples/pane_grid.gif) //! +//! This distribution of space is common in tiling window managers (like +//! [`awesome`](https://awesomewm.org/), [`i3`](https://i3wm.org/), or even +//! [`tmux`](https://github.com/tmux/tmux)). +//! +//! A [`PaneGrid`] supports: +//! +//! * Vertical and horizontal splits +//! * Tracking of the last active pane +//! * Mouse-based resizing +//! * Drag and drop to reorganize panes +//! * Hotkey support +//! * Configurable modifier keys +//! * [`State`] API to perform actions programmatically (`split`, `swap`, `resize`, etc.) +//! //! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::{pane_grid, text}; +//! +//! struct State { +//! panes: pane_grid::State, +//! } +//! +//! enum Pane { +//! SomePane, +//! AnotherKindOfPane, +//! } +//! +//! enum Message { +//! PaneDragged(pane_grid::DragEvent), +//! PaneResized(pane_grid::ResizeEvent), +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! pane_grid(&state.panes, |pane, state, is_maximized| { +//! pane_grid::Content::new(match state { +//! Pane::SomePane => text("This is some pane"), +//! Pane::AnotherKindOfPane => text("This is another kind of pane"), +//! }) +//! }) +//! .on_drag(Message::PaneDragged) +//! .on_resize(10, Message::PaneResized) +//! .into() +//! } +//! ``` //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, //! drag and drop, and hotkey support. //! @@ -68,14 +114,18 @@ const THICKNESS_RATIO: f32 = 25.0; /// * Configurable modifier keys /// * [`State`] API to perform actions programmatically (`split`, `swap`, `resize`, etc.) /// -/// ## Example -/// +/// # Example /// ```no_run -/// # use iced_widget::{pane_grid, text}; -/// # -/// # type PaneGrid<'a, Message> = iced_widget::PaneGrid<'a, Message>; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # -/// enum PaneState { +/// use iced::widget::{pane_grid, text}; +/// +/// struct State { +/// panes: pane_grid::State, +/// } +/// +/// enum Pane { /// SomePane, /// AnotherKindOfPane, /// } @@ -85,17 +135,17 @@ const THICKNESS_RATIO: f32 = 25.0; /// PaneResized(pane_grid::ResizeEvent), /// } /// -/// let (mut state, _) = pane_grid::State::new(PaneState::SomePane); -/// -/// let pane_grid = -/// PaneGrid::new(&state, |pane, state, is_maximized| { +/// fn view(state: &State) -> Element<'_, Message> { +/// pane_grid(&state.panes, |pane, state, is_maximized| { /// pane_grid::Content::new(match state { -/// PaneState::SomePane => text("This is some pane"), -/// PaneState::AnotherKindOfPane => text("This is another kind of pane"), +/// Pane::SomePane => text("This is some pane"), +/// Pane::AnotherKindOfPane => text("This is another kind of pane"), /// }) /// }) /// .on_drag(Message::PaneDragged) -/// .on_resize(10, Message::PaneResized); +/// .on_resize(10, Message::PaneResized) +/// .into() +/// } /// ``` #[allow(missing_debug_implementations)] pub struct PaneGrid< diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 1fc9951e34..ff54fe8a69 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -1,4 +1,65 @@ -//! Display a dropdown list of selectable values. +//! Pick lists display a dropdown list of selectable options. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::pick_list; +//! +//! struct State { +//! favorite: Option, +//! } +//! +//! #[derive(Debug, Clone, Copy, PartialEq, Eq)] +//! enum Fruit { +//! Apple, +//! Orange, +//! Strawberry, +//! Tomato, +//! } +//! +//! #[derive(Debug, Clone)] +//! enum Message { +//! FruitSelected(Fruit), +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! let fruits = [ +//! Fruit::Apple, +//! Fruit::Orange, +//! Fruit::Strawberry, +//! Fruit::Tomato, +//! ]; +//! +//! pick_list( +//! fruits, +//! state.favorite, +//! Message::FruitSelected, +//! ) +//! .placeholder("Select your favorite fruit...") +//! .into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::FruitSelected(fruit) => { +//! state.favorite = Some(fruit); +//! } +//! } +//! } +//! +//! impl std::fmt::Display for Fruit { +//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +//! f.write_str(match self { +//! Self::Apple => "Apple", +//! Self::Orange => "Orange", +//! Self::Strawberry => "Strawberry", +//! Self::Tomato => "Tomato", +//! }) +//! } +//! } +//! ``` use crate::core::alignment; use crate::core::event::{self, Event}; use crate::core::keyboard; @@ -20,6 +81,67 @@ use std::borrow::Borrow; use std::f32; /// A widget for selecting a single value from a list of options. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::pick_list; +/// +/// struct State { +/// favorite: Option, +/// } +/// +/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// enum Fruit { +/// Apple, +/// Orange, +/// Strawberry, +/// Tomato, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// FruitSelected(Fruit), +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// let fruits = [ +/// Fruit::Apple, +/// Fruit::Orange, +/// Fruit::Strawberry, +/// Fruit::Tomato, +/// ]; +/// +/// pick_list( +/// fruits, +/// state.favorite, +/// Message::FruitSelected, +/// ) +/// .placeholder("Select your favorite fruit...") +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::FruitSelected(fruit) => { +/// state.favorite = Some(fruit); +/// } +/// } +/// } +/// +/// impl std::fmt::Display for Fruit { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// f.write_str(match self { +/// Self::Apple => "Apple", +/// Self::Orange => "Orange", +/// Self::Strawberry => "Strawberry", +/// Self::Tomato => "Tomato", +/// }) +/// } +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct PickList< 'a, diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs index a10feea6d3..8c665c8cba 100644 --- a/widget/src/progress_bar.rs +++ b/widget/src/progress_bar.rs @@ -1,4 +1,24 @@ -//! Provide progress feedback to your users. +//! Progress bars visualize the progression of an extended computer operation, such as a download, file transfer, or installation. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::progress_bar; +//! +//! struct State { +//! progress: f32, +//! } +//! +//! enum Message { +//! // ... +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! progress_bar(0.0..=100.0, state.progress).into() +//! } +//! ``` use crate::core::border::{self, Border}; use crate::core::layout; use crate::core::mouse; @@ -15,14 +35,23 @@ use std::ops::RangeInclusive; /// /// # Example /// ```no_run -/// # type ProgressBar<'a> = iced_widget::ProgressBar<'a>; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # -/// let value = 50.0; +/// use iced::widget::progress_bar; /// -/// ProgressBar::new(0.0..=100.0, value); -/// ``` +/// struct State { +/// progress: f32, +/// } +/// +/// enum Message { +/// // ... +/// } /// -/// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png) +/// fn view(state: &State) -> Element<'_, Message> { +/// progress_bar(0.0..=100.0, state.progress).into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct ProgressBar<'a, Theme = crate::Theme> where diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs index e064aadae7..21dee6b158 100644 --- a/widget/src/qr_code.rs +++ b/widget/src/qr_code.rs @@ -1,4 +1,25 @@ -//! Encode and display information in a QR code. +//! QR codes display information in a type of two-dimensional matrix barcode. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::qr_code; +//! +//! struct State { +//! data: qr_code::Data, +//! } +//! +//! #[derive(Debug, Clone)] +//! enum Message { +//! // ... +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! qr_code(&state.data).into() +//! } +//! ``` use crate::canvas; use crate::core::layout; use crate::core::mouse; @@ -18,6 +39,27 @@ const QUIET_ZONE: usize = 2; /// A type of matrix barcode consisting of squares arranged in a grid which /// can be read by an imaging device, such as a camera. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::qr_code; +/// +/// struct State { +/// data: qr_code::Data, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// qr_code(&state.data).into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct QRCode<'a, Theme = crate::Theme> where diff --git a/widget/src/radio.rs b/widget/src/radio.rs index cfa961f38e..300318fde8 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -1,4 +1,61 @@ -//! Create choices using radio buttons. +//! Radio buttons let users choose a single option from a bunch of options. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::{column, radio}; +//! +//! struct State { +//! selection: Option, +//! } +//! +//! #[derive(Debug, Clone, Copy)] +//! enum Message { +//! RadioSelected(Choice), +//! } +//! +//! #[derive(Debug, Clone, Copy, PartialEq, Eq)] +//! enum Choice { +//! A, +//! B, +//! C, +//! All, +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! let a = radio( +//! "A", +//! Choice::A, +//! state.selection, +//! Message::RadioSelected, +//! ); +//! +//! let b = radio( +//! "B", +//! Choice::B, +//! state.selection, +//! Message::RadioSelected, +//! ); +//! +//! let c = radio( +//! "C", +//! Choice::C, +//! state.selection, +//! Message::RadioSelected, +//! ); +//! +//! let all = radio( +//! "All of the above", +//! Choice::All, +//! state.selection, +//! Message::RadioSelected +//! ); +//! +//! column![a, b, c, all].into() +//! } +//! ``` use crate::core::alignment; use crate::core::border::{self, Border}; use crate::core::event::{self, Event}; @@ -18,54 +75,59 @@ use crate::core::{ /// /// # Example /// ```no_run -/// # type Radio<'a, Message> = -/// # iced_widget::Radio<'a, Message, iced_widget::Theme, iced_widget::renderer::Renderer>; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # -/// # use iced_widget::column; -/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] -/// pub enum Choice { -/// A, -/// B, -/// C, -/// All, +/// use iced::widget::{column, radio}; +/// +/// struct State { +/// selection: Option, /// } /// /// #[derive(Debug, Clone, Copy)] -/// pub enum Message { +/// enum Message { /// RadioSelected(Choice), /// } /// -/// let selected_choice = Some(Choice::A); +/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// enum Choice { +/// A, +/// B, +/// C, +/// All, +/// } /// -/// let a = Radio::new( -/// "A", -/// Choice::A, -/// selected_choice, -/// Message::RadioSelected, -/// ); +/// fn view(state: &State) -> Element<'_, Message> { +/// let a = radio( +/// "A", +/// Choice::A, +/// state.selection, +/// Message::RadioSelected, +/// ); /// -/// let b = Radio::new( -/// "B", -/// Choice::B, -/// selected_choice, -/// Message::RadioSelected, -/// ); +/// let b = radio( +/// "B", +/// Choice::B, +/// state.selection, +/// Message::RadioSelected, +/// ); /// -/// let c = Radio::new( -/// "C", -/// Choice::C, -/// selected_choice, -/// Message::RadioSelected, -/// ); +/// let c = radio( +/// "C", +/// Choice::C, +/// state.selection, +/// Message::RadioSelected, +/// ); /// -/// let all = Radio::new( -/// "All of the above", -/// Choice::All, -/// selected_choice, -/// Message::RadioSelected -/// ); +/// let all = radio( +/// "All of the above", +/// Choice::All, +/// state.selection, +/// Message::RadioSelected +/// ); /// -/// let content = column![a, b, c, all]; +/// column![a, b, c, all].into() +/// } /// ``` #[allow(missing_debug_implementations)] pub struct Radio<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> diff --git a/widget/src/row.rs b/widget/src/row.rs index 85af912f07..fbb3f06676 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -12,6 +12,27 @@ use crate::core::{ }; /// A container that distributes its contents horizontally. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{button, row}; +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// row![ +/// "I am to the left!", +/// button("I am in the middle!"), +/// "I am to the right!", +/// ].into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Row<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> { spacing: f32, diff --git a/widget/src/rule.rs b/widget/src/rule.rs index bbcd577e5d..92199ca9bf 100644 --- a/widget/src/rule.rs +++ b/widget/src/rule.rs @@ -1,4 +1,21 @@ -//! Display a horizontal or vertical rule for dividing content. +//! Rules divide space horizontally or vertically. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! use iced::widget::horizontal_rule; +//! +//! #[derive(Clone)] +//! enum Message { +//! // ..., +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! horizontal_rule(2).into() +//! } +//! ``` use crate::core; use crate::core::border; use crate::core::layout; @@ -10,6 +27,23 @@ use crate::core::{ }; /// Display a horizontal or vertical rule for dividing content. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::horizontal_rule; +/// +/// #[derive(Clone)] +/// enum Message { +/// // ..., +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// horizontal_rule(2).into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Rule<'a, Theme = crate::Theme> where diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 00a6b556ae..6d7f251efb 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -1,4 +1,24 @@ -//! Navigate an endless amount of content with a scrollbar. +//! Scrollables let users navigate an endless amount of content with a scrollbar. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! use iced::widget::{column, scrollable, vertical_space}; +//! +//! enum Message { +//! // ... +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! scrollable(column![ +//! "Scroll me!", +//! vertical_space().height(3000), +//! "You did it!", +//! ]).into() +//! } +//! ``` use crate::container; use crate::core::border::{self, Border}; use crate::core::event::{self, Event}; @@ -24,6 +44,26 @@ pub use operation::scrollable::{AbsoluteOffset, RelativeOffset}; /// A widget that can vertically display an infinite amount of content with a /// scrollbar. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{column, scrollable, vertical_space}; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// scrollable(column![ +/// "Scroll me!", +/// vertical_space().height(3000), +/// "You did it!", +/// ]).into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Scrollable< 'a, diff --git a/widget/src/slider.rs b/widget/src/slider.rs index 15514afe0a..9477958d3d 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -1,4 +1,33 @@ -//! Display an interactive selector of a single value from a range of values. +//! Sliders let users set a value by moving an indicator. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::slider; +//! +//! struct State { +//! value: f32, +//! } +//! +//! #[derive(Debug, Clone)] +//! enum Message { +//! ValueChanged(f32), +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! slider(0.0..=100.0, state.value, Message::ValueChanged).into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::ValueChanged(value) => { +//! state.value = value; +//! } +//! } +//! } +//! ``` use crate::core::border::{self, Border}; use crate::core::event::{self, Event}; use crate::core::keyboard; @@ -25,19 +54,32 @@ use std::ops::RangeInclusive; /// /// # Example /// ```no_run -/// # type Slider<'a, T, Message> = iced_widget::Slider<'a, Message, T>; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # -/// #[derive(Clone)] -/// pub enum Message { -/// SliderChanged(f32), +/// use iced::widget::slider; +/// +/// struct State { +/// value: f32, /// } /// -/// let value = 50.0; +/// #[derive(Debug, Clone)] +/// enum Message { +/// ValueChanged(f32), +/// } /// -/// Slider::new(0.0..=100.0, value, Message::SliderChanged); -/// ``` +/// fn view(state: &State) -> Element<'_, Message> { +/// slider(0.0..=100.0, state.value, Message::ValueChanged).into() +/// } /// -/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true) +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::ValueChanged(value) => { +/// state.value = value; +/// } +/// } +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Slider<'a, T, Message, Theme = crate::Theme> where diff --git a/widget/src/svg.rs b/widget/src/svg.rs index bec0090fb4..8d57265a7a 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -1,4 +1,20 @@ -//! Display vector graphics in your application. +//! Svg widgets display vector graphics in your application. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! use iced::widget::svg; +//! +//! enum Message { +//! // ... +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! svg("tiger.svg").into() +//! } +//! ``` use crate::core::layout; use crate::core::mouse; use crate::core::renderer; @@ -19,6 +35,22 @@ pub use crate::core::svg::Handle; /// /// [`Svg`] images can have a considerable rendering cost when resized, /// specially when they are complex. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::svg; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// svg("tiger.svg").into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Svg<'a, Theme = crate::Theme> where diff --git a/widget/src/text.rs b/widget/src/text.rs index 9bf7fce41c..c22434342a 100644 --- a/widget/src/text.rs +++ b/widget/src/text.rs @@ -5,6 +5,26 @@ pub use crate::core::text::{Fragment, Highlighter, IntoFragment, Span}; pub use crate::core::widget::text::*; pub use rich::Rich; -/// A paragraph. +/// A bunch of text. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::text; +/// use iced::color; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// text("Hello, this is iced!") +/// .size(20) +/// .color(color!(0x0000ff)) +/// .into() +/// } +/// ``` pub type Text<'a, Theme = crate::Theme, Renderer = crate::Renderer> = crate::core::widget::Text<'a, Theme, Renderer>; diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 1df97962a2..5979531896 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -1,4 +1,36 @@ -//! Display a multi-line text input for text editing. +//! Text editors display a multi-line text input for text editing. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::text_editor; +//! +//! struct State { +//! content: text_editor::Content, +//! } +//! +//! #[derive(Debug, Clone)] +//! enum Message { +//! Edit(text_editor::Action) +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! text_editor(&state.content) +//! .placeholder("Type something here...") +//! .on_action(Message::Edit) +//! .into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::Edit(action) => { +//! state.content.perform(action); +//! } +//! } +//! } +//! ``` use crate::core::alignment; use crate::core::clipboard::{self, Clipboard}; use crate::core::event::{self, Event}; @@ -27,6 +59,38 @@ use std::sync::Arc; pub use text::editor::{Action, Edit, Motion}; /// A multi-line text input. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// # +/// use iced::widget::text_editor; +/// +/// struct State { +/// content: text_editor::Content, +/// } +/// +/// #[derive(Debug, Clone)] +/// enum Message { +/// Edit(text_editor::Action) +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// text_editor(&state.content) +/// .placeholder("Type something here...") +/// .on_action(Message::Edit) +/// .into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::Edit(action) => { +/// state.content.perform(action); +/// } +/// } +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct TextEditor< 'a, diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 3032dd1353..5bbf76f59a 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -1,6 +1,35 @@ -//! Display fields that can be filled with text. +//! Text inputs display fields that can be filled with text. //! -//! A [`TextInput`] has some local [`State`]. +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::text_input; +//! +//! struct State { +//! content: String, +//! } +//! +//! #[derive(Debug, Clone)] +//! enum Message { +//! ContentChanged(String) +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! text_input("Type something here...", &state.content) +//! .on_input(Message::ContentChanged) +//! .into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::ContentChanged(content) => { +//! state.content = content; +//! } +//! } +//! } +//! ``` mod editor; mod value; @@ -38,23 +67,34 @@ use crate::runtime::Action; /// /// # Example /// ```no_run -/// # pub type TextInput<'a, Message> = iced_widget::TextInput<'a, Message>; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # +/// use iced::widget::text_input; +/// +/// struct State { +/// content: String, +/// } +/// /// #[derive(Debug, Clone)] /// enum Message { -/// TextInputChanged(String), +/// ContentChanged(String) /// } /// -/// let value = "Some text"; +/// fn view(state: &State) -> Element<'_, Message> { +/// text_input("Type something here...", &state.content) +/// .on_input(Message::ContentChanged) +/// .into() +/// } /// -/// let input = TextInput::new( -/// "This is the placeholder...", -/// value, -/// ) -/// .on_input(Message::TextInputChanged) -/// .padding(10); +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::ContentChanged(content) => { +/// state.content = content; +/// } +/// } +/// } /// ``` -/// ![Text input drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) #[allow(missing_debug_implementations)] pub struct TextInput< 'a, diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index 1c425dc194..3b41208195 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -1,4 +1,35 @@ -//! Show toggle controls using togglers. +//! Togglers let users make binary choices by toggling a switch. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::toggler; +//! +//! struct State { +//! is_checked: bool, +//! } +//! +//! enum Message { +//! TogglerToggled(bool), +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! toggler(state.is_checked) +//! .label("Toggle me!") +//! .on_toggle(Message::TogglerToggled) +//! .into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::TogglerToggled(is_checked) => { +//! state.is_checked = is_checked; +//! } +//! } +//! } +//! ``` use crate::core::alignment; use crate::core::event; use crate::core::layout; @@ -16,19 +47,34 @@ use crate::core::{ /// A toggler widget. /// /// # Example -/// /// ```no_run -/// # type Toggler<'a, Message> = iced_widget::Toggler<'a, Message>; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # -/// pub enum Message { +/// use iced::widget::toggler; +/// +/// struct State { +/// is_checked: bool, +/// } +/// +/// enum Message { /// TogglerToggled(bool), /// } /// -/// let is_toggled = true; +/// fn view(state: &State) -> Element<'_, Message> { +/// toggler(state.is_checked) +/// .label("Toggle me!") +/// .on_toggle(Message::TogglerToggled) +/// .into() +/// } /// -/// Toggler::new(is_toggled) -/// .label("Toggle me!") -/// .on_toggle(Message::TogglerToggled); +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::TogglerToggled(is_checked) => { +/// state.is_checked = is_checked; +/// } +/// } +/// } /// ``` #[allow(missing_debug_implementations)] pub struct Toggler< diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 39f2e07de0..e98f4da7ea 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -1,4 +1,26 @@ -//! Display a widget over another. +//! Tooltips display a hint of information over some element when hovered. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! use iced::widget::{container, tooltip}; +//! +//! enum Message { +//! // ... +//! } +//! +//! fn view(_state: &State) -> Element<'_, Message> { +//! tooltip( +//! "Hover me to display the tooltip!", +//! container("This is the tooltip contents!") +//! .padding(10) +//! .style(container::rounded_box), +//! tooltip::Position::Bottom, +//! ).into() +//! } +//! ``` use crate::container; use crate::core::event::{self, Event}; use crate::core::layout::{self, Layout}; @@ -13,6 +35,28 @@ use crate::core::{ }; /// An element to display a widget over another. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::{container, tooltip}; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(_state: &State) -> Element<'_, Message> { +/// tooltip( +/// "Hover me to display the tooltip!", +/// container("This is the tooltip contents!") +/// .padding(10) +/// .style(container::rounded_box), +/// tooltip::Position::Bottom, +/// ).into() +/// } +/// ``` #[allow(missing_debug_implementations)] pub struct Tooltip< 'a, diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index a75ba49c3f..18633474e2 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -1,4 +1,33 @@ -//! Display an interactive selector of a single value from a range of values. +//! Sliders let users set a value by moving an indicator. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! # +//! use iced::widget::slider; +//! +//! struct State { +//! value: f32, +//! } +//! +//! #[derive(Debug, Clone)] +//! enum Message { +//! ValueChanged(f32), +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! slider(0.0..=100.0, state.value, Message::ValueChanged).into() +//! } +//! +//! fn update(state: &mut State, message: Message) { +//! match message { +//! Message::ValueChanged(value) => { +//! state.value = value; +//! } +//! } +//! } +//! ``` use std::ops::RangeInclusive; pub use crate::slider::{ @@ -29,16 +58,31 @@ use crate::core::{ /// /// # Example /// ```no_run -/// # type VerticalSlider<'a, T, Message> = iced_widget::VerticalSlider<'a, T, Message>; +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; } +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; /// # -/// #[derive(Clone)] -/// pub enum Message { -/// SliderChanged(f32), +/// use iced::widget::vertical_slider; +/// +/// struct State { +/// value: f32, /// } /// -/// let value = 50.0; +/// #[derive(Debug, Clone)] +/// enum Message { +/// ValueChanged(f32), +/// } /// -/// VerticalSlider::new(0.0..=100.0, value, Message::SliderChanged); +/// fn view(state: &State) -> Element<'_, Message> { +/// vertical_slider(0.0..=100.0, state.value, Message::ValueChanged).into() +/// } +/// +/// fn update(state: &mut State, message: Message) { +/// match message { +/// Message::ValueChanged(value) => { +/// state.value = value; +/// } +/// } +/// } /// ``` #[allow(missing_debug_implementations)] pub struct VerticalSlider<'a, T, Message, Theme = crate::Theme>