diff --git a/Cargo.lock b/Cargo.lock index fc3734c0..91594dca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2403,6 +2403,7 @@ dependencies = [ "criterion", "iced_core", "iced_debug", + "iced_devtools", "iced_futures", "iced_highlighter", "iced_renderer", @@ -2454,6 +2455,14 @@ dependencies = [ "iced_core", ] +[[package]] +name = "iced_devtools" +version = "0.14.0-dev" +dependencies = [ + "iced_program", + "iced_widget", +] + [[package]] name = "iced_futures" version = "0.14.0-dev" diff --git a/Cargo.toml b/Cargo.toml index eec4e705..7bffcde5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ markdown = ["iced_widget/markdown"] # Enables lazy widgets lazy = ["iced_widget/lazy"] # Enables a debug view in native platforms (press F12) -debug = ["iced_winit/debug"] +debug = ["iced_winit/debug", "iced_devtools"] # Enables the `thread-pool` futures executor as the `executor::Default` on native platforms thread-pool = ["iced_futures/thread-pool"] # Enables `tokio` as the `executor::Default` on native platforms @@ -80,6 +80,9 @@ iced_widget.workspace = true iced_winit.features = ["program"] iced_winit.workspace = true +iced_devtools.workspace = true +iced_devtools.optional = true + iced_highlighter.workspace = true iced_highlighter.optional = true @@ -112,6 +115,7 @@ members = [ "beacon", "core", "debug", + "devtools", "futures", "graphics", "highlighter", @@ -142,6 +146,7 @@ iced = { version = "0.14.0-dev", path = "." } iced_beacon = { version = "0.14.0-dev", path = "beacon" } iced_core = { version = "0.14.0-dev", path = "core" } iced_debug = { version = "0.14.0-dev", path = "debug" } +iced_devtools = { version = "0.14.0-dev", path = "devtools" } iced_futures = { version = "0.14.0-dev", path = "futures" } iced_graphics = { version = "0.14.0-dev", path = "graphics" } iced_highlighter = { version = "0.14.0-dev", path = "highlighter" } diff --git a/devtools/Cargo.toml b/devtools/Cargo.toml new file mode 100644 index 00000000..1d43566b --- /dev/null +++ b/devtools/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "iced_devtools" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +categories.workspace = true +keywords.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] +iced_program.workspace = true +iced_widget.workspace = true diff --git a/devtools/src/lib.rs b/devtools/src/lib.rs new file mode 100644 index 00000000..7ef39ead --- /dev/null +++ b/devtools/src/lib.rs @@ -0,0 +1,161 @@ +#![allow(missing_docs)] +use crate::runtime::futures; +use iced_program as program; +use iced_widget::core; +use iced_widget::runtime; + +use crate::core::Element; +use crate::core::theme; +use crate::core::window; +use crate::futures::Subscription; +use crate::program::Program; +use crate::runtime::Task; + +use std::fmt; + +pub fn attach(program: impl Program + 'static) -> impl Program { + struct Attach

{ + program: P, + } + + impl

Program for Attach

+ where + P: Program + 'static, + { + type State = DevTools

; + type Message = Message

; + type Theme = P::Theme; + type Renderer = P::Renderer; + type Executor = P::Executor; + + fn name() -> &'static str { + P::name() + } + + fn boot(&self) -> (Self::State, Task) { + let (state, task) = self.program.boot(); + + (DevTools { state }, task.map(Message::Program)) + } + + fn update( + &self, + state: &mut Self::State, + message: Self::Message, + ) -> Task { + state.update(&self.program, message) + } + + fn view<'a>( + &self, + state: &'a Self::State, + window: window::Id, + ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { + state.view(&self.program, window) + } + + fn title(&self, state: &Self::State, window: window::Id) -> String { + state.title(&self.program, window) + } + + fn subscription( + &self, + state: &Self::State, + ) -> runtime::futures::Subscription { + state.subscription(&self.program) + } + + fn theme( + &self, + state: &Self::State, + window: window::Id, + ) -> Self::Theme { + state.theme(&self.program, window) + } + + fn style( + &self, + state: &Self::State, + theme: &Self::Theme, + ) -> theme::Style { + state.style(&self.program, theme) + } + + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + state.scale_factor(&self.program, window) + } + } + + Attach { program } +} + +struct DevTools

+where + P: Program, +{ + state: P::State, +} + +impl

DevTools

+where + P: Program + 'static, +{ + pub fn title(&self, program: &P, window: window::Id) -> String { + program.title(&self.state, window) + } + + pub fn update( + &mut self, + program: &P, + message: Message

, + ) -> Task> { + match message { + Message::Program(message) => program + .update(&mut self.state, message) + .map(Message::Program), + } + } + + pub fn view( + &self, + program: &P, + window: window::Id, + ) -> Element<'_, Message

, P::Theme, P::Renderer> { + program.view(&self.state, window).map(Message::Program) + } + + pub fn subscription(&self, program: &P) -> Subscription> { + program.subscription(&self.state).map(Message::Program) + } + + pub fn theme(&self, program: &P, window: window::Id) -> P::Theme { + program.theme(&self.state, window) + } + + pub fn style(&self, program: &P, theme: &P::Theme) -> theme::Style { + program.style(&self.state, theme) + } + + pub fn scale_factor(&self, program: &P, window: window::Id) -> f64 { + program.scale_factor(&self.state, window) + } +} + +#[derive(Clone)] +enum Message

+where + P: Program, +{ + Program(P::Message), +} + +impl

fmt::Debug for Message

+where + P: Program, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Message::Program(message) => message.fmt(f), + } + } +} diff --git a/src/application.rs b/src/application.rs index e1b70c52..f2ed2d4d 100644 --- a/src/application.rs +++ b/src/application.rs @@ -169,7 +169,13 @@ impl Application

{ where Self: 'static, { - Ok(shell::run(self.raw, self.settings, Some(self.window))?) + #[cfg(feature = "debug")] + let program = iced_devtools::attach(self.raw); + + #[cfg(not(feature = "debug"))] + let program = self.raw; + + Ok(shell::run(program, self.settings, Some(self.window))?) } /// Sets the [`Settings`] that will be used to run the [`Application`].