1use crate::message;
34use crate::program::{self, Program};
35use crate::shell;
36use crate::theme;
37use crate::window;
38use crate::{
39 Element, Executor, Font, Preset, Result, Settings, Size, Subscription,
40 Task, Theme,
41};
42
43use iced_debug as debug;
44
45use std::borrow::Cow;
46
47pub mod timed;
48
49pub use timed::timed;
50
51pub fn application<State, Message, Theme, Renderer>(
80 boot: impl BootFn<State, Message>,
81 update: impl UpdateFn<State, Message>,
82 view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>,
83) -> Application<impl Program<State = State, Message = Message, Theme = Theme>>
84where
85 State: 'static,
86 Message: Send + 'static,
87 Theme: theme::Base,
88 Renderer: program::Renderer,
89{
90 use std::marker::PhantomData;
91
92 struct Instance<State, Message, Theme, Renderer, Boot, Update, View> {
93 boot: Boot,
94 update: Update,
95 view: View,
96 _state: PhantomData<State>,
97 _message: PhantomData<Message>,
98 _theme: PhantomData<Theme>,
99 _renderer: PhantomData<Renderer>,
100 }
101
102 impl<State, Message, Theme, Renderer, Boot, Update, View> Program
103 for Instance<State, Message, Theme, Renderer, Boot, Update, View>
104 where
105 Message: Send + 'static,
106 Theme: theme::Base,
107 Renderer: program::Renderer,
108 Boot: self::BootFn<State, Message>,
109 Update: self::UpdateFn<State, Message>,
110 View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>,
111 {
112 type State = State;
113 type Message = Message;
114 type Theme = Theme;
115 type Renderer = Renderer;
116 type Executor = iced_futures::backend::default::Executor;
117
118 fn name() -> &'static str {
119 let name = std::any::type_name::<State>();
120
121 name.split("::").next().unwrap_or("a_cool_application")
122 }
123
124 fn boot(&self) -> (State, Task<Message>) {
125 self.boot.boot()
126 }
127
128 fn update(
129 &self,
130 state: &mut Self::State,
131 message: Self::Message,
132 ) -> Task<Self::Message> {
133 self.update.update(state, message)
134 }
135
136 fn view<'a>(
137 &self,
138 state: &'a Self::State,
139 _window: window::Id,
140 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
141 self.view.view(state)
142 }
143
144 fn settings(&self) -> Settings {
145 Settings::default()
146 }
147
148 fn window(&self) -> Option<iced_core::window::Settings> {
149 Some(window::Settings::default())
150 }
151 }
152
153 Application {
154 raw: Instance {
155 boot,
156 update,
157 view,
158 _state: PhantomData,
159 _message: PhantomData,
160 _theme: PhantomData,
161 _renderer: PhantomData,
162 },
163 settings: Settings::default(),
164 window: window::Settings::default(),
165 presets: Vec::new(),
166 }
167}
168
169#[derive(Debug)]
177pub struct Application<P: Program> {
178 raw: P,
179 settings: Settings,
180 window: window::Settings,
181 presets: Vec<Preset<P::State, P::Message>>,
182}
183
184impl<P: Program> Application<P> {
185 pub fn run(self) -> Result
187 where
188 Self: 'static,
189 P::Message: message::MaybeDebug + message::MaybeClone,
190 {
191 #[cfg(feature = "debug")]
192 iced_debug::init(iced_debug::Metadata {
193 name: P::name(),
194 theme: None,
195 can_time_travel: cfg!(feature = "time-travel"),
196 });
197
198 #[cfg(feature = "tester")]
199 let program = iced_tester::attach(self);
200
201 #[cfg(all(
202 feature = "debug",
203 not(feature = "tester"),
204 not(target_arch = "wasm32")
205 ))]
206 let program = iced_devtools::attach(self);
207
208 #[cfg(not(any(
209 feature = "tester",
210 all(feature = "debug", not(target_arch = "wasm32"))
211 )))]
212 let program = self;
213
214 Ok(shell::run(program)?)
215 }
216
217 pub fn settings(self, settings: Settings) -> Self {
219 Self { settings, ..self }
220 }
221
222 pub fn antialiasing(self, antialiasing: bool) -> Self {
224 Self {
225 settings: Settings {
226 antialiasing,
227 ..self.settings
228 },
229 ..self
230 }
231 }
232
233 pub fn default_font(self, default_font: Font) -> Self {
235 Self {
236 settings: Settings {
237 default_font,
238 ..self.settings
239 },
240 ..self
241 }
242 }
243
244 pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
246 self.settings.fonts.push(font.into());
247 self
248 }
249
250 pub fn window(self, window: window::Settings) -> Self {
254 Self { window, ..self }
255 }
256
257 pub fn centered(self) -> Self {
259 Self {
260 window: window::Settings {
261 position: window::Position::Centered,
262 ..self.window
263 },
264 ..self
265 }
266 }
267
268 pub fn exit_on_close_request(self, exit_on_close_request: bool) -> Self {
270 Self {
271 window: window::Settings {
272 exit_on_close_request,
273 ..self.window
274 },
275 ..self
276 }
277 }
278
279 pub fn window_size(self, size: impl Into<Size>) -> Self {
281 Self {
282 window: window::Settings {
283 size: size.into(),
284 ..self.window
285 },
286 ..self
287 }
288 }
289
290 pub fn transparent(self, transparent: bool) -> Self {
292 Self {
293 window: window::Settings {
294 transparent,
295 ..self.window
296 },
297 ..self
298 }
299 }
300
301 pub fn resizable(self, resizable: bool) -> Self {
303 Self {
304 window: window::Settings {
305 resizable,
306 ..self.window
307 },
308 ..self
309 }
310 }
311
312 pub fn decorations(self, decorations: bool) -> Self {
314 Self {
315 window: window::Settings {
316 decorations,
317 ..self.window
318 },
319 ..self
320 }
321 }
322
323 pub fn position(self, position: window::Position) -> Self {
325 Self {
326 window: window::Settings {
327 position,
328 ..self.window
329 },
330 ..self
331 }
332 }
333
334 pub fn level(self, level: window::Level) -> Self {
336 Self {
337 window: window::Settings {
338 level,
339 ..self.window
340 },
341 ..self
342 }
343 }
344
345 pub fn title(
347 self,
348 title: impl TitleFn<P::State>,
349 ) -> Application<
350 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
351 > {
352 Application {
353 raw: program::with_title(self.raw, move |state, _window| {
354 title.title(state)
355 }),
356 settings: self.settings,
357 window: self.window,
358 presets: self.presets,
359 }
360 }
361
362 pub fn subscription(
364 self,
365 f: impl Fn(&P::State) -> Subscription<P::Message>,
366 ) -> Application<
367 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
368 > {
369 Application {
370 raw: program::with_subscription(self.raw, f),
371 settings: self.settings,
372 window: self.window,
373 presets: self.presets,
374 }
375 }
376
377 pub fn theme(
379 self,
380 f: impl ThemeFn<P::State, P::Theme>,
381 ) -> Application<
382 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
383 > {
384 Application {
385 raw: program::with_theme(self.raw, move |state, _window| {
386 f.theme(state)
387 }),
388 settings: self.settings,
389 window: self.window,
390 presets: self.presets,
391 }
392 }
393
394 pub fn style(
396 self,
397 f: impl Fn(&P::State, &P::Theme) -> theme::Style,
398 ) -> Application<
399 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
400 > {
401 Application {
402 raw: program::with_style(self.raw, f),
403 settings: self.settings,
404 window: self.window,
405 presets: self.presets,
406 }
407 }
408
409 pub fn scale_factor(
411 self,
412 f: impl Fn(&P::State) -> f32,
413 ) -> Application<
414 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
415 > {
416 Application {
417 raw: program::with_scale_factor(self.raw, move |state, _window| {
418 f(state)
419 }),
420 settings: self.settings,
421 window: self.window,
422 presets: self.presets,
423 }
424 }
425
426 pub fn executor<E>(
428 self,
429 ) -> Application<
430 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
431 >
432 where
433 E: Executor,
434 {
435 Application {
436 raw: program::with_executor::<P, E>(self.raw),
437 settings: self.settings,
438 window: self.window,
439 presets: self.presets,
440 }
441 }
442
443 pub fn presets(
449 self,
450 presets: impl IntoIterator<Item = Preset<P::State, P::Message>>,
451 ) -> Self {
452 Self {
453 presets: presets.into_iter().collect(),
454 ..self
455 }
456 }
457}
458
459impl<P: Program> Program for Application<P> {
460 type State = P::State;
461 type Message = P::Message;
462 type Theme = P::Theme;
463 type Renderer = P::Renderer;
464 type Executor = P::Executor;
465
466 fn name() -> &'static str {
467 P::name()
468 }
469
470 fn settings(&self) -> Settings {
471 self.settings.clone()
472 }
473
474 fn window(&self) -> Option<window::Settings> {
475 Some(self.window.clone())
476 }
477
478 fn boot(&self) -> (Self::State, Task<Self::Message>) {
479 self.raw.boot()
480 }
481
482 fn update(
483 &self,
484 state: &mut Self::State,
485 message: Self::Message,
486 ) -> Task<Self::Message> {
487 debug::hot(|| self.raw.update(state, message))
488 }
489
490 fn view<'a>(
491 &self,
492 state: &'a Self::State,
493 window: window::Id,
494 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
495 debug::hot(|| self.raw.view(state, window))
496 }
497
498 fn title(&self, state: &Self::State, window: window::Id) -> String {
499 debug::hot(|| self.raw.title(state, window))
500 }
501
502 fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
503 debug::hot(|| self.raw.subscription(state))
504 }
505
506 fn theme(
507 &self,
508 state: &Self::State,
509 window: iced_core::window::Id,
510 ) -> Option<Self::Theme> {
511 debug::hot(|| self.raw.theme(state, window))
512 }
513
514 fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
515 debug::hot(|| self.raw.style(state, theme))
516 }
517
518 fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
519 debug::hot(|| self.raw.scale_factor(state, window))
520 }
521
522 fn presets(&self) -> &[Preset<Self::State, Self::Message>] {
523 &self.presets
524 }
525}
526
527pub trait BootFn<State, Message> {
536 fn boot(&self) -> (State, Task<Message>);
538}
539
540impl<T, C, State, Message> BootFn<State, Message> for T
541where
542 T: Fn() -> C,
543 C: IntoBoot<State, Message>,
544{
545 fn boot(&self) -> (State, Task<Message>) {
546 self().into_boot()
547 }
548}
549
550pub trait IntoBoot<State, Message> {
552 fn into_boot(self) -> (State, Task<Message>);
554}
555
556impl<State, Message> IntoBoot<State, Message> for State {
557 fn into_boot(self) -> (State, Task<Message>) {
558 (self, Task::none())
559 }
560}
561
562impl<State, Message> IntoBoot<State, Message> for (State, Task<Message>) {
563 fn into_boot(self) -> (State, Task<Message>) {
564 self
565 }
566}
567
568pub trait TitleFn<State> {
575 fn title(&self, state: &State) -> String;
577}
578
579impl<State> TitleFn<State> for &'static str {
580 fn title(&self, _state: &State) -> String {
581 self.to_string()
582 }
583}
584
585impl<T, State> TitleFn<State> for T
586where
587 T: Fn(&State) -> String,
588{
589 fn title(&self, state: &State) -> String {
590 self(state)
591 }
592}
593
594pub trait UpdateFn<State, Message> {
599 fn update(&self, state: &mut State, message: Message) -> Task<Message>;
601}
602
603impl<State, Message> UpdateFn<State, Message> for () {
604 fn update(&self, _state: &mut State, _message: Message) -> Task<Message> {
605 Task::none()
606 }
607}
608
609impl<T, State, Message, C> UpdateFn<State, Message> for T
610where
611 T: Fn(&mut State, Message) -> C,
612 C: Into<Task<Message>>,
613{
614 fn update(&self, state: &mut State, message: Message) -> Task<Message> {
615 self(state, message).into()
616 }
617}
618
619pub trait ViewFn<'a, State, Message, Theme, Renderer> {
624 fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer>;
626}
627
628impl<'a, T, State, Message, Theme, Renderer, Widget>
629 ViewFn<'a, State, Message, Theme, Renderer> for T
630where
631 T: Fn(&'a State) -> Widget,
632 State: 'static,
633 Widget: Into<Element<'a, Message, Theme, Renderer>>,
634{
635 fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer> {
636 self(state).into()
637 }
638}
639
640pub trait ThemeFn<State, Theme> {
649 fn theme(&self, state: &State) -> Option<Theme>;
654}
655
656impl<State> ThemeFn<State, Theme> for Theme {
657 fn theme(&self, _state: &State) -> Option<Theme> {
658 Some(self.clone())
659 }
660}
661
662impl<F, T, State, Theme> ThemeFn<State, Theme> for F
663where
664 F: Fn(&State) -> T,
665 T: Into<Option<Theme>>,
666{
667 fn theme(&self, state: &State) -> Option<Theme> {
668 (self)(state).into()
669 }
670}