1use dyn_clone::DynClone;
18use iced::{
19 window::{self, Id},
20 Element, Task,
21};
22use std::{any::type_name, collections::HashMap};
23
24#[allow(private_bounds)]
25pub trait Window<App, Theme, Message, Renderer = iced::Renderer>:
26 Send + std::fmt::Debug + DynClone
27{
28 fn view<'a>(&'a self, app: &'a App) -> iced::Element<'a, Message, Theme, Renderer>;
29 fn title(&self, app: &App) -> String;
30 fn theme(&self, app: &App) -> Theme;
31 fn settings(&self) -> window::Settings;
32 fn id(&self) -> String {
34 let data = format!("{:?}", self);
35 let data = if let Some(i) = data.find(" {") {
36 data[i..].to_string()
37 } else {
38 format!("::{}", data)
39 };
40
41 format!("{}{}", type_name::<Self>(), data)
42 }
43 fn class(&self) -> &'static str {
45 type_name::<Self>()
46 }
47}
48
49dyn_clone::clone_trait_object!(<App, Theme, Message, Renderer> Window<App, Theme, Message, Renderer>);
50
51impl<App, Theme, Message, Renderer, T: Window<App, Theme, Message, Renderer>> PartialEq<T>
52 for Box<dyn Window<App, Theme, Message, Renderer>>
53{
54 fn eq(&self, other: &T) -> bool {
55 self.id() == other.id()
56 }
57}
58
59impl<App, Theme, Message, Renderer> Window<App, Theme, Message, Renderer>
60 for Box<dyn Window<App, Theme, Message, Renderer>>
61{
62 fn view<'a>(&'a self, app: &'a App) -> iced::Element<'a, Message, Theme, Renderer> {
63 self.as_ref().view(app)
64 }
65
66 fn title(&self, app: &App) -> String {
67 self.as_ref().title(app)
68 }
69
70 fn theme(&self, app: &App) -> Theme {
71 self.as_ref().theme(app)
72 }
73
74 fn settings(&self) -> window::Settings {
75 self.as_ref().settings()
76 }
77
78 fn id(&self) -> String {
79 self.as_ref().id()
80 }
81
82 fn class(&self) -> &'static str {
83 self.as_ref().class()
84 }
85}
86
87pub struct WindowManager<App, Theme, Message, Renderer = iced::Renderer> {
88 windows: HashMap<Id, Box<dyn Window<App, Theme, Message, Renderer>>>,
89}
90
91impl<App, Theme, Message, Renderer> WindowManager<App, Theme, Message, Renderer> {
92 fn get(&self, id: Id) -> &dyn Window<App, Theme, Message, Renderer> {
94 self.windows
95 .get(&id)
96 .expect("No window found with given Id")
97 .as_ref()
98 }
99
100 pub fn view<'a>(&'a self, app: &'a App, id: Id) -> Element<'a, Message, Theme, Renderer> {
101 self.get(id).view(app)
102 }
103
104 pub fn title(&self, app: &App, id: Id) -> String {
105 self.get(id).title(app)
106 }
107
108 pub fn theme(&self, app: &App, id: Id) -> Theme {
109 self.get(id).theme(app)
110 }
111
112 pub fn open(
113 &mut self,
114 window: Box<dyn Window<App, Theme, Message, Renderer>>,
115 ) -> (Id, Task<Id>) {
116 let (id, task) = window::open(window.settings());
117 self.windows.insert(id, window);
118 (id, task)
119 }
120
121 pub fn close_all(&mut self) -> Task<Id> {
122 let mut tasks = Vec::new();
123 for id in self.windows.keys() {
124 tasks.push(window::close(*id));
125 }
126 Task::batch(tasks)
127 }
128
129 pub fn close_all_of(
130 &mut self,
131 window: Box<dyn Window<App, Theme, Message, Renderer>>,
132 ) -> Task<Id> {
133 let mut tasks = Vec::new();
134 for (id, w) in self.windows.iter() {
135 if *w == window {
136 tasks.push(window::close(*id));
137 }
138 }
139
140 Task::batch(tasks)
141 }
142
143 pub fn any_of(&self, window: &impl Window<App, Theme, Message, Renderer>) -> bool {
145 self.windows.values().any(|w| w == window)
146 }
147
148 pub fn was_closed(&mut self, id: Id) {
150 self.windows.remove(&id);
151 }
152
153 #[allow(clippy::type_complexity)]
155 pub fn instances_of(
156 &self,
157 window: &impl Window<App, Theme, Message, Renderer>,
158 ) -> Vec<(&Id, &Box<dyn Window<App, Theme, Message, Renderer>>)> {
159 self.windows.iter().filter(|(_, w)| *w == window).collect()
160 }
161
162 pub fn empty(&self) -> bool {
163 self.windows.is_empty()
164 }
165}
166
167impl<App, Theme, Message, Renderer> Default for WindowManager<App, Theme, Message, Renderer> {
168 fn default() -> Self {
169 Self {
170 windows: HashMap::new(),
171 }
172 }
173}