summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--README.md32
-rw-r--r--core/src/angle.rs24
-rw-r--r--core/src/size.rs29
-rw-r--r--core/src/theme.rs6
-rw-r--r--core/src/theme/palette.rs15
-rw-r--r--core/src/window/redraw_request.rs2
-rw-r--r--docs/logo.svg3
-rw-r--r--examples/arc/src/main.rs53
-rw-r--r--examples/bezier_tool/src/main.rs21
-rw-r--r--examples/checkbox/src/main.rs33
-rw-r--r--examples/clock/src/main.rs89
-rw-r--r--examples/color_palette/src/main.rs34
-rw-r--r--examples/combo_box/src/main.rs18
-rw-r--r--examples/component/src/main.rs16
-rw-r--r--examples/counter/src/main.rs36
-rw-r--r--examples/custom_quad/src/main.rs20
-rw-r--r--examples/custom_shader/src/main.rs48
-rw-r--r--examples/custom_widget/src/main.rs18
-rw-r--r--examples/download_progress/src/main.rs50
-rw-r--r--examples/editor/src/main.rs69
-rw-r--r--examples/events/src/main.rs32
-rw-r--r--examples/exit/src/main.rs20
-rw-r--r--examples/game_of_life/src/main.rs56
-rw-r--r--examples/geometry/src/main.rs58
-rw-r--r--examples/gradient/src/main.rs41
-rw-r--r--examples/layout/src/main.rs34
-rw-r--r--examples/lazy/src/main.rs16
-rw-r--r--examples/loading_spinners/src/main.rs49
-rw-r--r--examples/loupe/src/main.rs33
-rw-r--r--examples/modal/src/main.rs26
-rw-r--r--examples/multitouch/src/main.rs73
-rw-r--r--examples/pane_grid/src/main.rs46
-rw-r--r--examples/pick_list/src/main.rs16
-rw-r--r--examples/pokedex/src/main.rs29
-rw-r--r--examples/progress_bar/src/main.rs16
-rw-r--r--examples/qr_code/src/main.rs22
-rw-r--r--examples/screenshot/src/main.rs42
-rw-r--r--examples/scrollable/src/main.rs54
-rw-r--r--examples/sierpinski_triangle/src/main.rs53
-rw-r--r--examples/slider/src/main.rs20
-rw-r--r--examples/solar_system/src/main.rs58
-rw-r--r--examples/stopwatch/src/main.rs43
-rw-r--r--examples/styling/src/main.rs18
-rw-r--r--examples/svg/src/main.rs20
-rw-r--r--examples/system_information/src/main.rs35
-rw-r--r--examples/toast/src/main.rs56
-rw-r--r--examples/todos/src/main.rs42
-rw-r--r--examples/tooltip/src/main.rs23
-rw-r--r--examples/tour/src/main.rs24
-rw-r--r--examples/url_handler/src/main.rs30
-rw-r--r--examples/vectorial_text/src/main.rs42
-rw-r--r--examples/visible_bounds/src/main.rs37
-rw-r--r--examples/websocket/src/main.rs28
-rw-r--r--renderer/src/geometry.rs4
-rw-r--r--runtime/src/command.rs6
-rw-r--r--src/advanced.rs1
-rw-r--r--src/application.rs12
-rw-r--r--src/lib.rs106
-rw-r--r--src/multi_window.rs4
-rw-r--r--src/program.rs851
-rw-r--r--src/sandbox.rs199
-rw-r--r--src/settings.rs14
-rw-r--r--tiny_skia/src/geometry.rs12
-rw-r--r--wgpu/src/geometry.rs8
-rw-r--r--widget/src/tooltip.rs7
65 files changed, 1650 insertions, 1382 deletions
diff --git a/README.md b/README.md
index 9f21fc83..0db09ded 100644
--- a/README.md
+++ b/README.md
@@ -98,8 +98,8 @@ that can be incremented and decremented using two buttons.
We start by modelling the __state__ of our application:
```rust
+#[derive(Default)]
struct Counter {
- // The counter value
value: i32,
}
```
@@ -110,8 +110,8 @@ the button presses. These interactions are our __messages__:
```rust
#[derive(Debug, Clone, Copy)]
pub enum Message {
- IncrementPressed,
- DecrementPressed,
+ Increment,
+ Decrement,
}
```
@@ -126,15 +126,15 @@ impl Counter {
// We use a column: a simple vertical layout
column![
// The increment button. We tell it to produce an
- // `IncrementPressed` message when pressed
- button("+").on_press(Message::IncrementPressed),
+ // `Increment` message when pressed
+ button("+").on_press(Message::Increment),
// We show the value of the counter here
text(self.value).size(50),
// The decrement button. We tell it to produce a
- // `DecrementPressed` message when pressed
- button("-").on_press(Message::DecrementPressed),
+ // `Decrement` message when pressed
+ button("-").on_press(Message::Decrement),
]
}
}
@@ -149,10 +149,10 @@ impl Counter {
pub fn update(&mut self, message: Message) {
match message {
- Message::IncrementPressed => {
+ Message::Increment => {
self.value += 1;
}
- Message::DecrementPressed => {
+ Message::Decrement => {
self.value -= 1;
}
}
@@ -160,15 +160,22 @@ impl Counter {
}
```
-And that's everything! We just wrote a whole user interface. Iced is now able
-to:
+And that's everything! We just wrote a whole user interface. Let's run it:
+
+```rust
+fn main() -> iced::Result {
+ iced::run("A cool counter", Counter::update, Counter::view)
+}
+```
+
+Iced will automatically:
1. Take the result of our __view logic__ and layout its widgets.
1. Process events from our system and produce __messages__ for our
__update logic__.
1. Draw the resulting user interface.
-Browse the [documentation] and the [examples] to learn more!
+Read the [book], the [documentation], and the [examples] to learn more!
## Implementation details
@@ -208,6 +215,7 @@ come chat to [our Discord server].
The development of Iced is sponsored by the [Cryptowatch] team at [Kraken.com]
+[book]: https://book.iced.rs/
[documentation]: https://docs.rs/iced/
[examples]: https://github.com/iced-rs/iced/tree/master/examples
[Coffee]: https://github.com/hecrj/coffee
diff --git a/core/src/angle.rs b/core/src/angle.rs
index 30ddad83..dc3c0e93 100644
--- a/core/src/angle.rs
+++ b/core/src/angle.rs
@@ -7,6 +7,18 @@ use std::ops::{Add, AddAssign, Div, Mul, RangeInclusive, Sub, SubAssign};
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct Degrees(pub f32);
+impl PartialEq<f32> for Degrees {
+ fn eq(&self, other: &f32) -> bool {
+ self.0.eq(other)
+ }
+}
+
+impl PartialOrd<f32> for Degrees {
+ fn partial_cmp(&self, other: &f32) -> Option<std::cmp::Ordering> {
+ self.0.partial_cmp(other)
+ }
+}
+
/// Radians
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct Radians(pub f32);
@@ -140,3 +152,15 @@ impl Div for Radians {
Self(self.0 / rhs.0)
}
}
+
+impl PartialEq<f32> for Radians {
+ fn eq(&self, other: &f32) -> bool {
+ self.0.eq(other)
+ }
+}
+
+impl PartialOrd<f32> for Radians {
+ fn partial_cmp(&self, other: &f32) -> Option<std::cmp::Ordering> {
+ self.0.partial_cmp(other)
+ }
+}
diff --git a/core/src/size.rs b/core/src/size.rs
index 90e50d13..267fc90e 100644
--- a/core/src/size.rs
+++ b/core/src/size.rs
@@ -53,20 +53,20 @@ impl Size {
}
}
-impl From<[f32; 2]> for Size {
- fn from([width, height]: [f32; 2]) -> Self {
+impl<T> From<[T; 2]> for Size<T> {
+ fn from([width, height]: [T; 2]) -> Self {
Size { width, height }
}
}
-impl From<[u16; 2]> for Size {
- fn from([width, height]: [u16; 2]) -> Self {
- Size::new(width.into(), height.into())
+impl<T> From<(T, T)> for Size<T> {
+ fn from((width, height): (T, T)) -> Self {
+ Self { width, height }
}
}
-impl From<Vector<f32>> for Size {
- fn from(vector: Vector<f32>) -> Self {
+impl<T> From<Vector<T>> for Size<T> {
+ fn from(vector: Vector<T>) -> Self {
Size {
width: vector.x,
height: vector.y,
@@ -74,20 +74,23 @@ impl From<Vector<f32>> for Size {
}
}
-impl From<Size> for [f32; 2] {
- fn from(size: Size) -> [f32; 2] {
+impl<T> From<Size<T>> for [T; 2] {
+ fn from(size: Size<T>) -> Self {
[size.width, size.height]
}
}
-impl From<Size> for Vector<f32> {
- fn from(size: Size) -> Self {
+impl<T> From<Size<T>> for Vector<T> {
+ fn from(size: Size<T>) -> Self {
Vector::new(size.width, size.height)
}
}
-impl std::ops::Sub for Size {
- type Output = Size;
+impl<T> std::ops::Sub for Size<T>
+where
+ T: std::ops::Sub<Output = T>,
+{
+ type Output = Size<T>;
fn sub(self, rhs: Self) -> Self::Output {
Size {
diff --git a/core/src/theme.rs b/core/src/theme.rs
index 21ba2a37..948aaf83 100644
--- a/core/src/theme.rs
+++ b/core/src/theme.rs
@@ -52,6 +52,8 @@ pub enum Theme {
Nightfly,
/// The built-in Oxocarbon variant.
Oxocarbon,
+ /// The built-in Ferra variant:
+ Ferra,
/// A [`Theme`] that uses a [`Custom`] palette.
Custom(Arc<Custom>),
}
@@ -80,6 +82,7 @@ impl Theme {
Self::Moonfly,
Self::Nightfly,
Self::Oxocarbon,
+ Self::Ferra,
];
/// Creates a new custom [`Theme`] from the given [`Palette`].
@@ -121,6 +124,7 @@ impl Theme {
Self::Moonfly => Palette::MOONFLY,
Self::Nightfly => Palette::NIGHTFLY,
Self::Oxocarbon => Palette::OXOCARBON,
+ Self::Ferra => Palette::FERRA,
Self::Custom(custom) => custom.palette,
}
}
@@ -151,6 +155,7 @@ impl Theme {
Self::Moonfly => &palette::EXTENDED_MOONFLY,
Self::Nightfly => &palette::EXTENDED_NIGHTFLY,
Self::Oxocarbon => &palette::EXTENDED_OXOCARBON,
+ Self::Ferra => &palette::EXTENDED_FERRA,
Self::Custom(custom) => &custom.extended,
}
}
@@ -180,6 +185,7 @@ impl fmt::Display for Theme {
Self::Moonfly => write!(f, "Moonfly"),
Self::Nightfly => write!(f, "Nightfly"),
Self::Oxocarbon => write!(f, "Oxocarbon"),
+ Self::Ferra => write!(f, "Ferra"),
Self::Custom(custom) => custom.fmt(f),
}
}
diff --git a/core/src/theme/palette.rs b/core/src/theme/palette.rs
index 985a54a8..ca91c248 100644
--- a/core/src/theme/palette.rs
+++ b/core/src/theme/palette.rs
@@ -276,6 +276,17 @@ impl Palette {
success: color!(0x00c15a),
danger: color!(0xf62d0f),
};
+
+ /// The built-in [Ferra] variant of a [`Palette`].
+ ///
+ /// [Ferra]: https://github.com/casperstorm/ferra
+ pub const FERRA: Self = Self {
+ background: color!(0x2b292d),
+ text: color!(0xfecdb2),
+ primary: color!(0xd1d1e0),
+ success: color!(0xb1b695),
+ danger: color!(0xe06b75),
+ };
}
/// An extended set of colors generated from a [`Palette`].
@@ -379,6 +390,10 @@ pub static EXTENDED_NIGHTFLY: Lazy<Extended> =
pub static EXTENDED_OXOCARBON: Lazy<Extended> =
Lazy::new(|| Extended::generate(Palette::OXOCARBON));
+/// The built-in Ferra variant of an [`Extended`] palette.
+pub static EXTENDED_FERRA: Lazy<Extended> =
+ Lazy::new(|| Extended::generate(Palette::FERRA));
+
impl Extended {
/// Generates an [`Extended`] palette from a simple [`Palette`].
pub fn generate(palette: Palette) -> Self {
diff --git a/core/src/window/redraw_request.rs b/core/src/window/redraw_request.rs
index 8a59e83c..b0c000d6 100644
--- a/core/src/window/redraw_request.rs
+++ b/core/src/window/redraw_request.rs
@@ -13,7 +13,7 @@ pub enum RedrawRequest {
#[cfg(test)]
mod tests {
use super::*;
- use crate::time::{Duration, Instant};
+ use crate::time::Duration;
#[test]
fn ordering() {
diff --git a/docs/logo.svg b/docs/logo.svg
index ff4eb3a7..aa1924c2 100644
--- a/docs/logo.svg
+++ b/docs/logo.svg
@@ -1 +1,2 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="68" height="68" fill="none" viewBox="0 0 68 68"><rect width="68" height="68" fill="url(#paint0_linear)" rx="18"/><path fill="url(#paint1_linear)" fill-rule="evenodd" d="M52.2952 16.986L43.0273 26.4088L41.0081 24.4228L50.2761 15L52.2952 16.986ZM43.6175 17.0578L29.7156 31.192L27.6964 29.206L41.5983 15.0718L43.6175 17.0578ZM23.3109 24.9347C25.065 23.1615 26.8188 21.3887 30.4157 17.7317L32.4348 19.7177C28.8353 23.3774 27.0795 25.1523 25.3241 26.9267C23.8744 28.3921 22.425 29.8572 19.9355 32.3857L30.4685 35.1388L39.0223 26.442L41.0414 28.428L32.4876 37.1247L35.4218 47.6353L49.3019 33.7718L51.3033 35.7756L35.1269 51.9327L20.0283 47.7728L15.6165 32.7371L16.2 32.1438C19.7995 28.4842 21.5555 26.7091 23.3109 24.9347ZM32.6721 48.3186L29.7581 37.8804L19.2731 35.1398L22.3017 45.4614L32.6721 48.3186ZM48.3953 29.7021L50.7414 27.3561L48.7387 25.3535L46.3844 27.7078L36.4462 37.812L38.4654 39.7979L48.3953 29.7021Z" clip-rule="evenodd"/><defs><linearGradient id="paint0_linear" x1="34" x2="34" y1="0" y2="68" gradientUnits="userSpaceOnUse"><stop stop-color="#00A3FF"/><stop offset="1" stop-color="#30F"/></linearGradient><linearGradient id="paint1_linear" x1="20.5" x2="60" y1="47.466" y2="6.966" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity=".65"/></linearGradient></defs></svg> \ No newline at end of file
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="160" height="180" fill="none" version="1.1" viewBox="35 20 185 195" xmlns="http://www.w3.org/2000/svg"><g filter="url(#filter0_f)"><rect x="59" y="51.001" width="136" height="160" rx="32" fill="url(#paint0_linear)"/></g><rect x="42" y="31.001" width="169.9" height="169.9" rx="49.815" fill="url(#paint1_linear)"/><path d="m182.62 65.747-28.136 28.606-6.13-6.0291 28.136-28.606 6.13 6.0291zm-26.344 0.218-42.204 42.909-6.13-6.029 42.204-42.909 6.13 6.0291zm-61.648 23.913c5.3254-5.3831 10.65-10.765 21.569-21.867l6.13 6.0291c-10.927 11.11-16.258 16.498-21.587 21.885-4.4007 4.4488-8.8009 8.8968-16.359 16.573l31.977 8.358 25.968-26.402 6.13 6.0292-25.968 26.402 8.907 31.908 42.138-42.087 6.076 6.083-49.109 49.05-45.837-12.628-13.394-45.646 1.7714-1.801c10.928-11.111 16.258-16.499 21.588-21.886zm28.419 70.99-8.846-31.689-31.831-8.32 9.1945 31.335 31.482 8.674zm47.734-56.517 7.122-7.1221-6.08-6.0797-7.147 7.1474-30.171 30.674 6.13 6.029 30.146-30.649z" clip-rule="evenodd" fill="url(#paint2_linear)" fill-rule="evenodd"/><defs><filter id="filter0_f" x="55" y="47.001" width="144" height="168" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur" stdDeviation="2"/></filter><linearGradient id="paint0_linear" x1="127" x2="127" y1="51.001" y2="211" gradientUnits="userSpaceOnUse"><stop offset=".052083"/><stop stop-opacity=".08" offset="1"/></linearGradient><linearGradient id="paint1_linear" x1="212" x2="57.5" y1="31.001" y2="189" gradientUnits="userSpaceOnUse"><stop stop-color="#00A3FF" offset="0"/><stop stop-color="#30f" offset="1"/></linearGradient><linearGradient id="paint2_linear" x1="86.098" x2="206.01" y1="158.28" y2="35.327" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" offset="0"/><stop stop-color="#fff" offset="1"/></linearGradient></defs></svg>
diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs
index 6a68cca1..4576404f 100644
--- a/examples/arc/src/main.rs
+++ b/examples/arc/src/main.rs
@@ -1,20 +1,17 @@
use std::{f32::consts::PI, time::Instant};
-use iced::executor;
use iced::mouse;
use iced::widget::canvas::{
self, stroke, Cache, Canvas, Geometry, Path, Stroke,
};
-use iced::{
- Application, Command, Element, Length, Point, Rectangle, Renderer,
- Settings, Subscription, Theme,
-};
+use iced::{Element, Length, Point, Rectangle, Renderer, Subscription, Theme};
pub fn main() -> iced::Result {
- Arc::run(Settings {
- antialiasing: true,
- ..Settings::default()
- })
+ iced::program("Arc - Iced", Arc::update, Arc::view)
+ .subscription(Arc::subscription)
+ .theme(|_| Theme::Dark)
+ .antialiasing(true)
+ .run()
}
struct Arc {
@@ -27,30 +24,9 @@ enum Message {
Tick,
}
-impl Application for Arc {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (
- Arc {
- start: Instant::now(),
- cache: Cache::default(),
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Arc - Iced")
- }
-
- fn update(&mut self, _: Message) -> Command<Message> {
+impl Arc {
+ fn update(&mut self, _: Message) {
self.cache.clear();
-
- Command::none()
}
fn view(&self) -> Element<Message> {
@@ -60,16 +36,21 @@ impl Application for Arc {
.into()
}
- fn theme(&self) -> Theme {
- Theme::Dark
- }
-
fn subscription(&self) -> Subscription<Message> {
iced::time::every(std::time::Duration::from_millis(10))
.map(|_| Message::Tick)
}
}
+impl Default for Arc {
+ fn default() -> Self {
+ Arc {
+ start: Instant::now(),
+ cache: Cache::default(),
+ }
+ }
+}
+
impl<Message> canvas::Program<Message> for Arc {
type State = ();
diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs
index 897e7df8..cf70bd40 100644
--- a/examples/bezier_tool/src/main.rs
+++ b/examples/bezier_tool/src/main.rs
@@ -1,12 +1,11 @@
//! This example showcases an interactive `Canvas` for drawing Bézier curves.
use iced::widget::{button, column, text};
-use iced::{Alignment, Element, Length, Sandbox, Settings};
+use iced::{Alignment, Element, Length};
pub fn main() -> iced::Result {
- Example::run(Settings {
- antialiasing: true,
- ..Settings::default()
- })
+ iced::program("Bezier Tool - Iced", Example::update, Example::view)
+ .antialiasing(true)
+ .run()
}
#[derive(Default)]
@@ -21,17 +20,7 @@ enum Message {
Clear,
}
-impl Sandbox for Example {
- type Message = Message;
-
- fn new() -> Self {
- Example::default()
- }
-
- fn title(&self) -> String {
- String::from("Bezier tool - Iced")
- }
-
+impl Example {
fn update(&mut self, message: Message) {
match message {
Message::AddCurve(curve) => {
diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs
index ee745c03..38949336 100644
--- a/examples/checkbox/src/main.rs
+++ b/examples/checkbox/src/main.rs
@@ -1,12 +1,12 @@
-use iced::executor;
-use iced::font::{self, Font};
use iced::widget::{checkbox, column, container, row, text};
-use iced::{Application, Command, Element, Length, Settings, Theme};
+use iced::{Element, Font, Length};
const ICON_FONT: Font = Font::with_name("icons");
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::program("Checkbox - Iced", Example::update, Example::view)
+ .font(include_bytes!("../fonts/icons.ttf").as_slice())
+ .run()
}
#[derive(Default)]
@@ -21,28 +21,10 @@ enum Message {
DefaultToggled(bool),
CustomToggled(bool),
StyledToggled(bool),
- FontLoaded(Result<(), font::Error>),
}
-impl Application for Example {
- type Message = Message;
- type Flags = ();
- type Executor = executor::Default;
- type Theme = Theme;
-
- fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
- (
- Self::default(),
- font::load(include_bytes!("../fonts/icons.ttf").as_slice())
- .map(Message::FontLoaded),
- )
- }
-
- fn title(&self) -> String {
- String::from("Checkbox - Iced")
- }
-
- fn update(&mut self, message: Message) -> Command<Message> {
+impl Example {
+ fn update(&mut self, message: Message) {
match message {
Message::DefaultToggled(default) => {
self.default = default;
@@ -53,10 +35,7 @@ impl Application for Example {
Message::CustomToggled(custom) => {
self.custom = custom;
}
- Message::FontLoaded(_) => (),
}
-
- Command::none()
}
fn view(&self) -> Element<Message> {
diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs
index 87da0c7e..897f8f1b 100644
--- a/examples/clock/src/main.rs
+++ b/examples/clock/src/main.rs
@@ -1,17 +1,18 @@
-use iced::executor;
+use iced::alignment;
use iced::mouse;
use iced::widget::canvas::{stroke, Cache, Geometry, LineCap, Path, Stroke};
use iced::widget::{canvas, container};
use iced::{
- Application, Command, Element, Length, Point, Rectangle, Renderer,
- Settings, Subscription, Theme, Vector,
+ Degrees, Element, Font, Length, Point, Rectangle, Renderer, Subscription,
+ Theme, Vector,
};
pub fn main() -> iced::Result {
- Clock::run(Settings {
- antialiasing: true,
- ..Settings::default()
- })
+ iced::program("Clock - Iced", Clock::update, Clock::view)
+ .subscription(Clock::subscription)
+ .theme(Clock::theme)
+ .antialiasing(true)
+ .run()
}
struct Clock {
@@ -24,28 +25,8 @@ enum Message {
Tick(time::OffsetDateTime),
}
-impl Application for Clock {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (
- Clock {
- now: time::OffsetDateTime::now_local()
- .unwrap_or_else(|_| time::OffsetDateTime::now_utc()),
- clock: Cache::default(),
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Clock - Iced")
- }
-
- fn update(&mut self, message: Message) -> Command<Message> {
+impl Clock {
+ fn update(&mut self, message: Message) {
match message {
Message::Tick(local_time) => {
let now = local_time;
@@ -56,8 +37,6 @@ impl Application for Clock {
}
}
}
-
- Command::none()
}
fn view(&self) -> Element<Message> {
@@ -82,7 +61,18 @@ impl Application for Clock {
}
fn theme(&self) -> Theme {
- Theme::TokyoNight
+ Theme::ALL[(self.now.unix_timestamp() as usize / 10) % Theme::ALL.len()]
+ .clone()
+ }
+}
+
+impl Default for Clock {
+ fn default() -> Self {
+ Self {
+ now: time::OffsetDateTime::now_local()
+ .unwrap_or_else(|_| time::OffsetDateTime::now_utc()),
+ clock: Cache::default(),
+ }
}
}
@@ -104,7 +94,7 @@ impl<Message> canvas::Program<Message> for Clock {
let radius = frame.width().min(frame.height()) / 2.0;
let background = Path::circle(center, radius);
- frame.fill(&background, palette.primary.weak.color);
+ frame.fill(&background, palette.secondary.strong.color);
let short_hand =
Path::line(Point::ORIGIN, Point::new(0.0, -0.5 * radius));
@@ -117,7 +107,7 @@ impl<Message> canvas::Program<Message> for Clock {
let thin_stroke = || -> Stroke {
Stroke {
width,
- style: stroke::Style::Solid(palette.primary.weak.text),
+ style: stroke::Style::Solid(palette.secondary.strong.text),
line_cap: LineCap::Round,
..Stroke::default()
}
@@ -126,7 +116,7 @@ impl<Message> canvas::Program<Message> for Clock {
let wide_stroke = || -> Stroke {
Stroke {
width: width * 3.0,
- style: stroke::Style::Solid(palette.primary.weak.text),
+ style: stroke::Style::Solid(palette.secondary.strong.text),
line_cap: LineCap::Round,
..Stroke::default()
}
@@ -145,8 +135,31 @@ impl<Message> canvas::Program<Message> for Clock {
});
frame.with_save(|frame| {
- frame.rotate(hand_rotation(self.now.second(), 60));
+ let rotation = hand_rotation(self.now.second(), 60);
+
+ frame.rotate(rotation);
frame.stroke(&long_hand, thin_stroke());
+
+ let rotate_factor = if rotation < 180.0 { 1.0 } else { -1.0 };
+
+ frame.rotate(Degrees(-90.0 * rotate_factor));
+ frame.fill_text(canvas::Text {
+ content: theme.to_string(),
+ size: (radius / 15.0).into(),
+ position: Point::new(
+ (0.78 * radius) * rotate_factor,
+ -width * 2.0,
+ ),
+ color: palette.secondary.strong.text,
+ horizontal_alignment: if rotate_factor > 0.0 {
+ alignment::Horizontal::Right
+ } else {
+ alignment::Horizontal::Left
+ },
+ vertical_alignment: alignment::Vertical::Bottom,
+ font: Font::MONOSPACE,
+ ..canvas::Text::default()
+ });
});
});
@@ -154,8 +167,8 @@ impl<Message> canvas::Program<Message> for Clock {
}
}
-fn hand_rotation(n: u8, total: u8) -> f32 {
+fn hand_rotation(n: u8, total: u8) -> Degrees {
let turns = n as f32 / total as f32;
- 2.0 * std::f32::consts::PI * turns
+ Degrees(360.0 * turns)
}
diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs
index 4150c641..d9325edb 100644
--- a/examples/color_palette/src/main.rs
+++ b/examples/color_palette/src/main.rs
@@ -3,21 +3,23 @@ use iced::mouse;
use iced::widget::canvas::{self, Canvas, Frame, Geometry, Path};
use iced::widget::{column, row, text, Slider};
use iced::{
- Color, Element, Font, Length, Pixels, Point, Rectangle, Renderer, Sandbox,
- Settings, Size, Vector,
-};
-use palette::{
- self, convert::FromColor, rgb::Rgb, Darken, Hsl, Lighten, ShiftHue,
+ Color, Element, Font, Length, Pixels, Point, Rectangle, Renderer, Size,
+ Vector,
};
+use palette::{convert::FromColor, rgb::Rgb, Darken, Hsl, Lighten, ShiftHue};
use std::marker::PhantomData;
use std::ops::RangeInclusive;
pub fn main() -> iced::Result {
- ColorPalette::run(Settings {
- antialiasing: true,
- default_font: Font::MONOSPACE,
- ..Settings::default()
- })
+ iced::program(
+ "Color Palette - Iced",
+ ColorPalette::update,
+ ColorPalette::view,
+ )
+ .theme(ColorPalette::theme)
+ .default_font(Font::MONOSPACE)
+ .antialiasing(true)
+ .run()
}
#[derive(Default)]
@@ -41,17 +43,7 @@ pub enum Message {
LchColorChanged(palette::Lch),
}
-impl Sandbox for ColorPalette {
- type Message = Message;
-
- fn new() -> Self {
- Self::default()
- }
-
- fn title(&self) -> String {
- String::from("Color palette - Iced")
- }
-
+impl ColorPalette {
fn update(&mut self, message: Message) {
let srgb = match message {
Message::RgbColorChanged(rgb) => Rgb::from(rgb),
diff --git a/examples/combo_box/src/main.rs b/examples/combo_box/src/main.rs
index fcf5feaa..2feb4522 100644
--- a/examples/combo_box/src/main.rs
+++ b/examples/combo_box/src/main.rs
@@ -1,10 +1,10 @@
use iced::widget::{
column, combo_box, container, scrollable, text, vertical_space,
};
-use iced::{Alignment, Element, Length, Sandbox, Settings};
+use iced::{Alignment, Element, Length};
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::run("Combo Box - Iced", Example::update, Example::view)
}
struct Example {
@@ -20,9 +20,7 @@ enum Message {
Closed,
}
-impl Sandbox for Example {
- type Message = Message;
-
+impl Example {
fn new() -> Self {
Self {
languages: combo_box::State::new(Language::ALL.to_vec()),
@@ -31,10 +29,6 @@ impl Sandbox for Example {
}
}
- fn title(&self) -> String {
- String::from("Combo box - Iced")
- }
-
fn update(&mut self, message: Message) {
match message {
Message::Selected(language) => {
@@ -83,6 +77,12 @@ impl Sandbox for Example {
}
}
+impl Default for Example {
+ fn default() -> Self {
+ Example::new()
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Language {
Danish,
diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs
index d4f99798..43ba3187 100644
--- a/examples/component/src/main.rs
+++ b/examples/component/src/main.rs
@@ -1,10 +1,10 @@
use iced::widget::container;
-use iced::{Element, Length, Sandbox, Settings};
+use iced::{Element, Length};
use numeric_input::numeric_input;
pub fn main() -> iced::Result {
- Component::run(Settings::default())
+ iced::run("Component - Iced", Component::update, Component::view)
}
#[derive(Default)]
@@ -17,17 +17,7 @@ enum Message {
NumericInputChanged(Option<u32>),
}
-impl Sandbox for Component {
- type Message = Message;
-
- fn new() -> Self {
- Self::default()
- }
-
- fn title(&self) -> String {
- String::from("Component - Iced")
- }
-
+impl Component {
fn update(&mut self, message: Message) {
match message {
Message::NumericInputChanged(value) => {
diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs
index 13dcbf86..0dd7a976 100644
--- a/examples/counter/src/main.rs
+++ b/examples/counter/src/main.rs
@@ -1,50 +1,40 @@
-use iced::widget::{button, column, text};
-use iced::{Alignment, Element, Sandbox, Settings};
+use iced::widget::{button, column, text, Column};
+use iced::Alignment;
pub fn main() -> iced::Result {
- Counter::run(Settings::default())
+ iced::run("A cool counter", Counter::update, Counter::view)
}
+#[derive(Default)]
struct Counter {
- value: i32,
+ value: i64,
}
#[derive(Debug, Clone, Copy)]
enum Message {
- IncrementPressed,
- DecrementPressed,
+ Increment,
+ Decrement,
}
-impl Sandbox for Counter {
- type Message = Message;
-
- fn new() -> Self {
- Self { value: 0 }
- }
-
- fn title(&self) -> String {
- String::from("Counter - Iced")
- }
-
+impl Counter {
fn update(&mut self, message: Message) {
match message {
- Message::IncrementPressed => {
+ Message::Increment => {
self.value += 1;
}
- Message::DecrementPressed => {
+ Message::Decrement => {
self.value -= 1;
}
}
}
- fn view(&self) -> Element<Message> {
+ fn view(&self) -> Column<Message> {
column![
- button("Increment").on_press(Message::IncrementPressed),
+ button("Increment").on_press(Message::Increment),
text(self.value).size(50),
- button("Decrement").on_press(Message::DecrementPressed)
+ button("Decrement").on_press(Message::Decrement)
]
.padding(20)
.align_items(Alignment::Center)
- .into()
}
}
diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs
index f64379fa..c093e240 100644
--- a/examples/custom_quad/src/main.rs
+++ b/examples/custom_quad/src/main.rs
@@ -82,12 +82,10 @@ mod quad {
}
use iced::widget::{column, container, slider, text};
-use iced::{
- Alignment, Color, Element, Length, Sandbox, Settings, Shadow, Vector,
-};
+use iced::{Alignment, Color, Element, Length, Shadow, Vector};
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::run("Custom Quad - Iced", Example::update, Example::view)
}
struct Example {
@@ -109,9 +107,7 @@ enum Message {
ShadowBlurRadiusChanged(f32),
}
-impl Sandbox for Example {
- type Message = Message;
-
+impl Example {
fn new() -> Self {
Self {
radius: [50.0; 4],
@@ -124,10 +120,6 @@ impl Sandbox for Example {
}
}
- fn title(&self) -> String {
- String::from("Custom widget - Iced")
- }
-
fn update(&mut self, message: Message) {
let [tl, tr, br, bl] = self.radius;
match message {
@@ -203,3 +195,9 @@ impl Sandbox for Example {
.into()
}
}
+
+impl Default for Example {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/examples/custom_shader/src/main.rs b/examples/custom_shader/src/main.rs
index 9e8da3ba..aa3dafe9 100644
--- a/examples/custom_shader/src/main.rs
+++ b/examples/custom_shader/src/main.rs
@@ -2,18 +2,16 @@ mod scene;
use scene::Scene;
-use iced::executor;
use iced::time::Instant;
use iced::widget::shader::wgpu;
use iced::widget::{checkbox, column, container, row, shader, slider, text};
use iced::window;
-use iced::{
- Alignment, Application, Color, Command, Element, Length, Subscription,
- Theme,
-};
+use iced::{Alignment, Color, Element, Length, Subscription};
fn main() -> iced::Result {
- IcedCubes::run(iced::Settings::default())
+ iced::program("Custom Shader - Iced", IcedCubes::update, IcedCubes::view)
+ .subscription(IcedCubes::subscription)
+ .run()
}
struct IcedCubes {
@@ -30,27 +28,15 @@ enum Message {
LightColorChanged(Color),
}
-impl Application for IcedCubes {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
- (
- Self {
- start: Instant::now(),
- scene: Scene::new(),
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- "Iced Cubes".to_string()
+impl IcedCubes {
+ fn new() -> Self {
+ Self {
+ start: Instant::now(),
+ scene: Scene::new(),
+ }
}
- fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
+ fn update(&mut self, message: Message) {
match message {
Message::CubeAmountChanged(amount) => {
self.scene.change_amount(amount);
@@ -68,11 +54,9 @@ impl Application for IcedCubes {
self.scene.light_color = color;
}
}
-
- Command::none()
}
- fn view(&self) -> Element<'_, Self::Message> {
+ fn view(&self) -> Element<'_, Message> {
let top_controls = row![
control(
"Amount",
@@ -147,11 +131,17 @@ impl Application for IcedCubes {
.into()
}
- fn subscription(&self) -> Subscription<Self::Message> {
+ fn subscription(&self) -> Subscription<Message> {
window::frames().map(Message::Tick)
}
}
+impl Default for IcedCubes {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
fn control<'a>(
label: &'static str,
control: impl Into<Element<'a, Message>>,
diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs
index 305ef7dd..aa49ebd0 100644
--- a/examples/custom_widget/src/main.rs
+++ b/examples/custom_widget/src/main.rs
@@ -83,10 +83,10 @@ mod circle {
use circle::circle;
use iced::widget::{column, container, slider, text};
-use iced::{Alignment, Element, Length, Sandbox, Settings};
+use iced::{Alignment, Element, Length};
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::run("Custom Widget - Iced", Example::update, Example::view)
}
struct Example {
@@ -98,17 +98,11 @@ enum Message {
RadiusChanged(f32),
}
-impl Sandbox for Example {
- type Message = Message;
-
+impl Example {
fn new() -> Self {
Example { radius: 50.0 }
}
- fn title(&self) -> String {
- String::from("Custom widget - Iced")
- }
-
fn update(&mut self, message: Message) {
match message {
Message::RadiusChanged(radius) => {
@@ -136,3 +130,9 @@ impl Sandbox for Example {
.into()
}
}
+
+impl Default for Example {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs
index 675e9e26..9f4769e0 100644
--- a/examples/download_progress/src/main.rs
+++ b/examples/download_progress/src/main.rs
@@ -1,14 +1,12 @@
-use iced::executor;
-use iced::widget::{button, column, container, progress_bar, text, Column};
-use iced::{
- Alignment, Application, Command, Element, Length, Settings, Subscription,
- Theme,
-};
-
mod download;
+use iced::widget::{button, column, container, progress_bar, text, Column};
+use iced::{Alignment, Element, Length, Subscription};
+
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::program("Download Progress - Iced", Example::update, Example::view)
+ .subscription(Example::subscription)
+ .run()
}
#[derive(Debug)]
@@ -24,27 +22,15 @@ pub enum Message {
DownloadProgressed((usize, download::Progress)),
}
-impl Application for Example {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (Example, Command<Message>) {
- (
- Example {
- downloads: vec![Download::new(0)],
- last_id: 0,
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Download progress - Iced")
+impl Example {
+ fn new() -> Self {
+ Self {
+ downloads: vec![Download::new(0)],
+ last_id: 0,
+ }
}
- fn update(&mut self, message: Message) -> Command<Message> {
+ fn update(&mut self, message: Message) {
match message {
Message::Add => {
self.last_id += 1;
@@ -63,9 +49,7 @@ impl Application for Example {
download.progress(progress);
}
}
- };
-
- Command::none()
+ }
}
fn subscription(&self) -> Subscription<Message> {
@@ -93,6 +77,12 @@ impl Application for Example {
}
}
+impl Default for Example {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[derive(Debug)]
struct Download {
id: usize,
diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs
index 4c97acb1..ed16018a 100644
--- a/examples/editor/src/main.rs
+++ b/examples/editor/src/main.rs
@@ -1,14 +1,10 @@
-use iced::executor;
use iced::highlighter::{self, Highlighter};
use iced::keyboard;
use iced::widget::{
button, column, container, horizontal_space, pick_list, row, text,
text_editor, tooltip,
};
-use iced::{
- Alignment, Application, Command, Element, Font, Length, Settings,
- Subscription, Theme,
-};
+use iced::{Alignment, Command, Element, Font, Length, Subscription, Theme};
use std::ffi;
use std::io;
@@ -16,11 +12,13 @@ use std::path::{Path, PathBuf};
use std::sync::Arc;
pub fn main() -> iced::Result {
- Editor::run(Settings {
- fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()],
- default_font: Font::MONOSPACE,
- ..Settings::default()
- })
+ iced::program("Editor - Iced", Editor::update, Editor::view)
+ .load(Editor::load)
+ .subscription(Editor::subscription)
+ .theme(Editor::theme)
+ .font(include_bytes!("../fonts/icons.ttf").as_slice())
+ .default_font(Font::MONOSPACE)
+ .run()
}
struct Editor {
@@ -42,27 +40,22 @@ enum Message {
FileSaved(Result<PathBuf, Error>),
}
-impl Application for Editor {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
- (
- Self {
- file: None,
- content: text_editor::Content::new(),
- theme: highlighter::Theme::SolarizedDark,
- is_loading: true,
- is_dirty: false,
- },
- Command::perform(load_file(default_file()), Message::FileOpened),
- )
+impl Editor {
+ fn new() -> Self {
+ Self {
+ file: None,
+ content: text_editor::Content::new(),
+ theme: highlighter::Theme::SolarizedDark,
+ is_loading: true,
+ is_dirty: false,
+ }
}
- fn title(&self) -> String {
- String::from("Editor - Iced")
+ fn load() -> Command<Message> {
+ Command::perform(
+ load_file(format!("{}/src/main.rs", env!("CARGO_MANIFEST_DIR"))),
+ Message::FileOpened,
+ )
}
fn update(&mut self, message: Message) -> Command<Message> {
@@ -221,16 +214,18 @@ impl Application for Editor {
}
}
+impl Default for Editor {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[derive(Debug, Clone)]
pub enum Error {
DialogClosed,
IoError(io::ErrorKind),
}
-fn default_file() -> PathBuf {
- PathBuf::from(format!("{}/src/main.rs", env!("CARGO_MANIFEST_DIR")))
-}
-
async fn open_file() -> Result<(PathBuf, Arc<String>), Error> {
let picked_file = rfd::AsyncFileDialog::new()
.set_title("Open a text file...")
@@ -238,10 +233,14 @@ async fn open_file() -> Result<(PathBuf, Arc<String>), Error> {
.await
.ok_or(Error::DialogClosed)?;
- load_file(picked_file.path().to_owned()).await
+ load_file(picked_file).await
}
-async fn load_file(path: PathBuf) -> Result<(PathBuf, Arc<String>), Error> {
+async fn load_file(
+ path: impl Into<PathBuf>,
+) -> Result<(PathBuf, Arc<String>), Error> {
+ let path = path.into();
+
let contents = tokio::fs::read_to_string(&path)
.await
.map(Arc::new)
diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs
index d5d496c7..bf568c94 100644
--- a/examples/events/src/main.rs
+++ b/examples/events/src/main.rs
@@ -1,21 +1,14 @@
use iced::alignment;
use iced::event::{self, Event};
-use iced::executor;
use iced::widget::{button, checkbox, container, text, Column};
use iced::window;
-use iced::{
- Alignment, Application, Command, Element, Length, Settings, Subscription,
- Theme,
-};
+use iced::{Alignment, Command, Element, Length, Subscription};
pub fn main() -> iced::Result {
- Events::run(Settings {
- window: window::Settings {
- exit_on_close_request: false,
- ..window::Settings::default()
- },
- ..Settings::default()
- })
+ iced::program("Events - Iced", Events::update, Events::view)
+ .subscription(Events::subscription)
+ .exit_on_close_request(false)
+ .run()
}
#[derive(Debug, Default)]
@@ -31,20 +24,7 @@ enum Message {
Exit,
}
-impl Application for Events {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (Events, Command<Message>) {
- (Events::default(), Command::none())
- }
-
- fn title(&self) -> String {
- String::from("Events - Iced")
- }
-
+impl Events {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::EventOccurred(event) if self.enabled => {
diff --git a/examples/exit/src/main.rs b/examples/exit/src/main.rs
index ec618dc1..7bed272d 100644
--- a/examples/exit/src/main.rs
+++ b/examples/exit/src/main.rs
@@ -1,10 +1,9 @@
-use iced::executor;
use iced::widget::{button, column, container};
use iced::window;
-use iced::{Alignment, Application, Command, Element, Length, Settings, Theme};
+use iced::{Alignment, Command, Element, Length};
pub fn main() -> iced::Result {
- Exit::run(Settings::default())
+ iced::program("Exit - Iced", Exit::update, Exit::view).run()
}
#[derive(Default)]
@@ -18,20 +17,7 @@ enum Message {
Exit,
}
-impl Application for Exit {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (Self::default(), Command::none())
- }
-
- fn title(&self) -> String {
- String::from("Exit - Iced")
- }
-
+impl Exit {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Confirm => window::close(window::Id::MAIN),
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 5ec1a11c..2b0fae0b 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -5,32 +5,24 @@ mod preset;
use grid::Grid;
use preset::Preset;
-use iced::executor;
use iced::time;
use iced::widget::{
button, checkbox, column, container, pick_list, row, slider, text,
};
-use iced::window;
-use iced::{
- Alignment, Application, Command, Element, Length, Settings, Subscription,
- Theme,
-};
+use iced::{Alignment, Command, Element, Length, Subscription, Theme};
use std::time::Duration;
pub fn main() -> iced::Result {
tracing_subscriber::fmt::init();
- GameOfLife::run(Settings {
- antialiasing: true,
- window: window::Settings {
- position: window::Position::Centered,
- ..window::Settings::default()
- },
- ..Settings::default()
- })
+ iced::program("Game of Life - Iced", GameOfLife::update, GameOfLife::view)
+ .subscription(GameOfLife::subscription)
+ .theme(|_| Theme::Dark)
+ .antialiasing(true)
+ .centered()
+ .run()
}
-#[derive(Default)]
struct GameOfLife {
grid: Grid,
is_playing: bool,
@@ -52,24 +44,16 @@ enum Message {
PresetPicked(Preset),
}
-impl Application for GameOfLife {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (
- Self {
- speed: 5,
- ..Self::default()
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Game of Life - Iced")
+impl GameOfLife {
+ fn new() -> Self {
+ Self {
+ grid: Grid::default(),
+ is_playing: false,
+ queued_ticks: 0,
+ speed: 5,
+ next_speed: None,
+ version: 0,
+ }
}
fn update(&mut self, message: Message) -> Command<Message> {
@@ -154,9 +138,11 @@ impl Application for GameOfLife {
.height(Length::Fill)
.into()
}
+}
- fn theme(&self) -> Theme {
- Theme::Dark
+impl Default for GameOfLife {
+ fn default() -> Self {
+ Self::new()
}
}
diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs
index 1ccc4dd6..63efcbdd 100644
--- a/examples/geometry/src/main.rs
+++ b/examples/geometry/src/main.rs
@@ -147,51 +147,35 @@ mod rainbow {
}
use iced::widget::{column, container, scrollable};
-use iced::{Element, Length, Sandbox, Settings};
+use iced::{Element, Length};
use rainbow::rainbow;
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::run("Custom 2D Geometry - Iced", |_: &mut _, _| {}, view)
}
-struct Example;
-
-impl Sandbox for Example {
- type Message = ();
-
- fn new() -> Self {
- Self
- }
-
- fn title(&self) -> String {
- String::from("Custom 2D geometry - Iced")
- }
-
- fn update(&mut self, _: ()) {}
-
- fn view(&self) -> Element<()> {
- let content = column![
- rainbow(),
- "In this example we draw a custom widget Rainbow, using \
+fn view(_state: &()) -> Element<'_, ()> {
+ let content = column![
+ rainbow(),
+ "In this example we draw a custom widget Rainbow, using \
the Mesh2D primitive. This primitive supplies a list of \
triangles, expressed as vertices and indices.",
- "Move your cursor over it, and see the center vertex \
+ "Move your cursor over it, and see the center vertex \
follow you!",
- "Every Vertex2D defines its own color. You could use the \
+ "Every Vertex2D defines its own color. You could use the \
Mesh2D primitive to render virtually any two-dimensional \
geometry for your widget.",
- ]
- .padding(20)
- .spacing(20)
- .max_width(500);
-
- let scrollable =
- scrollable(container(content).width(Length::Fill).center_x());
-
- container(scrollable)
- .width(Length::Fill)
- .height(Length::Fill)
- .center_y()
- .into()
- }
+ ]
+ .padding(20)
+ .spacing(20)
+ .max_width(500);
+
+ let scrollable =
+ scrollable(container(content).width(Length::Fill).center_x());
+
+ container(scrollable)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_y()
+ .into()
}
diff --git a/examples/gradient/src/main.rs b/examples/gradient/src/main.rs
index 8ed4c830..22c21cdd 100644
--- a/examples/gradient/src/main.rs
+++ b/examples/gradient/src/main.rs
@@ -1,22 +1,17 @@
-use iced::application;
+use iced::gradient;
+use iced::program;
use iced::widget::{
checkbox, column, container, horizontal_space, row, slider, text,
};
-use iced::{gradient, window};
-use iced::{
- Alignment, Color, Element, Length, Radians, Sandbox, Settings, Theme,
-};
+use iced::{Alignment, Color, Element, Length, Radians, Theme};
pub fn main() -> iced::Result {
tracing_subscriber::fmt::init();
- Gradient::run(Settings {
- window: window::Settings {
- transparent: true,
- ..Default::default()
- },
- ..Default::default()
- })
+ iced::program("Gradient - Iced", Gradient::update, Gradient::view)
+ .style(Gradient::style)
+ .transparent(true)
+ .run()
}
#[derive(Debug, Clone, Copy)]
@@ -35,9 +30,7 @@ enum Message {
TransparentToggled(bool),
}
-impl Sandbox for Gradient {
- type Message = Message;
-
+impl Gradient {
fn new() -> Self {
Self {
start: Color::WHITE,
@@ -47,10 +40,6 @@ impl Sandbox for Gradient {
}
}
- fn title(&self) -> String {
- String::from("Gradient")
- }
-
fn update(&mut self, message: Message) {
match message {
Message::StartChanged(color) => self.start = color,
@@ -106,18 +95,26 @@ impl Sandbox for Gradient {
.into()
}
- fn style(&self, theme: &Theme) -> application::Appearance {
+ fn style(&self, theme: &Theme) -> program::Appearance {
+ use program::DefaultStyle;
+
if self.transparent {
- application::Appearance {
+ program::Appearance {
background_color: Color::TRANSPARENT,
text_color: theme.palette().text,
}
} else {
- application::default(theme)
+ Theme::default_style(theme)
}
}
}
+impl Default for Gradient {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
fn color_picker(label: &str, color: Color) -> Element<'_, Color> {
row![
text(label).width(64),
diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs
index 17c51e3d..713e2b70 100644
--- a/examples/layout/src/main.rs
+++ b/examples/layout/src/main.rs
@@ -1,4 +1,3 @@
-use iced::executor;
use iced::keyboard;
use iced::mouse;
use iced::widget::{
@@ -6,15 +5,18 @@ use iced::widget::{
row, scrollable, text,
};
use iced::{
- color, Alignment, Application, Command, Element, Font, Length, Point,
- Rectangle, Renderer, Settings, Subscription, Theme,
+ color, Alignment, Element, Font, Length, Point, Rectangle, Renderer,
+ Subscription, Theme,
};
pub fn main() -> iced::Result {
- Layout::run(Settings::default())
+ iced::program(Layout::title, Layout::update, Layout::view)
+ .subscription(Layout::subscription)
+ .theme(Layout::theme)
+ .run()
}
-#[derive(Debug)]
+#[derive(Default, Debug)]
struct Layout {
example: Example,
explain: bool,
@@ -29,28 +31,12 @@ enum Message {
ThemeSelected(Theme),
}
-impl Application for Layout {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
- (
- Self {
- example: Example::default(),
- explain: false,
- theme: Theme::Light,
- },
- Command::none(),
- )
- }
-
+impl Layout {
fn title(&self) -> String {
format!("{} - Layout - Iced", self.example.title)
}
- fn update(&mut self, message: Self::Message) -> Command<Message> {
+ fn update(&mut self, message: Message) {
match message {
Message::Next => {
self.example = self.example.next();
@@ -65,8 +51,6 @@ impl Application for Layout {
self.theme = theme;
}
}
-
- Command::none()
}
fn subscription(&self) -> Subscription<Message> {
diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs
index 8758fa66..2d53df93 100644
--- a/examples/lazy/src/main.rs
+++ b/examples/lazy/src/main.rs
@@ -2,13 +2,13 @@ use iced::widget::{
button, column, horizontal_space, lazy, pick_list, row, scrollable, text,
text_input,
};
-use iced::{Element, Length, Sandbox, Settings};
+use iced::{Element, Length};
use std::collections::HashSet;
use std::hash::Hash;
pub fn main() -> iced::Result {
- App::run(Settings::default())
+ iced::run("Lazy - Iced", App::update, App::view)
}
struct App {
@@ -120,17 +120,7 @@ enum Message {
ItemColorChanged(Item, Color),
}
-impl Sandbox for App {
- type Message = Message;
-
- fn new() -> Self {
- Self::default()
- }
-
- fn title(&self) -> String {
- String::from("Lazy - Iced")
- }
-
+impl App {
fn update(&mut self, message: Message) {
match message {
Message::InputChanged(input) => {
diff --git a/examples/loading_spinners/src/main.rs b/examples/loading_spinners/src/main.rs
index 93a4605e..eaa4d57e 100644
--- a/examples/loading_spinners/src/main.rs
+++ b/examples/loading_spinners/src/main.rs
@@ -1,6 +1,5 @@
-use iced::executor;
use iced::widget::{column, container, row, slider, text};
-use iced::{Application, Command, Element, Length, Settings, Theme};
+use iced::{Element, Length};
use std::time::Duration;
@@ -12,51 +11,31 @@ use circular::Circular;
use linear::Linear;
pub fn main() -> iced::Result {
- LoadingSpinners::run(Settings {
- antialiasing: true,
- ..Default::default()
- })
+ iced::program(
+ "Loading Spinners - Iced",
+ LoadingSpinners::update,
+ LoadingSpinners::view,
+ )
+ .antialiasing(true)
+ .run()
}
struct LoadingSpinners {
cycle_duration: f32,
}
-impl Default for LoadingSpinners {
- fn default() -> Self {
- Self {
- cycle_duration: 2.0,
- }
- }
-}
-
#[derive(Debug, Clone, Copy)]
enum Message {
CycleDurationChanged(f32),
}
-impl Application for LoadingSpinners {
- type Message = Message;
- type Flags = ();
- type Executor = executor::Default;
- type Theme = Theme;
-
- fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
- (Self::default(), Command::none())
- }
-
- fn title(&self) -> String {
- String::from("Loading Spinners - Iced")
- }
-
- fn update(&mut self, message: Message) -> Command<Message> {
+impl LoadingSpinners {
+ fn update(&mut self, message: Message) {
match message {
Message::CycleDurationChanged(duration) => {
self.cycle_duration = duration;
}
}
-
- Command::none()
}
fn view(&self) -> Element<Message> {
@@ -115,3 +94,11 @@ impl Application for LoadingSpinners {
.into()
}
}
+
+impl Default for LoadingSpinners {
+ fn default() -> Self {
+ Self {
+ cycle_duration: 2.0,
+ }
+ }
+}
diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs
index 8602edb7..6a5ff123 100644
--- a/examples/loupe/src/main.rs
+++ b/examples/loupe/src/main.rs
@@ -1,39 +1,30 @@
use iced::widget::{button, column, container, text};
-use iced::{Alignment, Element, Length, Sandbox, Settings};
+use iced::{Alignment, Element, Length};
use loupe::loupe;
pub fn main() -> iced::Result {
- Counter::run(Settings::default())
+ iced::run("Loupe - Iced", Loupe::update, Loupe::view)
}
-struct Counter {
- value: i32,
+#[derive(Default)]
+struct Loupe {
+ value: i64,
}
#[derive(Debug, Clone, Copy)]
enum Message {
- IncrementPressed,
- DecrementPressed,
+ Increment,
+ Decrement,
}
-impl Sandbox for Counter {
- type Message = Message;
-
- fn new() -> Self {
- Self { value: 0 }
- }
-
- fn title(&self) -> String {
- String::from("Counter - Iced")
- }
-
+impl Loupe {
fn update(&mut self, message: Message) {
match message {
- Message::IncrementPressed => {
+ Message::Increment => {
self.value += 1;
}
- Message::DecrementPressed => {
+ Message::Decrement => {
self.value -= 1;
}
}
@@ -43,9 +34,9 @@ impl Sandbox for Counter {
container(loupe(
3.0,
column![
- button("Increment").on_press(Message::IncrementPressed),
+ button("Increment").on_press(Message::Increment),
text(self.value).size(50),
- button("Decrement").on_press(Message::DecrementPressed)
+ button("Decrement").on_press(Message::Decrement)
]
.padding(20)
.align_items(Alignment::Center),
diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs
index e1fc04c0..398728e0 100644
--- a/examples/modal/src/main.rs
+++ b/examples/modal/src/main.rs
@@ -1,20 +1,19 @@
use iced::event::{self, Event};
-use iced::executor;
use iced::keyboard;
use iced::keyboard::key;
use iced::widget::{
self, button, column, container, horizontal_space, pick_list, row, text,
text_input,
};
-use iced::{
- Alignment, Application, Command, Element, Length, Settings, Subscription,
-};
+use iced::{Alignment, Command, Element, Length, Subscription};
use modal::Modal;
use std::fmt;
pub fn main() -> iced::Result {
- App::run(Settings::default())
+ iced::program("Modal - Iced", App::update, App::view)
+ .subscription(App::subscription)
+ .run()
}
#[derive(Default)]
@@ -36,21 +35,8 @@ enum Message {
Event(Event),
}
-impl Application for App {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = iced::Theme;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (App::default(), Command::none())
- }
-
- fn title(&self) -> String {
- String::from("Modal - Iced")
- }
-
- fn subscription(&self) -> Subscription<Self::Message> {
+impl App {
+ fn subscription(&self) -> Subscription<Message> {
event::listen().map(Message::Event)
}
diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs
index 956ad471..2453c7f5 100644
--- a/examples/multitouch/src/main.rs
+++ b/examples/multitouch/src/main.rs
@@ -2,101 +2,58 @@
//! a circle around each fingertip. This only works on touch-enabled
//! computers like Microsoft Surface.
use iced::mouse;
+use iced::touch;
use iced::widget::canvas::event;
use iced::widget::canvas::stroke::{self, Stroke};
use iced::widget::canvas::{self, Canvas, Geometry};
-use iced::{
- executor, touch, window, Application, Color, Command, Element, Length,
- Point, Rectangle, Renderer, Settings, Subscription, Theme,
-};
+use iced::{Color, Element, Length, Point, Rectangle, Renderer, Theme};
use std::collections::HashMap;
pub fn main() -> iced::Result {
tracing_subscriber::fmt::init();
- Multitouch::run(Settings {
- antialiasing: true,
- window: window::Settings {
- position: window::Position::Centered,
- ..window::Settings::default()
- },
- ..Settings::default()
- })
+ iced::program("Multitouch - Iced", Multitouch::update, Multitouch::view)
+ .antialiasing(true)
+ .centered()
+ .run()
}
+#[derive(Default)]
struct Multitouch {
- state: State,
-}
-
-#[derive(Debug)]
-struct State {
cache: canvas::Cache,
fingers: HashMap<touch::Finger, Point>,
}
-impl State {
- fn new() -> Self {
- Self {
- cache: canvas::Cache::new(),
- fingers: HashMap::new(),
- }
- }
-}
-
#[derive(Debug)]
enum Message {
FingerPressed { id: touch::Finger, position: Point },
FingerLifted { id: touch::Finger },
}
-impl Application for Multitouch {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (
- Multitouch {
- state: State::new(),
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Multitouch - Iced")
- }
-
- fn update(&mut self, message: Message) -> Command<Message> {
+impl Multitouch {
+ fn update(&mut self, message: Message) {
match message {
Message::FingerPressed { id, position } => {
- self.state.fingers.insert(id, position);
- self.state.cache.clear();
+ self.fingers.insert(id, position);
+ self.cache.clear();
}
Message::FingerLifted { id } => {
- self.state.fingers.remove(&id);
- self.state.cache.clear();
+ self.fingers.remove(&id);
+ self.cache.clear();
}
}
-
- Command::none()
- }
-
- fn subscription(&self) -> Subscription<Message> {
- Subscription::none()
}
fn view(&self) -> Element<Message> {
- Canvas::new(&self.state)
+ Canvas::new(self)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
-impl canvas::Program<Message> for State {
+impl canvas::Program<Message> for Multitouch {
type State = ();
fn update(
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index 5e728ce1..829996d8 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -1,17 +1,15 @@
use iced::alignment::{self, Alignment};
-use iced::executor;
use iced::keyboard;
use iced::widget::pane_grid::{self, PaneGrid};
use iced::widget::{
button, column, container, responsive, row, scrollable, text,
};
-use iced::{
- Application, Color, Command, Element, Length, Settings, Size, Subscription,
- Theme,
-};
+use iced::{Color, Element, Length, Size, Subscription};
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::program("Pane Grid - Iced", Example::update, Example::view)
+ .subscription(Example::subscription)
+ .run()
}
struct Example {
@@ -35,30 +33,18 @@ enum Message {
CloseFocused,
}
-impl Application for Example {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
+impl Example {
+ fn new() -> Self {
let (panes, _) = pane_grid::State::new(Pane::new(0));
- (
- Example {
- panes,
- panes_created: 1,
- focus: None,
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Pane grid - Iced")
+ Example {
+ panes,
+ panes_created: 1,
+ focus: None,
+ }
}
- fn update(&mut self, message: Message) -> Command<Message> {
+ fn update(&mut self, message: Message) {
match message {
Message::Split(axis, pane) => {
let result =
@@ -132,8 +118,6 @@ impl Application for Example {
}
}
}
-
- Command::none()
}
fn subscription(&self) -> Subscription<Message> {
@@ -209,6 +193,12 @@ impl Application for Example {
}
}
+impl Default for Example {
+ fn default() -> Self {
+ Example::new()
+ }
+}
+
const PANE_ID_COLOR_UNFOCUSED: Color = Color::from_rgb(
0xFF as f32 / 255.0,
0xC7 as f32 / 255.0,
diff --git a/examples/pick_list/src/main.rs b/examples/pick_list/src/main.rs
index c40493e2..2be6f5b0 100644
--- a/examples/pick_list/src/main.rs
+++ b/examples/pick_list/src/main.rs
@@ -1,8 +1,8 @@
use iced::widget::{column, pick_list, scrollable, vertical_space};
-use iced::{Alignment, Element, Length, Sandbox, Settings};
+use iced::{Alignment, Element, Length};
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::run("Pick List - Iced", Example::update, Example::view)
}
#[derive(Default)]
@@ -15,17 +15,7 @@ enum Message {
LanguageSelected(Language),
}
-impl Sandbox for Example {
- type Message = Message;
-
- fn new() -> Self {
- Self::default()
- }
-
- fn title(&self) -> String {
- String::from("Pick list - Iced")
- }
-
+impl Example {
fn update(&mut self, message: Message) {
match message {
Message::LanguageSelected(language) => {
diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs
index 193f85f2..0811c08d 100644
--- a/examples/pokedex/src/main.rs
+++ b/examples/pokedex/src/main.rs
@@ -1,15 +1,20 @@
use iced::futures;
use iced::widget::{self, column, container, image, row, text};
-use iced::{Alignment, Application, Command, Element, Length, Settings, Theme};
+use iced::{Alignment, Command, Element, Length};
pub fn main() -> iced::Result {
- Pokedex::run(Settings::default())
+ iced::program(Pokedex::title, Pokedex::update, Pokedex::view)
+ .load(Pokedex::search)
+ .run()
}
-#[derive(Debug)]
+#[derive(Debug, Default)]
enum Pokedex {
+ #[default]
Loading,
- Loaded { pokemon: Pokemon },
+ Loaded {
+ pokemon: Pokemon,
+ },
Errored,
}
@@ -19,17 +24,9 @@ enum Message {
Search,
}
-impl Application for Pokedex {
- type Message = Message;
- type Theme = Theme;
- type Executor = iced::executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (Pokedex, Command<Message>) {
- (
- Pokedex::Loading,
- Command::perform(Pokemon::search(), Message::PokemonFound),
- )
+impl Pokedex {
+ fn search() -> Command<Message> {
+ Command::perform(Pokemon::search(), Message::PokemonFound)
}
fn title(&self) -> String {
@@ -59,7 +56,7 @@ impl Application for Pokedex {
_ => {
*self = Pokedex::Loading;
- Command::perform(Pokemon::search(), Message::PokemonFound)
+ Self::search()
}
},
}
diff --git a/examples/progress_bar/src/main.rs b/examples/progress_bar/src/main.rs
index d4ebe4d3..67da62f2 100644
--- a/examples/progress_bar/src/main.rs
+++ b/examples/progress_bar/src/main.rs
@@ -1,8 +1,8 @@
use iced::widget::{column, progress_bar, slider};
-use iced::{Element, Sandbox, Settings};
+use iced::Element;
pub fn main() -> iced::Result {
- Progress::run(Settings::default())
+ iced::run("Progress Bar - Iced", Progress::update, Progress::view)
}
#[derive(Default)]
@@ -15,17 +15,7 @@ enum Message {
SliderChanged(f32),
}
-impl Sandbox for Progress {
- type Message = Message;
-
- fn new() -> Self {
- Self::default()
- }
-
- fn title(&self) -> String {
- String::from("A simple Progressbar")
- }
-
+impl Progress {
fn update(&mut self, message: Message) {
match message {
Message::SliderChanged(x) => self.value = x,
diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs
index 36f79a31..b93adf04 100644
--- a/examples/qr_code/src/main.rs
+++ b/examples/qr_code/src/main.rs
@@ -1,10 +1,16 @@
use iced::widget::{
column, container, pick_list, qr_code, row, text, text_input,
};
-use iced::{Alignment, Element, Length, Sandbox, Settings, Theme};
+use iced::{Alignment, Element, Length, Theme};
pub fn main() -> iced::Result {
- QRGenerator::run(Settings::default())
+ iced::program(
+ "QR Code Generator - Iced",
+ QRGenerator::update,
+ QRGenerator::view,
+ )
+ .theme(QRGenerator::theme)
+ .run()
}
#[derive(Default)]
@@ -20,17 +26,7 @@ enum Message {
ThemeChanged(Theme),
}
-impl Sandbox for QRGenerator {
- type Message = Message;
-
- fn new() -> Self {
- QRGenerator::default()
- }
-
- fn title(&self) -> String {
- String::from("QR Code Generator - Iced")
- }
-
+impl QRGenerator {
fn update(&mut self, message: Message) {
match message {
Message::DataChanged(mut data) => {
diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs
index 2a1eded7..d887c41b 100644
--- a/examples/screenshot/src/main.rs
+++ b/examples/screenshot/src/main.rs
@@ -1,12 +1,10 @@
use iced::alignment;
-use iced::executor;
use iced::keyboard;
use iced::widget::{button, column, container, image, row, text, text_input};
use iced::window;
use iced::window::screenshot::{self, Screenshot};
use iced::{
- Alignment, Application, Command, ContentFit, Element, Length, Rectangle,
- Subscription, Theme,
+ Alignment, Command, ContentFit, Element, Length, Rectangle, Subscription,
};
use ::image as img;
@@ -15,9 +13,12 @@ use ::image::ColorType;
fn main() -> iced::Result {
tracing_subscriber::fmt::init();
- Example::run(iced::Settings::default())
+ iced::program("Screenshot - Iced", Example::update, Example::view)
+ .subscription(Example::subscription)
+ .run()
}
+#[derive(Default)]
struct Example {
screenshot: Option<Screenshot>,
saved_png_path: Option<Result<String, PngError>>,
@@ -42,33 +43,8 @@ enum Message {
HeightInputChanged(Option<u32>),
}
-impl Application for Example {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
- (
- Example {
- screenshot: None,
- saved_png_path: None,
- png_saving: false,
- crop_error: None,
- x_input_value: None,
- y_input_value: None,
- width_input_value: None,
- height_input_value: None,
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- "Screenshot".to_string()
- }
-
- fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
+impl Example {
+ fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Screenshot => {
return iced::window::screenshot(
@@ -130,7 +106,7 @@ impl Application for Example {
Command::none()
}
- fn view(&self) -> Element<'_, Self::Message> {
+ fn view(&self) -> Element<'_, Message> {
let image: Element<Message> = if let Some(screenshot) = &self.screenshot
{
image(image::Handle::from_pixels(
@@ -259,7 +235,7 @@ impl Application for Example {
.into()
}
- fn subscription(&self) -> Subscription<Self::Message> {
+ fn subscription(&self) -> Subscription<Message> {
use keyboard::key;
keyboard::on_key_press(|key, _modifiers| {
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
index 2ad7272b..240ae908 100644
--- a/examples/scrollable/src/main.rs
+++ b/examples/scrollable/src/main.rs
@@ -1,20 +1,22 @@
-use iced::executor;
use iced::widget::scrollable::Properties;
use iced::widget::{
button, column, container, horizontal_space, progress_bar, radio, row,
scrollable, slider, text, vertical_space, Scrollable,
};
-use iced::{
- Alignment, Application, Border, Color, Command, Element, Length, Settings,
- Theme,
-};
+use iced::{Alignment, Border, Color, Command, Element, Length, Theme};
use once_cell::sync::Lazy;
static SCROLLABLE_ID: Lazy<scrollable::Id> = Lazy::new(scrollable::Id::unique);
pub fn main() -> iced::Result {
- ScrollableDemo::run(Settings::default())
+ iced::program(
+ "Scrollable - Iced",
+ ScrollableDemo::update,
+ ScrollableDemo::view,
+ )
+ .theme(ScrollableDemo::theme)
+ .run()
}
struct ScrollableDemo {
@@ -45,28 +47,16 @@ enum Message {
Scrolled(scrollable::Viewport),
}
-impl Application for ScrollableDemo {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
- (
- ScrollableDemo {
- scrollable_direction: Direction::Vertical,
- scrollbar_width: 10,
- scrollbar_margin: 0,
- scroller_width: 10,
- current_scroll_offset: scrollable::RelativeOffset::START,
- alignment: scrollable::Alignment::Start,
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Scrollable - Iced")
+impl ScrollableDemo {
+ fn new() -> Self {
+ ScrollableDemo {
+ scrollable_direction: Direction::Vertical,
+ scrollbar_width: 10,
+ scrollbar_margin: 0,
+ scroller_width: 10,
+ current_scroll_offset: scrollable::RelativeOffset::START,
+ alignment: scrollable::Alignment::Start,
+ }
}
fn update(&mut self, message: Message) -> Command<Message> {
@@ -340,11 +330,17 @@ impl Application for ScrollableDemo {
container(content).padding(20).center_x().center_y().into()
}
- fn theme(&self) -> Self::Theme {
+ fn theme(&self) -> Theme {
Theme::Dark
}
}
+impl Default for ScrollableDemo {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
fn progress_bar_custom_style(theme: &Theme) -> progress_bar::Appearance {
progress_bar::Appearance {
background: theme.extended_palette().background.strong.color.into(),
diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs
index 01a114bb..07ae05d6 100644
--- a/examples/sierpinski_triangle/src/main.rs
+++ b/examples/sierpinski_triangle/src/main.rs
@@ -1,25 +1,23 @@
-use std::fmt::Debug;
-
-use iced::executor;
use iced::mouse;
use iced::widget::canvas::event::{self, Event};
use iced::widget::canvas::{self, Canvas};
use iced::widget::{column, row, slider, text};
-use iced::{
- Application, Color, Command, Length, Point, Rectangle, Renderer, Settings,
- Size, Theme,
-};
+use iced::{Color, Length, Point, Rectangle, Renderer, Size, Theme};
use rand::Rng;
+use std::fmt::Debug;
fn main() -> iced::Result {
- SierpinskiEmulator::run(Settings {
- antialiasing: true,
- ..Settings::default()
- })
+ iced::program(
+ "Sierpinski Triangle - Iced",
+ SierpinskiEmulator::update,
+ SierpinskiEmulator::view,
+ )
+ .antialiasing(true)
+ .run()
}
-#[derive(Debug)]
+#[derive(Debug, Default)]
struct SierpinskiEmulator {
graph: SierpinskiGraph,
}
@@ -31,27 +29,8 @@ pub enum Message {
PointRemoved,
}
-impl Application for SierpinskiEmulator {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: Self::Flags) -> (Self, iced::Command<Self::Message>) {
- let emulator = SierpinskiEmulator {
- graph: SierpinskiGraph::new(),
- };
- (emulator, Command::none())
- }
-
- fn title(&self) -> String {
- "Sierpinski Triangle Emulator".to_string()
- }
-
- fn update(
- &mut self,
- message: Self::Message,
- ) -> iced::Command<Self::Message> {
+impl SierpinskiEmulator {
+ fn update(&mut self, message: Message) {
match message {
Message::IterationSet(cur_iter) => {
self.graph.iteration = cur_iter;
@@ -67,11 +46,9 @@ impl Application for SierpinskiEmulator {
}
self.graph.redraw();
-
- Command::none()
}
- fn view(&self) -> iced::Element<'_, Self::Message> {
+ fn view(&self) -> iced::Element<'_, Message> {
column![
Canvas::new(&self.graph)
.width(Length::Fill)
@@ -167,10 +144,6 @@ impl canvas::Program<Message> for SierpinskiGraph {
}
impl SierpinskiGraph {
- fn new() -> SierpinskiGraph {
- SierpinskiGraph::default()
- }
-
fn redraw(&mut self) {
self.cache.clear();
}
diff --git a/examples/slider/src/main.rs b/examples/slider/src/main.rs
index f71dac01..b3a47614 100644
--- a/examples/slider/src/main.rs
+++ b/examples/slider/src/main.rs
@@ -1,8 +1,8 @@
use iced::widget::{column, container, slider, text, vertical_slider};
-use iced::{Element, Length, Sandbox, Settings};
+use iced::{Element, Length};
pub fn main() -> iced::Result {
- Slider::run(Settings::default())
+ iced::run("Slider - Iced", Slider::update, Slider::view)
}
#[derive(Debug, Clone)]
@@ -17,10 +17,8 @@ pub struct Slider {
shift_step: u8,
}
-impl Sandbox for Slider {
- type Message = Message;
-
- fn new() -> Slider {
+impl Slider {
+ fn new() -> Self {
Slider {
value: 50,
default: 50,
@@ -29,10 +27,6 @@ impl Sandbox for Slider {
}
}
- fn title(&self) -> String {
- String::from("Slider - Iced")
- }
-
fn update(&mut self, message: Message) {
match message {
Message::SliderChanged(value) => {
@@ -75,3 +69,9 @@ impl Sandbox for Slider {
.into()
}
}
+
+impl Default for Slider {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs
index 4cc625da..b5228f09 100644
--- a/examples/solar_system/src/main.rs
+++ b/examples/solar_system/src/main.rs
@@ -6,8 +6,6 @@
//! Inspired by the example found in the MDN docs[1].
//!
//! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system
-use iced::application;
-use iced::executor;
use iced::mouse;
use iced::widget::canvas;
use iced::widget::canvas::gradient;
@@ -15,8 +13,8 @@ use iced::widget::canvas::stroke::{self, Stroke};
use iced::widget::canvas::Path;
use iced::window;
use iced::{
- Application, Color, Command, Element, Length, Point, Rectangle, Renderer,
- Settings, Size, Subscription, Theme, Vector,
+ Color, Element, Length, Point, Rectangle, Renderer, Size, Subscription,
+ Theme, Vector,
};
use std::time::Instant;
@@ -24,12 +22,17 @@ use std::time::Instant;
pub fn main() -> iced::Result {
tracing_subscriber::fmt::init();
- SolarSystem::run(Settings {
- antialiasing: true,
- ..Settings::default()
- })
+ iced::program(
+ "Solar System - Iced",
+ SolarSystem::update,
+ SolarSystem::view,
+ )
+ .subscription(SolarSystem::subscription)
+ .theme(SolarSystem::theme)
+ .run()
}
+#[derive(Default)]
struct SolarSystem {
state: State,
}
@@ -39,33 +42,13 @@ enum Message {
Tick(Instant),
}
-impl Application for SolarSystem {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (
- SolarSystem {
- state: State::new(),
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Solar system - Iced")
- }
-
- fn update(&mut self, message: Message) -> Command<Message> {
+impl SolarSystem {
+ fn update(&mut self, message: Message) {
match message {
Message::Tick(instant) => {
self.state.update(instant);
}
}
-
- Command::none()
}
fn view(&self) -> Element<Message> {
@@ -76,14 +59,7 @@ impl Application for SolarSystem {
}
fn theme(&self) -> Theme {
- Theme::Dark
- }
-
- fn style(&self, _theme: &Theme) -> application::Appearance {
- application::Appearance {
- background_color: Color::BLACK,
- text_color: Color::WHITE,
- }
+ Theme::Moonfly
}
fn subscription(&self) -> Subscription<Message> {
@@ -224,3 +200,9 @@ impl<Message> canvas::Program<Message> for State {
vec![background, system]
}
}
+
+impl Default for State {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs
index 56b7686e..b9eb19cf 100644
--- a/examples/stopwatch/src/main.rs
+++ b/examples/stopwatch/src/main.rs
@@ -1,27 +1,31 @@
use iced::alignment;
-use iced::executor;
use iced::keyboard;
use iced::time;
use iced::widget::{button, column, container, row, text};
-use iced::{
- Alignment, Application, Command, Element, Length, Settings, Subscription,
- Theme,
-};
+use iced::{Alignment, Element, Length, Subscription, Theme};
use std::time::{Duration, Instant};
pub fn main() -> iced::Result {
- Stopwatch::run(Settings::default())
+ iced::program("Stopwatch - Iced", Stopwatch::update, Stopwatch::view)
+ .subscription(Stopwatch::subscription)
+ .theme(Stopwatch::theme)
+ .run()
}
+#[derive(Default)]
struct Stopwatch {
duration: Duration,
state: State,
}
+#[derive(Default)]
enum State {
+ #[default]
Idle,
- Ticking { last_tick: Instant },
+ Ticking {
+ last_tick: Instant,
+ },
}
#[derive(Debug, Clone)]
@@ -31,27 +35,8 @@ enum Message {
Tick(Instant),
}
-impl Application for Stopwatch {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (Stopwatch, Command<Message>) {
- (
- Stopwatch {
- duration: Duration::default(),
- state: State::Idle,
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Stopwatch - Iced")
- }
-
- fn update(&mut self, message: Message) -> Command<Message> {
+impl Stopwatch {
+ fn update(&mut self, message: Message) {
match message {
Message::Toggle => match self.state {
State::Idle => {
@@ -73,8 +58,6 @@ impl Application for Stopwatch {
self.duration = Duration::default();
}
}
-
- Command::none()
}
fn subscription(&self) -> Subscription<Message> {
diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs
index befdfc1b..73268da0 100644
--- a/examples/styling/src/main.rs
+++ b/examples/styling/src/main.rs
@@ -3,10 +3,12 @@ use iced::widget::{
progress_bar, row, scrollable, slider, text, text_input, toggler,
vertical_rule, vertical_space,
};
-use iced::{Alignment, Element, Length, Sandbox, Settings, Theme};
+use iced::{Alignment, Element, Length, Theme};
pub fn main() -> iced::Result {
- Styling::run(Settings::default())
+ iced::program("Styling - Iced", Styling::update, Styling::view)
+ .theme(Styling::theme)
+ .run()
}
#[derive(Default)]
@@ -28,17 +30,7 @@ enum Message {
TogglerToggled(bool),
}
-impl Sandbox for Styling {
- type Message = Message;
-
- fn new() -> Self {
- Styling::default()
- }
-
- fn title(&self) -> String {
- String::from("Styling - Iced")
- }
-
+impl Styling {
fn update(&mut self, message: Message) {
match message {
Message::ThemeChanged(theme) => {
diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs
index 0870dce4..cc686dca 100644
--- a/examples/svg/src/main.rs
+++ b/examples/svg/src/main.rs
@@ -1,8 +1,8 @@
use iced::widget::{checkbox, column, container, svg};
-use iced::{color, Element, Length, Sandbox, Settings};
+use iced::{color, Element, Length};
pub fn main() -> iced::Result {
- Tiger::run(Settings::default())
+ iced::run("SVG - Iced", Tiger::update, Tiger::view)
}
#[derive(Debug, Default)]
@@ -15,18 +15,8 @@ pub enum Message {
ToggleColorFilter(bool),
}
-impl Sandbox for Tiger {
- type Message = Message;
-
- fn new() -> Self {
- Tiger::default()
- }
-
- fn title(&self) -> String {
- String::from("SVG - Iced")
- }
-
- fn update(&mut self, message: Self::Message) {
+impl Tiger {
+ fn update(&mut self, message: Message) {
match message {
Message::ToggleColorFilter(apply_color_filter) => {
self.apply_color_filter = apply_color_filter;
@@ -34,7 +24,7 @@ impl Sandbox for Tiger {
}
}
- fn view(&self) -> Element<Self::Message> {
+ fn view(&self) -> Element<Message> {
let handle = svg::Handle::from_path(format!(
"{}/resources/tiger.svg",
env!("CARGO_MANIFEST_DIR")
diff --git a/examples/system_information/src/main.rs b/examples/system_information/src/main.rs
index 31dc92f1..a6ac27a6 100644
--- a/examples/system_information/src/main.rs
+++ b/examples/system_information/src/main.rs
@@ -1,18 +1,19 @@
use iced::widget::{button, column, container, text};
-use iced::{
- executor, system, Application, Command, Element, Length, Settings, Theme,
-};
-
-use bytesize::ByteSize;
+use iced::{system, Command, Element, Length};
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::program("System Information - Iced", Example::update, Example::view)
+ .run()
}
+#[derive(Default)]
#[allow(clippy::large_enum_variant)]
enum Example {
+ #[default]
Loading,
- Loaded { information: system::Information },
+ Loaded {
+ information: system::Information,
+ },
}
#[derive(Clone, Debug)]
@@ -22,23 +23,7 @@ enum Message {
Refresh,
}
-impl Application for Example {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (
- Self::Loading,
- system::fetch_information(Message::InformationReceived),
- )
- }
-
- fn title(&self) -> String {
- String::from("System Information - Iced")
- }
-
+impl Example {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Refresh => {
@@ -55,6 +40,8 @@ impl Application for Example {
}
fn view(&self) -> Element<Message> {
+ use bytesize::ByteSize;
+
let content: Element<_> = match self {
Example::Loading => text("Loading...").size(40).into(),
Example::Loaded { information } => {
diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs
index 5a07da3e..fdae1dc1 100644
--- a/examples/toast/src/main.rs
+++ b/examples/toast/src/main.rs
@@ -1,21 +1,19 @@
use iced::event::{self, Event};
-use iced::executor;
use iced::keyboard;
use iced::keyboard::key;
use iced::widget::{
self, button, column, container, pick_list, row, slider, text, text_input,
};
-use iced::{
- Alignment, Application, Command, Element, Length, Settings, Subscription,
-};
+use iced::{Alignment, Command, Element, Length, Subscription};
use toast::{Status, Toast};
pub fn main() -> iced::Result {
- App::run(Settings::default())
+ iced::program("Toast - Iced", App::update, App::view)
+ .subscription(App::subscription)
+ .run()
}
-#[derive(Default)]
struct App {
toasts: Vec<Toast>,
editing: Toast,
@@ -34,32 +32,20 @@ enum Message {
Event(Event),
}
-impl Application for App {
- type Executor = executor::Default;
- type Message = Message;
- type Theme = iced::Theme;
- type Flags = ();
-
- fn new(_flags: ()) -> (Self, Command<Message>) {
- (
- App {
- toasts: vec![Toast {
- title: "Example Toast".into(),
- body: "Add more toasts in the form below!".into(),
- status: Status::Primary,
- }],
- timeout_secs: toast::DEFAULT_TIMEOUT,
- ..Default::default()
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Toast - Iced")
+impl App {
+ fn new() -> Self {
+ App {
+ toasts: vec![Toast {
+ title: "Example Toast".into(),
+ body: "Add more toasts in the form below!".into(),
+ status: Status::Primary,
+ }],
+ timeout_secs: toast::DEFAULT_TIMEOUT,
+ editing: Toast::default(),
+ }
}
- fn subscription(&self) -> Subscription<Self::Message> {
+ fn subscription(&self) -> Subscription<Message> {
event::listen().map(Message::Event)
}
@@ -106,8 +92,8 @@ impl Application for App {
}
}
- fn view<'a>(&'a self) -> Element<'a, Message> {
- let subtitle = |title, content: Element<'a, Message>| {
+ fn view(&self) -> Element<'_, Message> {
+ let subtitle = |title, content: Element<'static, Message>| {
column![text(title).size(14), content].spacing(5)
};
@@ -172,6 +158,12 @@ impl Application for App {
}
}
+impl Default for App {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
mod toast {
use std::fmt;
use std::time::{Duration, Instant};
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index aaa86ef8..7768c1d5 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -1,14 +1,11 @@
use iced::alignment::{self, Alignment};
-use iced::font::{self, Font};
use iced::keyboard;
use iced::widget::{
self, button, checkbox, column, container, keyed_column, row, scrollable,
text, text_input, Text,
};
use iced::window;
-use iced::{
- Application, Command, Element, Length, Settings, Size, Subscription, Theme,
-};
+use iced::{Command, Element, Font, Length, Subscription};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
@@ -20,17 +17,17 @@ pub fn main() -> iced::Result {
#[cfg(not(target_arch = "wasm32"))]
tracing_subscriber::fmt::init();
- Todos::run(Settings {
- window: window::Settings {
- size: Size::new(500.0, 800.0),
- ..window::Settings::default()
- },
- ..Settings::default()
- })
+ iced::program(Todos::title, Todos::update, Todos::view)
+ .load(Todos::load)
+ .subscription(Todos::subscription)
+ .font(include_bytes!("../fonts/icons.ttf").as_slice())
+ .window_size((500.0, 800.0))
+ .run()
}
-#[derive(Debug)]
+#[derive(Default, Debug)]
enum Todos {
+ #[default]
Loading,
Loaded(State),
}
@@ -47,7 +44,6 @@ struct State {
#[derive(Debug, Clone)]
enum Message {
Loaded(Result<SavedState, LoadError>),
- FontLoaded(Result<(), font::Error>),
Saved(Result<(), SaveError>),
InputChanged(String),
CreateTask,
@@ -57,21 +53,9 @@ enum Message {
ToggleFullscreen(window::Mode),
}
-impl Application for Todos {
- type Message = Message;
- type Theme = Theme;
- type Executor = iced::executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (Todos, Command<Message>) {
- (
- Todos::Loading,
- Command::batch(vec![
- font::load(include_bytes!("../fonts/icons.ttf").as_slice())
- .map(Message::FontLoaded),
- Command::perform(SavedState::load(), Message::Loaded),
- ]),
- )
+impl Todos {
+ fn load() -> Command<Message> {
+ Command::perform(SavedState::load(), Message::Loaded)
}
fn title(&self) -> String {
@@ -168,7 +152,7 @@ impl Application for Todos {
Message::ToggleFullscreen(mode) => {
window::change_mode(window::Id::MAIN, mode)
}
- _ => Command::none(),
+ Message::Loaded(_) => Command::none(),
};
if !saved {
diff --git a/examples/tooltip/src/main.rs b/examples/tooltip/src/main.rs
index ee757311..b6603068 100644
--- a/examples/tooltip/src/main.rs
+++ b/examples/tooltip/src/main.rs
@@ -1,12 +1,13 @@
use iced::widget::tooltip::Position;
use iced::widget::{button, container, tooltip};
-use iced::{Element, Length, Sandbox, Settings};
+use iced::{Element, Length};
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::run("Tooltip - Iced", Tooltip::update, Tooltip::view)
}
-struct Example {
+#[derive(Default)]
+struct Tooltip {
position: Position,
}
@@ -15,28 +16,16 @@ 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")
- }
-
+impl Tooltip {
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,
+ Position::FollowCursor => Position::Top,
};
self.position = position;
diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs
index f5791ad7..a88c0dba 100644
--- a/examples/tour/src/main.rs
+++ b/examples/tour/src/main.rs
@@ -4,7 +4,7 @@ use iced::widget::{
scrollable, slider, text, text_input, toggler, vertical_space,
};
use iced::widget::{Button, Column, Container, Slider};
-use iced::{Color, Element, Font, Length, Pixels, Sandbox, Settings};
+use iced::{Color, Element, Font, Length, Pixels};
pub fn main() -> iced::Result {
#[cfg(target_arch = "wasm32")]
@@ -16,24 +16,18 @@ pub fn main() -> iced::Result {
#[cfg(not(target_arch = "wasm32"))]
tracing_subscriber::fmt::init();
- Tour::run(Settings::default())
+ iced::program(Tour::title, Tour::update, Tour::view)
+ .centered()
+ .run()
}
+#[derive(Default)]
pub struct Tour {
steps: Steps,
debug: bool,
}
-impl Sandbox for Tour {
- type Message = Message;
-
- fn new() -> Tour {
- Tour {
- steps: Steps::new(),
- debug: false,
- }
- }
-
+impl Tour {
fn title(&self) -> String {
format!("{} - Iced", self.steps.title())
}
@@ -171,6 +165,12 @@ impl Steps {
}
}
+impl Default for Steps {
+ fn default() -> Self {
+ Steps::new()
+ }
+}
+
enum Step {
Welcome,
Slider {
diff --git a/examples/url_handler/src/main.rs b/examples/url_handler/src/main.rs
index bf570123..df705b6c 100644
--- a/examples/url_handler/src/main.rs
+++ b/examples/url_handler/src/main.rs
@@ -1,12 +1,11 @@
use iced::event::{self, Event};
-use iced::executor;
use iced::widget::{container, text};
-use iced::{
- Application, Command, Element, Length, Settings, Subscription, Theme,
-};
+use iced::{Element, Length, Subscription};
pub fn main() -> iced::Result {
- App::run(Settings::default())
+ iced::program("URL Handler - Iced", App::update, App::view)
+ .subscription(App::subscription)
+ .run()
}
#[derive(Debug, Default)]
@@ -19,21 +18,8 @@ enum Message {
EventOccurred(Event),
}
-impl Application for App {
- type Message = Message;
- type Theme = Theme;
- type Executor = executor::Default;
- type Flags = ();
-
- fn new(_flags: ()) -> (App, Command<Message>) {
- (App::default(), Command::none())
- }
-
- fn title(&self) -> String {
- String::from("Url - Iced")
- }
-
- fn update(&mut self, message: Message) -> Command<Message> {
+impl App {
+ fn update(&mut self, message: Message) {
match message {
Message::EventOccurred(event) => {
if let Event::PlatformSpecific(
@@ -45,9 +31,7 @@ impl Application for App {
self.url = Some(url);
}
}
- };
-
- Command::none()
+ }
}
fn subscription(&self) -> Subscription<Message> {
diff --git a/examples/vectorial_text/src/main.rs b/examples/vectorial_text/src/main.rs
index 0b9ea938..a7391e23 100644
--- a/examples/vectorial_text/src/main.rs
+++ b/examples/vectorial_text/src/main.rs
@@ -3,18 +3,20 @@ use iced::mouse;
use iced::widget::{
canvas, checkbox, column, horizontal_space, row, slider, text,
};
-use iced::{
- Element, Length, Point, Rectangle, Renderer, Sandbox, Settings, Theme,
- Vector,
-};
+use iced::{Element, Length, Point, Rectangle, Renderer, Theme, Vector};
pub fn main() -> iced::Result {
- VectorialText::run(Settings {
- antialiasing: true,
- ..Settings::default()
- })
+ iced::program(
+ "Vectorial Text - Iced",
+ VectorialText::update,
+ VectorialText::view,
+ )
+ .theme(|_| Theme::Dark)
+ .antialiasing(true)
+ .run()
}
+#[derive(Default)]
struct VectorialText {
state: State,
}
@@ -27,19 +29,7 @@ enum Message {
ToggleJapanese(bool),
}
-impl Sandbox for VectorialText {
- type Message = Message;
-
- fn new() -> Self {
- Self {
- state: State::new(),
- }
- }
-
- fn title(&self) -> String {
- String::from("Vectorial Text - Iced")
- }
-
+impl VectorialText {
fn update(&mut self, message: Message) {
match message {
Message::SizeChanged(size) => {
@@ -106,10 +96,6 @@ impl Sandbox for VectorialText {
.padding(20)
.into()
}
-
- fn theme(&self) -> Theme {
- Theme::Dark
- }
}
struct State {
@@ -170,3 +156,9 @@ impl<Message> canvas::Program<Message> for State {
vec![geometry]
}
}
+
+impl Default for State {
+ fn default() -> Self {
+ State::new()
+ }
+}
diff --git a/examples/visible_bounds/src/main.rs b/examples/visible_bounds/src/main.rs
index 400d5753..332b6a7b 100644
--- a/examples/visible_bounds/src/main.rs
+++ b/examples/visible_bounds/src/main.rs
@@ -1,19 +1,22 @@
use iced::event::{self, Event};
-use iced::executor;
use iced::mouse;
use iced::widget::{
column, container, horizontal_space, row, scrollable, text, vertical_space,
};
use iced::window;
use iced::{
- Alignment, Application, Color, Command, Element, Font, Length, Point,
- Rectangle, Settings, Subscription, Theme,
+ Alignment, Color, Command, Element, Font, Length, Point, Rectangle,
+ Subscription, Theme,
};
pub fn main() -> iced::Result {
- Example::run(Settings::default())
+ iced::program("Visible Bounds - Iced", Example::update, Example::view)
+ .subscription(Example::subscription)
+ .theme(|_| Theme::Dark)
+ .run()
}
+#[derive(Default)]
struct Example {
mouse_position: Option<Point>,
outer_bounds: Option<Rectangle>,
@@ -29,27 +32,7 @@ enum Message {
InnerBoundsFetched(Option<Rectangle>),
}
-impl Application for Example {
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
- type Executor = executor::Default;
-
- fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
- (
- Self {
- mouse_position: None,
- outer_bounds: None,
- inner_bounds: None,
- },
- Command::none(),
- )
- }
-
- fn title(&self) -> String {
- String::from("Visible bounds - Iced")
- }
-
+impl Example {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::MouseMoved(position) => {
@@ -172,10 +155,6 @@ impl Application for Example {
_ => None,
})
}
-
- fn theme(&self) -> Theme {
- Theme::Dark
- }
}
use once_cell::sync::Lazy;
diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs
index 47c1898a..460d9a08 100644
--- a/examples/websocket/src/main.rs
+++ b/examples/websocket/src/main.rs
@@ -1,17 +1,17 @@
mod echo;
use iced::alignment::{self, Alignment};
-use iced::executor;
use iced::widget::{
button, column, container, row, scrollable, text, text_input,
};
-use iced::{
- color, Application, Command, Element, Length, Settings, Subscription, Theme,
-};
+use iced::{color, Command, Element, Length, Subscription};
use once_cell::sync::Lazy;
pub fn main() -> iced::Result {
- WebSocket::run(Settings::default())
+ iced::program("WebSocket - Iced", WebSocket::update, WebSocket::view)
+ .load(WebSocket::load)
+ .subscription(WebSocket::subscription)
+ .run()
}
#[derive(Default)]
@@ -29,21 +29,9 @@ enum Message {
Server,
}
-impl Application for WebSocket {
- type Message = Message;
- type Theme = Theme;
- type Flags = ();
- type Executor = executor::Default;
-
- fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
- (
- Self::default(),
- Command::perform(echo::server::run(), |_| Message::Server),
- )
- }
-
- fn title(&self) -> String {
- String::from("WebSocket - Iced")
+impl WebSocket {
+ fn load() -> Command<Message> {
+ Command::perform(echo::server::run(), |_| Message::Server)
}
fn update(&mut self, message: Message) -> Command<Message> {
diff --git a/renderer/src/geometry.rs b/renderer/src/geometry.rs
index f09ccfbf..36435148 100644
--- a/renderer/src/geometry.rs
+++ b/renderer/src/geometry.rs
@@ -2,7 +2,7 @@ mod cache;
pub use cache::Cache;
-use crate::core::{Point, Rectangle, Size, Transformation, Vector};
+use crate::core::{Point, Radians, Rectangle, Size, Transformation, Vector};
use crate::graphics::geometry::{Fill, Path, Stroke, Text};
use crate::Renderer;
@@ -184,7 +184,7 @@ impl Frame {
/// Applies a rotation in radians to the current transform of the [`Frame`].
#[inline]
- pub fn rotate(&mut self, angle: f32) {
+ pub fn rotate(&mut self, angle: impl Into<Radians>) {
delegate!(self, frame, frame.rotate(angle));
}
diff --git a/runtime/src/command.rs b/runtime/src/command.rs
index f70da915..f7a746fe 100644
--- a/runtime/src/command.rs
+++ b/runtime/src/command.rs
@@ -112,6 +112,12 @@ impl<T> Command<T> {
}
}
+impl<Message> From<()> for Command<Message> {
+ fn from(_value: ()) -> Self {
+ Self::none()
+ }
+}
+
impl<T> fmt::Debug for Command<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Command(command) = self;
diff --git a/src/advanced.rs b/src/advanced.rs
index 8e026f84..306c3559 100644
--- a/src/advanced.rs
+++ b/src/advanced.rs
@@ -1,4 +1,5 @@
//! Leverage advanced concepts like custom widgets.
+pub use crate::application::Application;
pub use crate::core::clipboard::{self, Clipboard};
pub use crate::core::image;
pub use crate::core::layout::{self, Layout};
diff --git a/src/application.rs b/src/application.rs
index be0fa0de..8317abcb 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -1,9 +1,8 @@
//! Build interactive cross-platform applications.
-use crate::{Command, Element, Executor, Settings, Subscription};
-
use crate::shell::application;
+use crate::{Command, Element, Executor, Settings, Subscription};
-pub use application::{default, Appearance, DefaultStyle};
+pub use application::{Appearance, DefaultStyle};
/// An interactive cross-platform application.
///
@@ -15,9 +14,7 @@ pub use application::{default, Appearance, DefaultStyle};
/// document.
///
/// An [`Application`] can execute asynchronous actions by returning a
-/// [`Command`] in some of its methods. If you do not intend to perform any
-/// background work in your program, the [`Sandbox`] trait offers a simplified
-/// interface.
+/// [`Command`] in some of its methods.
///
/// When using an [`Application`] with the `debug` feature enabled, a debug view
/// can be toggled by pressing `F12`.
@@ -61,8 +58,9 @@ pub use application::{default, Appearance, DefaultStyle};
/// says "Hello, world!":
///
/// ```no_run
+/// use iced::advanced::Application;
/// use iced::executor;
-/// use iced::{Application, Command, Element, Settings, Theme};
+/// use iced::{Command, Element, Settings, Theme};
///
/// pub fn main() -> iced::Result {
/// Hello::run(Settings::default())
diff --git a/src/lib.rs b/src/lib.rs
index c596f2a6..0e9566e2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -63,8 +63,8 @@
//! ```
//! #[derive(Debug, Clone, Copy)]
//! pub enum Message {
-//! IncrementPressed,
-//! DecrementPressed,
+//! Increment,
+//! Decrement,
//! }
//! ```
//!
@@ -79,8 +79,8 @@
//! #
//! # #[derive(Debug, Clone, Copy)]
//! # pub enum Message {
-//! # IncrementPressed,
-//! # DecrementPressed,
+//! # Increment,
+//! # Decrement,
//! # }
//! #
//! use iced::widget::{button, column, text, Column};
@@ -90,15 +90,15 @@
//! // We use a column: a simple vertical layout
//! column![
//! // The increment button. We tell it to produce an
-//! // `IncrementPressed` message when pressed
-//! button("+").on_press(Message::IncrementPressed),
+//! // `Increment` message when pressed
+//! button("+").on_press(Message::Increment),
//!
//! // We show the value of the counter here
//! text(self.value).size(50),
//!
//! // The decrement button. We tell it to produce a
-//! // `DecrementPressed` message when pressed
-//! button("-").on_press(Message::DecrementPressed),
+//! // `Decrement` message when pressed
+//! button("-").on_press(Message::Decrement),
//! ]
//! }
//! }
@@ -115,18 +115,18 @@
//! #
//! # #[derive(Debug, Clone, Copy)]
//! # pub enum Message {
-//! # IncrementPressed,
-//! # DecrementPressed,
+//! # Increment,
+//! # Decrement,
//! # }
//! impl Counter {
//! // ...
//!
//! pub fn update(&mut self, message: Message) {
//! match message {
-//! Message::IncrementPressed => {
+//! Message::Increment => {
//! self.value += 1;
//! }
-//! Message::DecrementPressed => {
+//! Message::Decrement => {
//! self.value -= 1;
//! }
//! }
@@ -134,8 +134,22 @@
//! }
//! ```
//!
-//! And that's everything! We just wrote a whole user interface. Iced is now
-//! able to:
+//! And that's everything! We just wrote a whole user interface. Let's run it:
+//!
+//! ```no_run
+//! # #[derive(Default)]
+//! # struct Counter;
+//! # impl Counter {
+//! # fn update(&mut self, _message: ()) {}
+//! # fn view(&self) -> iced::Element<()> { unimplemented!() }
+//! # }
+//! #
+//! fn main() -> iced::Result {
+//! iced::run("A cool counter", Counter::update, Counter::view)
+//! }
+//! ```
+//!
+//! Iced will automatically:
//!
//! 1. Take the result of our __view logic__ and layout its widgets.
//! 1. Process events from our system and produce __messages__ for our
@@ -143,11 +157,11 @@
//! 1. Draw the resulting user interface.
//!
//! # Usage
-//! The [`Application`] and [`Sandbox`] traits should get you started quickly,
-//! streamlining all the process described above!
+//! Use [`run`] or the [`program`] builder.
//!
//! [Elm]: https://elm-lang.org/
//! [The Elm Architecture]: https://guide.elm-lang.org/architecture/
+//! [`program`]: program()
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
@@ -171,10 +185,10 @@ pub use iced_futures::futures;
#[cfg(feature = "highlighter")]
pub use iced_highlighter as highlighter;
+mod application;
mod error;
-mod sandbox;
-pub mod application;
+pub mod program;
pub mod settings;
pub mod time;
pub mod window;
@@ -302,14 +316,13 @@ pub mod widget {
mod runtime {}
}
-pub use application::Application;
pub use command::Command;
pub use error::Error;
pub use event::Event;
pub use executor::Executor;
pub use font::Font;
+pub use program::Program;
pub use renderer::Renderer;
-pub use sandbox::Sandbox;
pub use settings::Settings;
pub use subscription::Subscription;
@@ -323,7 +336,54 @@ pub type Element<
Renderer = crate::Renderer,
> = crate::core::Element<'a, Message, Theme, Renderer>;
-/// The result of running an [`Application`].
-///
-/// [`Application`]: crate::Application
+/// The result of running a [`Program`].
pub type Result = std::result::Result<(), Error>;
+
+/// Runs a basic iced application with default [`Settings`] given its title,
+/// update, and view logic.
+///
+/// This is equivalent to chaining [`program`] with [`Program::run`].
+///
+/// [`program`]: program()
+///
+/// # Example
+/// ```no_run
+/// use iced::widget::{button, column, text, Column};
+///
+/// pub fn main() -> iced::Result {
+/// iced::run("A counter", update, view)
+/// }
+///
+/// #[derive(Debug, Clone)]
+/// enum Message {
+/// Increment,
+/// }
+///
+/// fn update(value: &mut u64, message: Message) {
+/// match message {
+/// Message::Increment => *value += 1,
+/// }
+/// }
+///
+/// fn view(value: &u64) -> Column<Message> {
+/// column![
+/// text(value),
+/// button("+").on_press(Message::Increment),
+/// ]
+/// }
+/// ```
+pub fn run<State, Message, Theme>(
+ title: impl program::Title<State> + 'static,
+ update: impl program::Update<State, Message> + 'static,
+ view: impl for<'a> program::View<'a, State, Message, Theme> + 'static,
+) -> Result
+where
+ State: Default + 'static,
+ Message: std::fmt::Debug + Send + 'static,
+ Theme: Default + program::DefaultStyle + 'static,
+{
+ program(title, update, view).run()
+}
+
+#[doc(inline)]
+pub use program::program;
diff --git a/src/multi_window.rs b/src/multi_window.rs
index c4063563..fca0be46 100644
--- a/src/multi_window.rs
+++ b/src/multi_window.rs
@@ -14,9 +14,7 @@ pub use crate::application::{Appearance, DefaultStyle};
/// document and display only the contents of the `window::Id::MAIN` window.
///
/// An [`Application`] can execute asynchronous actions by returning a
-/// [`Command`] in some of its methods. If you do not intend to perform any
-/// background work in your program, the [`Sandbox`] trait offers a simplified
-/// interface.
+/// [`Command`] in some of its methods.
///
/// When using an [`Application`] with the `debug` feature enabled, a debug view
/// can be toggled by pressing `F12`.
diff --git a/src/program.rs b/src/program.rs
new file mode 100644
index 00000000..7a366585
--- /dev/null
+++ b/src/program.rs
@@ -0,0 +1,851 @@
+//! Create and run iced applications step by step.
+//!
+//! # Example
+//! ```no_run
+//! use iced::widget::{button, column, text, Column};
+//! use iced::Theme;
+//!
+//! pub fn main() -> iced::Result {
+//! iced::program("A counter", update, view)
+//! .theme(|_| Theme::Dark)
+//! .centered()
+//! .run()
+//! }
+//!
+//! #[derive(Debug, Clone)]
+//! enum Message {
+//! Increment,
+//! }
+//!
+//! fn update(value: &mut u64, message: Message) {
+//! match message {
+//! Message::Increment => *value += 1,
+//! }
+//! }
+//!
+//! fn view(value: &u64) -> Column<Message> {
+//! column![
+//! text(value),
+//! button("+").on_press(Message::Increment),
+//! ]
+//! }
+//! ```
+use crate::application::Application;
+use crate::executor::{self, Executor};
+use crate::window;
+use crate::{Command, Element, Font, Result, Settings, Size, Subscription};
+
+pub use crate::application::{Appearance, DefaultStyle};
+
+use std::borrow::Cow;
+
+/// Creates an iced [`Program`] given its title, update, and view logic.
+///
+/// # Example
+/// ```no_run
+/// use iced::widget::{button, column, text, Column};
+///
+/// pub fn main() -> iced::Result {
+/// iced::program("A counter", update, view).run()
+/// }
+///
+/// #[derive(Debug, Clone)]
+/// enum Message {
+/// Increment,
+/// }
+///
+/// fn update(value: &mut u64, message: Message) {
+/// match message {
+/// Message::Increment => *value += 1,
+/// }
+/// }
+///
+/// fn view(value: &u64) -> Column<Message> {
+/// column![
+/// text(value),
+/// button("+").on_press(Message::Increment),
+/// ]
+/// }
+/// ```
+pub fn program<State, Message, Theme>(
+ title: impl Title<State>,
+ update: impl Update<State, Message>,
+ view: impl for<'a> self::View<'a, State, Message, Theme>,
+) -> Program<impl Definition<State = State, Message = Message, Theme = Theme>>
+where
+ State: 'static,
+ Message: Send + std::fmt::Debug,
+ Theme: Default + DefaultStyle,
+{
+ use std::marker::PhantomData;
+
+ struct Application<State, Message, Theme, Update, View> {
+ update: Update,
+ view: View,
+ _state: PhantomData<State>,
+ _message: PhantomData<Message>,
+ _theme: PhantomData<Theme>,
+ }
+
+ impl<State, Message, Theme, Update, View> Definition
+ for Application<State, Message, Theme, Update, View>
+ where
+ Message: Send + std::fmt::Debug,
+ Theme: Default + DefaultStyle,
+ Update: self::Update<State, Message>,
+ View: for<'a> self::View<'a, State, Message, Theme>,
+ {
+ type State = State;
+ type Message = Message;
+ type Theme = Theme;
+ type Executor = executor::Default;
+
+ fn load(&self) -> Command<Self::Message> {
+ Command::none()
+ }
+
+ fn update(
+ &self,
+ state: &mut Self::State,
+ message: Self::Message,
+ ) -> Command<Self::Message> {
+ self.update.update(state, message).into()
+ }
+
+ fn view<'a>(
+ &self,
+ state: &'a Self::State,
+ ) -> Element<'a, Self::Message, Self::Theme> {
+ self.view.view(state).into()
+ }
+ }
+
+ Program {
+ raw: Application {
+ update,
+ view,
+ _state: PhantomData,
+ _message: PhantomData,
+ _theme: PhantomData,
+ },
+ settings: Settings::default(),
+ }
+ .title(title)
+}
+
+/// The underlying definition and configuration of an iced application.
+///
+/// You can use this API to create and run iced applications
+/// step by step—without coupling your logic to a trait
+/// or a specific type.
+///
+/// You can create a [`Program`] with the [`program`] helper.
+///
+/// [`run`]: Program::run
+#[derive(Debug)]
+pub struct Program<P: Definition> {
+ raw: P,
+ settings: Settings,
+}
+
+impl<P: Definition> Program<P> {
+ /// Runs the underlying [`Application`] of the [`Program`].
+ ///
+ /// The state of the [`Program`] must implement [`Default`].
+ /// If your state does not implement [`Default`], use [`run_with`]
+ /// instead.
+ ///
+ /// [`run_with`]: Self::run_with
+ pub fn run(self) -> Result
+ where
+ Self: 'static,
+ P::State: Default,
+ {
+ self.run_with(P::State::default)
+ }
+
+ /// Runs the underlying [`Application`] of the [`Program`] with a
+ /// closure that creates the initial state.
+ pub fn run_with(
+ self,
+ initialize: impl Fn() -> P::State + Clone + 'static,
+ ) -> Result
+ where
+ Self: 'static,
+ {
+ use std::marker::PhantomData;
+
+ struct Instance<P: Definition, I> {
+ program: P,
+ state: P::State,
+ _initialize: PhantomData<I>,
+ }
+
+ impl<P: Definition, I: Fn() -> P::State> Application for Instance<P, I> {
+ type Message = P::Message;
+ type Theme = P::Theme;
+ type Flags = (P, I);
+ type Executor = P::Executor;
+
+ fn new(
+ (program, initialize): Self::Flags,
+ ) -> (Self, Command<Self::Message>) {
+ let state = initialize();
+ let command = program.load();
+
+ (
+ Self {
+ program,
+ state,
+ _initialize: PhantomData,
+ },
+ command,
+ )
+ }
+
+ fn title(&self) -> String {
+ self.program.title(&self.state)
+ }
+
+ fn update(
+ &mut self,
+ message: Self::Message,
+ ) -> Command<Self::Message> {
+ self.program.update(&mut self.state, message)
+ }
+
+ fn view(
+ &self,
+ ) -> crate::Element<'_, Self::Message, Self::Theme, crate::Renderer>
+ {
+ self.program.view(&self.state)
+ }
+
+ fn subscription(&self) -> Subscription<Self::Message> {
+ self.program.subscription(&self.state)
+ }
+
+ fn theme(&self) -> Self::Theme {
+ self.program.theme(&self.state)
+ }
+
+ fn style(&self, theme: &Self::Theme) -> Appearance {
+ self.program.style(&self.state, theme)
+ }
+ }
+
+ let Self { raw, settings } = self;
+
+ Instance::run(Settings {
+ flags: (raw, initialize),
+ id: settings.id,
+ window: settings.window,
+ fonts: settings.fonts,
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ antialiasing: settings.antialiasing,
+ })
+ }
+
+ /// Sets the [`Settings`] that will be used to run the [`Program`].
+ pub fn settings(self, settings: Settings) -> Self {
+ Self { settings, ..self }
+ }
+
+ /// Sets the [`Settings::antialiasing`] of the [`Program`].
+ pub fn antialiasing(self, antialiasing: bool) -> Self {
+ Self {
+ settings: Settings {
+ antialiasing,
+ ..self.settings
+ },
+ ..self
+ }
+ }
+
+ /// Sets the default [`Font`] of the [`Program`].
+ pub fn default_font(self, default_font: Font) -> Self {
+ Self {
+ settings: Settings {
+ default_font,
+ ..self.settings
+ },
+ ..self
+ }
+ }
+
+ /// Adds a font to the list of fonts that will be loaded at the start of the [`Program`].
+ pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
+ self.settings.fonts.push(font.into());
+ self
+ }
+
+ /// Sets the [`window::Settings::position`] to [`window::Position::Centered`] in the [`Program`].
+ pub fn centered(self) -> Self {
+ Self {
+ settings: Settings {
+ window: window::Settings {
+ position: window::Position::Centered,
+ ..self.settings.window
+ },
+ ..self.settings
+ },
+ ..self
+ }
+ }
+
+ /// Sets the [`window::Settings::exit_on_close_request`] of the [`Program`].
+ pub fn exit_on_close_request(self, exit_on_close_request: bool) -> Self {
+ Self {
+ settings: Settings {
+ window: window::Settings {
+ exit_on_close_request,
+ ..self.settings.window
+ },
+ ..self.settings
+ },
+ ..self
+ }
+ }
+
+ /// Sets the [`window::Settings::size`] of the [`Program`].
+ pub fn window_size(self, size: impl Into<Size>) -> Self {
+ Self {
+ settings: Settings {
+ window: window::Settings {
+ size: size.into(),
+ ..self.settings.window
+ },
+ ..self.settings
+ },
+ ..self
+ }
+ }
+
+ /// Sets the [`window::Settings::transparent`] of the [`Program`].
+ pub fn transparent(self, transparent: bool) -> Self {
+ Self {
+ settings: Settings {
+ window: window::Settings {
+ transparent,
+ ..self.settings.window
+ },
+ ..self.settings
+ },
+ ..self
+ }
+ }
+
+ /// Sets the [`Title`] of the [`Program`].
+ pub(crate) fn title(
+ self,
+ title: impl Title<P::State>,
+ ) -> Program<
+ impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>,
+ > {
+ Program {
+ raw: with_title(self.raw, title),
+ settings: self.settings,
+ }
+ }
+
+ /// Runs the [`Command`] produced by the closure at startup.
+ pub fn load(
+ self,
+ f: impl Fn() -> Command<P::Message>,
+ ) -> Program<
+ impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>,
+ > {
+ Program {
+ raw: with_load(self.raw, f),
+ settings: self.settings,
+ }
+ }
+
+ /// Sets the subscription logic of the [`Program`].
+ pub fn subscription(
+ self,
+ f: impl Fn(&P::State) -> Subscription<P::Message>,
+ ) -> Program<
+ impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>,
+ > {
+ Program {
+ raw: with_subscription(self.raw, f),
+ settings: self.settings,
+ }
+ }
+
+ /// Sets the theme logic of the [`Program`].
+ pub fn theme(
+ self,
+ f: impl Fn(&P::State) -> P::Theme,
+ ) -> Program<
+ impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>,
+ > {
+ Program {
+ raw: with_theme(self.raw, f),
+ settings: self.settings,
+ }
+ }
+
+ /// Sets the style logic of the [`Program`].
+ pub fn style(
+ self,
+ f: impl Fn(&P::State, &P::Theme) -> Appearance,
+ ) -> Program<
+ impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>,
+ > {
+ Program {
+ raw: with_style(self.raw, f),
+ settings: self.settings,
+ }
+ }
+}
+
+/// The internal definition of a [`Program`].
+///
+/// You should not need to implement this trait directly. Instead, use the
+/// methods available in the [`Program`] struct.
+#[allow(missing_docs)]
+pub trait Definition: Sized {
+ /// The state of the program.
+ type State;
+
+ /// The message of the program.
+ type Message: Send + std::fmt::Debug;
+
+ /// The theme of the program.
+ type Theme: Default + DefaultStyle;
+
+ /// The executor of the program.
+ type Executor: Executor;
+
+ fn load(&self) -> Command<Self::Message>;
+
+ fn update(
+ &self,
+ state: &mut Self::State,
+ message: Self::Message,
+ ) -> Command<Self::Message>;
+
+ fn view<'a>(
+ &self,
+ state: &'a Self::State,
+ ) -> Element<'a, Self::Message, Self::Theme>;
+
+ fn title(&self, _state: &Self::State) -> String {
+ String::from("A cool iced application!")
+ }
+
+ fn subscription(
+ &self,
+ _state: &Self::State,
+ ) -> Subscription<Self::Message> {
+ Subscription::none()
+ }
+
+ fn theme(&self, _state: &Self::State) -> Self::Theme {
+ Self::Theme::default()
+ }
+
+ fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance {
+ DefaultStyle::default_style(theme)
+ }
+}
+
+fn with_title<P: Definition>(
+ program: P,
+ title: impl Title<P::State>,
+) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> {
+ struct WithTitle<P, Title> {
+ program: P,
+ title: Title,
+ }
+
+ impl<P, Title> Definition for WithTitle<P, Title>
+ where
+ P: Definition,
+ Title: self::Title<P::State>,
+ {
+ type State = P::State;
+ type Message = P::Message;
+ type Theme = P::Theme;
+ type Executor = P::Executor;
+
+ fn load(&self) -> Command<Self::Message> {
+ self.program.load()
+ }
+
+ fn title(&self, state: &Self::State) -> String {
+ self.title.title(state)
+ }
+
+ fn update(
+ &self,
+ state: &mut Self::State,
+ message: Self::Message,
+ ) -> Command<Self::Message> {
+ self.program.update(state, message)
+ }
+
+ fn view<'a>(
+ &self,
+ state: &'a Self::State,
+ ) -> Element<'a, Self::Message, Self::Theme> {
+ self.program.view(state)
+ }
+
+ fn theme(&self, state: &Self::State) -> Self::Theme {
+ self.program.theme(state)
+ }
+
+ fn subscription(
+ &self,
+ state: &Self::State,
+ ) -> Subscription<Self::Message> {
+ self.program.subscription(state)
+ }
+
+ fn style(
+ &self,
+ state: &Self::State,
+ theme: &Self::Theme,
+ ) -> Appearance {
+ self.program.style(state, theme)
+ }
+ }
+
+ WithTitle { program, title }
+}
+
+fn with_load<P: Definition>(
+ program: P,
+ f: impl Fn() -> Command<P::Message>,
+) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> {
+ struct WithLoad<P, F> {
+ program: P,
+ load: F,
+ }
+
+ impl<P: Definition, F> Definition for WithLoad<P, F>
+ where
+ F: Fn() -> Command<P::Message>,
+ {
+ type State = P::State;
+ type Message = P::Message;
+ type Theme = P::Theme;
+ type Executor = executor::Default;
+
+ fn load(&self) -> Command<Self::Message> {
+ Command::batch([self.program.load(), (self.load)()])
+ }
+
+ fn update(
+ &self,
+ state: &mut Self::State,
+ message: Self::Message,
+ ) -> Command<Self::Message> {
+ self.program.update(state, message)
+ }
+
+ fn view<'a>(
+ &self,
+ state: &'a Self::State,
+ ) -> Element<'a, Self::Message, Self::Theme> {
+ self.program.view(state)
+ }
+
+ fn title(&self, state: &Self::State) -> String {
+ self.program.title(state)
+ }
+
+ fn subscription(
+ &self,
+ state: &Self::State,
+ ) -> Subscription<Self::Message> {
+ self.program.subscription(state)
+ }
+
+ fn theme(&self, state: &Self::State) -> Self::Theme {
+ self.program.theme(state)
+ }
+
+ fn style(
+ &self,
+ state: &Self::State,
+ theme: &Self::Theme,
+ ) -> Appearance {
+ self.program.style(state, theme)
+ }
+ }
+
+ WithLoad { program, load: f }
+}
+
+fn with_subscription<P: Definition>(
+ program: P,
+ f: impl Fn(&P::State) -> Subscription<P::Message>,
+) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> {
+ struct WithSubscription<P, F> {
+ program: P,
+ subscription: F,
+ }
+
+ impl<P: Definition, F> Definition for WithSubscription<P, F>
+ where
+ F: Fn(&P::State) -> Subscription<P::Message>,
+ {
+ type State = P::State;
+ type Message = P::Message;
+ type Theme = P::Theme;
+ type Executor = executor::Default;
+
+ fn subscription(
+ &self,
+ state: &Self::State,
+ ) -> Subscription<Self::Message> {
+ (self.subscription)(state)
+ }
+
+ fn load(&self) -> Command<Self::Message> {
+ self.program.load()
+ }
+
+ fn update(
+ &self,
+ state: &mut Self::State,
+ message: Self::Message,
+ ) -> Command<Self::Message> {
+ self.program.update(state, message)
+ }
+
+ fn view<'a>(
+ &self,
+ state: &'a Self::State,
+ ) -> Element<'a, Self::Message, Self::Theme> {
+ self.program.view(state)
+ }
+
+ fn title(&self, state: &Self::State) -> String {
+ self.program.title(state)
+ }
+
+ fn theme(&self, state: &Self::State) -> Self::Theme {
+ self.program.theme(state)
+ }
+
+ fn style(
+ &self,
+ state: &Self::State,
+ theme: &Self::Theme,
+ ) -> Appearance {
+ self.program.style(state, theme)
+ }
+ }
+
+ WithSubscription {
+ program,
+ subscription: f,
+ }
+}
+
+fn with_theme<P: Definition>(
+ program: P,
+ f: impl Fn(&P::State) -> P::Theme,
+) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> {
+ struct WithTheme<P, F> {
+ program: P,
+ theme: F,
+ }
+
+ impl<P: Definition, F> Definition for WithTheme<P, F>
+ where
+ F: Fn(&P::State) -> P::Theme,
+ {
+ type State = P::State;
+ type Message = P::Message;
+ type Theme = P::Theme;
+ type Executor = P::Executor;
+
+ fn theme(&self, state: &Self::State) -> Self::Theme {
+ (self.theme)(state)
+ }
+
+ fn load(&self) -> Command<Self::Message> {
+ self.program.load()
+ }
+
+ fn title(&self, state: &Self::State) -> String {
+ self.program.title(state)
+ }
+
+ fn update(
+ &self,
+ state: &mut Self::State,
+ message: Self::Message,
+ ) -> Command<Self::Message> {
+ self.program.update(state, message)
+ }
+
+ fn view<'a>(
+ &self,
+ state: &'a Self::State,
+ ) -> Element<'a, Self::Message, Self::Theme> {
+ self.program.view(state)
+ }
+
+ fn subscription(
+ &self,
+ state: &Self::State,
+ ) -> Subscription<Self::Message> {
+ self.program.subscription(state)
+ }
+
+ fn style(
+ &self,
+ state: &Self::State,
+ theme: &Self::Theme,
+ ) -> Appearance {
+ self.program.style(state, theme)
+ }
+ }
+
+ WithTheme { program, theme: f }
+}
+
+fn with_style<P: Definition>(
+ program: P,
+ f: impl Fn(&P::State, &P::Theme) -> Appearance,
+) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> {
+ struct WithStyle<P, F> {
+ program: P,
+ style: F,
+ }
+
+ impl<P: Definition, F> Definition for WithStyle<P, F>
+ where
+ F: Fn(&P::State, &P::Theme) -> Appearance,
+ {
+ type State = P::State;
+ type Message = P::Message;
+ type Theme = P::Theme;
+ type Executor = P::Executor;
+
+ fn style(
+ &self,
+ state: &Self::State,
+ theme: &Self::Theme,
+ ) -> Appearance {
+ (self.style)(state, theme)
+ }
+
+ fn load(&self) -> Command<Self::Message> {
+ self.program.load()
+ }
+
+ fn title(&self, state: &Self::State) -> String {
+ self.program.title(state)
+ }
+
+ fn update(
+ &self,
+ state: &mut Self::State,
+ message: Self::Message,
+ ) -> Command<Self::Message> {
+ self.program.update(state, message)
+ }
+
+ fn view<'a>(
+ &self,
+ state: &'a Self::State,
+ ) -> Element<'a, Self::Message, Self::Theme> {
+ self.program.view(state)
+ }
+
+ fn subscription(
+ &self,
+ state: &Self::State,
+ ) -> Subscription<Self::Message> {
+ self.program.subscription(state)
+ }
+
+ fn theme(&self, state: &Self::State) -> Self::Theme {
+ self.program.theme(state)
+ }
+ }
+
+ WithStyle { program, style: f }
+}
+
+/// The title logic of some [`Program`].
+///
+/// This trait is implemented both for `&static str` and
+/// any closure `Fn(&State) -> String`.
+///
+/// This trait allows the [`program`] builder to take any of them.
+pub trait Title<State> {
+ /// Produces the title of the [`Program`].
+ fn title(&self, state: &State) -> String;
+}
+
+impl<State> Title<State> for &'static str {
+ fn title(&self, _state: &State) -> String {
+ self.to_string()
+ }
+}
+
+impl<T, State> Title<State> for T
+where
+ T: Fn(&State) -> String,
+{
+ fn title(&self, state: &State) -> String {
+ self(state)
+ }
+}
+
+/// The update logic of some [`Program`].
+///
+/// This trait allows the [`program`] builder to take any closure that
+/// returns any `Into<Command<Message>>`.
+pub trait Update<State, Message> {
+ /// Processes the message and updates the state of the [`Program`].
+ fn update(
+ &self,
+ state: &mut State,
+ message: Message,
+ ) -> impl Into<Command<Message>>;
+}
+
+impl<T, State, Message, C> Update<State, Message> for T
+where
+ T: Fn(&mut State, Message) -> C,
+ C: Into<Command<Message>>,
+{
+ fn update(
+ &self,
+ state: &mut State,
+ message: Message,
+ ) -> impl Into<Command<Message>> {
+ self(state, message)
+ }
+}
+
+/// The view logic of some [`Program`].
+///
+/// This trait allows the [`program`] builder to take any closure that
+/// returns any `Into<Element<'_, Message>>`.
+pub trait View<'a, State, Message, Theme> {
+ /// Produces the widget of the [`Program`].
+ fn view(&self, state: &'a State) -> impl Into<Element<'a, Message, Theme>>;
+}
+
+impl<'a, T, State, Message, Theme, Widget> View<'a, State, Message, Theme> for T
+where
+ T: Fn(&'a State) -> Widget,
+ State: 'static,
+ Widget: Into<Element<'a, Message, Theme>>,
+{
+ fn view(&self, state: &'a State) -> impl Into<Element<'a, Message, Theme>> {
+ self(state)
+ }
+}
diff --git a/src/sandbox.rs b/src/sandbox.rs
deleted file mode 100644
index 568b673e..00000000
--- a/src/sandbox.rs
+++ /dev/null
@@ -1,199 +0,0 @@
-use crate::application::{self, Application};
-use crate::{Command, Element, Error, Settings, Subscription, Theme};
-
-/// A sandboxed [`Application`].
-///
-/// If you are a just getting started with the library, this trait offers a
-/// simpler interface than [`Application`].
-///
-/// Unlike an [`Application`], a [`Sandbox`] cannot run any asynchronous
-/// actions or be initialized with some external flags. However, both traits
-/// are very similar and upgrading from a [`Sandbox`] is very straightforward.
-///
-/// Therefore, it is recommended to always start by implementing this trait and
-/// upgrade only once necessary.
-///
-/// # Examples
-/// [The repository has a bunch of examples] that use the [`Sandbox`] trait:
-///
-/// - [`bezier_tool`], a Paint-like tool for drawing Bézier curves using the
-/// [`Canvas widget`].
-/// - [`counter`], the classic counter example explained in [the overview].
-/// - [`custom_widget`], a demonstration of how to build a custom widget that
-/// draws a circle.
-/// - [`geometry`], a custom widget showcasing how to draw geometry with the
-/// `Mesh2D` primitive in [`iced_wgpu`].
-/// - [`pane_grid`], a grid of panes that can be split, resized, and
-/// reorganized.
-/// - [`progress_bar`], a simple progress bar that can be filled by using a
-/// slider.
-/// - [`styling`], an example showcasing custom styling with a light and dark
-/// theme.
-/// - [`svg`], an application that renders the [Ghostscript Tiger] by leveraging
-/// the [`Svg` widget].
-/// - [`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/iced-rs/iced/tree/0.12/examples
-/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.12/examples/bezier_tool
-/// [`counter`]: https://github.com/iced-rs/iced/tree/0.12/examples/counter
-/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.12/examples/custom_widget
-/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.12/examples/geometry
-/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.12/examples/pane_grid
-/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.12/examples/progress_bar
-/// [`styling`]: https://github.com/iced-rs/iced/tree/0.12/examples/styling
-/// [`svg`]: https://github.com/iced-rs/iced/tree/0.12/examples/svg
-/// [`tour`]: https://github.com/iced-rs/iced/tree/0.12/examples/tour
-/// [`Canvas widget`]: crate::widget::Canvas
-/// [the overview]: index.html#overview
-/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.12/wgpu
-/// [`Svg` widget]: crate::widget::Svg
-/// [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
-///
-/// ## A simple "Hello, world!"
-///
-/// If you just want to get started, here is a simple [`Sandbox`] that
-/// says "Hello, world!":
-///
-/// ```no_run
-/// use iced::{Element, Sandbox, Settings};
-///
-/// pub fn main() -> iced::Result {
-/// Hello::run(Settings::default())
-/// }
-///
-/// struct Hello;
-///
-/// impl Sandbox for Hello {
-/// type Message = ();
-///
-/// fn new() -> Hello {
-/// Hello
-/// }
-///
-/// fn title(&self) -> String {
-/// String::from("A cool application")
-/// }
-///
-/// fn update(&mut self, _message: Self::Message) {
-/// // This application has no interactions
-/// }
-///
-/// fn view(&self) -> Element<Self::Message> {
-/// "Hello, world!".into()
-/// }
-/// }
-/// ```
-pub trait Sandbox {
- /// The type of __messages__ your [`Sandbox`] will produce.
- type Message: std::fmt::Debug + Send;
-
- /// Initializes the [`Sandbox`].
- ///
- /// Here is where you should return the initial state of your app.
- fn new() -> Self;
-
- /// Returns the current title of the [`Sandbox`].
- ///
- /// This title can be dynamic! The runtime will automatically update the
- /// title of your application when necessary.
- fn title(&self) -> String;
-
- /// Handles a __message__ and updates the state of the [`Sandbox`].
- ///
- /// This is where you define your __update logic__. All the __messages__,
- /// produced by user interactions, will be handled by this method.
- fn update(&mut self, message: Self::Message);
-
- /// Returns the widgets to display in the [`Sandbox`].
- ///
- /// These widgets can produce __messages__ based on user interaction.
- fn view(&self) -> Element<'_, Self::Message>;
-
- /// Returns the current [`Theme`] of the [`Sandbox`].
- ///
- /// If you want to use your own custom theme type, you will have to use an
- /// [`Application`].
- ///
- /// By default, it returns [`Theme::default`].
- fn theme(&self) -> Theme {
- Theme::default()
- }
-
- /// Returns the current [`application::Appearance`].
- fn style(&self, theme: &Theme) -> application::Appearance {
- use application::DefaultStyle;
-
- theme.default_style()
- }
-
- /// Returns the scale factor of the [`Sandbox`].
- ///
- /// It can be used to dynamically control the size of the UI at runtime
- /// (i.e. zooming).
- ///
- /// For instance, a scale factor of `2.0` will make widgets twice as big,
- /// while a scale factor of `0.5` will shrink them to half their size.
- ///
- /// By default, it returns `1.0`.
- fn scale_factor(&self) -> f64 {
- 1.0
- }
-
- /// Runs the [`Sandbox`].
- ///
- /// On native platforms, this method will take control of the current thread
- /// and __will NOT return__.
- ///
- /// It should probably be that last thing you call in your `main` function.
- fn run(settings: Settings<()>) -> Result<(), Error>
- where
- Self: 'static + Sized,
- {
- <Self as Application>::run(settings)
- }
-}
-
-impl<T> Application for T
-where
- T: Sandbox,
-{
- type Executor = iced_futures::backend::null::Executor;
- type Flags = ();
- type Message = T::Message;
- type Theme = Theme;
-
- fn new(_flags: ()) -> (Self, Command<T::Message>) {
- (T::new(), Command::none())
- }
-
- fn title(&self) -> String {
- T::title(self)
- }
-
- fn update(&mut self, message: T::Message) -> Command<T::Message> {
- T::update(self, message);
-
- Command::none()
- }
-
- fn view(&self) -> Element<'_, T::Message> {
- T::view(self)
- }
-
- fn theme(&self) -> Self::Theme {
- T::theme(self)
- }
-
- fn style(&self, theme: &Theme) -> application::Appearance {
- T::style(self, theme)
- }
-
- fn subscription(&self) -> Subscription<T::Message> {
- Subscription::none()
- }
-
- fn scale_factor(&self) -> f64 {
- T::scale_factor(self)
- }
-}
diff --git a/src/settings.rs b/src/settings.rs
index d9476b61..f7947841 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -4,9 +4,11 @@ use crate::{Font, Pixels};
use std::borrow::Cow;
-/// The settings of an application.
+/// The settings of an iced [`Program`].
+///
+/// [`Program`]: crate::Program
#[derive(Debug, Clone)]
-pub struct Settings<Flags> {
+pub struct Settings<Flags = ()> {
/// The identifier of the application.
///
/// If provided, this identifier may be used to identify the application or
@@ -18,9 +20,9 @@ pub struct Settings<Flags> {
/// They will be ignored on the Web.
pub window: window::Settings,
- /// The data needed to initialize the [`Application`].
+ /// The data needed to initialize the [`Program`].
///
- /// [`Application`]: crate::Application
+ /// [`Program`]: crate::Program
pub flags: Flags,
/// The fonts to load on boot.
@@ -49,9 +51,9 @@ pub struct Settings<Flags> {
}
impl<Flags> Settings<Flags> {
- /// Initialize [`Application`] settings using the given data.
+ /// Initialize [`Program`] settings using the given data.
///
- /// [`Application`]: crate::Application
+ /// [`Program`]: crate::Program
pub fn with_flags(flags: Flags) -> Self {
let default_settings = Settings::<()>::default();
diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs
index f7518731..16787f89 100644
--- a/tiny_skia/src/geometry.rs
+++ b/tiny_skia/src/geometry.rs
@@ -1,5 +1,7 @@
use crate::core::text::LineHeight;
-use crate::core::{Pixels, Point, Rectangle, Size, Transformation, Vector};
+use crate::core::{
+ Pixels, Point, Radians, Rectangle, Size, Transformation, Vector,
+};
use crate::graphics::geometry::fill::{self, Fill};
use crate::graphics::geometry::stroke::{self, Stroke};
use crate::graphics::geometry::{Path, Style, Text};
@@ -192,10 +194,10 @@ impl Frame {
self.transform.pre_translate(translation.x, translation.y);
}
- pub fn rotate(&mut self, angle: f32) {
- self.transform = self
- .transform
- .pre_concat(tiny_skia::Transform::from_rotate(angle.to_degrees()));
+ pub fn rotate(&mut self, angle: impl Into<Radians>) {
+ self.transform = self.transform.pre_concat(
+ tiny_skia::Transform::from_rotate(angle.into().0.to_degrees()),
+ );
}
pub fn scale(&mut self, scale: impl Into<f32>) {
diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index 8cfcfff0..f4e0fbda 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -1,6 +1,8 @@
//! Build and draw geometry.
use crate::core::text::LineHeight;
-use crate::core::{Pixels, Point, Rectangle, Size, Transformation, Vector};
+use crate::core::{
+ Pixels, Point, Radians, Rectangle, Size, Transformation, Vector,
+};
use crate::graphics::color;
use crate::graphics::geometry::fill::{self, Fill};
use crate::graphics::geometry::{
@@ -475,12 +477,12 @@ impl Frame {
/// Applies a rotation in radians to the current transform of the [`Frame`].
#[inline]
- pub fn rotate(&mut self, angle: f32) {
+ pub fn rotate(&mut self, angle: impl Into<Radians>) {
self.transforms.current.0 = self
.transforms
.current
.0
- .pre_rotate(lyon::math::Angle::radians(angle));
+ .pre_rotate(lyon::math::Angle::radians(angle.into().0));
}
/// Applies a uniform scaling to the current transform of the [`Frame`].
diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs
index 8e11ca98..32c962fc 100644
--- a/widget/src/tooltip.rs
+++ b/widget/src/tooltip.rs
@@ -273,11 +273,10 @@ where
}
/// The position of the tooltip. Defaults to following the cursor.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Position {
- /// The tooltip will follow the cursor.
- FollowCursor,
/// The tooltip will appear on the top of the widget.
+ #[default]
Top,
/// The tooltip will appear on the bottom of the widget.
Bottom,
@@ -285,6 +284,8 @@ pub enum Position {
Left,
/// The tooltip will appear on the right of the widget.
Right,
+ /// The tooltip will follow the cursor.
+ FollowCursor,
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]