use crate::conversion; use crate::core::alignment; use crate::core::mouse; use crate::core::renderer; use crate::core::text; use crate::core::theme; use crate::core::time::Instant; use crate::core::window::{Id, RedrawRequest}; use crate::core::{ Color, InputMethod, Padding, Point, Rectangle, Size, Text, Vector, }; use crate::graphics::Compositor; use crate::program::{Program, State}; use std::collections::BTreeMap; use std::sync::Arc; use winit::dpi::{LogicalPosition, LogicalSize}; use winit::monitor::MonitorHandle; #[allow(missing_debug_implementations)] pub struct WindowManager where P: Program, C: Compositor, P::Theme: theme::Base, { aliases: BTreeMap, entries: BTreeMap>, } impl WindowManager where P: Program, C: Compositor, P::Theme: theme::Base, { pub fn new() -> Self { Self { aliases: BTreeMap::new(), entries: BTreeMap::new(), } } pub fn insert( &mut self, id: Id, window: Arc, application: &P, compositor: &mut C, exit_on_close_request: bool, ) -> &mut Window { let state = State::new(application, id, &window); let viewport_version = state.viewport_version(); let physical_size = state.physical_size(); let surface = compositor.create_surface( window.clone(), physical_size.width, physical_size.height, ); let renderer = compositor.create_renderer(); let _ = self.aliases.insert(window.id(), id); let _ = self.entries.insert( id, Window { raw: window, state, viewport_version, exit_on_close_request, surface, renderer, mouse_interaction: mouse::Interaction::None, redraw_at: None, preedit: None, }, ); self.entries .get_mut(&id) .expect("Get window that was just inserted") } pub fn is_empty(&self) -> bool { self.entries.is_empty() } pub fn is_idle(&self) -> bool { self.entries .values() .all(|window| window.redraw_at.is_none()) } pub fn redraw_at(&self) -> Option { self.entries .values() .filter_map(|window| window.redraw_at) .min() } pub fn first(&self) -> Option<&Window> { self.entries.first_key_value().map(|(_id, window)| window) } pub fn iter_mut( &mut self, ) -> impl Iterator)> { self.entries.iter_mut().map(|(k, v)| (*k, v)) } pub fn get(&self, id: Id) -> Option<&Window> { self.entries.get(&id) } pub fn get_mut(&mut self, id: Id) -> Option<&mut Window> { self.entries.get_mut(&id) } pub fn get_mut_alias( &mut self, id: winit::window::WindowId, ) -> Option<(Id, &mut Window)> { let id = self.aliases.get(&id).copied()?; Some((id, self.get_mut(id)?)) } pub fn last_monitor(&self) -> Option { self.entries.values().last()?.raw.current_monitor() } pub fn remove(&mut self, id: Id) -> Option> { let window = self.entries.remove(&id)?; let _ = self.aliases.remove(&window.raw.id()); Some(window) } } impl Default for WindowManager where P: Program, C: Compositor, P::Theme: theme::Base, { fn default() -> Self { Self::new() } } #[allow(missing_debug_implementations)] pub struct Window where P: Program, C: Compositor, P::Theme: theme::Base, { pub raw: Arc, pub state: State

, pub viewport_version: u64, pub exit_on_close_request: bool, pub mouse_interaction: mouse::Interaction, pub surface: C::Surface, pub renderer: P::Renderer, pub redraw_at: Option, preedit: Option>, } impl Window where P: Program, C: Compositor, P::Theme: theme::Base, { pub fn position(&self) -> Option { self.raw .outer_position() .ok() .map(|position| position.to_logical(self.raw.scale_factor())) .map(|position| Point { x: position.x, y: position.y, }) } pub fn size(&self) -> Size { let size = self.raw.inner_size().to_logical(self.raw.scale_factor()); Size::new(size.width, size.height) } pub fn request_redraw(&mut self, redraw_request: RedrawRequest) { match redraw_request { RedrawRequest::NextFrame => { self.raw.request_redraw(); self.redraw_at = None; } RedrawRequest::At(at) => { self.redraw_at = Some(at); } RedrawRequest::Wait => {} } } pub fn request_input_method(&mut self, input_method: InputMethod) { self.raw.set_ime_allowed(match input_method { InputMethod::Disabled => false, InputMethod::Allowed | InputMethod::Open { .. } => true, }); if let InputMethod::Open { position, purpose, preedit, } = input_method { self.raw.set_ime_cursor_area( LogicalPosition::new(position.x, position.y), LogicalSize::new(10, 10), ); self.raw.set_ime_purpose(conversion::ime_purpose(purpose)); if let Some(content) = preedit { if let Some(preedit) = &mut self.preedit { preedit.update(&content, &self.renderer); } else { let mut preedit = Preedit::new(); preedit.update(&content, &self.renderer); self.preedit = Some(preedit); } } } else { self.preedit = None; } } pub fn draw_preedit(&mut self) { if let Some(preedit) = &self.preedit { preedit.draw( &mut self.renderer, self.state.text_color(), self.state.background_color(), ); } } } struct Preedit where Renderer: text::Renderer, { position: Point, content: text::paragraph::Plain, } impl Preedit where Renderer: text::Renderer, { fn new() -> Self { Self { position: Point::ORIGIN, content: text::paragraph::Plain::default(), } } fn update(&mut self, text: &str, renderer: &Renderer) { self.content.update(Text { content: text, bounds: Size::INFINITY, size: renderer.default_size(), line_height: text::LineHeight::default(), font: renderer.default_font(), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, //Bottom, shaping: text::Shaping::Advanced, wrapping: text::Wrapping::None, }); } fn draw(&self, renderer: &mut Renderer, color: Color, background: Color) { if self.content.min_width() < 1.0 { return; } let top_left = self.position - Vector::new(0.0, self.content.min_height()); let bounds = Rectangle::new(top_left, self.content.min_bounds()); renderer.with_layer(bounds, |renderer| { renderer.fill_quad( renderer::Quad { bounds, ..Default::default() }, background, ); renderer.fill_paragraph( self.content.raw(), top_left, color, bounds, ); const UNDERLINE: f32 = 2.0; renderer.fill_quad( renderer::Quad { bounds: bounds.shrink(Padding { top: bounds.height - UNDERLINE, ..Default::default() }), ..Default::default() }, color, ); }); } }