2025-04-05 19:27:15 +02:00
|
|
|
#![allow(missing_docs)]
|
2025-04-05 20:08:54 +02:00
|
|
|
use iced_debug as debug;
|
2025-04-05 19:27:15 +02:00
|
|
|
use iced_program as program;
|
2025-04-05 20:08:54 +02:00
|
|
|
use iced_widget as widget;
|
2025-04-05 19:27:15 +02:00
|
|
|
use iced_widget::core;
|
|
|
|
|
use iced_widget::runtime;
|
2025-04-05 20:08:54 +02:00
|
|
|
use iced_widget::runtime::futures;
|
2025-04-05 19:27:15 +02:00
|
|
|
|
|
|
|
|
use crate::core::Element;
|
2025-04-05 20:08:54 +02:00
|
|
|
use crate::core::keyboard;
|
|
|
|
|
use crate::core::theme::{self, Base, Theme};
|
|
|
|
|
use crate::core::time::seconds;
|
2025-04-05 19:27:15 +02:00
|
|
|
use crate::core::window;
|
|
|
|
|
use crate::futures::Subscription;
|
2025-04-05 20:14:51 +02:00
|
|
|
use crate::futures::futures::channel::oneshot;
|
2025-04-05 19:27:15 +02:00
|
|
|
use crate::program::Program;
|
|
|
|
|
use crate::runtime::Task;
|
2025-04-05 20:08:54 +02:00
|
|
|
use crate::widget::{bottom_right, container, stack, text, themer};
|
2025-04-05 19:27:15 +02:00
|
|
|
|
|
|
|
|
use std::fmt;
|
2025-04-05 20:14:51 +02:00
|
|
|
use std::thread;
|
2025-04-05 19:27:15 +02:00
|
|
|
|
|
|
|
|
pub fn attach(program: impl Program + 'static) -> impl Program {
|
|
|
|
|
struct Attach<P> {
|
|
|
|
|
program: P,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<P> Program for Attach<P>
|
|
|
|
|
where
|
|
|
|
|
P: Program + 'static,
|
|
|
|
|
{
|
|
|
|
|
type State = DevTools<P>;
|
|
|
|
|
type Message = Message<P>;
|
|
|
|
|
type Theme = P::Theme;
|
|
|
|
|
type Renderer = P::Renderer;
|
|
|
|
|
type Executor = P::Executor;
|
|
|
|
|
|
|
|
|
|
fn name() -> &'static str {
|
|
|
|
|
P::name()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn boot(&self) -> (Self::State, Task<Self::Message>) {
|
2025-04-05 20:08:54 +02:00
|
|
|
let (state, boot) = self.program.boot();
|
|
|
|
|
let (devtools, task) = DevTools::new(state);
|
2025-04-05 19:27:15 +02:00
|
|
|
|
2025-04-05 20:08:54 +02:00
|
|
|
(devtools, Task::batch([boot.map(Message::Program), task]))
|
2025-04-05 19:27:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn update(
|
|
|
|
|
&self,
|
|
|
|
|
state: &mut Self::State,
|
|
|
|
|
message: Self::Message,
|
|
|
|
|
) -> Task<Self::Message> {
|
|
|
|
|
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<Self::Message> {
|
|
|
|
|
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<P>
|
|
|
|
|
where
|
|
|
|
|
P: Program,
|
|
|
|
|
{
|
|
|
|
|
state: P::State,
|
2025-04-05 20:08:54 +02:00
|
|
|
show_notification: bool,
|
2025-04-05 19:27:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<P> DevTools<P>
|
|
|
|
|
where
|
|
|
|
|
P: Program + 'static,
|
|
|
|
|
{
|
2025-04-05 20:08:54 +02:00
|
|
|
pub fn new(state: P::State) -> (Self, Task<Message<P>>) {
|
|
|
|
|
(
|
|
|
|
|
Self {
|
|
|
|
|
state,
|
|
|
|
|
show_notification: true,
|
|
|
|
|
},
|
2025-04-05 20:14:51 +02:00
|
|
|
Task::perform(
|
|
|
|
|
async move {
|
|
|
|
|
let (sender, receiver) = oneshot::channel();
|
|
|
|
|
|
|
|
|
|
let _ = thread::spawn(|| {
|
|
|
|
|
thread::sleep(seconds(2));
|
|
|
|
|
let _ = sender.send(());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let _ = receiver.await;
|
|
|
|
|
},
|
|
|
|
|
|_| Message::HideNotification,
|
|
|
|
|
),
|
2025-04-05 20:08:54 +02:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-05 19:27:15 +02:00
|
|
|
pub fn title(&self, program: &P, window: window::Id) -> String {
|
|
|
|
|
program.title(&self.state, window)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn update(
|
|
|
|
|
&mut self,
|
|
|
|
|
program: &P,
|
|
|
|
|
message: Message<P>,
|
|
|
|
|
) -> Task<Message<P>> {
|
|
|
|
|
match message {
|
2025-04-05 20:08:54 +02:00
|
|
|
Message::HideNotification => {
|
|
|
|
|
self.show_notification = false;
|
|
|
|
|
|
|
|
|
|
Task::none()
|
|
|
|
|
}
|
|
|
|
|
Message::ToggleComet => {
|
|
|
|
|
debug::toggle_comet();
|
|
|
|
|
|
|
|
|
|
Task::none()
|
|
|
|
|
}
|
2025-04-05 19:27:15 +02:00
|
|
|
Message::Program(message) => program
|
|
|
|
|
.update(&mut self.state, message)
|
|
|
|
|
.map(Message::Program),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn view(
|
|
|
|
|
&self,
|
|
|
|
|
program: &P,
|
|
|
|
|
window: window::Id,
|
|
|
|
|
) -> Element<'_, Message<P>, P::Theme, P::Renderer> {
|
2025-04-05 20:08:54 +02:00
|
|
|
let view = program.view(&self.state, window).map(Message::Program);
|
|
|
|
|
let theme = program.theme(&self.state, window);
|
|
|
|
|
|
|
|
|
|
let notification = themer(
|
|
|
|
|
theme
|
|
|
|
|
.palette()
|
|
|
|
|
.map(|palette| Theme::custom("DevTools".to_owned(), palette))
|
|
|
|
|
.unwrap_or_default(),
|
|
|
|
|
bottom_right(
|
|
|
|
|
container(text("Press F12 to open debug metrics"))
|
|
|
|
|
.padding(10)
|
|
|
|
|
.style(container::dark),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
stack![view]
|
|
|
|
|
.push_maybe(self.show_notification.then_some(notification))
|
|
|
|
|
.into()
|
2025-04-05 19:27:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn subscription(&self, program: &P) -> Subscription<Message<P>> {
|
2025-04-05 20:08:54 +02:00
|
|
|
let subscription =
|
|
|
|
|
program.subscription(&self.state).map(Message::Program);
|
|
|
|
|
|
|
|
|
|
let hotkeys =
|
|
|
|
|
futures::keyboard::on_key_press(|key, _modifiers| match key {
|
|
|
|
|
keyboard::Key::Named(keyboard::key::Named::F12) => {
|
|
|
|
|
Some(Message::ToggleComet)
|
|
|
|
|
}
|
|
|
|
|
_ => None,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Subscription::batch([subscription, hotkeys])
|
2025-04-05 19:27:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<P>
|
|
|
|
|
where
|
|
|
|
|
P: Program,
|
|
|
|
|
{
|
2025-04-05 20:08:54 +02:00
|
|
|
HideNotification,
|
|
|
|
|
ToggleComet,
|
2025-04-05 19:27:15 +02:00
|
|
|
Program(P::Message),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<P> fmt::Debug for Message<P>
|
|
|
|
|
where
|
|
|
|
|
P: Program,
|
|
|
|
|
{
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
match self {
|
2025-04-05 20:08:54 +02:00
|
|
|
Message::HideNotification => {
|
|
|
|
|
f.write_str("DevTools(HideNotification)")
|
|
|
|
|
}
|
|
|
|
|
Message::ToggleComet => f.write_str("DevTools(ToggleComet)"),
|
2025-04-05 19:27:15 +02:00
|
|
|
Message::Program(message) => message.fmt(f),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|