1use crate::conversion;
2use crate::core::alignment;
3use crate::core::input_method;
4use crate::core::mouse;
5use crate::core::renderer;
6use crate::core::text;
7use crate::core::theme;
8use crate::core::time::Instant;
9use crate::core::window::{Id, RedrawRequest};
10use crate::core::{
11 Color, InputMethod, Padding, Point, Rectangle, Size, Text, Vector,
12};
13use crate::graphics::Compositor;
14use crate::program::{Program, State};
15
16use winit::dpi::{LogicalPosition, LogicalSize};
17use winit::monitor::MonitorHandle;
18
19use std::collections::BTreeMap;
20use std::sync::Arc;
21
22#[allow(missing_debug_implementations)]
23pub struct WindowManager<P, C>
24where
25 P: Program,
26 C: Compositor<Renderer = P::Renderer>,
27 P::Theme: theme::Base,
28{
29 aliases: BTreeMap<winit::window::WindowId, Id>,
30 entries: BTreeMap<Id, Window<P, C>>,
31}
32
33impl<P, C> WindowManager<P, C>
34where
35 P: Program,
36 C: Compositor<Renderer = P::Renderer>,
37 P::Theme: theme::Base,
38{
39 pub fn new() -> Self {
40 Self {
41 aliases: BTreeMap::new(),
42 entries: BTreeMap::new(),
43 }
44 }
45
46 pub fn insert(
47 &mut self,
48 id: Id,
49 window: Arc<winit::window::Window>,
50 application: &P,
51 compositor: &mut C,
52 exit_on_close_request: bool,
53 ) -> &mut Window<P, C> {
54 let state = State::new(application, id, &window);
55 let viewport_version = state.viewport_version();
56 let physical_size = state.physical_size();
57 let surface = compositor.create_surface(
58 window.clone(),
59 physical_size.width,
60 physical_size.height,
61 );
62 let renderer = compositor.create_renderer();
63
64 let _ = self.aliases.insert(window.id(), id);
65
66 let _ = self.entries.insert(
67 id,
68 Window {
69 raw: window,
70 state,
71 viewport_version,
72 exit_on_close_request,
73 surface,
74 renderer,
75 mouse_interaction: mouse::Interaction::None,
76 redraw_at: None,
77 preedit: None,
78 ime_state: None,
79 },
80 );
81
82 self.entries
83 .get_mut(&id)
84 .expect("Get window that was just inserted")
85 }
86
87 pub fn is_empty(&self) -> bool {
88 self.entries.is_empty()
89 }
90
91 pub fn is_idle(&self) -> bool {
92 self.entries
93 .values()
94 .all(|window| window.redraw_at.is_none())
95 }
96
97 pub fn redraw_at(&self) -> Option<Instant> {
98 self.entries
99 .values()
100 .filter_map(|window| window.redraw_at)
101 .min()
102 }
103
104 pub fn first(&self) -> Option<&Window<P, C>> {
105 self.entries.first_key_value().map(|(_id, window)| window)
106 }
107
108 pub fn iter_mut(
109 &mut self,
110 ) -> impl Iterator<Item = (Id, &mut Window<P, C>)> {
111 self.entries.iter_mut().map(|(k, v)| (*k, v))
112 }
113
114 pub fn get(&self, id: Id) -> Option<&Window<P, C>> {
115 self.entries.get(&id)
116 }
117
118 pub fn get_mut(&mut self, id: Id) -> Option<&mut Window<P, C>> {
119 self.entries.get_mut(&id)
120 }
121
122 pub fn get_mut_alias(
123 &mut self,
124 id: winit::window::WindowId,
125 ) -> Option<(Id, &mut Window<P, C>)> {
126 let id = self.aliases.get(&id).copied()?;
127
128 Some((id, self.get_mut(id)?))
129 }
130
131 pub fn last_monitor(&self) -> Option<MonitorHandle> {
132 self.entries.values().last()?.raw.current_monitor()
133 }
134
135 pub fn remove(&mut self, id: Id) -> Option<Window<P, C>> {
136 let window = self.entries.remove(&id)?;
137 let _ = self.aliases.remove(&window.raw.id());
138
139 Some(window)
140 }
141}
142
143impl<P, C> Default for WindowManager<P, C>
144where
145 P: Program,
146 C: Compositor<Renderer = P::Renderer>,
147 P::Theme: theme::Base,
148{
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154#[allow(missing_debug_implementations)]
155pub struct Window<P, C>
156where
157 P: Program,
158 C: Compositor<Renderer = P::Renderer>,
159 P::Theme: theme::Base,
160{
161 pub raw: Arc<winit::window::Window>,
162 pub state: State<P>,
163 pub viewport_version: u64,
164 pub exit_on_close_request: bool,
165 pub mouse_interaction: mouse::Interaction,
166 pub surface: C::Surface,
167 pub renderer: P::Renderer,
168 pub redraw_at: Option<Instant>,
169 preedit: Option<Preedit<P::Renderer>>,
170 ime_state: Option<(Point, input_method::Purpose)>,
171}
172
173impl<P, C> Window<P, C>
174where
175 P: Program,
176 C: Compositor<Renderer = P::Renderer>,
177 P::Theme: theme::Base,
178{
179 pub fn position(&self) -> Option<Point> {
180 self.raw
181 .outer_position()
182 .ok()
183 .map(|position| position.to_logical(self.raw.scale_factor()))
184 .map(|position| Point {
185 x: position.x,
186 y: position.y,
187 })
188 }
189
190 pub fn size(&self) -> Size {
191 let size = self.raw.inner_size().to_logical(self.raw.scale_factor());
192
193 Size::new(size.width, size.height)
194 }
195
196 pub fn request_redraw(&mut self, redraw_request: RedrawRequest) {
197 match redraw_request {
198 RedrawRequest::NextFrame => {
199 self.raw.request_redraw();
200 self.redraw_at = None;
201 }
202 RedrawRequest::At(at) => {
203 self.redraw_at = Some(at);
204 }
205 RedrawRequest::Wait => {}
206 }
207 }
208
209 pub fn request_input_method(&mut self, input_method: InputMethod) {
210 match input_method {
211 InputMethod::Disabled => {
212 self.disable_ime();
213 }
214 InputMethod::Enabled {
215 position,
216 purpose,
217 preedit,
218 } => {
219 self.enable_ime(position, purpose);
220
221 if let Some(preedit) = preedit {
222 if preedit.content.is_empty() {
223 self.preedit = None;
224 } else {
225 let mut overlay =
226 self.preedit.take().unwrap_or_else(Preedit::new);
227
228 overlay.update(
229 position,
230 &preedit,
231 self.state.background_color(),
232 &self.renderer,
233 );
234
235 self.preedit = Some(overlay);
236 }
237 } else {
238 self.preedit = None;
239 }
240 }
241 }
242 }
243
244 pub fn draw_preedit(&mut self) {
245 if let Some(preedit) = &self.preedit {
246 preedit.draw(
247 &mut self.renderer,
248 self.state.text_color(),
249 self.state.background_color(),
250 &Rectangle::new(
251 Point::ORIGIN,
252 self.state.viewport().logical_size(),
253 ),
254 );
255 }
256 }
257
258 fn enable_ime(&mut self, position: Point, purpose: input_method::Purpose) {
259 if self.ime_state.is_none() {
260 self.raw.set_ime_allowed(true);
261 }
262
263 if self.ime_state != Some((position, purpose)) {
264 self.raw.set_ime_cursor_area(
265 LogicalPosition::new(position.x, position.y),
266 LogicalSize::new(10, 10), );
268 self.raw.set_ime_purpose(conversion::ime_purpose(purpose));
269
270 self.ime_state = Some((position, purpose));
271 }
272 }
273
274 fn disable_ime(&mut self) {
275 if self.ime_state.is_some() {
276 self.raw.set_ime_allowed(false);
277 self.ime_state = None;
278 }
279
280 self.preedit = None;
281 }
282}
283
284struct Preedit<Renderer>
285where
286 Renderer: text::Renderer,
287{
288 position: Point,
289 content: Renderer::Paragraph,
290 spans: Vec<text::Span<'static, (), Renderer::Font>>,
291}
292
293impl<Renderer> Preedit<Renderer>
294where
295 Renderer: text::Renderer,
296{
297 fn new() -> Self {
298 Self {
299 position: Point::ORIGIN,
300 spans: Vec::new(),
301 content: Renderer::Paragraph::default(),
302 }
303 }
304
305 fn update(
306 &mut self,
307 position: Point,
308 preedit: &input_method::Preedit,
309 background: Color,
310 renderer: &Renderer,
311 ) {
312 self.position = position;
313
314 let spans = match &preedit.selection {
315 Some(selection) => {
316 vec![
317 text::Span::new(&preedit.content[..selection.start]),
318 text::Span::new(if selection.start == selection.end {
319 "\u{200A}"
320 } else {
321 &preedit.content[selection.start..selection.end]
322 })
323 .color(background),
324 text::Span::new(&preedit.content[selection.end..]),
325 ]
326 }
327 _ => vec![text::Span::new(&preedit.content)],
328 };
329
330 if spans != self.spans.as_slice() {
331 use text::Paragraph as _;
332
333 self.content = Renderer::Paragraph::with_spans(Text {
334 content: &spans,
335 bounds: Size::INFINITY,
336 size: preedit
337 .text_size
338 .unwrap_or_else(|| renderer.default_size()),
339 line_height: text::LineHeight::default(),
340 font: renderer.default_font(),
341 align_x: text::Alignment::Default,
342 align_y: alignment::Vertical::Top,
343 shaping: text::Shaping::Advanced,
344 wrapping: text::Wrapping::None,
345 });
346
347 self.spans.clear();
348 self.spans
349 .extend(spans.into_iter().map(text::Span::to_static));
350 }
351 }
352
353 fn draw(
354 &self,
355 renderer: &mut Renderer,
356 color: Color,
357 background: Color,
358 viewport: &Rectangle,
359 ) {
360 use text::Paragraph as _;
361
362 if self.content.min_width() < 1.0 {
363 return;
364 }
365
366 let mut bounds = Rectangle::new(
367 self.position - Vector::new(0.0, self.content.min_height()),
368 self.content.min_bounds(),
369 );
370
371 bounds.x = bounds
372 .x
373 .max(viewport.x)
374 .min(viewport.x + viewport.width - bounds.width);
375
376 bounds.y = bounds
377 .y
378 .max(viewport.y)
379 .min(viewport.y + viewport.height - bounds.height);
380
381 renderer.with_layer(bounds, |renderer| {
382 renderer.fill_quad(
383 renderer::Quad {
384 bounds,
385 ..Default::default()
386 },
387 background,
388 );
389
390 renderer.fill_paragraph(
391 &self.content,
392 bounds.position(),
393 color,
394 bounds,
395 );
396
397 const UNDERLINE: f32 = 2.0;
398
399 renderer.fill_quad(
400 renderer::Quad {
401 bounds: bounds.shrink(Padding {
402 top: bounds.height - UNDERLINE,
403 ..Default::default()
404 }),
405 ..Default::default()
406 },
407 color,
408 );
409
410 for span_bounds in self.content.span_bounds(1) {
411 renderer.fill_quad(
412 renderer::Quad {
413 bounds: span_bounds
414 + (bounds.position() - Point::ORIGIN),
415 ..Default::default()
416 },
417 color,
418 );
419 }
420 });
421 }
422}