Merge branch 'master' into dev/system-information

This commit is contained in:
Héctor Ramón Jiménez 2022-05-04 17:19:28 +02:00
commit 27fdc70756
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
90 changed files with 1363 additions and 334 deletions

View file

@ -10,13 +10,19 @@ jobs:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- uses: hecrj/setup-rust-action@v1
with:
rust-version: nightly
- uses: actions/checkout@v2
- name: Generate documentation
run: |
cargo doc --no-deps --all-features \
RUSTDOCFLAGS="--cfg docsrs" \
cargo doc --no-deps --all-features \
-p iced_core \
-p iced_style \
-p iced_futures \
-p iced_native \
-p iced_lazy \
-p iced_pure \
-p iced_graphics \
-p iced_wgpu \
-p iced_glow \

View file

@ -6,6 +6,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.4.2] - 2022-05-03
### Fixed
- `Padding` type not exposed in `iced`.
## [0.4.1] - 2022-05-02
### Fixed
- Version number in `README`.
## [0.4.0] - 2022-05-02
### Added
- __[Stateless widgets][stateless]__ (#1284)
A brand new widget API that removes the need to keep track of internal widget state. No more `button::State` in your application!
- __[`Component` trait][component]__ (#1131)
A new trait to implement custom widgets with internal mutable state while using composition and [The Elm Architecture].
- __[`Responsive` widget][responsive]__ (#1193)
A widget that is aware of its dimensions and can be used to easily build responsive user interfaces.
- __[Experimental WebGL support][webgl]__ (#1096)
Applications can now be rendered into an HTML `canvas` when targeting Wasm by leveraging the WebGL support in [`wgpu`]. Thanks to @pacmancoder and @kaimast!
- __[Support for Raspberry Pis and older devices][raspberry]__ (#1160)
The compatibility of our OpenGL renderer has been improved and should run on any hardware that supports OpenGL 3.0+ or OpenGL ES 2.0+. Additionally, we started maintaining [Docker images for `aarch64` and `armv7`](https://github.com/orgs/iced-rs/packages) to easily cross-compile `iced` applications and target Raspberry Pis. Thanks to @derezzedex!
- __[Simpler `Renderer` APIs][renderer_apis]__ (#1110)
The surface of the `Renderer` APIs of the library has been considerably reduced. Instead of a `Renderer` trait per widget, now there are only 3 traits that are reused by all the widgets.
[webgl]: https://github.com/iced-rs/iced/pull/1096
[renderer_apis]: https://github.com/iced-rs/iced/pull/1110
[component]: https://github.com/iced-rs/iced/pull/1131
[raspberry]: https://github.com/iced-rs/iced/pull/1160
[responsive]: https://github.com/iced-rs/iced/pull/1193
[stateless]: https://github.com/iced-rs/iced/pull/1284
[The Elm Architecture]: https://guide.elm-lang.org/architecture/
[`wgpu`]: https://github.com/gfx-rs/wgpu
## [0.3.0] - 2021-03-31
### Added
- Touch support. [#57] [#650] (thanks to @simlay and @discordance!)
@ -219,7 +257,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- First release! :tada:
[Unreleased]: https://github.com/iced-rs/iced/compare/0.3.0...HEAD
[Unreleased]: https://github.com/iced-rs/iced/compare/0.4.2...HEAD
[0.4.2]: https://github.com/iced-rs/iced/compare/0.4.1...0.4.2
[0.4.1]: https://github.com/iced-rs/iced/compare/0.4.0...0.4.1
[0.4.0]: https://github.com/iced-rs/iced/compare/0.3.0...0.4.0
[0.3.0]: https://github.com/iced-rs/iced/compare/0.2.0...0.3.0
[0.2.0]: https://github.com/iced-rs/iced/compare/0.1.1...0.2.0
[0.1.1]: https://github.com/iced-rs/iced/compare/0.1.0...0.1.1

View file

@ -2,7 +2,7 @@
Thank you for considering contributing to Iced! Feel free to read [the ecosystem overview] and [the roadmap] to get an idea of the current state of the library.
The main advice for new contributors is to share your ideas with the community. Introduce yourself over our [Discord server] or [start a discussion in an issue](https://github.com/hecrj/iced/issues) explaining what you have in mind (do not be afraid of duplicated issues!). If you want to talk directly to me (@hecrj), you can also find me on Discord (`lone_scientist#9554`).
The main advice for new contributors is to share your ideas with the community. Introduce yourself over our [Discord server] or [start a discussion in an issue](https://github.com/iced-rs/iced/issues) explaining what you have in mind (do not be afraid of duplicated issues!). If you want to talk directly to me (@hecrj), you can also find me on Discord (`lone_scientist#9554`).
This is a very important step. It helps to coordinate work, get on the same page, and start building trust. Please, do not skip it! Remember that [Code is the Easy Part] and also [The Hard Parts of Open Source]!

View file

@ -1,6 +1,6 @@
[package]
name = "iced"
version = "0.3.0"
version = "0.4.2"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A cross-platform GUI library inspired by Elm"
@ -96,26 +96,27 @@ members = [
"examples/pure/pane_grid",
"examples/pure/pick_list",
"examples/pure/todos",
"examples/pure/tooltip",
"examples/pure/tour",
]
[dependencies]
iced_core = { version = "0.4", path = "core" }
iced_futures = { version = "0.3", path = "futures" }
iced_native = { version = "0.4", path = "native" }
iced_graphics = { version = "0.2", path = "graphics" }
iced_winit = { version = "0.3", path = "winit" }
iced_glutin = { version = "0.2", path = "glutin", optional = true }
iced_glow = { version = "0.2", path = "glow", optional = true }
iced_pure = { version = "0.1", path = "pure", optional = true }
iced_core = { version = "0.5", path = "core" }
iced_futures = { version = "0.4", path = "futures" }
iced_native = { version = "0.5", path = "native" }
iced_graphics = { version = "0.3", path = "graphics" }
iced_winit = { version = "0.4", path = "winit" }
iced_glutin = { version = "0.3", path = "glutin", optional = true }
iced_glow = { version = "0.3", path = "glow", optional = true }
iced_pure = { version = "0.2", path = "pure", optional = true }
thiserror = "1.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
iced_wgpu = { version = "0.4", path = "wgpu", optional = true }
iced_wgpu = { version = "0.5", path = "wgpu", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced_wgpu = { version = "0.4", path = "wgpu", features = ["webgl"], optional = true }
iced_wgpu = { version = "0.5", path = "wgpu", features = ["webgl"], optional = true }
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
features = ["image", "svg", "canvas", "qr_code"]
features = ["image", "svg", "canvas", "qr_code", "pure"]

View file

@ -2,13 +2,13 @@
<img src="docs/logo.svg" width="140px" />
# iced
# Iced
[![Documentation](https://docs.rs/iced/badge.svg)][documentation]
[![Crates.io](https://img.shields.io/crates/v/iced.svg)](https://crates.io/crates/iced)
[![License](https://img.shields.io/crates/l/iced.svg)](https://github.com/hecrj/iced/blob/master/LICENSE)
[![License](https://img.shields.io/crates/l/iced.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
[![Downloads](https://img.shields.io/crates/d/iced.svg)](https://crates.io/crates/iced)
[![Test Status](https://github.com/hecrj/iced/workflows/Test/badge.svg?event=push)](https://github.com/hecrj/iced/actions)
[![Test Status](https://github.com/iced-rs/iced/workflows/Test/badge.svg?event=push)](https://github.com/iced-rs/iced/actions)
[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
A cross-platform GUI library for Rust focused on simplicity and type-safety.
@ -40,11 +40,11 @@ Inspired by [Elm].
* A [windowing shell]
* A [web runtime] leveraging the DOM
__iced is currently experimental software.__ [Take a look at the roadmap],
__Iced is currently experimental software.__ [Take a look at the roadmap],
[check out the issues], and [feel free to contribute!]
[Cross-platform support]: https://raw.githubusercontent.com/iced-rs/iced/master/docs/images/todos_desktop.jpg
[the Web]: https://iced.rs/
[the Web]: https://github.com/iced-rs/iced_web
[text inputs]: https://gfycat.com/alertcalmcrow-rust-gui
[scrollables]: https://gfycat.com/perkybaggybaboon-rust-gui
[Debug overlay with performance metrics]: https://gfycat.com/incredibledarlingbee
@ -66,16 +66,16 @@ __iced is currently experimental software.__ [Take a look at the roadmap],
Add `iced` as a dependency in your `Cargo.toml`:
```toml
iced = "0.3"
iced = "0.4"
```
__iced moves fast and the `master` branch can contain breaking changes!__ If
__Iced moves fast and the `master` branch can contain breaking changes!__ If
you want to learn about a specific release, check out [the release list].
[the release list]: https://github.com/iced-rs/iced/releases
## Overview
Inspired by [The Elm Architecture], iced expects you to split user interfaces
Inspired by [The Elm Architecture], Iced expects you to split user interfaces
into four different concepts:
* __State__ — the state of your application
@ -165,7 +165,7 @@ impl Counter {
}
```
And that's everything! We just wrote a whole user interface. iced is now able
And that's everything! We just wrote a whole user interface. Iced is now able
to:
1. Take the result of our __view logic__ and layout its widgets.
@ -176,7 +176,7 @@ to:
Browse the [documentation] and the [examples] to learn more!
## Implementation details
iced was originally born as an attempt at bringing the simplicity of [Elm] and
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 core of the library was implemented during May 2019 in [this pull request].
@ -189,7 +189,7 @@ end-user-oriented GUI library, while keeping [the ecosystem] modular:
<p align="center">
<a href="ECOSYSTEM.md">
<img alt="iced ecosystem" src="docs/graphs/ecosystem.png" width="80%">
<img alt="The Iced Ecosystem" src="docs/graphs/ecosystem.png" width="80%">
</a>
</p>
@ -215,14 +215,14 @@ $ cargo run --features iced/glow --package game_of_life
and then use it in your project with
```toml
iced = { version = "0.3", default-features = false, features = ["glow"] }
iced = { version = "0.4", default-features = false, features = ["glow"] }
```
**NOTE:** Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0,
but if you don't, right now there's no software fallback, so it means your hardware
doesn't support Iced.
[built-in renderer]: https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md#Renderers
[built-in renderer]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md#Renderers
## Contributing / Feedback
Contributions are greatly appreciated! If you want to contribute, please
@ -234,15 +234,15 @@ awesome folks) over the `#games-and-graphics` and `#gui-and-ui` channels in
the [Rust Community Discord]. I go by `lone_scientist#9554` there.
## Sponsors
The development of iced is sponsored by the [Cryptowatch] team at [Kraken.com]
The development of Iced is sponsored by the [Cryptowatch] team at [Kraken.com]
[documentation]: https://docs.rs/iced/
[examples]: https://github.com/hecrj/iced/tree/master/examples
[examples]: https://github.com/iced-rs/iced/tree/master/examples
[Coffee]: https://github.com/hecrj/coffee
[Elm]: https://elm-lang.org/
[The Elm Architecture]: https://guide.elm-lang.org/architecture/
[the current issues]: https://github.com/hecrj/iced/issues
[contributing guidelines]: https://github.com/hecrj/iced/blob/master/CONTRIBUTING.md
[the current issues]: https://github.com/iced-rs/iced/issues
[contributing guidelines]: https://github.com/iced-rs/iced/blob/master/CONTRIBUTING.md
[Discord server]: https://discord.gg/3xZJ65GAhd
[Rust Community Discord]: https://bit.ly/rust-community
[Cryptowatch]: https://cryptowat.ch/charts

View file

@ -1,6 +1,6 @@
[package]
name = "iced_core"
version = "0.4.0"
version = "0.5.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "The essential concepts of Iced"

View file

@ -41,21 +41,29 @@ impl Modifiers {
};
/// Returns true if the [`SHIFT`] key is pressed in the [`Modifiers`].
///
/// [`SHIFT`]: Self::SHIFT
pub fn shift(self) -> bool {
self.contains(Self::SHIFT)
}
/// Returns true if the [`CTRL`] key is pressed in the [`Modifiers`].
///
/// [`CTRL`]: Self::CTRL
pub fn control(self) -> bool {
self.contains(Self::CTRL)
}
/// Returns true if the [`ALT`] key is pressed in the [`Modifiers`].
///
/// [`ALT`]: Self::ALT
pub fn alt(self) -> bool {
self.contains(Self::ALT)
}
/// Returns true if the [`LOGO`] key is pressed in the [`Modifiers`].
///
/// [`LOGO`]: Self::LOGO
pub fn logo(self) -> bool {
self.contains(Self::LOGO)
}

View file

@ -7,8 +7,11 @@
//! ![The foundations of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true)
//!
//! [Iced]: https://github.com/iced-rs/iced
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/master/native
//! [`iced_web`]: https://github.com/iced-rs/iced/tree/master/web
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
//! [`iced_web`]: https://github.com/iced-rs/iced_web
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unused_results)]

View file

@ -0,0 +1,9 @@
[package]
name = "pure_tooltip"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>", "Casper Rogild Storm"]
edition = "2021"
publish = false
[dependencies]
iced = { path = "../../..", features = ["pure"] }

View file

@ -0,0 +1,93 @@
use iced::pure::{
button, container, tooltip, widget::tooltip::Position, Element, Sandbox,
};
use iced::{Length, Settings};
pub fn main() -> iced::Result {
Example::run(Settings::default())
}
struct Example {
position: Position,
}
#[derive(Debug, Clone)]
enum Message {
ChangePosition,
}
impl Sandbox for Example {
type Message = Message;
fn new() -> Self {
Self {
position: Position::Bottom,
}
}
fn title(&self) -> String {
String::from("Tooltip - Iced")
}
fn update(&mut self, message: Message) {
match message {
Message::ChangePosition => {
let position = match &self.position {
Position::FollowCursor => Position::Top,
Position::Top => Position::Bottom,
Position::Bottom => Position::Left,
Position::Left => Position::Right,
Position::Right => Position::FollowCursor,
};
self.position = position
}
}
}
fn view(&self) -> Element<Message> {
let tooltip = tooltip(
button("Press to change position")
.on_press(Message::ChangePosition),
position_to_text(self.position),
self.position,
)
.gap(10)
.style(style::Tooltip);
container(tooltip)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into()
}
}
fn position_to_text<'a>(position: Position) -> &'a str {
match position {
Position::FollowCursor => "Follow Cursor",
Position::Top => "Top",
Position::Bottom => "Bottom",
Position::Left => "Left",
Position::Right => "Right",
}
}
mod style {
use iced::container;
use iced::Color;
pub struct Tooltip;
impl container::StyleSheet for Tooltip {
fn style(&self) -> container::Style {
container::Style {
text_color: Some(Color::from_rgb8(0xEE, 0xEE, 0xEE)),
background: Some(Color::from_rgb(0.11, 0.42, 0.87).into()),
border_radius: 12.0,
..container::Style::default()
}
}
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "iced_futures"
version = "0.3.0"
version = "0.4.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "Commands, subscriptions, and runtimes for Iced"

View file

@ -17,7 +17,7 @@ impl<T> Command<T> {
Self(Internal::None)
}
/// Creates a [`Command`] that performs a single [`Action`].
/// Creates a [`Command`] that performs a single action.
pub const fn single(action: T) -> Self {
Self(Internal::Single(action))
}

View file

@ -9,6 +9,8 @@ use std::marker::PhantomData;
///
/// If you have an [`Executor`], a [`Runtime`] can be leveraged to run any
/// [`Command`] or [`Subscription`] and get notified of the results!
///
/// [`Command`]: crate::Command
#[derive(Debug)]
pub struct Runtime<Hasher, Event, Executor, Sender, Message> {
executor: Executor,
@ -51,10 +53,12 @@ where
self.executor.enter(f)
}
/// Spawns a [`Command`] in the [`Runtime`].
/// Spawns a [`Future`] in the [`Runtime`].
///
/// The resulting `Message` will be forwarded to the `Sender` of the
/// [`Runtime`].
///
/// [`Future`]: BoxFuture
pub fn spawn(&mut self, future: BoxFuture<Message>) {
use futures::{FutureExt, SinkExt};

View file

@ -125,9 +125,9 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
/// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how
/// to listen to time.
///
/// [examples]: https://github.com/iced-rs/iced/tree/0.3/examples
/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.3/examples/download_progress
/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.3/examples/stopwatch
/// [examples]: https://github.com/iced-rs/iced/tree/0.4/examples
/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.4/examples/download_progress
/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.4/examples/stopwatch
pub trait Recipe<Hasher: std::hash::Hasher, Event> {
/// The events that will be produced by a [`Subscription`] with this
/// [`Recipe`].

View file

@ -1,11 +1,11 @@
[package]
name = "iced_glow"
version = "0.2.0"
version = "0.3.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A glow renderer for iced"
license = "MIT AND OFL-1.1"
repository = "https://github.com/hecrj/iced"
repository = "https://github.com/iced-rs/iced"
[features]
canvas = ["iced_graphics/canvas"]
@ -24,11 +24,11 @@ bytemuck = "1.4"
log = "0.4"
[dependencies.iced_native]
version = "0.4"
version = "0.5"
path = "../native"
[dependencies.iced_graphics]
version = "0.2"
version = "0.3"
path = "../graphics"
features = ["font-fallback", "font-icons", "opengl"]

View file

@ -1,12 +1,12 @@
# `iced_glow`
[![Documentation](https://docs.rs/iced_glow/badge.svg)][documentation]
[![Crates.io](https://img.shields.io/crates/v/iced_glow.svg)](https://crates.io/crates/iced_glow)
[![License](https://img.shields.io/crates/l/iced_glow.svg)](https://github.com/hecrj/iced/blob/master/LICENSE)
[![License](https://img.shields.io/crates/l/iced_glow.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
`iced_glow` is a [`glow`] renderer for [`iced_native`]. This renderer supports OpenGL 3.0+ and OpenGL ES 2.0.
This is renderer is mostly used as a fallback for hardware that doesn't support [`wgpu`] (Vulkan, Metal or DX12).
This renderer is mostly used as a fallback for hardware that doesn't support [`wgpu`] (Vulkan, Metal or DX12).
Currently, `iced_glow` supports the following primitives:
- Text, which is rendered using [`glow_glyph`]. No shaping at all.
@ -34,7 +34,7 @@ iced_glow = "0.2"
__Iced moves fast and the `master` branch can contain breaking changes!__ If
you want to learn about a specific release, check out [the release list].
[the release list]: https://github.com/hecrj/iced/releases
[the release list]: https://github.com/iced-rs/iced/releases
## Current limitations

View file

@ -13,7 +13,7 @@ use iced_native::{Font, Size};
/// A [`glow`] graphics backend for [`iced`].
///
/// [`glow`]: https://github.com/grovesNL/glow
/// [`iced`]: https://github.com/hecrj/iced
/// [`iced`]: https://github.com/iced-rs/iced
#[derive(Debug)]
pub struct Backend {
quad_pipeline: quad::Pipeline,

View file

@ -1,9 +1,9 @@
//! A [`glow`] renderer for [`iced_native`].
//!
//! ![The native path of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//!
//! [`glow`]: https://github.com/grovesNL/glow
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
@ -37,5 +37,5 @@ pub use iced_native::{Alignment, Background, Color, Command, Length, Vector};
/// A [`glow`] graphics renderer for [`iced`].
///
/// [`glow`]: https://github.com/grovesNL/glow
/// [`iced`]: https://github.com/hecrj/iced
/// [`iced`]: https://github.com/iced-rs/iced
pub type Renderer = iced_graphics::Renderer<Backend>;

View file

@ -1,6 +1,6 @@
[package]
name = "iced_glutin"
version = "0.2.0"
version = "0.3.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A glutin runtime for Iced"
@ -23,14 +23,14 @@ git = "https://github.com/iced-rs/glutin"
rev = "7a0ee02782eb2bf059095e0c953c4bb53f1eef0e"
[dependencies.iced_native]
version = "0.4"
version = "0.5"
path = "../native"
[dependencies.iced_winit]
version = "0.3"
version = "0.4"
path = "../winit"
[dependencies.iced_graphics]
version = "0.2"
version = "0.3"
path = "../graphics"
features = ["opengl"]

View file

@ -1,11 +1,11 @@
[package]
name = "iced_graphics"
version = "0.2.0"
version = "0.3.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced"
license = "MIT"
repository = "https://github.com/hecrj/iced"
repository = "https://github.com/iced-rs/iced"
documentation = "https://docs.rs/iced_graphics"
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
@ -29,15 +29,15 @@ version = "1.4"
features = ["derive"]
[dependencies.iced_native]
version = "0.4"
version = "0.5"
path = "../native"
[dependencies.iced_style]
version = "0.3"
version = "0.4"
path = "../style"
[dependencies.iced_pure]
version = "0.1"
version = "0.2"
path = "../pure"
optional = true

View file

@ -1,9 +1,9 @@
//! A bunch of backend-agnostic types that can be leveraged to build a renderer
//! for [`iced`].
//!
//! ![The native path of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//!
//! [`iced`]: https://github.com/hecrj/iced
//! [`iced`]: https://github.com/iced-rs/iced
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]

View file

@ -51,10 +51,10 @@ use std::marker::PhantomData;
/// - [`solar_system`], an animated solar system drawn using the [`Canvas`] widget
/// and showcasing how to compose different transforms.
///
/// [examples]: https://github.com/hecrj/iced/tree/master/examples
/// [`clock`]: https://github.com/hecrj/iced/tree/master/examples/clock
/// [`game_of_life`]: https://github.com/hecrj/iced/tree/master/examples/game_of_life
/// [`solar_system`]: https://github.com/hecrj/iced/tree/master/examples/solar_system
/// [examples]: https://github.com/iced-rs/iced/tree/0.4/examples
/// [`clock`]: https://github.com/iced-rs/iced/tree/0.4/examples/clock
/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.4/examples/game_of_life
/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.4/examples/solar_system
///
/// ## Drawing a simple circle
/// If you want to get a quick overview, here's how we can draw a simple circle:

View file

@ -10,10 +10,10 @@ use crate::Rectangle;
///
/// [`Canvas`]: crate::widget::Canvas
pub trait Program<Message> {
/// The internal [`State`] mutated by the [`Program`].
/// The internal state mutated by the [`Program`].
type State: Default + 'static;
/// Updates the state of the [`Program`].
/// Updates the [`State`](Self::State) of the [`Program`].
///
/// When a [`Program`] is used in a [`Canvas`], the runtime will call this
/// method for each [`Event`].

View file

@ -45,7 +45,8 @@ pub trait Compositor: Sized {
/// Presents the [`Renderer`] primitives to the next frame of the given [`Surface`].
///
/// [`SwapChain`]: Self::SwapChain
/// [`Renderer`]: Self::Renderer
/// [`Surface`]: Self::Surface
fn present<T: AsRef<str>>(
&mut self,
renderer: &mut Self::Renderer,
@ -56,7 +57,7 @@ pub trait Compositor: Sized {
) -> Result<(), SurfaceError>;
}
/// Result of an unsuccessful call to [`Compositor::draw`].
/// Result of an unsuccessful call to [`Compositor::present`].
#[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum SurfaceError {
/// A timeout was encountered while trying to acquire the next frame.

View file

@ -1,7 +1,14 @@
[package]
name = "iced_lazy"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "Lazy widgets for Iced"
license = "MIT"
repository = "https://github.com/iced-rs/iced"
documentation = "https://docs.rs/iced_lazy"
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[features]
pure = ["iced_pure"]
@ -10,10 +17,10 @@ pure = ["iced_pure"]
ouroboros = "0.13"
[dependencies.iced_native]
version = "0.4"
version = "0.5"
path = "../native"
[dependencies.iced_pure]
version = "0.1"
version = "0.2"
path = "../pure"
optional = true

View file

@ -1,7 +1,12 @@
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod component;
pub mod responsive;
#[cfg(feature = "pure")]
#[cfg_attr(docsrs, doc(cfg(feature = "pure")))]
pub mod pure;
pub use component::Component;

View file

@ -67,12 +67,13 @@ impl<'a, Message, Renderer> Content<'a, Message, Renderer> {
self.element = view(new_size);
self.size = new_size;
tree.diff(&self.element);
self.layout = self
.element
.as_widget()
.layout(renderer, &layout::Limits::new(Size::ZERO, self.size));
tree.diff(&self.element);
}
fn resolve<R, T>(

View file

@ -1,6 +1,6 @@
[package]
name = "iced_native"
version = "0.4.0"
version = "0.5.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A renderer-agnostic library for native GUIs"
@ -16,14 +16,14 @@ unicode-segmentation = "1.6"
num-traits = "0.2"
[dependencies.iced_core]
version = "0.4"
version = "0.5"
path = "../core"
[dependencies.iced_futures]
version = "0.3"
version = "0.4"
path = "../futures"
features = ["thread-pool"]
[dependencies.iced_style]
version = "0.3"
version = "0.4"
path = "../style"

View file

@ -11,6 +11,8 @@ use std::fmt;
/// [`Command`]: crate::Command
pub enum Action<T> {
/// Run a [`Future`] to completion.
///
/// [`Future`]: iced_futures::BoxFuture
Future(iced_futures::BoxFuture<T>),
/// Run a clipboard action.
@ -25,6 +27,8 @@ pub enum Action<T> {
impl<T> Action<T> {
/// Applies a transformation to the result of a [`Command`].
///
/// [`Command`]: crate::Command
pub fn map<A>(
self,
f: impl Fn(T) -> A + 'static + MaybeSend + Sync,

View file

@ -5,7 +5,7 @@ use std::hash::{Hash, Hasher as _};
use std::path::PathBuf;
use std::sync::Arc;
/// An [`Image`] handle.
/// A handle of some image data.
#[derive(Debug, Clone)]
pub struct Handle {
id: u64,
@ -79,7 +79,7 @@ impl Hash for Handle {
}
}
/// The data of an [`Image`].
/// The data of a raster image.
#[derive(Clone, Hash)]
pub enum Data {
/// File data

View file

@ -23,8 +23,8 @@
//! - Build a new renderer, see the [renderer] module.
//! - Build a custom widget, start at the [`Widget`] trait.
//!
//! [`iced_core`]: https://github.com/iced-rs/iced/tree/master/core
//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/master/winit
//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.4/core
//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.4/winit
//! [`druid`]: https://github.com/xi-editor/druid
//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
//! [renderer]: crate::renderer

View file

@ -63,7 +63,7 @@ where
event::Status::Ignored
}
/// Returns the current [`mouse::Interaction`] of the [`Widget`].
/// Returns the current [`mouse::Interaction`] of the [`Overlay`].
///
/// By default, it returns [`mouse::Interaction::Idle`].
fn mouse_interaction(

View file

@ -1,24 +1,4 @@
//! Write your own renderer.
//!
//! You will need to implement the `Renderer` trait first. It simply contains
//! an `Output` associated type.
//!
//! There is no common trait to draw all the widgets. Instead, every [`Widget`]
//! constrains its generic `Renderer` type as necessary.
//!
//! This approach is flexible and composable. For instance, the
//! [`Text`] widget only needs a [`text::Renderer`] while a [`Checkbox`] widget
//! needs both a [`text::Renderer`] and a [`checkbox::Renderer`], reusing logic.
//!
//! In the end, a __renderer__ satisfying all the constraints is
//! needed to build a [`UserInterface`].
//!
//! [`Widget`]: crate::Widget
//! [`UserInterface`]: crate::UserInterface
//! [`Text`]: crate::widget::Text
//! [`text::Renderer`]: crate::widget::text::Renderer
//! [`Checkbox`]: crate::widget::Checkbox
//! [`checkbox::Renderer`]: crate::widget::checkbox::Renderer
#[cfg(debug_assertions)]
mod null;
#[cfg(debug_assertions)]
@ -27,8 +7,7 @@ pub use null::Null;
use crate::layout;
use crate::{Background, Color, Element, Rectangle, Vector};
/// A component that can take the state of a user interface and produce an
/// output for its users.
/// A component that can be used by widgets to draw themselves on a screen.
pub trait Renderer: Sized {
/// Lays out the elements of a user interface.
///

View file

@ -5,7 +5,7 @@ use std::hash::{Hash, Hasher as _};
use std::path::PathBuf;
use std::sync::Arc;
/// An [`Svg`] handle.
/// A handle of Svg data.
#[derive(Debug, Clone)]
pub struct Handle {
id: u64,
@ -55,7 +55,7 @@ impl Hash for Handle {
}
}
/// The data of an [`Svg`].
/// The data of a vectorial image.
#[derive(Clone, Hash)]
pub enum Data {
/// File data

View file

@ -39,7 +39,7 @@ pub enum Hit {
}
impl Hit {
/// Computes the cursor position corresponding to this [`HitTestResult`] .
/// Computes the cursor position of the [`Hit`] .
pub fn cursor(self) -> usize {
match self {
Self::CharOffset(i) => i,

View file

@ -16,7 +16,7 @@ use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
/// The [`integration` example] uses a [`UserInterface`] to integrate Iced in
/// an existing graphical application.
///
/// [`integration` example]: https://github.com/iced-rs/iced/tree/0.3/examples/integration
/// [`integration` example]: https://github.com/iced-rs/iced/tree/0.4/examples/integration
#[allow(missing_debug_implementations)]
pub struct UserInterface<'a, Message, Renderer> {
root: Element<'a, Message, Renderer>,
@ -264,11 +264,10 @@ where
/// Draws the [`UserInterface`] with the provided [`Renderer`].
///
/// It returns the some [`Renderer::Output`]. You should update the icon of
/// the mouse cursor accordingly in your system.
/// It returns the current [`mouse::Interaction`]. You should update the
/// icon of the mouse cursor accordingly in your system.
///
/// [`Renderer`]: crate::Renderer
/// [`Renderer::Output`]: crate::Renderer::Output
///
/// # Example
/// We can finally draw our [counter](index.html#usage) by

View file

@ -93,12 +93,12 @@ use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell};
/// - [`geometry`], a custom widget showcasing how to draw geometry with the
/// `Mesh2D` primitive in [`iced_wgpu`].
///
/// [examples]: https://github.com/iced-rs/iced/tree/0.3/examples
/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.3/examples/bezier_tool
/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.3/examples/custom_widget
/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.3/examples/geometry
/// [examples]: https://github.com/iced-rs/iced/tree/0.4/examples
/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.4/examples/bezier_tool
/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.4/examples/custom_widget
/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.4/examples/geometry
/// [`lyon`]: https://github.com/nical/lyon
/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.3/wgpu
/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.4/wgpu
pub trait Widget<Message, Renderer>
where
Renderer: crate::Renderer,

View file

@ -102,7 +102,7 @@ impl<'a, Message, Renderer: text::Renderer> Checkbox<'a, Message, Renderer> {
/// Sets the [`Font`] of the text of the [`Checkbox`].
///
/// [`Font`]: crate::widget::text::Renderer::Font
/// [`Font`]: crate::text::Renderer::Font
pub fn font(mut self, font: Renderer::Font) -> Self {
self.font = font;
self

View file

@ -48,7 +48,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
/// Sets the vertical spacing _between_ elements.
///
/// Custom margins per element do not exist in Iced. You should use this
/// Custom margins per element do not exist in iced. You should use this
/// method instead! While less flexible, it helps you keep spacing between
/// elements consistent.
pub fn spacing(mut self, units: u16) -> Self {

View file

@ -6,7 +6,7 @@
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support.
//!
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.3/examples/pane_grid
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.4/examples/pane_grid
mod axis;
mod configuration;
mod content;

View file

@ -2,7 +2,7 @@ use crate::widget::pane_grid::Axis;
/// The arrangement of a [`PaneGrid`].
///
/// [`PaneGrid`]: crate::pane_grid::PaneGrid
/// [`PaneGrid`]: crate::widget::PaneGrid
#[derive(Debug, Clone)]
pub enum Configuration<T> {
/// A split of the available space.
@ -21,6 +21,6 @@ pub enum Configuration<T> {
},
/// A [`Pane`].
///
/// [`Pane`]: crate::pane_grid::Pane
/// [`Pane`]: crate::widget::pane_grid::Pane
Pane(T),
}

View file

@ -55,7 +55,7 @@ where
{
/// Draws the [`Content`] with the provided [`Renderer`] and [`Layout`].
///
/// [`Renderer`]: crate::widget::pane_grid::Renderer
/// [`Renderer`]: crate::Renderer
pub fn draw(
&self,
renderer: &mut Renderer,

View file

@ -1,4 +1,6 @@
//! The state of a [`PaneGrid`].
//!
//! [`PaneGrid`]: crate::widget::PaneGrid
use crate::widget::pane_grid::{
Axis, Configuration, Direction, Node, Pane, Split,
};
@ -21,9 +23,13 @@ use std::collections::{BTreeMap, HashMap};
#[derive(Debug, Clone)]
pub struct State<T> {
/// The panes of the [`PaneGrid`].
///
/// [`PaneGrid`]: crate::widget::PaneGrid
pub panes: HashMap<Pane, T>,
/// The internal state of the [`PaneGrid`].
///
/// [`PaneGrid`]: crate::widget::PaneGrid
pub internal: Internal,
pub(super) action: Action,
@ -198,6 +204,8 @@ impl<T> State<T> {
}
/// The internal state of a [`PaneGrid`].
///
/// [`PaneGrid`]: crate::widget::PaneGrid
#[derive(Debug, Clone)]
pub struct Internal {
layout: Node,
@ -207,6 +215,8 @@ pub struct Internal {
impl Internal {
/// Initializes the [`Internal`] state of a [`PaneGrid`] from a
/// [`Configuration`].
///
/// [`PaneGrid`]: crate::widget::PaneGrid
pub fn from_configuration<T>(
panes: &mut HashMap<Pane, T>,
content: Configuration<T>,
@ -248,11 +258,17 @@ impl Internal {
}
/// The current action of a [`PaneGrid`].
///
/// [`PaneGrid`]: crate::widget::PaneGrid
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Action {
/// The [`PaneGrid`] is idle.
///
/// [`PaneGrid`]: crate::widget::PaneGrid
Idle,
/// A [`Pane`] in the [`PaneGrid`] is being dragged.
///
/// [`PaneGrid`]: crate::widget::PaneGrid
Dragging {
/// The [`Pane`] being dragged.
pane: Pane,
@ -260,6 +276,8 @@ pub enum Action {
origin: Point,
},
/// A [`Split`] in the [`PaneGrid`] is being dragged.
///
/// [`PaneGrid`]: crate::widget::PaneGrid
Resizing {
/// The [`Split`] being dragged.
split: Split,
@ -288,6 +306,8 @@ impl Action {
impl Internal {
/// Calculates the current [`Pane`] regions from the [`PaneGrid`] layout.
///
/// [`PaneGrid`]: crate::widget::PaneGrid
pub fn pane_regions(
&self,
spacing: f32,
@ -297,6 +317,8 @@ impl Internal {
}
/// Calculates the current [`Split`] regions from the [`PaneGrid`] layout.
///
/// [`PaneGrid`]: crate::widget::PaneGrid
pub fn split_regions(
&self,
spacing: f32,

View file

@ -82,7 +82,7 @@ where
{
/// Draws the [`TitleBar`] with the provided [`Renderer`] and [`Layout`].
///
/// [`Renderer`]: crate::widget::pane_grid::Renderer
/// [`Renderer`]: crate::Renderer
pub fn draw(
&self,
renderer: &mut Renderer,

View file

@ -402,21 +402,24 @@ pub fn draw<T, Renderer>(
if let Some(label) =
label.as_ref().map(String::as_str).or_else(|| placeholder)
{
let text_size = f32::from(text_size.unwrap_or(renderer.default_size()));
renderer.fill_text(Text {
content: label,
size: f32::from(text_size.unwrap_or(renderer.default_size())),
size: text_size,
font: font.clone(),
color: is_selected
.then(|| style.text_color)
.unwrap_or(style.placeholder_color),
bounds: Rectangle {
x: bounds.x + f32::from(padding.left),
y: bounds.center_y(),
..bounds
y: bounds.center_y() - text_size / 2.0,
width: bounds.width - f32::from(padding.horizontal()),
height: text_size,
},
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center,
})
vertical_alignment: alignment::Vertical::Top,
});
}
}

View file

@ -48,7 +48,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
/// Sets the horizontal spacing _between_ elements.
///
/// Custom margins per element do not exist in Iced. You should use this
/// Custom margins per element do not exist in iced. You should use this
/// method instead! While less flexible, it helps you keep spacing between
/// elements consistent.
pub fn spacing(mut self, units: u16) -> Self {

View file

@ -59,7 +59,7 @@ impl<Renderer: text::Renderer> Text<Renderer> {
/// Sets the [`Font`] of the [`Text`].
///
/// [`Font`]: Renderer::Font
/// [`Font`]: crate::text::Renderer::Font
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
self.font = font.into();
self
@ -77,7 +77,7 @@ impl<Renderer: text::Renderer> Text<Renderer> {
self
}
/// Sets the [`HorizontalAlignment`] of the [`Text`].
/// Sets the [`alignment::Horizontal`] of the [`Text`].
pub fn horizontal_alignment(
mut self,
alignment: alignment::Horizontal,
@ -86,7 +86,7 @@ impl<Renderer: text::Renderer> Text<Renderer> {
self
}
/// Sets the [`VerticalAlignment`] of the [`Text`].
/// Sets the [`alignment::Vertical`] of the [`Text`].
pub fn vertical_alignment(
mut self,
alignment: alignment::Vertical,

View file

@ -108,10 +108,9 @@ where
self
}
/// Sets the [`Font`] of the [`Text`].
/// Sets the [`Font`] of the [`TextInput`].
///
/// [`Font`]: crate::widget::text::Renderer::Font
/// [`Text`]: crate::widget::Text
/// [`Font`]: crate::text::Renderer::Font
pub fn font(mut self, font: Renderer::Font) -> Self {
self.font = font;
self
@ -157,6 +156,8 @@ where
/// Draws the [`TextInput`] with the given [`Renderer`], overriding its
/// [`Value`] if provided.
///
/// [`Renderer`]: text::Renderer
pub fn draw(
&self,
renderer: &mut Renderer,
@ -570,6 +571,8 @@ where
/// Draws the [`TextInput`] with the given [`Renderer`], overriding its
/// [`Value`] if provided.
///
/// [`Renderer`]: text::Renderer
pub fn draw<Renderer>(
renderer: &mut Renderer,
layout: Layout<'_>,

View file

@ -107,6 +107,8 @@ impl<'a, Message, Renderer: text::Renderer> Toggler<'a, Message, Renderer> {
}
/// Sets the [`Font`] of the text of the [`Toggler`]
///
/// [`Font`]: crate::text::Renderer::Font
pub fn font(mut self, font: Renderer::Font) -> Self {
self.font = font;
self

View file

@ -29,7 +29,7 @@ where
/// The default padding of a [`Tooltip`] drawn by this renderer.
const DEFAULT_PADDING: u16 = 5;
/// Creates an empty [`Tooltip`].
/// Creates a new [`Tooltip`].
///
/// [`Tooltip`]: struct.Tooltip.html
pub fn new(
@ -98,6 +98,117 @@ pub enum Position {
Right,
}
/// Draws a [`Tooltip`].
pub fn draw<Renderer: crate::Renderer>(
renderer: &mut Renderer,
inherited_style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
position: Position,
gap: u16,
padding: u16,
style_sheet: &dyn container::StyleSheet,
layout_text: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
draw_text: impl FnOnce(
&mut Renderer,
&renderer::Style,
Layout<'_>,
Point,
&Rectangle,
),
) {
let bounds = layout.bounds();
if bounds.contains(cursor_position) {
let gap = f32::from(gap);
let style = style_sheet.style();
let defaults = renderer::Style {
text_color: style.text_color.unwrap_or(inherited_style.text_color),
};
let text_layout = layout_text(
renderer,
&layout::Limits::new(Size::ZERO, viewport.size())
.pad(Padding::new(padding)),
);
let padding = f32::from(padding);
let text_bounds = text_layout.bounds();
let x_center = bounds.x + (bounds.width - text_bounds.width) / 2.0;
let y_center = bounds.y + (bounds.height - text_bounds.height) / 2.0;
let mut tooltip_bounds = {
let offset = match position {
Position::Top => Vector::new(
x_center,
bounds.y - text_bounds.height - gap - padding,
),
Position::Bottom => Vector::new(
x_center,
bounds.y + bounds.height + gap + padding,
),
Position::Left => Vector::new(
bounds.x - text_bounds.width - gap - padding,
y_center,
),
Position::Right => Vector::new(
bounds.x + bounds.width + gap + padding,
y_center,
),
Position::FollowCursor => Vector::new(
cursor_position.x,
cursor_position.y - text_bounds.height,
),
};
Rectangle {
x: offset.x - padding,
y: offset.y - padding,
width: text_bounds.width + padding * 2.0,
height: text_bounds.height + padding * 2.0,
}
};
if tooltip_bounds.x < viewport.x {
tooltip_bounds.x = viewport.x;
} else if viewport.x + viewport.width
< tooltip_bounds.x + tooltip_bounds.width
{
tooltip_bounds.x =
viewport.x + viewport.width - tooltip_bounds.width;
}
if tooltip_bounds.y < viewport.y {
tooltip_bounds.y = viewport.y;
} else if viewport.y + viewport.height
< tooltip_bounds.y + tooltip_bounds.height
{
tooltip_bounds.y =
viewport.y + viewport.height - tooltip_bounds.height;
}
renderer.with_layer(*viewport, |renderer| {
container::draw_background(renderer, &style, tooltip_bounds);
draw_text(
renderer,
&defaults,
Layout::with_offset(
Vector::new(
tooltip_bounds.x + padding,
tooltip_bounds.y + padding,
),
&text_layout,
),
cursor_position,
viewport,
)
});
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Tooltip<'a, Message, Renderer>
where
@ -169,100 +280,32 @@ where
viewport,
);
let bounds = layout.bounds();
if bounds.contains(cursor_position) {
let gap = f32::from(self.gap);
let style = self.style_sheet.style();
let defaults = renderer::Style {
text_color: style
.text_color
.unwrap_or(inherited_style.text_color),
};
let text_layout = Widget::<(), Renderer>::layout(
&self.tooltip,
renderer,
&layout::Limits::new(Size::ZERO, viewport.size())
.pad(Padding::new(self.padding)),
);
let padding = f32::from(self.padding);
let text_bounds = text_layout.bounds();
let x_center = bounds.x + (bounds.width - text_bounds.width) / 2.0;
let y_center =
bounds.y + (bounds.height - text_bounds.height) / 2.0;
let mut tooltip_bounds = {
let offset = match self.position {
Position::Top => Vector::new(
x_center,
bounds.y - text_bounds.height - gap - padding,
),
Position::Bottom => Vector::new(
x_center,
bounds.y + bounds.height + gap + padding,
),
Position::Left => Vector::new(
bounds.x - text_bounds.width - gap - padding,
y_center,
),
Position::Right => Vector::new(
bounds.x + bounds.width + gap + padding,
y_center,
),
Position::FollowCursor => Vector::new(
cursor_position.x,
cursor_position.y - text_bounds.height,
),
};
Rectangle {
x: offset.x - padding,
y: offset.y - padding,
width: text_bounds.width + padding * 2.0,
height: text_bounds.height + padding * 2.0,
}
};
if tooltip_bounds.x < viewport.x {
tooltip_bounds.x = viewport.x;
} else if viewport.x + viewport.width
< tooltip_bounds.x + tooltip_bounds.width
{
tooltip_bounds.x =
viewport.x + viewport.width - tooltip_bounds.width;
}
if tooltip_bounds.y < viewport.y {
tooltip_bounds.y = viewport.y;
} else if viewport.y + viewport.height
< tooltip_bounds.y + tooltip_bounds.height
{
tooltip_bounds.y =
viewport.y + viewport.height - tooltip_bounds.height;
}
renderer.with_layer(*viewport, |renderer| {
container::draw_background(renderer, &style, tooltip_bounds);
let tooltip = &self.tooltip;
draw(
renderer,
inherited_style,
layout,
cursor_position,
viewport,
self.position,
self.gap,
self.padding,
self.style_sheet.as_ref(),
|renderer, limits| {
Widget::<(), Renderer>::layout(tooltip, renderer, limits)
},
|renderer, defaults, layout, cursor_position, viewport| {
Widget::<(), Renderer>::draw(
&self.tooltip,
tooltip,
renderer,
&defaults,
Layout::with_offset(
Vector::new(
tooltip_bounds.x + padding,
tooltip_bounds.y + padding,
),
&text_layout,
),
defaults,
layout,
cursor_position,
viewport,
);
});
}
},
)
}
}
@ -273,8 +316,8 @@ where
Message: 'a,
{
fn from(
column: Tooltip<'a, Message, Renderer>,
tooltip: Tooltip<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(column)
Element::new(tooltip)
}
}

View file

@ -1,9 +1,15 @@
[package]
name = "iced_pure"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
description = "Pure widgets for Iced"
license = "MIT"
repository = "https://github.com/iced-rs/iced"
documentation = "https://docs.rs/iced_pure"
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[dependencies]
iced_native = { version = "0.4", path = "../native" }
iced_style = { version = "0.3", path = "../style" }
iced_native = { version = "0.5", path = "../native" }
iced_style = { version = "0.4", path = "../style" }
num-traits = "0.2"

View file

@ -8,25 +8,171 @@ use iced_native::mouse;
use iced_native::renderer;
use iced_native::{Clipboard, Length, Point, Rectangle, Shell};
/// A generic [`Widget`].
///
/// It is useful to build composable user interfaces that do not leak
/// implementation details in their __view logic__.
///
/// If you have a [built-in widget], you should be able to use `Into<Element>`
/// to turn it into an [`Element`].
///
/// [built-in widget]: crate::widget
pub struct Element<'a, Message, Renderer> {
widget: Box<dyn Widget<Message, Renderer> + 'a>,
}
impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// Creates a new [`Element`] containing the given [`Widget`].
pub fn new(widget: impl Widget<Message, Renderer> + 'a) -> Self {
Self {
widget: Box::new(widget),
}
}
/// Returns a reference to the [`Widget`] of the [`Element`],
pub fn as_widget(&self) -> &dyn Widget<Message, Renderer> {
self.widget.as_ref()
}
/// Returns a mutable reference to the [`Widget`] of the [`Element`],
pub fn as_widget_mut(&mut self) -> &mut dyn Widget<Message, Renderer> {
self.widget.as_mut()
}
/// Applies a transformation to the produced message of the [`Element`].
///
/// This method is useful when you want to decouple different parts of your
/// UI and make them __composable__.
///
/// # Example
/// Imagine we want to use [our counter](index.html#usage). But instead of
/// showing a single counter, we want to display many of them. We can reuse
/// the `Counter` type as it is!
///
/// We use composition to model the __state__ of our new application:
///
/// ```
/// # mod counter {
/// # pub struct Counter;
/// # }
/// use counter::Counter;
///
/// struct ManyCounters {
/// counters: Vec<Counter>,
/// }
/// ```
///
/// We can store the state of multiple counters now. However, the
/// __messages__ we implemented before describe the user interactions
/// of a __single__ counter. Right now, we need to also identify which
/// counter is receiving user interactions. Can we use composition again?
/// Yes.
///
/// ```
/// # mod counter {
/// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {}
/// # }
/// #[derive(Debug, Clone, Copy)]
/// pub enum Message {
/// Counter(usize, counter::Message)
/// }
/// ```
///
/// We compose the previous __messages__ with the index of the counter
/// producing them. Let's implement our __view logic__ now:
///
/// ```
/// # mod counter {
/// # type Text = iced_pure::widget::Text<iced_native::renderer::Null>;
/// #
/// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {}
/// # pub struct Counter;
/// #
/// # impl Counter {
/// # pub fn view(&mut self) -> Text {
/// # Text::new("")
/// # }
/// # }
/// # }
/// #
/// # mod iced_wgpu {
/// # pub use iced_native::renderer::Null as Renderer;
/// # }
/// #
/// # use counter::Counter;
/// #
/// # struct ManyCounters {
/// # counters: Vec<Counter>,
/// # }
/// #
/// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {
/// # Counter(usize, counter::Message)
/// # }
/// use iced_pure::Element;
/// use iced_pure::widget::Row;
/// use iced_wgpu::Renderer;
///
/// impl ManyCounters {
/// pub fn view(&mut self) -> Row<Message, Renderer> {
/// // We can quickly populate a `Row` by folding over our counters
/// self.counters.iter_mut().enumerate().fold(
/// Row::new().spacing(20),
/// |row, (index, counter)| {
/// // We display the counter
/// let element: Element<counter::Message, Renderer> =
/// counter.view().into();
///
/// row.push(
/// // Here we turn our `Element<counter::Message>` into
/// // an `Element<Message>` by combining the `index` and the
/// // message of the `element`.
/// element.map(move |message| Message::Counter(index, message))
/// )
/// }
/// )
/// }
/// }
/// ```
///
/// Finally, our __update logic__ is pretty straightforward: simple
/// delegation.
///
/// ```
/// # mod counter {
/// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {}
/// # pub struct Counter;
/// #
/// # impl Counter {
/// # pub fn update(&mut self, _message: Message) {}
/// # }
/// # }
/// #
/// # use counter::Counter;
/// #
/// # struct ManyCounters {
/// # counters: Vec<Counter>,
/// # }
/// #
/// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {
/// # Counter(usize, counter::Message)
/// # }
/// impl ManyCounters {
/// pub fn update(&mut self, message: Message) {
/// match message {
/// Message::Counter(index, counter_msg) => {
/// if let Some(counter) = self.counters.get_mut(index) {
/// counter.update(counter_msg);
/// }
/// }
/// }
/// }
/// }
/// ```
pub fn map<B>(
self,
f: impl Fn(Message) -> B + 'a,

View file

@ -65,7 +65,7 @@ pub fn resolve<Message, Renderer>(
padding: Padding,
spacing: f32,
align_items: Alignment,
items: &[Element<Message, Renderer>],
items: &[Element<'_, Message, Renderer>],
) -> Node
where
Renderer: iced_native::Renderer,

View file

@ -1,3 +1,4 @@
//! Helper functions to create pure widgets.
use crate::widget;
use crate::Element;
@ -5,6 +6,9 @@ use iced_native::Length;
use std::borrow::Cow;
use std::ops::RangeInclusive;
/// Creates a new [`Container`] with the provided content.
///
/// [`Container`]: widget::Container
pub fn container<'a, Message, Renderer>(
content: impl Into<Element<'a, Message, Renderer>>,
) -> widget::Container<'a, Message, Renderer>
@ -14,15 +18,24 @@ where
widget::Container::new(content)
}
/// Creates a new [`Column`].
///
/// [`Column`]: widget::Column
pub fn column<'a, Message, Renderer>() -> widget::Column<'a, Message, Renderer>
{
widget::Column::new()
}
/// Creates a new [`Row`].
///
/// [`Row`]: widget::Row
pub fn row<'a, Message, Renderer>() -> widget::Row<'a, Message, Renderer> {
widget::Row::new()
}
/// Creates a new [`Scrollable`] with the provided content.
///
/// [`Scrollable`]: widget::Scrollable
pub fn scrollable<'a, Message, Renderer>(
content: impl Into<Element<'a, Message, Renderer>>,
) -> widget::Scrollable<'a, Message, Renderer>
@ -32,12 +45,33 @@ where
widget::Scrollable::new(content)
}
/// Creates a new [`Button`] with the provided content.
///
/// [`Button`]: widget::Button
pub fn button<'a, Message, Renderer>(
content: impl Into<Element<'a, Message, Renderer>>,
) -> widget::Button<'a, Message, Renderer> {
widget::Button::new(content)
}
/// Creates a new [`Tooltip`] with the provided content, tooltip text, and [`tooltip::Position`].
///
/// [`Tooltip`]: widget::Tooltip
/// [`tooltip::Position`]: widget::tooltip::Position
pub fn tooltip<'a, Message, Renderer>(
content: impl Into<Element<'a, Message, Renderer>>,
tooltip: impl ToString,
position: widget::tooltip::Position,
) -> widget::Tooltip<'a, Message, Renderer>
where
Renderer: iced_native::text::Renderer,
{
widget::Tooltip::new(content, tooltip, position)
}
/// Creates a new [`Text`] widget with the provided content.
///
/// [`Text`]: widget::Text
pub fn text<Renderer>(text: impl Into<String>) -> widget::Text<Renderer>
where
Renderer: iced_native::text::Renderer,
@ -45,6 +79,9 @@ where
widget::Text::new(text)
}
/// Creates a new [`Checkbox`].
///
/// [`Checkbox`]: widget::Checkbox
pub fn checkbox<'a, Message, Renderer>(
label: impl Into<String>,
is_checked: bool,
@ -56,6 +93,9 @@ where
widget::Checkbox::new(is_checked, label, f)
}
/// Creates a new [`Radio`].
///
/// [`Radio`]: widget::Radio
pub fn radio<'a, Message, Renderer, V>(
label: impl Into<String>,
value: V,
@ -70,6 +110,9 @@ where
widget::Radio::new(value, label, selected, on_click)
}
/// Creates a new [`Toggler`].
///
/// [`Toggler`]: widget::Toggler
pub fn toggler<'a, Message, Renderer>(
label: impl Into<Option<String>>,
is_checked: bool,
@ -81,6 +124,9 @@ where
widget::Toggler::new(is_checked, label, f)
}
/// Creates a new [`TextInput`].
///
/// [`TextInput`]: widget::TextInput
pub fn text_input<'a, Message, Renderer>(
placeholder: &str,
value: &str,
@ -93,6 +139,9 @@ where
widget::TextInput::new(placeholder, value, on_change)
}
/// Creates a new [`Slider`].
///
/// [`Slider`]: widget::Slider
pub fn slider<'a, Message, T>(
range: std::ops::RangeInclusive<T>,
value: T,
@ -105,6 +154,9 @@ where
widget::Slider::new(range, value, on_change)
}
/// Creates a new [`PickList`].
///
/// [`PickList`]: widget::PickList
pub fn pick_list<'a, Message, Renderer, T>(
options: impl Into<Cow<'a, [T]>>,
selected: Option<T>,
@ -118,24 +170,37 @@ where
widget::PickList::new(options, selected, on_selected)
}
/// Creates a new [`Image`].
///
/// [`Image`]: widget::Image
pub fn image<Handle>(handle: impl Into<Handle>) -> widget::Image<Handle> {
widget::Image::new(handle.into())
}
/// Creates a new horizontal [`Space`] with the given [`Length`].
///
/// [`Space`]: widget::Space
pub fn horizontal_space(width: Length) -> widget::Space {
widget::Space::with_width(width)
}
/// Creates a new vertical [`Space`] with the given [`Length`].
///
/// [`Space`]: widget::Space
pub fn vertical_space(height: Length) -> widget::Space {
widget::Space::with_height(height)
}
/// Creates a horizontal [`Rule`] with the given height.
///
/// [`Rule`]: widget::Rule
pub fn horizontal_rule<'a>(height: u16) -> widget::Rule<'a> {
widget::Rule::horizontal(height)
}
/// Creates a vertical [`Rule`] with the given width.
///
/// [`Rule`]: widget::Rule
pub fn vertical_rule<'a>(width: u16) -> widget::Rule<'a> {
widget::Rule::horizontal(width)
}
@ -143,8 +208,10 @@ pub fn vertical_rule<'a>(width: u16) -> widget::Rule<'a> {
/// Creates a new [`ProgressBar`].
///
/// It expects:
/// * an inclusive range of possible values
/// * the current value of the [`ProgressBar`]
/// * an inclusive range of possible values, and
/// * the current value of the [`ProgressBar`].
///
/// [`ProgressBar`]: widget::ProgressBar
pub fn progress_bar<'a>(
range: RangeInclusive<f32>,
value: f32,

View file

@ -1,3 +1,92 @@
//! Stateless, pure widgets for iced.
//!
//! # The Elm Architecture, purity, and continuity
//! As you may know, applications made with `iced` use [The Elm Architecture].
//!
//! In a nutshell, this architecture defines the initial state of the application, a way to `view` it, and a way to `update` it after a user interaction. The `update` logic is called after a meaningful user interaction, which in turn updates the state of the application. Then, the `view` logic is executed to redisplay the application.
//!
//! Since `view` logic is only run after an `update`, all of the mutations to the application state must only happen in the `update` logic. If the application state changes anywhere else, the `view` logic will not be rerun and, therefore, the previously generated `view` may stay outdated.
//!
//! However, the `Application` trait in `iced` defines `view` as:
//!
//! ```ignore
//! pub trait Application {
//! fn view(&mut self) -> Element<Self::Message>;
//! }
//! ```
//!
//! As a consequence, the application state can be mutated in `view` logic. The `view` logic in `iced` is __impure__.
//!
//! This impurity is necessary because `iced` puts the burden of widget __continuity__ on its users. In other words, it's up to you to provide `iced` with the internal state of each widget every time `view` is called.
//!
//! If we take a look at the classic `counter` example:
//!
//! ```ignore
//! struct Counter {
//! value: i32,
//! increment_button: button::State,
//! decrement_button: button::State,
//! }
//!
//! // ...
//!
//! impl Counter {
//! pub fn view(&mut self) -> Column<Message> {
//! Column::new()
//! .push(
//! Button::new(&mut self.increment_button, Text::new("+"))
//! .on_press(Message::IncrementPressed),
//! )
//! .push(Text::new(self.value.to_string()).size(50))
//! .push(
//! Button::new(&mut self.decrement_button, Text::new("-"))
//! .on_press(Message::DecrementPressed),
//! )
//! }
//! }
//! ```
//!
//! We can see how we need to keep track of the `button::State` of each `Button` in our `Counter` state and provide a mutable reference to the widgets in our `view` logic. The widgets produced by `view` are __stateful__.
//!
//! While this approach forces users to keep track of widget state and causes impurity, I originally chose it because it allows `iced` to directly consume the widget tree produced by `view`. Since there is no internal state decoupled from `view` maintained by the runtime, `iced` does not need to compare (e.g. reconciliate) widget trees in order to ensure continuity.
//!
//! # Stateless widgets
//! As the library matures, the need for some kind of persistent widget data (see #553) between `view` calls becomes more apparent (e.g. incremental rendering, animations, accessibility, etc.).
//!
//! If we are going to end up having persistent widget data anyways... There is no reason to have impure, stateful widgets anymore!
//!
//! And so I started exploring and ended up creating a new subcrate called `iced_pure`, which introduces a completely stateless implementation for every widget in `iced`.
//!
//! With the help of this crate, we can now write a pure `counter` example:
//!
//! ```ignore
//! struct Counter {
//! value: i32,
//! }
//!
//! // ...
//!
//! impl Counter {
//! fn view(&self) -> Column<Message> {
//! Column::new()
//! .push(Button::new("Increment").on_press(Message::IncrementPressed))
//! .push(Text::new(self.value.to_string()).size(50))
//! .push(Button::new("Decrement").on_press(Message::DecrementPressed))
//! }
//! }
//! ```
//!
//! Notice how we no longer need to keep track of the `button::State`! The widgets in `iced_pure` do not take any mutable application state in `view`. They are __stateless__ widgets. As a consequence, we do not need mutable access to `self` in `view` anymore. `view` becomes __pure__.
//!
//! [The Elm Architecture]: https://guide.elm-lang.org/architecture/
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
#![deny(missing_docs)]
#![deny(unused_results)]
#![forbid(unsafe_code)]
#![forbid(rust_2018_idioms)]
pub mod helpers;
pub mod overlay;
pub mod widget;
@ -16,6 +105,32 @@ use iced_native::mouse;
use iced_native::renderer;
use iced_native::{Clipboard, Length, Point, Rectangle, Shell};
/// A bridge between impure and pure widgets.
///
/// If you already have an existing `iced` application, you do not need to switch completely to the new traits in order to benefit from the `pure` module. Instead, you can leverage the new `Pure` widget to include `pure` widgets in your impure `Application`.
///
/// For instance, let's say we want to use our pure `Counter` in an impure application:
///
/// ```ignore
/// use iced_pure::{self, Pure};
///
/// struct Impure {
/// state: pure::State,
/// counter: Counter,
/// }
///
/// impl Sandbox for Impure {
/// // ...
///
/// pub fn view(&mut self) -> Element<Self::Message> {
/// Pure::new(&mut self.state, self.counter.view()).into()
/// }
/// }
/// ```
///
/// [`Pure`] acts as a bridge between pure and impure widgets. It is completely opt-in and can be used to slowly migrate your application to the new architecture.
///
/// The purification of your application may trigger a bunch of important refactors, since it's far easier to keep your data decoupled from the GUI state with stateless widgets. For this reason, I recommend starting small in the most nested views of your application and slowly expand the purity upwards.
pub struct Pure<'a, Message, Renderer> {
state: &'a mut State,
element: Element<'a, Message, Renderer>,
@ -26,6 +141,7 @@ where
Message: 'a,
Renderer: iced_native::Renderer + 'a,
{
/// Creates a new [`Pure`] widget with the given [`State`] and impure [`Element`].
pub fn new(
state: &'a mut State,
content: impl Into<Element<'a, Message, Renderer>>,
@ -37,6 +153,7 @@ where
}
}
/// The internal state of a [`Pure`] widget.
pub struct State {
state_tree: widget::Tree,
}
@ -48,6 +165,7 @@ impl Default for State {
}
impl State {
/// Creates a new [`State`] for a [`Pure`] widget.
pub fn new() -> Self {
Self {
state_tree: widget::Tree::empty(),
@ -56,7 +174,7 @@ impl State {
fn diff<Message, Renderer>(
&mut self,
new_element: &Element<Message, Renderer>,
new_element: &Element<'_, Message, Renderer>,
) {
self.state_tree.diff(new_element);
}

View file

@ -1,9 +1,14 @@
//! Display interactive elements on top of other widgets.
use crate::widget::Tree;
use iced_native::Layout;
pub use iced_native::overlay::*;
/// Obtains the first overlay [`Element`] found in the given children.
///
/// This method will generally only be used by advanced users that are
/// implementing the [`Widget`](crate::Widget) trait.
pub fn from_children<'a, Message, Renderer>(
children: &'a [crate::Element<'_, Message, Renderer>],
tree: &'a mut Tree,

View file

@ -1,3 +1,4 @@
//! Use the built-in widgets or create your own.
pub mod button;
pub mod checkbox;
pub mod container;
@ -12,6 +13,7 @@ pub mod slider;
pub mod svg;
pub mod text_input;
pub mod toggler;
pub mod tooltip;
pub mod tree;
mod column;
@ -37,6 +39,7 @@ pub use svg::Svg;
pub use text::Text;
pub use text_input::TextInput;
pub use toggler::Toggler;
pub use tooltip::{Position, Tooltip};
pub use tree::Tree;
use iced_native::event::{self, Event};
@ -46,17 +49,28 @@ use iced_native::overlay;
use iced_native::renderer;
use iced_native::{Clipboard, Length, Point, Rectangle, Shell};
/// A component that displays information and allows interaction.
///
/// If you want to build your own widgets, you will need to implement this
/// trait.
pub trait Widget<Message, Renderer> {
/// Returns the width of the [`Widget`].
fn width(&self) -> Length;
/// Returns the height of the [`Widget`].
fn height(&self) -> Length;
/// Returns the [`layout::Node`] of the [`Widget`].
///
/// This [`layout::Node`] is used by the runtime to compute the [`Layout`] of the
/// user interface.
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node;
/// Draws the [`Widget`] using the associated `Renderer`.
fn draw(
&self,
state: &Tree,
@ -67,31 +81,31 @@ pub trait Widget<Message, Renderer> {
viewport: &Rectangle,
);
/// Returns the [`Tag`] of the [`Widget`].
///
/// [`Tag`]: tree::Tag
fn tag(&self) -> tree::Tag {
tree::Tag::stateless()
}
/// Returns the [`State`] of the [`Widget`].
///
/// [`State`]: tree::State
fn state(&self) -> tree::State {
tree::State::None
}
/// Returns the state [`Tree`] of the children of the [`Widget`].
fn children(&self) -> Vec<Tree> {
Vec::new()
}
/// Reconciliates the [`Widget`] with the provided [`Tree`].
fn diff(&self, _tree: &mut Tree) {}
fn mouse_interaction(
&self,
_state: &Tree,
_layout: Layout<'_>,
_cursor_position: Point,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
mouse::Interaction::Idle
}
/// Processes a runtime [`Event`].
///
/// By default, it does nothing.
fn on_event(
&mut self,
_state: &mut Tree,
@ -105,6 +119,21 @@ pub trait Widget<Message, Renderer> {
event::Status::Ignored
}
/// Returns the current [`mouse::Interaction`] of the [`Widget`].
///
/// By default, it returns [`mouse::Interaction::Idle`].
fn mouse_interaction(
&self,
_state: &Tree,
_layout: Layout<'_>,
_cursor_position: Point,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
mouse::Interaction::Idle
}
/// Returns the overlay of the [`Widget`], if there is any.
fn overlay<'a>(
&'a self,
_state: &'a mut Tree,

View file

@ -1,3 +1,4 @@
//! Allow your users to perform actions by pressing a button.
use crate::overlay;
use crate::widget::tree::{self, Tree};
use crate::{Element, Widget};
@ -15,6 +16,40 @@ pub use iced_style::button::{Style, StyleSheet};
use button::State;
/// A generic widget that produces a message when pressed.
///
/// ```
/// # type Button<'a, Message> =
/// # iced_pure::widget::Button<'a, Message, iced_native::renderer::Null>;
/// #
/// #[derive(Clone)]
/// enum Message {
/// ButtonPressed,
/// }
///
/// let button = Button::new("Press me!").on_press(Message::ButtonPressed);
/// ```
///
/// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will
/// be disabled:
///
/// ```
/// # type Button<'a, Message> =
/// # iced_pure::widget::Button<'a, Message, iced_native::renderer::Null>;
/// #
/// #[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)
/// }
/// ```
pub struct Button<'a, Message, Renderer> {
content: Element<'a, Message, Renderer>,
on_press: Option<Message>,
@ -25,6 +60,7 @@ pub struct Button<'a, Message, Renderer> {
}
impl<'a, Message, Renderer> Button<'a, Message, Renderer> {
/// Creates a new [`Button`] with the given content.
pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self {
Button {
content: content.into(),

View file

@ -1,3 +1,4 @@
//! Show toggle controls using checkboxes.
use crate::widget::Tree;
use crate::{Element, Widget};

View file

@ -13,6 +13,7 @@ use iced_native::{
use std::u32;
/// A container that distributes its contents vertically.
pub struct Column<'a, Message, Renderer> {
spacing: u16,
padding: Padding,
@ -24,10 +25,12 @@ pub struct Column<'a, Message, Renderer> {
}
impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
/// Creates an empty [`Column`].
pub fn new() -> Self {
Self::with_children(Vec::new())
}
/// Creates a [`Column`] with the given elements.
pub fn with_children(
children: Vec<Element<'a, Message, Renderer>>,
) -> Self {
@ -42,21 +45,29 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
}
}
/// Sets the vertical spacing _between_ elements.
///
/// Custom margins per element do not exist in iced. You should use this
/// method instead! While less flexible, it helps you keep spacing between
/// elements consistent.
pub fn spacing(mut self, units: u16) -> Self {
self.spacing = units;
self
}
/// Sets the [`Padding`] of the [`Column`].
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
self.padding = padding.into();
self
}
/// Sets the width of the [`Column`].
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`Column`].
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
@ -68,11 +79,13 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
self
}
/// Sets the horizontal alignment of the contents of the [`Column`] .
pub fn align_items(mut self, align: Alignment) -> Self {
self.align_items = align;
self
}
/// Adds an element to the [`Column`].
pub fn push(
mut self,
child: impl Into<Element<'a, Message, Renderer>>,

View file

@ -1,3 +1,4 @@
//! Display images in your user interface.
use crate::widget::{Tree, Widget};
use crate::Element;

View file

@ -6,7 +6,7 @@
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support.
//!
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.3/examples/pane_grid
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.4/examples/pane_grid
mod content;
mod title_bar;
@ -213,7 +213,7 @@ where
fn diff(&self, tree: &mut Tree) {
tree.diff_children_custom(
&self.elements,
|(_, content), state| content.diff(state),
|state, (_, content)| content.diff(state),
|(_, content)| content.state(),
)
}

View file

@ -57,7 +57,7 @@ impl<'a, Message, Renderer> Content<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
{
pub fn state(&self) -> Tree {
pub(super) fn state(&self) -> Tree {
let children = if let Some(title_bar) = self.title_bar.as_ref() {
vec![Tree::new(&self.body), title_bar.state()]
} else {
@ -70,7 +70,7 @@ where
}
}
pub fn diff(&self, tree: &mut Tree) {
pub(super) fn diff(&self, tree: &mut Tree) {
if tree.children.len() == 2 {
if let Some(title_bar) = self.title_bar.as_ref() {
title_bar.diff(&mut tree.children[1]);
@ -84,7 +84,7 @@ where
/// Draws the [`Content`] with the provided [`Renderer`] and [`Layout`].
///
/// [`Renderer`]: crate::widget::pane_grid::Renderer
/// [`Renderer`]: iced_native::Renderer
pub fn draw(
&self,
tree: &Tree,

View file

@ -81,7 +81,7 @@ impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
{
pub fn state(&self) -> Tree {
pub(super) fn state(&self) -> Tree {
let children = if let Some(controls) = self.controls.as_ref() {
vec![Tree::new(&self.content), Tree::new(controls)]
} else {
@ -94,7 +94,7 @@ where
}
}
pub fn diff(&self, tree: &mut Tree) {
pub(super) fn diff(&self, tree: &mut Tree) {
if tree.children.len() == 2 {
if let Some(controls) = self.controls.as_ref() {
tree.children[1].diff(controls);
@ -108,7 +108,7 @@ where
/// Draws the [`TitleBar`] with the provided [`Renderer`] and [`Layout`].
///
/// [`Renderer`]: crate::widget::pane_grid::Renderer
/// [`Renderer`]: iced_native::Renderer
pub fn draw(
&self,
tree: &Tree,

View file

@ -43,9 +43,8 @@ where
/// The default padding of a [`PickList`].
pub const DEFAULT_PADDING: Padding = Padding::new(5);
/// Creates a new [`PickList`] with the given [`State`], a list of options,
/// the current selected value, and the message to produce when an option is
/// selected.
/// Creates a new [`PickList`] with the given list of options, the current
/// selected value, and the message to produce when an option is selected.
pub fn new(
options: impl Into<Cow<'a, [T]>>,
selected: Option<T>,

View file

@ -1,3 +1,4 @@
//! Provide progress feedback to your users.
use crate::widget::Tree;
use crate::{Element, Widget};

View file

@ -1,3 +1,4 @@
//! Create choices using radio buttons.
use crate::widget::Tree;
use crate::{Element, Widget};

View file

@ -11,6 +11,7 @@ use iced_native::{
Alignment, Clipboard, Length, Padding, Point, Rectangle, Shell,
};
/// A container that distributes its contents horizontally.
pub struct Row<'a, Message, Renderer> {
spacing: u16,
padding: Padding,
@ -21,10 +22,12 @@ pub struct Row<'a, Message, Renderer> {
}
impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
/// Creates an empty [`Row`].
pub fn new() -> Self {
Self::with_children(Vec::new())
}
/// Creates a [`Row`] with the given elements.
pub fn with_children(
children: Vec<Element<'a, Message, Renderer>>,
) -> Self {
@ -38,31 +41,41 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
}
}
/// Sets the horizontal spacing _between_ elements.
///
/// Custom margins per element do not exist in iced. You should use this
/// method instead! While less flexible, it helps you keep spacing between
/// elements consistent.
pub fn spacing(mut self, units: u16) -> Self {
self.spacing = units;
self
}
/// Sets the [`Padding`] of the [`Row`].
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
self.padding = padding.into();
self
}
/// Sets the width of the [`Row`].
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`Row`].
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
/// Sets the vertical alignment of the contents of the [`Row`] .
pub fn align_items(mut self, align: Alignment) -> Self {
self.align_items = align;
self
}
/// Adds an [`Element`] to the [`Row`].
pub fn push(
mut self,
child: impl Into<Element<'a, Message, Renderer>>,

View file

@ -1,3 +1,4 @@
//! Display a horizontal or vertical rule for dividing content.
use crate::widget::Tree;
use crate::{Element, Widget};

View file

@ -1,3 +1,4 @@
//! Navigate an endless amount of content with a scrollbar.
use crate::overlay;
use crate::widget::tree::{self, Tree};
use crate::{Element, Widget};

View file

@ -1,6 +1,4 @@
//! Display an interactive selector of a single value from a range of values.
//!
//! A [`Slider`] has some local [`State`].
use crate::widget::tree::{self, Tree};
use crate::{Element, Widget};
@ -25,17 +23,16 @@ pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet};
///
/// # Example
/// ```
/// # use iced_native::widget::slider::{self, Slider};
/// # use iced_pure::widget::Slider;
/// #
/// #[derive(Clone)]
/// pub enum Message {
/// SliderChanged(f32),
/// }
///
/// let state = &mut slider::State::new();
/// let value = 50.0;
///
/// Slider::new(state, 0.0..=100.0, value, Message::SliderChanged);
/// Slider::new(0.0..=100.0, value, Message::SliderChanged);
/// ```
///
/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true)

View file

@ -1,3 +1,4 @@
//! Display vector graphics in your application.
use crate::widget::{Tree, Widget};
use crate::Element;

View file

@ -1,3 +1,4 @@
//! Display fields that can be filled with text.
use crate::widget::tree::{self, Tree};
use crate::{Element, Widget};
@ -15,20 +16,15 @@ pub use iced_style::text_input::{Style, StyleSheet};
///
/// # Example
/// ```
/// # use iced_native::renderer::Null;
/// # use iced_native::widget::text_input;
/// #
/// # pub type TextInput<'a, Message> = iced_native::widget::TextInput<'a, Message, Null>;
/// # pub type TextInput<'a, Message> = iced_pure::widget::TextInput<'a, Message, iced_native::renderer::Null>;
/// #[derive(Debug, Clone)]
/// enum Message {
/// TextInputChanged(String),
/// }
///
/// let mut state = text_input::State::new();
/// let value = "Some text";
///
/// let input = TextInput::new(
/// &mut state,
/// "This is the placeholder...",
/// value,
/// Message::TextInputChanged,
@ -58,10 +54,9 @@ where
/// Creates a new [`TextInput`].
///
/// It expects:
/// - some [`State`]
/// - a placeholder
/// - the current value
/// - a function that produces a message when the [`TextInput`] changes
/// - a placeholder,
/// - the current value, and
/// - a function that produces a message when the [`TextInput`] changes.
pub fn new<F>(placeholder: &str, value: &str, on_change: F) -> Self
where
F: 'a + Fn(String) -> Message,
@ -86,10 +81,9 @@ where
self
}
/// Sets the [`Font`] of the [`Text`].
/// Sets the [`Font`] of the [`TextInput`].
///
/// [`Font`]: crate::widget::text::Renderer::Font
/// [`Text`]: crate::widget::Text
/// [`Font`]: iced_native::text::Renderer::Font
pub fn font(mut self, font: Renderer::Font) -> Self {
self.font = font;
self

View file

@ -1,3 +1,4 @@
//! Show toggle controls using togglers.
use crate::widget::{Tree, Widget};
use crate::Element;

228
pure/src/widget/tooltip.rs Normal file
View file

@ -0,0 +1,228 @@
//! Display a widget over another.
use crate::widget::Tree;
use crate::{Element, Widget};
use iced_native::event::{self, Event};
use iced_native::layout;
use iced_native::mouse;
use iced_native::overlay;
use iced_native::renderer;
use iced_native::text;
use iced_native::widget::tooltip;
use iced_native::widget::Text;
use iced_native::{Clipboard, Layout, Length, Point, Rectangle, Shell};
pub use iced_style::container::{Style, StyleSheet};
pub use tooltip::Position;
/// An element to display a widget over another.
#[allow(missing_debug_implementations)]
pub struct Tooltip<'a, Message, Renderer: text::Renderer> {
content: Element<'a, Message, Renderer>,
tooltip: Text<Renderer>,
position: Position,
style_sheet: Box<dyn StyleSheet + 'a>,
gap: u16,
padding: u16,
}
impl<'a, Message, Renderer> Tooltip<'a, Message, Renderer>
where
Renderer: text::Renderer,
{
/// The default padding of a [`Tooltip`] drawn by this renderer.
const DEFAULT_PADDING: u16 = 5;
/// Creates a new [`Tooltip`].
///
/// [`Tooltip`]: struct.Tooltip.html
pub fn new(
content: impl Into<Element<'a, Message, Renderer>>,
tooltip: impl ToString,
position: Position,
) -> Self {
Tooltip {
content: content.into(),
tooltip: Text::new(tooltip.to_string()),
position,
style_sheet: Default::default(),
gap: 0,
padding: Self::DEFAULT_PADDING,
}
}
/// Sets the size of the text of the [`Tooltip`].
pub fn size(mut self, size: u16) -> Self {
self.tooltip = self.tooltip.size(size);
self
}
/// Sets the font of the [`Tooltip`].
///
/// [`Font`]: Renderer::Font
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
self.tooltip = self.tooltip.font(font);
self
}
/// Sets the gap between the content and its [`Tooltip`].
pub fn gap(mut self, gap: u16) -> Self {
self.gap = gap;
self
}
/// Sets the padding of the [`Tooltip`].
pub fn padding(mut self, padding: u16) -> Self {
self.padding = padding;
self
}
/// Sets the style of the [`Tooltip`].
pub fn style(
mut self,
style_sheet: impl Into<Box<dyn StyleSheet + 'a>>,
) -> Self {
self.style_sheet = style_sheet.into();
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Tooltip<'a, Message, Renderer>
where
Renderer: text::Renderer,
{
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.content)]
}
fn diff(&self, tree: &mut Tree) {
tree.diff_children(std::slice::from_ref(&self.content))
}
fn width(&self) -> Length {
self.content.as_widget().width()
}
fn height(&self) -> Length {
self.content.as_widget().height()
}
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content.as_widget().layout(renderer, limits)
}
fn on_event(
&mut self,
tree: &mut Tree,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.content.as_widget_mut().on_event(
&mut tree.children[0],
event,
layout,
cursor_position,
renderer,
clipboard,
shell,
)
}
fn mouse_interaction(
&self,
tree: &Tree,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
self.content.as_widget().mouse_interaction(
&tree.children[0],
layout.children().next().unwrap(),
cursor_position,
viewport,
renderer,
)
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
inherited_style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
) {
self.content.as_widget().draw(
&tree.children[0],
renderer,
inherited_style,
layout,
cursor_position,
viewport,
);
let tooltip = &self.tooltip;
tooltip::draw(
renderer,
inherited_style,
layout,
cursor_position,
viewport,
self.position,
self.gap,
self.padding,
self.style_sheet.as_ref(),
|renderer, limits| {
Widget::<(), Renderer>::layout(tooltip, renderer, limits)
},
|renderer, defaults, layout, cursor_position, viewport| {
Widget::<(), Renderer>::draw(
tooltip,
&Tree::empty(),
renderer,
defaults,
layout,
cursor_position,
viewport,
);
},
);
}
fn overlay<'b>(
&'b self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
self.content.as_widget().overlay(
&mut tree.children[0],
layout,
renderer,
)
}
}
impl<'a, Message, Renderer> From<Tooltip<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'a + text::Renderer,
Message: 'a,
{
fn from(
tooltip: Tooltip<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(tooltip)
}
}

View file

@ -1,14 +1,24 @@
//! Store internal widget state in a state tree to ensure continuity.
use crate::Element;
use std::any::{self, Any};
/// A persistent state widget tree.
///
/// A [`Tree`] is normally associated with a specific widget in the widget tree.
pub struct Tree {
/// The tag of the [`Tree`].
pub tag: Tag,
/// The [`State`] of the [`Tree`].
pub state: State,
/// The children of the root widget of the [`Tree`].
pub children: Vec<Tree>,
}
impl Tree {
/// Creates an empty, stateless [`Tree`] with no children.
pub fn empty() -> Self {
Self {
tag: Tag::stateless(),
@ -17,6 +27,7 @@ impl Tree {
}
}
/// Creates a new [`Tree`] for the provided [`Element`].
pub fn new<Message, Renderer>(
element: &Element<'_, Message, Renderer>,
) -> Self {
@ -27,6 +38,14 @@ impl Tree {
}
}
/// Reconciliates the current tree with the provided [`Element`].
///
/// If the tag of the [`Element`] matches the tag of the [`Tree`], then the
/// [`Element`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called).
///
/// Otherwise, the whole [`Tree`] is recreated.
///
/// [`Widget::diff`]: crate::Widget::diff
pub fn diff<Message, Renderer>(
&mut self,
new: &Element<'_, Message, Renderer>,
@ -38,21 +57,20 @@ impl Tree {
}
}
/// Reconciliates the children of the tree with the provided list of [`Element`].
pub fn diff_children<Message, Renderer>(
&mut self,
new_children: &[Element<'_, Message, Renderer>],
) {
self.diff_children_custom(
new_children,
|new, child_state| child_state.diff(new),
Self::new,
)
self.diff_children_custom(new_children, Self::diff, Self::new)
}
/// Reconciliates the children of the tree with the provided list of [`Element`] using custom
/// logic both for diffing and creating new widget state.
pub fn diff_children_custom<T>(
&mut self,
new_children: &[T],
diff: impl Fn(&T, &mut Tree),
diff: impl Fn(&mut Tree, &T),
new_state: impl Fn(&T) -> Self,
) {
if self.children.len() > new_children.len() {
@ -62,7 +80,7 @@ impl Tree {
for (child_state, new) in
self.children.iter_mut().zip(new_children.iter())
{
diff(new, child_state);
diff(child_state, new);
}
if self.children.len() < new_children.len() {
@ -73,10 +91,12 @@ impl Tree {
}
}
/// The identifier of some widget state.
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Tag(any::TypeId);
impl Tag {
/// Creates a [`Tag`] for a state of type `T`.
pub fn of<T>() -> Self
where
T: 'static,
@ -84,17 +104,23 @@ impl Tag {
Self(any::TypeId::of::<T>())
}
/// Creates a [`Tag`] for a stateless widget.
pub fn stateless() -> Self {
Self::of::<()>()
}
}
/// The internal [`State`] of a widget.
pub enum State {
/// No meaningful internal state.
None,
/// Some meaningful internal state.
Some(Box<dyn Any>),
}
impl State {
/// Creates a new [`State`].
pub fn new<T>(state: T) -> Self
where
T: 'static,
@ -102,6 +128,10 @@ impl State {
State::Some(Box::new(state))
}
/// Downcasts the [`State`] to `T` and returns a reference to it.
///
/// # Panics
/// This method will panic if the downcast fails or the [`State`] is [`State::None`].
pub fn downcast_ref<T>(&self) -> &T
where
T: 'static,
@ -114,6 +144,10 @@ impl State {
}
}
/// Downcasts the [`State`] to `T` and returns a mutable reference to it.
///
/// # Panics
/// This method will panic if the downcast fails or the [`State`] is [`State::None`].
pub fn downcast_mut<T>(&mut self) -> &mut T
where
T: 'static,

View file

@ -37,15 +37,15 @@ use crate::{Color, Command, Element, Executor, Settings, Subscription};
/// to listen to time.
/// - [`todos`], a todos tracker inspired by [TodoMVC].
///
/// [The repository has a bunch of examples]: https://github.com/hecrj/iced/tree/0.3/examples
/// [`clock`]: https://github.com/hecrj/iced/tree/0.3/examples/clock
/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.3/examples/download_progress
/// [`events`]: https://github.com/hecrj/iced/tree/0.3/examples/events
/// [`game_of_life`]: https://github.com/hecrj/iced/tree/0.3/examples/game_of_life
/// [`pokedex`]: https://github.com/hecrj/iced/tree/0.3/examples/pokedex
/// [`solar_system`]: https://github.com/hecrj/iced/tree/0.3/examples/solar_system
/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.3/examples/stopwatch
/// [`todos`]: https://github.com/hecrj/iced/tree/0.3/examples/todos
/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.4/examples
/// [`clock`]: https://github.com/iced-rs/iced/tree/0.4/examples/clock
/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.4/examples/download_progress
/// [`events`]: https://github.com/iced-rs/iced/tree/0.4/examples/events
/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.4/examples/game_of_life
/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.4/examples/pokedex
/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.4/examples/solar_system
/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.4/examples/stopwatch
/// [`todos`]: https://github.com/iced-rs/iced/tree/0.4/examples/todos
/// [`Sandbox`]: crate::Sandbox
/// [`Canvas`]: crate::widget::Canvas
/// [PokéAPI]: https://pokeapi.co/

View file

@ -19,19 +19,19 @@
//!
//! Check out the [repository] and the [examples] for more details!
//!
//! [Cross-platform support]: https://github.com/hecrj/iced/blob/master/docs/images/todos_desktop.jpg?raw=true
//! [Cross-platform support]: https://github.com/iced-rs/iced/blob/master/docs/images/todos_desktop.jpg?raw=true
//! [text inputs]: https://gfycat.com/alertcalmcrow-rust-gui
//! [scrollables]: https://gfycat.com/perkybaggybaboon-rust-gui
//! [Debug overlay with performance metrics]: https://gfycat.com/incredibledarlingbee
//! [Modular ecosystem]: https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md
//! [renderer-agnostic native runtime]: https://github.com/hecrj/iced/tree/master/native
//! [Modular ecosystem]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md
//! [renderer-agnostic native runtime]: https://github.com/iced-rs/iced/0.4/master/native
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
//! [built-in renderer]: https://github.com/hecrj/iced/tree/master/wgpu
//! [windowing shell]: https://github.com/hecrj/iced/tree/master/winit
//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.4/wgpu
//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.4/winit
//! [`dodrio`]: https://github.com/fitzgen/dodrio
//! [web runtime]: https://github.com/hecrj/iced/tree/master/web
//! [examples]: https://github.com/hecrj/iced/tree/0.3/examples
//! [repository]: https://github.com/hecrj/iced
//! [web runtime]: https://github.com/iced-rs/iced_web
//! [examples]: https://github.com/iced-rs/iced/tree/0.4/examples
//! [repository]: https://github.com/iced-rs/iced
//!
//! # Overview
//! Inspired by [The Elm Architecture], Iced expects you to split user
@ -196,6 +196,7 @@ pub mod widget;
pub mod window;
#[cfg(feature = "pure")]
#[cfg_attr(docsrs, doc(cfg(feature = "pure")))]
pub mod pure;
#[cfg(all(not(feature = "glow"), feature = "wgpu"))]
@ -225,8 +226,8 @@ pub use settings::Settings;
pub use runtime::alignment;
pub use runtime::futures;
pub use runtime::{
Alignment, Background, Color, Command, ContentFit, Font, Length, Point,
Rectangle, Size, Subscription, Vector,
Alignment, Background, Color, Command, ContentFit, Font, Length, Padding,
Point, Rectangle, Size, Subscription, Vector,
};
#[cfg(feature = "system")]

View file

@ -14,6 +14,84 @@
//! offers an alternate [`Application`] trait with a completely pure `view`
//! method.
//!
//! # The Elm Architecture, purity, and continuity
//! As you may know, applications made with `iced` use [The Elm Architecture].
//!
//! In a nutshell, this architecture defines the initial state of the application, a way to `view` it, and a way to `update` it after a user interaction. The `update` logic is called after a meaningful user interaction, which in turn updates the state of the application. Then, the `view` logic is executed to redisplay the application.
//!
//! Since `view` logic is only run after an `update`, all of the mutations to the application state must only happen in the `update` logic. If the application state changes anywhere else, the `view` logic will not be rerun and, therefore, the previously generated `view` may stay outdated.
//!
//! However, the `Application` trait in `iced` defines `view` as:
//!
//! ```ignore
//! pub trait Application {
//! fn view(&mut self) -> Element<Self::Message>;
//! }
//! ```
//!
//! As a consequence, the application state can be mutated in `view` logic. The `view` logic in `iced` is __impure__.
//!
//! This impurity is necessary because `iced` puts the burden of widget __continuity__ on its users. In other words, it's up to you to provide `iced` with the internal state of each widget every time `view` is called.
//!
//! If we take a look at the classic `counter` example:
//!
//! ```ignore
//! struct Counter {
//! value: i32,
//! increment_button: button::State,
//! decrement_button: button::State,
//! }
//!
//! // ...
//!
//! impl Counter {
//! pub fn view(&mut self) -> Column<Message> {
//! Column::new()
//! .push(
//! Button::new(&mut self.increment_button, Text::new("+"))
//! .on_press(Message::IncrementPressed),
//! )
//! .push(Text::new(self.value.to_string()).size(50))
//! .push(
//! Button::new(&mut self.decrement_button, Text::new("-"))
//! .on_press(Message::DecrementPressed),
//! )
//! }
//! }
//! ```
//!
//! We can see how we need to keep track of the `button::State` of each `Button` in our `Counter` state and provide a mutable reference to the widgets in our `view` logic. The widgets produced by `view` are __stateful__.
//!
//! While this approach forces users to keep track of widget state and causes impurity, I originally chose it because it allows `iced` to directly consume the widget tree produced by `view`. Since there is no internal state decoupled from `view` maintained by the runtime, `iced` does not need to compare (e.g. reconciliate) widget trees in order to ensure continuity.
//!
//! # Stateless widgets
//! As the library matures, the need for some kind of persistent widget data (see #553) between `view` calls becomes more apparent (e.g. incremental rendering, animations, accessibility, etc.).
//!
//! If we are going to end up having persistent widget data anyways... There is no reason to have impure, stateful widgets anymore!
//!
//! With the help of this module, we can now write a pure `counter` example:
//!
//! ```ignore
//! struct Counter {
//! value: i32,
//! }
//!
//! // ...
//!
//! impl Counter {
//! fn view(&self) -> Column<Message> {
//! Column::new()
//! .push(Button::new("Increment").on_press(Message::IncrementPressed))
//! .push(Text::new(self.value.to_string()).size(50))
//! .push(Button::new("Decrement").on_press(Message::DecrementPressed))
//! }
//! }
//! ```
//!
//! Notice how we no longer need to keep track of the `button::State`! The widgets in `iced_pure` do not take any mutable application state in `view`. They are __stateless__ widgets. As a consequence, we do not need mutable access to `self` in `view` anymore. `view` becomes __pure__.
//!
//! [The Elm Architecture]: https://guide.elm-lang.org/architecture/
//!
//! [the original widgets]: crate::widget
//! [`button::State`]: crate::widget::button::State
//! [impure `Application`]: crate::Application
@ -26,6 +104,7 @@ pub use application::Application;
pub use sandbox::Sandbox;
pub use iced_pure::helpers::*;
pub use iced_pure::Widget;
pub use iced_pure::{Pure, State};
/// A generic, pure [`Widget`].

View file

@ -47,7 +47,7 @@ pub mod pane_grid {
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support.
//!
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.3/examples/pane_grid
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.4/examples/pane_grid
pub use iced_pure::widget::pane_grid::{
Axis, Configuration, Direction, DragEvent, Line, Node, Pane,
ResizeEvent, Split, State, StyleSheet,
@ -118,6 +118,15 @@ pub mod text_input {
iced_pure::widget::TextInput<'a, Message, Renderer>;
}
pub mod tooltip {
//! Display a widget over another.
pub use iced_pure::widget::tooltip::Position;
/// A widget allowing the selection of a single value from a list of options.
pub type Tooltip<'a, Message> =
iced_pure::widget::Tooltip<'a, Message, crate::Renderer>;
}
pub use iced_pure::widget::progress_bar;
pub use iced_pure::widget::rule;
pub use iced_pure::widget::slider;
@ -135,6 +144,7 @@ pub use scrollable::Scrollable;
pub use slider::Slider;
pub use text_input::TextInput;
pub use toggler::Toggler;
pub use tooltip::Tooltip;
#[cfg(feature = "canvas")]
pub use iced_graphics::widget::pure::canvas;

View file

@ -35,19 +35,19 @@ use crate::{
/// - [`tour`], a simple UI tour that can run both on native platforms and the
/// web!
///
/// [The repository has a bunch of examples]: https://github.com/hecrj/iced/tree/0.3/examples
/// [`bezier_tool`]: https://github.com/hecrj/iced/tree/0.3/examples/bezier_tool
/// [`counter`]: https://github.com/hecrj/iced/tree/0.3/examples/counter
/// [`custom_widget`]: https://github.com/hecrj/iced/tree/0.3/examples/custom_widget
/// [`geometry`]: https://github.com/hecrj/iced/tree/0.3/examples/geometry
/// [`pane_grid`]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid
/// [`progress_bar`]: https://github.com/hecrj/iced/tree/0.3/examples/progress_bar
/// [`styling`]: https://github.com/hecrj/iced/tree/0.3/examples/styling
/// [`svg`]: https://github.com/hecrj/iced/tree/0.3/examples/svg
/// [`tour`]: https://github.com/hecrj/iced/tree/0.3/examples/tour
/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.4/examples
/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.4/examples/bezier_tool
/// [`counter`]: https://github.com/iced-rs/iced/tree/0.4/examples/counter
/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.4/examples/custom_widget
/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.4/examples/geometry
/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.4/examples/pane_grid
/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.4/examples/progress_bar
/// [`styling`]: https://github.com/iced-rs/iced/tree/0.4/examples/styling
/// [`svg`]: https://github.com/iced-rs/iced/tree/0.4/examples/svg
/// [`tour`]: https://github.com/iced-rs/iced/tree/0.4/examples/tour
/// [`Canvas widget`]: crate::widget::Canvas
/// [the overview]: index.html#overview
/// [`iced_wgpu`]: https://github.com/hecrj/iced/tree/0.3/wgpu
/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.4/wgpu
/// [`Svg` widget]: crate::widget::Svg
/// [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
///

View file

@ -63,7 +63,7 @@ pub mod pane_grid {
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support.
//!
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.3/examples/pane_grid
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.4/examples/pane_grid
pub use iced_native::widget::pane_grid::{
Axis, Configuration, Direction, DragEvent, Line, Node, Pane,
ResizeEvent, Split, State, StyleSheet,
@ -167,9 +167,11 @@ pub use toggler::Toggler;
pub use tooltip::Tooltip;
#[cfg(feature = "canvas")]
#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
pub use iced_graphics::widget::canvas;
#[cfg(feature = "image")]
#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
pub mod image {
//! Display images in your user interface.
pub use iced_native::image::Handle;
@ -182,9 +184,11 @@ pub mod image {
}
#[cfg(feature = "qr_code")]
#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))]
pub use iced_graphics::widget::qr_code;
#[cfg(feature = "svg")]
#[cfg_attr(docsrs, doc(cfg(feature = "svg")))]
pub mod svg {
//! Display vector graphics in your application.
pub use iced_native::svg::Handle;
@ -192,13 +196,17 @@ pub mod svg {
}
#[cfg(feature = "canvas")]
#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
pub use canvas::Canvas;
#[cfg(feature = "image")]
#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
pub use image::Image;
#[cfg(feature = "qr_code")]
#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))]
pub use qr_code::QRCode;
#[cfg(feature = "svg")]
#[cfg_attr(docsrs, doc(cfg(feature = "svg")))]
pub use svg::Svg;

View file

@ -1,6 +1,6 @@
[package]
name = "iced_style"
version = "0.3.0"
version = "0.4.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "The default set of styles of Iced"
@ -11,5 +11,5 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[dependencies.iced_core]
version = "0.4"
version = "0.5"
path = "../core"

View file

@ -1,6 +1,6 @@
[package]
name = "iced_wgpu"
version = "0.4.0"
version = "0.5.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A wgpu renderer for Iced"
@ -43,11 +43,11 @@ version = "1.4"
features = ["derive"]
[dependencies.iced_native]
version = "0.4"
version = "0.5"
path = "../native"
[dependencies.iced_graphics]
version = "0.2"
version = "0.3"
path = "../graphics"
features = ["font-fallback", "font-icons"]

View file

@ -16,7 +16,7 @@
//! - Meshes of triangles, useful to draw geometry freely.
//!
//! [Iced]: https://github.com/iced-rs/iced
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/master/native
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
//! [WebGPU API]: https://gpuweb.github.io/gpuweb/
//! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph

View file

@ -1,11 +1,11 @@
[package]
name = "iced_winit"
version = "0.3.0"
version = "0.4.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A winit runtime for Iced"
license = "MIT"
repository = "https://github.com/hecrj/iced"
repository = "https://github.com/iced-rs/iced"
documentation = "https://docs.rs/iced_winit"
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
@ -25,15 +25,15 @@ git = "https://github.com/iced-rs/winit"
rev = "02a12380960cec2f351c09a33d6a7cc2789d96a6"
[dependencies.iced_native]
version = "0.4"
version = "0.5"
path = "../native"
[dependencies.iced_graphics]
version = "0.2"
version = "0.3"
path = "../graphics"
[dependencies.iced_futures]
version = "0.3"
version = "0.4"
path = "../futures"
[target.'cfg(target_os = "windows")'.dependencies.winapi]

View file

@ -1,7 +1,7 @@
# `iced_winit`
[![Documentation](https://docs.rs/iced_winit/badge.svg)][documentation]
[![Crates.io](https://img.shields.io/crates/v/iced_winit.svg)](https://crates.io/crates/iced_winit)
[![License](https://img.shields.io/crates/l/iced_winit.svg)](https://github.com/hecrj/iced/blob/master/LICENSE)
[![License](https://img.shields.io/crates/l/iced_winit.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
`iced_winit` offers some convenient abstractions on top of [`iced_native`] to quickstart development when using [`winit`].

View file

@ -1,7 +1,7 @@
//! Convert [`winit`] types into [`iced_native`] types, and viceversa.
//!
//! [`winit`]: https://github.com/rust-windowing/winit
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
use crate::keyboard;
use crate::mouse;
use crate::touch;
@ -208,7 +208,7 @@ pub fn visible(mode: Mode) -> bool {
/// Converts a `MouseCursor` from [`iced_native`] to a [`winit`] cursor icon.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
pub fn mouse_interaction(
interaction: mouse::Interaction,
) -> winit::window::CursorIcon {
@ -232,7 +232,7 @@ pub fn mouse_interaction(
/// Converts a `MouseButton` from [`winit`] to an [`iced_native`] mouse button.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button {
match mouse_button {
winit::event::MouseButton::Left => mouse::Button::Left,
@ -248,7 +248,7 @@ pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button {
/// modifiers state.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
pub fn modifiers(
modifiers: winit::event::ModifiersState,
) -> keyboard::Modifiers {
@ -275,7 +275,7 @@ pub fn cursor_position(
/// Converts a `Touch` from [`winit`] to an [`iced_native`] touch event.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
pub fn touch_event(
touch: winit::event::Touch,
scale_factor: f64,
@ -306,7 +306,7 @@ pub fn touch_event(
/// Converts a `VirtualKeyCode` from [`winit`] to an [`iced_native`] key code.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
pub fn key_code(
virtual_keycode: winit::event::VirtualKeyCode,
) -> keyboard::KeyCode {

View file

@ -1,6 +1,6 @@
//! A windowing shell for Iced, on top of [`winit`].
//!
//! ![The native path of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//!
//! `iced_winit` offers some convenient abstractions on top of [`iced_native`]
//! to quickstart development when using [`winit`].
@ -11,7 +11,7 @@
//! Additionally, a [`conversion`] module is available for users that decide to
//! implement a custom event loop.
//!
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.4/native
//! [`winit`]: https://github.com/rust-windowing/winit
//! [`conversion`]: crate::conversion
#![doc(

View file

@ -37,12 +37,16 @@ pub struct Settings<Flags> {
/// Whether the [`Application`] should exit when the user requests the
/// window to close (e.g. the user presses the close button).
///
/// [`Application`]: crate::Application
pub exit_on_close_request: bool,
/// Whether the [`Application`] should try to build the context
/// using OpenGL ES first then OpenGL.
///
/// NOTE: Only works for the `glow` backend.
///
/// [`Application`]: crate::Application
pub try_opengles_first: bool,
}