use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use kludgine_core::figures::num_traits::One;
use kludgine_core::figures::{Pixels, Point, Points};
use kludgine_core::flume;
use kludgine_core::math::{Scale, Scaled, Size};
use kludgine_core::scene::Target;
use kludgine_core::winit::window::{Theme, WindowBuilder as WinitWindowBuilder, WindowId};
use kludgine_core::winit::{self};
use lazy_static::lazy_static;
use crate::{Error, Runtime};
pub mod event;
mod open;
mod runtime_window;
pub use open::{OpenWindow, RedrawRequester, RedrawStatus};
pub use runtime_window::{opened_first_window, RuntimeWindow, RuntimeWindowConfig, WindowHandle};
pub use winit::window::Icon;
use self::event::InputEvent;
pub enum CloseResponse {
RemainOpen,
Close,
}
pub trait Window: Send + Sync + 'static {
fn initialize(
&mut self,
_scene: &Target,
_redrawer: RedrawRequester,
_window: WindowHandle,
) -> crate::Result<()>
where
Self: Sized,
{
Ok(())
}
fn close_requested(&mut self, _window: WindowHandle) -> crate::Result<CloseResponse> {
Ok(CloseResponse::Close)
}
fn process_input(
&mut self,
_input: InputEvent,
_status: &mut RedrawStatus,
_scene: &Target,
_window: WindowHandle,
) -> crate::Result<()>
where
Self: Sized,
{
Ok(())
}
fn receive_character(
&mut self,
_character: char,
_status: &mut RedrawStatus,
_scene: &Target,
_window: WindowHandle,
) -> crate::Result<()>
where
Self: Sized,
{
Ok(())
}
fn target_fps(&self) -> Option<u16> {
None
}
#[allow(unused_variables)]
fn render(
&mut self,
scene: &Target,
status: &mut RedrawStatus,
_window: WindowHandle,
) -> crate::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn update(
&mut self,
scene: &Target,
status: &mut RedrawStatus,
_window: WindowHandle,
) -> crate::Result<()>
where
Self: Sized,
{
Ok(())
}
fn additional_scale(&self) -> Scale<f32, Scaled, Points> {
Scale::one()
}
}
pub trait WindowCreator: Window {
#[must_use]
fn get_window_builder(&self) -> WindowBuilder {
let mut builder = WindowBuilder::default()
.with_title(self.window_title())
.with_initial_system_theme(self.initial_system_theme())
.with_size(self.initial_size())
.with_resizable(self.resizable())
.with_maximized(self.maximized())
.with_visible(self.visible())
.with_transparent(self.transparent())
.with_decorations(self.decorations())
.with_always_on_top(self.always_on_top());
if let Some(position) = self.initial_position() {
builder = builder.with_position(position);
}
builder
}
#[must_use]
fn window_title(&self) -> String {
"Kludgine".to_owned()
}
#[must_use]
fn initial_position(&self) -> Option<Point<i32, Pixels>> {
None
}
#[must_use]
fn initial_size(&self) -> Size<u32, Points> {
Size::new(1024, 768)
}
#[must_use]
fn resizable(&self) -> bool {
true
}
#[must_use]
fn maximized(&self) -> bool {
false
}
#[must_use]
fn visible(&self) -> bool {
true
}
#[must_use]
fn transparent(&self) -> bool {
false
}
#[must_use]
fn decorations(&self) -> bool {
true
}
#[must_use]
fn always_on_top(&self) -> bool {
false
}
#[must_use]
fn initial_system_theme(&self) -> Theme {
Theme::Light
}
}
#[derive(Default)]
pub struct WindowBuilder {
title: Option<String>,
position: Option<Point<i32, Pixels>>,
size: Option<Size<u32, Points>>,
resizable: Option<bool>,
maximized: Option<bool>,
visible: Option<bool>,
transparent: Option<bool>,
decorations: Option<bool>,
always_on_top: Option<bool>,
pub(crate) initial_system_theme: Option<Theme>,
icon: Option<winit::window::Icon>,
}
impl WindowBuilder {
#[must_use]
pub fn with_title<T: Into<String>>(mut self, title: T) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub const fn with_position(mut self, position: Point<i32, Pixels>) -> Self {
self.position = Some(position);
self
}
#[must_use]
pub const fn with_size(mut self, size: Size<u32, Points>) -> Self {
self.size = Some(size);
self
}
#[must_use]
pub const fn with_resizable(mut self, resizable: bool) -> Self {
self.resizable = Some(resizable);
self
}
#[must_use]
pub const fn with_maximized(mut self, maximized: bool) -> Self {
self.maximized = Some(maximized);
self
}
#[must_use]
pub const fn with_visible(mut self, visible: bool) -> Self {
self.visible = Some(visible);
self
}
#[must_use]
pub const fn with_transparent(mut self, transparent: bool) -> Self {
self.transparent = Some(transparent);
self
}
#[must_use]
pub const fn with_decorations(mut self, decorations: bool) -> Self {
self.decorations = Some(decorations);
self
}
#[must_use]
pub const fn with_always_on_top(mut self, always_on_top: bool) -> Self {
self.always_on_top = Some(always_on_top);
self
}
#[must_use]
#[allow(clippy::missing_const_for_fn)] pub fn with_icon(mut self, icon: Icon) -> Self {
self.icon = Some(icon);
self
}
#[must_use]
pub const fn with_initial_system_theme(mut self, system_theme: Theme) -> Self {
self.initial_system_theme = Some(system_theme);
self
}
}
impl From<WindowBuilder> for WinitWindowBuilder {
fn from(wb: WindowBuilder) -> Self {
let mut builder = Self::new();
if let Some(title) = wb.title {
builder = builder.with_title(title);
}
if let Some(position) = wb.position {
builder = builder.with_position(winit::dpi::Position::Physical(
winit::dpi::PhysicalPosition {
x: position.x,
y: position.y,
},
));
}
if let Some(size) = wb.size {
builder = builder.with_inner_size(winit::dpi::Size::Logical(winit::dpi::LogicalSize {
width: f64::from(size.width),
height: f64::from(size.height),
}));
}
if let Some(resizable) = wb.resizable {
builder = builder.with_resizable(resizable);
}
if let Some(maximized) = wb.maximized {
builder = builder.with_maximized(maximized);
}
if let Some(visible) = wb.visible {
builder = builder.with_visible(visible);
}
if let Some(transparent) = wb.transparent {
builder = builder.with_transparent(transparent);
}
if let Some(decorations) = wb.decorations {
builder = builder.with_decorations(decorations);
}
if let Some(always_on_top) = wb.always_on_top {
builder = builder.with_always_on_top(always_on_top);
}
builder = builder.with_window_icon(wb.icon);
builder
}
}
#[cfg(feature = "multiwindow")]
pub trait OpenableWindow {
fn open(self);
}
#[cfg(feature = "multiwindow")]
impl<T> OpenableWindow for T
where
T: Window + WindowCreator,
{
fn open(self) {
crate::runtime::Runtime::open_window(self.get_window_builder(), self);
}
}
lazy_static! {
static ref WINDOW_CHANNELS: Arc<Mutex<HashMap<WindowId, flume::Sender<WindowMessage>>>> =
Arc::default();
}
pub enum WindowMessage {
Close,
RequestClose,
SetAdditionalScale(Scale<f32, Scaled, Points>),
}
impl WindowMessage {
pub fn send_to(self, id: WindowId) -> crate::Result<()> {
let sender = {
let mut channels = WINDOW_CHANNELS.lock().unwrap();
if let Some(sender) = channels.get_mut(&id) {
sender.clone()
} else {
return Err(Error::InternalWindowMessageSend(
"Channel not found for id".to_owned(),
));
}
};
sender.send(self).unwrap_or_default();
Runtime::try_process_window_events(None);
Ok(())
}
}