Introduce Program API

This commit is contained in:
Héctor Ramón Jiménez 2024-03-16 05:33:47 +01:00
parent 0524e9b457
commit c22269bff3
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
43 changed files with 1141 additions and 831 deletions

View file

@ -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),
]
}
}
@ -160,8 +160,15 @@ 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

View file

@ -1,20 +1,18 @@
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::sandbox(Arc::update, Arc::view)
.title("Arc - Iced")
.subscription(Arc::subscription)
.theme(|_| Theme::Dark)
.antialiased()
.run()
}
struct Arc {
@ -27,30 +25,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 +37,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 = ();

View file

@ -1,12 +1,12 @@
//! 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::sandbox(Example::update, Example::view)
.title("Bezier tool - Iced")
.antialiased()
.run()
}
#[derive(Default)]
@ -21,17 +21,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) => {

View file

@ -1,12 +1,13 @@
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::sandbox(Example::update, Example::view)
.title("Checkbox - Iced")
.fonts([include_bytes!("../fonts/icons.ttf").as_slice().into()])
.run()
}
#[derive(Default)]
@ -21,28 +22,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 +36,7 @@ impl Application for Example {
Message::CustomToggled(custom) => {
self.custom = custom;
}
Message::FontLoaded(_) => (),
}
Command::none()
}
fn view(&self) -> Element<Message> {

View file

@ -1,17 +1,17 @@
use iced::executor;
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,
Element, Length, Point, Rectangle, Renderer, Subscription, Theme, Vector,
};
pub fn main() -> iced::Result {
Clock::run(Settings {
antialiasing: true,
..Settings::default()
})
iced::sandbox(Clock::update, Clock::view)
.title("Clock - Iced")
.subscription(Clock::subscription)
.theme(Clock::theme)
.antialiased()
.run()
}
struct Clock {
@ -24,28 +24,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 +36,6 @@ impl Application for Clock {
}
}
}
Command::none()
}
fn view(&self) -> Element<Message> {
@ -82,7 +60,18 @@ impl Application for Clock {
}
fn theme(&self) -> Theme {
Theme::TokyoNight
Theme::ALL[(self.now.unix_timestamp() as usize / 60) % 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(),
}
}
}

View file

@ -3,8 +3,8 @@ 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,
Color, Element, Font, Length, Pixels, Point, Rectangle, Renderer, Size,
Vector,
};
use palette::{
self, convert::FromColor, rgb::Rgb, Darken, Hsl, Lighten, ShiftHue,
@ -13,11 +13,12 @@ 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::sandbox(ColorPalette::update, ColorPalette::view)
.theme(ColorPalette::theme)
.title("Color Palette - Iced")
.default_font(Font::MONOSPACE)
.antialiased()
.run()
}
#[derive(Default)]
@ -41,17 +42,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),

View file

@ -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,

View file

@ -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) => {

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -2,18 +2,17 @@ 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::sandbox(IcedCubes::update, IcedCubes::view)
.subscription(IcedCubes::subscription)
.title("Custom Shader - Iced")
.run()
}
struct IcedCubes {
@ -30,27 +29,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>) {
(
impl IcedCubes {
fn new() -> Self {
Self {
start: Instant::now(),
scene: Scene::new(),
},
Command::none(),
)
}
}
fn title(&self) -> String {
"Iced Cubes".to_string()
}
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 +55,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 +132,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>>,

View file

@ -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()
}
}

View file

