nifty_runtime/reactive/
windows.rs1use std::rc::Rc;
5
6use super::{Engine, Logic, Mounted, WindowAction, WindowRequest, WindowTemplate};
7use crate::backend::Backend;
8use crate::geometry::Size;
9use crate::ui::{Node, WindowCloseHandler, WindowId};
10
11pub(super) struct MountedWindow<L: Logic> {
12 pub(super) window: WindowId,
13 pub(super) declared_size: Size,
15 pub(super) content: Node,
16 pub(super) children: Vec<Mounted<L>>,
17}
18
19#[derive(Clone, Copy)]
21pub(super) struct ReusedWindow {
22 window: WindowId,
23 declared_size: Size,
24}
25
26impl ReusedWindow {
27 pub(super) fn of<L: Logic>(mounted: &MountedWindow<L>) -> Self {
28 Self {
29 window: mounted.window,
30 declared_size: mounted.declared_size,
31 }
32 }
33
34 pub(super) fn window(self) -> WindowId {
35 self.window
36 }
37}
38
39pub(super) struct PreparedWindow<L: Logic> {
41 index: usize,
42 mounted: MountedWindow<L>,
43 reused: Option<ReusedWindow>,
44}
45
46impl<L: Logic, B: Backend> Engine<L, B> {
47 pub(super) fn windows_to_open(&self, reused: &[Option<ReusedWindow>]) -> Vec<bool> {
51 let starts_open = |template: &WindowTemplate<L>| template.starts_open;
52 let opening: Vec<bool> = self
53 .templates
54 .iter()
55 .enumerate()
56 .map(|(index, template)| {
57 reused
58 .get(index)
59 .map_or_else(|| starts_open(template), Option::is_some)
60 })
61 .collect();
62 if opening.contains(&true) {
63 opening
64 } else {
65 self.templates.iter().map(starts_open).collect()
66 }
67 }
68
69 pub(super) fn prepare_window(
72 &self,
73 index: usize,
74 reused: Option<ReusedWindow>,
75 ) -> Result<PreparedWindow<L>, L::Error> {
76 let template = &self.templates[index];
77 let window = match reused {
78 Some(reused) => reused.window,
79 None => self.ui.create_window(&template.title, template.size),
80 };
81 match self.mount_window(window, template) {
82 Ok(mounted) => Ok(PreparedWindow {
83 index,
84 mounted,
85 reused,
86 }),
87 Err(error) => {
88 if reused.is_none() {
89 self.ui.close(window);
90 }
91 Err(error)
92 }
93 }
94 }
95
96 pub(super) fn show_prepared(&self, prepared: PreparedWindow<L>) {
98 let PreparedWindow {
99 index,
100 mounted,
101 reused,
102 } = prepared;
103 let template = &self.templates[index];
104 self.ui.set_backdrop(mounted.window, template.backdrop);
105 self.ui.set_content(mounted.window, mounted.content);
106 match reused {
107 Some(reused) => {
108 self.ui.set_title(mounted.window, &template.title);
109 if reused.declared_size != mounted.declared_size {
110 self.ui.set_size(mounted.window, mounted.declared_size);
111 }
112 }
113 None => self.ui.show(mounted.window),
114 }
115 self.windows.borrow_mut()[index] = Some(mounted);
116 }
117
118 pub(super) fn discard(&self, prepared: Vec<PreparedWindow<L>>) {
120 for PreparedWindow {
121 mounted, reused, ..
122 } in prepared
123 {
124 self.ui.remove(mounted.content);
125 if reused.is_none() {
126 self.ui.close(mounted.window);
127 }
128 }
129 }
130
131 fn mount_window(
133 &self,
134 window: WindowId,
135 template: &WindowTemplate<L>,
136 ) -> Result<MountedWindow<L>, L::Error> {
137 let content = self.ui.content(window, &template.style);
138 match self.mount_list(&template.children, &template.frame, content, &mut 0) {
139 Ok(children) => Ok(MountedWindow {
140 window,
141 declared_size: template.size,
142 content,
143 children,
144 }),
145 Err(error) => {
146 self.ui.remove(content);
147 Err(error)
148 }
149 }
150 }
151
152 pub(super) fn apply_window_requests(&self, requests: &[WindowRequest]) -> Result<(), L::Error> {
156 let mut decided: Vec<WindowRequest> = Vec::new();
157 for request in requests {
158 match decided
159 .iter_mut()
160 .find(|decided| decided.window == request.window)
161 {
162 Some(decided) => decided.action = request.action,
163 None => decided.push(*request),
164 }
165 }
166 for request in &decided {
167 if request.action == WindowAction::Open {
168 self.open_window(request.window)?;
169 }
170 }
171 for request in &decided {
172 if request.action == WindowAction::Close {
173 self.close_window(request.window);
174 }
175 }
176 Ok(())
177 }
178
179 fn open_window(&self, index: usize) -> Result<(), L::Error> {
181 let open = self.windows.borrow()[index]
182 .as_ref()
183 .map(|mounted| mounted.window);
184 if let Some(window) = open {
185 self.ui.focus(window);
186 return Ok(());
187 }
188 let prepared = self.prepare_window(index, None)?;
189 self.show_prepared(prepared);
190 Ok(())
191 }
192
193 fn close_window(&self, index: usize) {
195 let closed = self.windows.borrow_mut()[index].take();
196 if let Some(mounted) = closed {
197 self.ui.close(mounted.window);
198 }
199 }
200
201 pub(super) fn on_close(&self) -> WindowCloseHandler {
203 let engine = self.this.clone();
204 Rc::new(move |window| {
205 let Some(engine) = engine.upgrade() else {
206 return;
207 };
208 let index = engine.windows.borrow().iter().position(|mounted| {
209 mounted
210 .as_ref()
211 .is_some_and(|mounted| mounted.window == window)
212 });
213 match index {
214 Some(index) => engine.close_window(index),
215 None => engine.ui.close(window),
216 }
217 })
218 }
219}