summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Gigas002 <24297712+Gigas002@users.noreply.github.com>2024-03-19 22:09:36 +0900
committerLibravatar GitHub <noreply@github.com>2024-03-19 22:09:36 +0900
commitf3a1c785b2743e9c48c3d28df0c6772ce579d7c8 (patch)
tree1b39799f45878d89b4f9e2f9bea8fa8a7ed07150 /src
parentc9453cd55d84f0dd2ad0050208863d036c98843f (diff)
parent8ce16aba6204cb5c02a709cdf79c309f7b7e0196 (diff)
downloadiced-f3a1c785b2743e9c48c3d28df0c6772ce579d7c8.tar.gz
iced-f3a1c785b2743e9c48c3d28df0c6772ce579d7c8.tar.bz2
iced-f3a1c785b2743e9c48c3d28df0c6772ce579d7c8.zip
Merge branch 'iced-rs:master' into viewer_content_fit
Diffstat (limited to '')
-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
7 files changed, 949 insertions, 238 deletions
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();