277 lines
7.8 KiB
Rust
Executable File
277 lines
7.8 KiB
Rust
Executable File
/*
|
|
* Copyright (C) 2022 Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
|
*
|
|
* 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}, dpi::PhysicalPosition, event::MouseButton,
|
|
};
|
|
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<Event, Vec<fn(&mut Context, &mut Window)>>,
|
|
root: Option<Box<dyn Widget>>,
|
|
ctx: Rc<RefCell<Context>>,
|
|
renderer: Option<Renderer>,
|
|
background: Color,
|
|
cursor_pos: (u32, u32),
|
|
}
|
|
|
|
impl Window {
|
|
pub fn new<S: Into<String>>(
|
|
ctx: Rc<RefCell<Context>>,
|
|
root: Box<dyn Widget>,
|
|
event_loop: &EventLoopWindowTarget<()>,
|
|
events: HashMap<Event, Vec<fn(&mut Context, &mut Window)>>,
|
|
always_on_top: bool,
|
|
background: Color,
|
|
decorations: bool,
|
|
maximized: bool,
|
|
resizable: bool,
|
|
title: S,
|
|
transparent: bool,
|
|
visible: bool,
|
|
) -> Result<Self, OsError> {
|
|
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: Some(Renderer::new(window.raw_window_handle())),
|
|
window,
|
|
background,
|
|
events,
|
|
root: Some(root),
|
|
ctx,
|
|
cursor_pos: (0, 0),
|
|
})
|
|
}
|
|
|
|
pub fn builder<W: Widget + 'static>(root: W) -> WindowBuilder {
|
|
WindowBuilder::new(root)
|
|
}
|
|
|
|
pub(crate) fn render(&mut self) {
|
|
let root = self.root.as_ref().unwrap();
|
|
let renderer = self.renderer.as_mut().unwrap();
|
|
let position = root.position(renderer);
|
|
renderer.begin_draw();
|
|
renderer.clear(self.background);
|
|
renderer.position_at(position.x(), position.y());
|
|
root.draw(renderer);
|
|
renderer.end_draw()
|
|
}
|
|
|
|
pub fn focus(&self) {
|
|
self.window.focus_window()
|
|
}
|
|
|
|
pub fn redraw(&self) {
|
|
self.window.request_redraw()
|
|
}
|
|
|
|
pub fn set_always_on_top(&self, value: bool) {
|
|
self.window.set_always_on_top(value)
|
|
}
|
|
|
|
pub fn set_title<S: AsRef<str> + Into<String>>(&mut self, title: S) {
|
|
self.window.set_title(title.as_ref())
|
|
}
|
|
|
|
pub fn size(&self) -> (u32, u32) {
|
|
let size = self.window.inner_size();
|
|
(size.width, size.height)
|
|
}
|
|
|
|
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 cursor_moved(&mut self, pos: PhysicalPosition<f64>) {
|
|
self.cursor_pos = (pos.x as u32, pos.y as u32)
|
|
}
|
|
|
|
pub(crate) fn id(&self) -> WindowId {
|
|
self.window.id()
|
|
}
|
|
|
|
pub(crate) fn mouse_pressed(&mut self, button: MouseButton) {
|
|
if self.root
|
|
.as_ref()
|
|
.unwrap()
|
|
.position(self.renderer.as_mut().unwrap())
|
|
.contains(self.cursor_pos.0, self.cursor_pos.1)
|
|
{
|
|
let mut root = self.root.take().unwrap();
|
|
let mut renderer = self.renderer.take().unwrap();
|
|
root.mouse_pressed(
|
|
&mut renderer,
|
|
self,
|
|
button,
|
|
self.cursor_pos.0,
|
|
self.cursor_pos.1,
|
|
);
|
|
|
|
self.root = Some(root);
|
|
self.renderer = Some(renderer);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn mouse_released(&mut self, button: MouseButton) {
|
|
if self.root
|
|
.as_ref()
|
|
.unwrap()
|
|
.position(self.renderer.as_mut().unwrap())
|
|
.contains(self.cursor_pos.0, self.cursor_pos.1)
|
|
{
|
|
let mut root = self.root.take().unwrap();
|
|
let mut renderer = self.renderer.take().unwrap();
|
|
root.mouse_released(
|
|
&mut renderer,
|
|
self,
|
|
button,
|
|
self.cursor_pos.0,
|
|
self.cursor_pos.1,
|
|
);
|
|
|
|
self.root = Some(root);
|
|
self.renderer = Some(renderer);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn resized(&mut self, size: PhysicalSize<u32>) {
|
|
self.renderer.as_mut().unwrap().resized(size.width, size.height);
|
|
self.emit_event(Event::Resized)
|
|
}
|
|
}
|
|
|
|
pub struct WindowBuilder {
|
|
events: HashMap<Event, Vec<fn(&mut Context, &mut Window)>>,
|
|
always_on_top: bool,
|
|
background: Option<Color>,
|
|
decorations: bool,
|
|
maximized: bool,
|
|
resizable: bool,
|
|
title: Option<String>,
|
|
transparent: bool,
|
|
visible: bool,
|
|
root: Box<dyn Widget>,
|
|
}
|
|
|
|
impl WindowBuilder {
|
|
pub fn new<W: Widget + 'static>(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<S: Into<String>>(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<RefCell<Context>>,
|
|
event_loop: &EventLoopWindowTarget<()>
|
|
) -> Result<Window, OsError> {
|
|
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,
|
|
)
|
|
}
|
|
}
|