/* * Copyright (C) 2022 Umut İnan Erdoğan * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use raw_window_handle::HasRawWindowHandle; use winit::{ event_loop::EventLoopWindowTarget, window::{Window as WinitWindow, WindowBuilder as WinitWindowBuilder}, }; use winit::dpi::PhysicalSize; use winit::error::OsError; use winit::window::WindowId; use abletk_common::color::Color; use abletk_common::Renderer; use crate::context::Context; use crate::event::window::Event; use crate::widget::Widget; pub struct Window { window: WinitWindow, events: HashMap>, root: Box, ctx: Rc>, renderer: Renderer, background: Color, } impl Window { pub fn new>( ctx: Rc>, root: Box, event_loop: &EventLoopWindowTarget<()>, events: HashMap>, always_on_top: bool, background: Color, decorations: bool, maximized: bool, resizable: bool, title: S, transparent: bool, visible: bool, ) -> Result { let window = WinitWindowBuilder::new() .with_always_on_top(always_on_top) .with_decorations(decorations) .with_maximized(maximized) .with_resizable(resizable) .with_title(title) .with_transparent(transparent) .with_visible(visible) .build(event_loop)?; Ok(Self { renderer: Renderer::new(window.raw_window_handle()), window, background, events, root, ctx, }) } pub fn builder(root: W) -> WindowBuilder { WindowBuilder::new(root) } pub fn render(&mut self) { let position = self.root.position(); eprintln!("Rendering window with id {:?}", self.id()); eprintln!("Root widget position: {position:?}"); self.renderer.begin_draw(); self.renderer.clear(self.background); self.renderer.position_at(position.x(), position.y()); self.root.draw(&mut self.renderer); self.renderer.end_draw() } pub fn set_always_on_top(&self, value: bool) { self.window.set_always_on_top(value) } pub fn focus(&self) { self.window.focus_window() } pub fn redraw(&self) { self.window.request_redraw() } pub fn set_title + Into>(&mut self, title: S) { self.window.set_title(title.as_ref()) } pub(crate) fn emit_event(&mut self, event: Event) { if let Some(handlers) = self.events.get(&event) { let ctx = self.ctx.clone(); let ctx = &mut ctx.borrow_mut(); handlers.clone().iter().for_each(|handler| handler(ctx, self)) } } pub(crate) fn id(&self) -> WindowId { self.window.id() } pub(crate) fn resized(&mut self, size: PhysicalSize) { self.renderer.resized(size.width, size.height) } } pub struct WindowBuilder { events: HashMap>, always_on_top: bool, background: Option, decorations: bool, maximized: bool, resizable: bool, title: Option, transparent: bool, visible: bool, root: Box, } impl WindowBuilder { pub fn new(root: W) -> Self { Self { events: Default::default(), always_on_top: false, background: None, decorations: true, maximized: false, resizable: true, title: None, transparent: false, visible: true, root: Box::new(root), } } pub fn on_event(mut self, event: Event, handler: fn(&mut Context, &mut Window), ) -> Self { if let Some(handlers) = self.events.get_mut(&event) { handlers.push(handler); } else { self.events.insert(event, vec![handler]); } self } pub fn always_on_top(mut self, value: bool) -> Self { self.always_on_top = value; self } pub fn background(mut self, value: Color) -> Self { self.background = Some(value); self } pub fn decorations(mut self, value: bool) -> Self { self.decorations = value; self } pub fn maximized(mut self, value: bool) -> Self { self.maximized = value; self } pub fn resizable(mut self, value: bool) -> Self { self.resizable = value; self } pub fn title>(mut self, value: S) -> Self { self.title = Some(value.into()); self } pub fn transparent(mut self, value: bool) -> Self { self.transparent = value; self } pub fn visible(mut self, value: bool) -> Self { self.visible = value; self } pub fn build(self, ctx: Rc>, event_loop: &EventLoopWindowTarget<()> ) -> Result { Window::new( ctx, self.root, event_loop, self.events, // todo: make this the application name self.always_on_top, self.background.unwrap_or(Color(1.0, 1.0, 1.0, 1.0)), self.decorations, self.maximized, self.resizable, self.title.unwrap_or("AbleTK Window".into()), self.transparent, self.visible, ) } }