@ -1,14 +1,13 @@
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::sandbox(Example::update, Example::view)
.subscription(Example::subscription)
.title("Download Progress - Iced")
.run()
}
#[derive(Debug)]
@ -24,27 +23,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 {
impl Example {
fn new() -> Self {
Self {
downloads: vec![Download::new(0)],
last_id: 0,
},
Command::none(),
)
}
}
fn title(&self) -> String {
String::from("Download progress - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(&mut self, message: Message) {
match message {
Message::Add => {
self.last_id += 1;
@ -63,9 +50,7 @@ impl Application for Example {
download.progress(progress);
}
}
};
Command::none()
}
}
fn subscription(&self) -> Subscription<Message> {
@ -93,6 +78,12 @@ impl Application for Example {
}
}
impl Default for Example {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
struct Download {
id: usize,

View file

@ -1,21 +1,15 @@
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::application(Events::new, Events::update, Events::view)
.title("Events - Iced")
.subscription(Events::subscription)
.ignore_close_request()
.run()
}
#[derive(Debug, Default)]
@ -31,20 +25,11 @@ 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>) {
impl Events {
fn new() -> (Events, Command<Message>) {
(Events::default(), Command::none())
}
fn title(&self) -> String {
String::from("Events - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::EventOccurred(event) if self.enabled => {

View file

@ -1,10 +1,11 @@
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::application(Exit::new, Exit::update, Exit::view)
.title("Exit - Iced")
.run()
}
#[derive(Default)]
@ -18,20 +19,11 @@ 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>) {
impl Exit {
fn new() -> (Self, Command<Message>) {
(Self::default(), Command::none())
}
fn title(&self) -> String {
String::from("Exit - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Confirm => window::close(window::Id::MAIN),

View file

@ -147,29 +147,14 @@ 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", |_, _| {}, 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<()> {
fn view(_state: &()) -> Element<'_, ()> {
let content = column![
rainbow(),
"In this example we draw a custom widget Rainbow, using \
@ -193,5 +178,4 @@ impl Sandbox for Example {
.height(Length::Fill)
.center_y()
.into()
}
}

View file

@ -1,4 +1,3 @@
use iced::executor;
use iced::keyboard;
use iced::mouse;
use iced::widget::{
@ -6,15 +5,19 @@ 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::sandbox(Layout::update, Layout::view)
.title(Layout::title)
.subscription(Layout::subscription)
.theme(Layout::theme)
.run()
}
#[derive(Debug)]
#[derive(Default, Debug)]
struct Layout {
example: Example,
explain: bool,
@ -29,28 +32,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 +52,6 @@ impl Application for Layout {
self.theme = theme;
}
}
Command::none()
}
fn subscription(&self) -> Subscription<Message> {

View file

@ -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) => {

View file

@ -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,28 @@ use circular::Circular;
use linear::Linear;
pub fn main() -> iced::Result {
LoadingSpinners::run(Settings {
antialiasing: true,
..Default::default()
})
iced::sandbox(LoadingSpinners::update, LoadingSpinners::view)
.title("Loading Spinners - Iced")
.antialiased()
.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 +91,11 @@ impl Application for LoadingSpinners {
.into()
}
}
impl Default for LoadingSpinners {
fn default() -> Self {
Self {
cycle_duration: 2.0,
}
}
}

View file

@ -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),

View file

@ -2,101 +2,59 @@
//! 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::sandbox(Multitouch::update, Multitouch::view)
.title("Multitouch - Iced")
.antialiased()
.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(

View file

@ -1,17 +1,16 @@
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::sandbox(Example::update, Example::view)
.subscription(Example::subscription)
.title("Pane Grid - Iced")
.run()
}
struct Example {
@ -35,30 +34,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")
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(&mut self, message: Message) {
match message {
Message::Split(axis, pane) => {
let result =
@ -132,8 +119,6 @@ impl Application for Example {
}
}
}
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
@ -209,6 +194,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,

View file

@ -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) => {

View file

@ -1,9 +1,11 @@
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::application(Pokedex::new, Pokedex::update, Pokedex::view)
.title(Pokedex::title)
.run()
}
#[derive(Debug)]
@ -19,13 +21,8 @@ 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>) {
impl Pokedex {
fn new() -> (Self, Command<Message>) {
(
Pokedex::Loading,
Command::perform(Pokemon::search(), Message::PokemonFound),

View file

@ -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,

View file

@ -1,10 +1,13 @@
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::sandbox(QRGenerator::update, QRGenerator::view)
.title("QR Code Generator - Iced")
.theme(QRGenerator::theme)
.run()
}
#[derive(Default)]
@ -20,17 +23,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) => {

View file

@ -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,7 +13,10 @@ use ::image::ColorType;
fn main() -> iced::Result {
tracing_subscriber::fmt::init();
Example::run(iced::Settings::default())
iced::application(Example::new, Example::update, Example::view)
.subscription(Example::subscription)
.title("Screenshot - Iced")
.run()
}
struct Example {
@ -42,13 +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>) {
impl Example {
fn new() -> (Self, Command<Message>) {
(
Example {
screenshot: None,
@ -64,11 +60,7 @@ impl Application for Example {
)
}
fn title(&self) -> String {
"Screenshot".to_string()
}
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Screenshot => {
return iced::window::screenshot(
@ -130,7 +122,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 +251,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| {

View file

@ -1,20 +1,23 @@
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::application(
ScrollableDemo::new,
ScrollableDemo::update,
ScrollableDemo::view,
)
.theme(ScrollableDemo::theme)
.title("Scrollable - Iced")
.run()
}
struct ScrollableDemo {
@ -45,13 +48,8 @@ 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>) {
impl ScrollableDemo {
fn new() -> (Self, Command<Message>) {
(
ScrollableDemo {
scrollable_direction: Direction::Vertical,
@ -65,10 +63,6 @@ impl Application for ScrollableDemo {
)
}
fn title(&self) -> String {
String::from("Scrollable - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::SwitchDirection(direction) => {
@ -340,7 +334,7 @@ impl Application for ScrollableDemo {
container(content).padding(20).center_x().center_y().into()
}
fn theme(&self) -> Self::Theme {
fn theme(&self) -> Theme {
Theme::Dark
}
}

View file

@ -1,25 +1,20 @@
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::sandbox(SierpinskiEmulator::update, SierpinskiEmulator::view)
.title("Sierpinski Triangle - Iced")
.antialiased()
.run()
}
#[derive(Debug)]
#[derive(Debug, Default)]
struct SierpinskiEmulator {
graph: SierpinskiGraph,
}
@ -31,27 +26,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 +43,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 +141,6 @@ impl canvas::Program<Message> for SierpinskiGraph {
}
impl SierpinskiGraph {
fn new() -> SierpinskiGraph {
SierpinskiGraph::default()
}
fn redraw(&mut self) {
self.cache.clear();
}

View file

@ -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()
}
}

View file

@ -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,14 @@ use std::time::Instant;
pub fn main() -> iced::Result {
tracing_subscriber::fmt::init();
SolarSystem::run(Settings {
antialiasing: true,
..Settings::default()
})
iced::sandbox(SolarSystem::update, SolarSystem::view)
.subscription(SolarSystem::subscription)
.theme(SolarSystem::theme)
.title("Solar System - Iced")
.run()
}
#[derive(Default)]
struct SolarSystem {
state: State,
}
@ -39,33 +39,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 +56,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 +197,9 @@ impl<Message> canvas::Program<Message> for State {
vec![background, system]
}
}
impl Default for State {
fn default() -> Self {
Self::new()
}
}

View file

@ -1,27 +1,32 @@
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::sandbox(Stopwatch::update, Stopwatch::view)
.subscription(Stopwatch::subscription)
.theme(Stopwatch::theme)
.title("Stopwatch - Iced")
.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 +36,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 +59,6 @@ impl Application for Stopwatch {
self.duration = Duration::default();
}
}
Command::none()
}
fn subscription(&self) -> Subscription<Message> {

View file

@ -3,10 +3,13 @@ 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::sandbox(Styling::update, Styling::view)
.theme(Styling::theme)
.title("Styling - Iced")
.run()
}
#[derive(Default)]
@ -28,17 +31,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) => {

View file

@ -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")

View file

@ -1,12 +1,12 @@
use iced::widget::{button, column, container, text};
use iced::{
executor, system, Application, Command, Element, Length, Settings, Theme,
};
use iced::{system, Command, Element, Length};
use bytesize::ByteSize;
pub fn main() -> iced::Result {
Example::run(Settings::default())
iced::application(Example::new, Example::update, Example::view)
.title("System Information - Iced")
.run()
}
#[allow(clippy::large_enum_variant)]
@ -22,23 +22,14 @@ 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>) {
impl Example {
fn new() -> (Self, Command<Message>) {
(
Self::Loading,
system::fetch_information(Message::InformationReceived),
)
}
fn title(&self) -> String {
String::from("System Information - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Refresh => {

View file

@ -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;

View file

@ -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,7 +16,10 @@ pub fn main() -> iced::Result {
#[cfg(not(target_arch = "wasm32"))]
tracing_subscriber::fmt::init();
Tour::run(Settings::default())
iced::sandbox(Tour::update, Tour::view)
.title(Tour::title)
.centered()
.run()
}
pub struct Tour {
@ -24,11 +27,9 @@ pub struct Tour {
debug: bool,
}
impl Sandbox for Tour {
type Message = Message;
fn new() -> Tour {
Tour {
impl Tour {
fn new() -> Self {
Self {
steps: Steps::new(),
debug: false,
}
@ -90,6 +91,12 @@ impl Sandbox for Tour {
}
}
impl Default for Tour {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum Message {
BackPressed,

View file

@ -1,12 +1,12 @@
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::sandbox(App::update, App::view)
.subscription(App::subscription)
.title("URL Handler - Iced")
.run()
}
#[derive(Debug, Default)]
@ -19,21 +19,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 +32,7 @@ impl Application for App {
self.url = Some(url);
}
}
};
Command::none()
}
}
fn subscription(&self) -> Subscription<Message> {

View file

@ -3,18 +3,17 @@ 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::sandbox(VectorialText::update, VectorialText::view)
.theme(|_| Theme::Dark)
.title("Vectorial Text - Iced")
.antialiased()
.run()
}
#[derive(Default)]
struct VectorialText {
state: State,
}
@ -27,19 +26,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 +93,6 @@ impl Sandbox for VectorialText {
.padding(20)
.into()
}
fn theme(&self) -> Theme {
Theme::Dark
}
}
struct State {
@ -170,3 +153,9 @@ impl<Message> canvas::Program<Message> for State {
vec![geometry]
}
}
impl Default for State {
fn default() -> Self {
State::new()
}
}

View file

@ -175,6 +175,7 @@ mod error;
mod sandbox;
pub mod application;
pub mod program;
pub mod settings;
pub mod time;
pub mod window;
@ -308,6 +309,7 @@ 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;
@ -327,3 +329,49 @@ pub type Element<
///
/// [`Application`]: crate::Application
pub type Result = std::result::Result<(), Error>;
/// Runs a basic iced application with default [`Settings`] given
/// - its window title,
/// - its update logic,
/// - and its view logic.
///
/// # 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>(
title: &'static str,
update: impl Fn(&mut State, Message) + 'static,
view: impl for<'a> program::View<'a, State, Message> + 'static,
) -> Result
where
State: Default + 'static,
Message: std::fmt::Debug + Send + 'static,
{
sandbox(update, view).title(title).run()
}
#[doc(inline)]
pub use program::{application, sandbox};

669
src/program.rs Normal file
View file

@ -0,0 +1,669 @@
//! Create iced applications out of simple functions.
//!
//! 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.
//!
//! This API is meant to be a more convenient—although less
//! powerful—alternative to the [`Sandbox`] and [`Application`] traits.
//!
//! [`Sandbox`]: crate::Sandbox
//!
//! # Example
//! ```no_run
//! use iced::widget::{button, column, text, Column};
//! use iced::Theme;
//!
//! pub fn main() -> iced::Result {
//! iced::sandbox(update, view)
//! .title("A counter")
//! .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::{self, Application};
use crate::executor::{self, Executor};
use crate::window;
use crate::{Command, Element, Font, Result, Settings, Subscription};
use std::borrow::Cow;
/// Creates the most basic kind of [`Program`] from some update and view logic.
///
/// # Example
/// ```no_run
/// use iced::widget::{button, column, text, Column};
///
/// pub fn main() -> iced::Result {
/// iced::sandbox(update, view).title("A counter").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 sandbox<State, Message>(
update: impl Fn(&mut State, Message),
view: impl for<'a> self::View<'a, State, Message>,
) -> Program<
impl Definition<State = State, Message = Message, Theme = crate::Theme>,
>
where
State: Default + 'static,
Message: Send + std::fmt::Debug,
{
use std::marker::PhantomData;
struct Sandbox<State, Message, Update, View> {
update: Update,
view: View,
_state: PhantomData<State>,
_message: PhantomData<Message>,
}
impl<State, Message, Update, View> Definition
for Sandbox<State, Message, Update, View>
where
State: Default + 'static,
Message: Send + std::fmt::Debug,
Update: Fn(&mut State, Message),
View: for<'a> self::View<'a, State, Message>,
{
type State = State;
type Message = Message;
type Theme = crate::Theme;
type Executor = iced_futures::backend::null::Executor;
fn new(&self) -> (Self::State, Command<Self::Message>) {
(State::default(), Command::none())
}
fn update(
&self,
state: &mut Self::State,
message: Self::Message,
) -> Command<Self::Message> {
(self.update)(state, message);
Command::none()
}
fn view<'a>(
&self,
state: &'a Self::State,
) -> Element<'a, Self::Message, Self::Theme> {
self.view.view(state).into()
}
}
Program {
raw: Sandbox {
update,
view,
_state: PhantomData,
_message: PhantomData,
},
settings: Settings::default(),
}
}
/// Creates a [`Program`] that can leverage the [`Command`] API for
/// concurrent operations.
pub fn application<State, Message>(
new: impl Fn() -> (State, Command<Message>),
update: impl Fn(&mut State, Message) -> Command<Message>,
view: impl for<'a> self::View<'a, State, Message>,
) -> Program<
impl Definition<State = State, Message = Message, Theme = crate::Theme>,
>
where
State: 'static,
Message: Send + std::fmt::Debug,
{
use std::marker::PhantomData;
struct Application<State, Message, New, Update, View> {
new: New,
update: Update,
view: View,
_state: PhantomData<State>,
_message: PhantomData<Message>,
}
impl<State, Message, New, Update, View> Definition
for Application<State, Message, New, Update, View>
where
Message: Send + std::fmt::Debug,
New: Fn() -> (State, Command<Message>),
Update: Fn(&mut State, Message) -> Command<Message>,
View: for<'a> self::View<'a, State, Message>,
{
type State = State;
type Message = Message;
type Theme = crate::Theme;
type Executor = executor::Default;
fn new(&self) -> (Self::State, Command<Self::Message>) {
(self.new)()
}
fn update(
&self,
state: &mut Self::State,
message: Self::Message,
) -> Command<Self::Message> {
(self.update)(state, message)
}
fn view<'a>(
&self,
state: &'a Self::State,
) -> Element<'a, Self::Message, Self::Theme> {
self.view.view(state).into()
}
}
Program {
raw: Application {
new,
update,
view,
_state: PhantomData,
_message: PhantomData,
},
settings: Settings::default(),
}
}
/// A fully functioning and configured iced application.
///
/// It can be [`run`]!
///
/// Create one with either the [`sandbox`] or [`application`] helpers.
///
/// [`run`]: Program::run
/// [`application`]: self::application()
#[derive(Debug)]
pub struct Program<P: Definition> {
raw: P,
settings: Settings,
}
impl<P: Definition> Program<P> {
/// Runs the [`Program`].
pub fn run(self) -> Result
where
Self: 'static,
{
struct Instance<P: Definition> {
program: P,
state: P::State,
}
impl<P: Definition> Application for Instance<P> {
type Message = P::Message;
type Theme = P::Theme;
type Flags = P;
type Executor = P::Executor;
fn new(program: Self::Flags) -> (Self, Command<Self::Message>) {
let (state, command) = P::new(&program);
(Self { program, state }, 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 theme(&self) -> Self::Theme {
self.program.theme(&self.state)
}
fn subscription(&self) -> Subscription<Self::Message> {
self.program.subscription(&self.state)
}
}
let Self { raw, settings } = self;
Instance::run(Settings {
flags: raw,
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 }
}
/// Toggles the [`Settings::antialiasing`] to `true` for the [`Program`].
pub fn antialiased(self) -> Self {
Self {
settings: Settings {
antialiasing: true,
..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
}
}
/// Sets the fonts that will be loaded at the start of the [`Program`].
pub fn fonts(
self,
fonts: impl IntoIterator<Item = Cow<'static, [u8]>>,
) -> Self {
Self {
settings: Settings {
fonts: fonts.into_iter().collect(),
..self.settings
},
..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`] to `false` in the [`Program`].
pub fn ignore_close_request(self) -> Self {
Self {
settings: Settings {
window: window::Settings {
exit_on_close_request: false,
..self.settings.window
},
..self.settings
},
..self
}
}
/// Sets the [`Title`] of the [`Program`].
pub 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,
}
}
/// 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,
}
}
}
/// The internal definition of a [`Program`].
///
/// You should not need to implement this trait directly. Instead, use the
/// helper functions available in the [`program`] module and the [`Program`] struct.
///
/// [`program`]: crate::program
#[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 + application::DefaultStyle;
/// The executor of the program.
type Executor: Executor;
fn new(&self) -> (Self::State, 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 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 new(&self) -> (Self::State, Command<Self::Message>) {
self.program.new()
}
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)
}
}
WithTitle { program, title }
}
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 new(&self) -> (Self::State, Command<Self::Message>) {
self.program.new()
}
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)
}
}
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 new(&self) -> (Self::State, Command<Self::Message>) {
self.program.new()
}
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)
}
}
WithTheme { program, theme: f }
}
/// The title logic of some [`Program`].
///
/// This trait is implemented both for `&static str` and
/// any closure `Fn(&State) -> String`.
///
/// You can use any of these in [`Program::title`].
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 view logic of some [`Program`].
///
/// This trait allows [`sandbox`] and [`application`] to
/// take any closure that returns any `Into<Element<'_, Message>>`.
///
/// [`application`]: self::application()
pub trait View<'a, State, Message> {
/// The widget returned by the view logic.
type Widget: Into<Element<'a, Message>>;
/// Produces the widget of the [`Program`].
fn view(&self, state: &'a State) -> Self::Widget;
}
impl<'a, T, State, Message, Widget> View<'a, State, Message> for T
where
T: Fn(&'a State) -> Widget,
State: 'static,
Widget: Into<Element<'a, Message>>,
{
type Widget = Widget;
fn view(&self, state: &'a State) -> Self::Widget {
self(state)
}
}

View file

@ -6,7 +6,7 @@ use std::borrow::Cow;
/// The settings of an application.
#[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

View file

@ -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)]