#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "macos")]
pub use macos::{MacOSApp, MacOSWindow};
use crate::support::point::Extent;
use crate::view::View;
use crate::element::ElementPtr;
#[cfg(target_os = "macos")]
use objc2_foundation::MainThreadMarker;
#[derive(Debug, Clone, Copy)]
pub struct WindowPosition {
pub x: i32,
pub y: i32,
}
impl WindowPosition {
pub fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
pub fn center() -> Self {
Self { x: -1, y: -1 } }
}
impl Default for WindowPosition {
fn default() -> Self {
Self::center()
}
}
#[derive(Debug, Clone, Copy)]
pub struct WindowStyle {
pub closable: bool,
pub miniaturizable: bool,
pub resizable: bool,
pub borderless: bool,
}
impl Default for WindowStyle {
fn default() -> Self {
Self {
closable: true,
miniaturizable: true,
resizable: true,
borderless: false,
}
}
}
impl WindowStyle {
pub fn borderless() -> Self {
Self {
closable: false,
miniaturizable: false,
resizable: false,
borderless: true,
}
}
}
pub type WindowHandle = *mut std::ffi::c_void;
pub type ViewHandle = *mut std::ffi::c_void;
pub struct WindowBuilder {
title: String,
size: Extent,
position: WindowPosition,
style: WindowStyle,
min_size: Option<Extent>,
max_size: Option<Extent>,
}
impl WindowBuilder {
pub fn new(title: impl Into<String>, size: Extent) -> Self {
Self {
title: title.into(),
size,
position: WindowPosition::default(),
style: WindowStyle::default(),
min_size: None,
max_size: None,
}
}
pub fn position(mut self, pos: WindowPosition) -> Self {
self.position = pos;
self
}
pub fn style(mut self, style: WindowStyle) -> Self {
self.style = style;
self
}
pub fn min_size(mut self, size: Extent) -> Self {
self.min_size = Some(size);
self
}
pub fn max_size(mut self, size: Extent) -> Self {
self.max_size = Some(size);
self
}
pub fn build(self) -> Window {
Window::new_with_options(self)
}
}
pub struct Window {
title: String,
size: Extent,
position: WindowPosition,
style: WindowStyle,
view: View,
handle: Option<WindowHandle>,
#[cfg(target_os = "macos")]
macos_window: Option<MacOSWindow>,
}
impl Window {
pub fn new(title: impl Into<String>, size: Extent) -> Self {
let title_str = title.into();
#[cfg(target_os = "macos")]
let macos_window = {
MainThreadMarker::new().map(|mtm| MacOSWindow::new(&title_str, size, mtm))
};
Self {
title: title_str,
size,
position: WindowPosition::default(),
style: WindowStyle::default(),
view: View::new(size),
handle: None,
#[cfg(target_os = "macos")]
macos_window,
}
}
fn new_with_options(builder: WindowBuilder) -> Self {
#[cfg(target_os = "macos")]
let macos_window = {
MainThreadMarker::new().map(|mtm| MacOSWindow::new(&builder.title, builder.size, mtm))
};
Self {
title: builder.title,
size: builder.size,
position: builder.position,
style: builder.style,
view: View::new(builder.size),
handle: None,
#[cfg(target_os = "macos")]
macos_window,
}
}
pub fn title(&self) -> &str {
&self.title
}
pub fn set_title(&mut self, title: impl Into<String>) {
self.title = title.into();
#[cfg(target_os = "macos")]
if let Some(ref win) = self.macos_window {
win.set_title(&self.title);
}
}
pub fn size(&self) -> Extent {
self.size
}
pub fn set_size(&mut self, size: Extent) {
self.size = size;
self.view.set_size(size);
#[cfg(target_os = "macos")]
if let Some(ref win) = self.macos_window {
win.set_size(size);
}
}
pub fn position(&self) -> WindowPosition {
self.position
}
pub fn set_position(&mut self, pos: WindowPosition) {
self.position = pos;
}
pub fn view(&self) -> &View {
&self.view
}
pub fn view_mut(&mut self) -> &mut View {
&mut self.view
}
pub fn set_content(&mut self, content: ElementPtr) {
self.view.set_content(content.clone());
#[cfg(target_os = "macos")]
if let Some(ref win) = self.macos_window {
win.set_content(content);
}
}
pub fn show(&mut self) {
#[cfg(target_os = "macos")]
if let Some(ref win) = self.macos_window {
win.show();
}
}
pub fn hide(&mut self) {
#[cfg(target_os = "macos")]
if let Some(ref win) = self.macos_window {
win.hide();
}
}
pub fn close(&mut self) {
#[cfg(target_os = "macos")]
if let Some(ref win) = self.macos_window {
win.close();
}
}
pub fn is_visible(&self) -> bool {
true }
pub fn refresh(&self) {
self.view.refresh();
}
pub fn handle(&self) -> Option<WindowHandle> {
self.handle
}
}
pub struct App {
running: bool,
#[cfg(target_os = "macos")]
macos_app: Option<MacOSApp>,
}
impl App {
pub fn new() -> Self {
#[cfg(target_os = "macos")]
{
Self {
running: false,
macos_app: MacOSApp::new(),
}
}
#[cfg(not(target_os = "macos"))]
{
Self { running: false }
}
}
pub fn run(&mut self) {
self.running = true;
#[cfg(target_os = "macos")]
{
if let Some(ref app) = self.macos_app {
app.run();
}
}
}
pub fn stop(&mut self) {
self.running = false;
#[cfg(target_os = "macos")]
{
if let Some(ref app) = self.macos_app {
app.stop();
}
}
}
pub fn is_running(&self) -> bool {
self.running
}
#[cfg(target_os = "macos")]
pub fn main_thread_marker(&self) -> Option<MainThreadMarker> {
MainThreadMarker::new()
}
}
impl Default for App {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, thiserror::Error)]
pub enum PlatformError {
#[error("Failed to create window: {0}")]
WindowCreation(String),
#[error("Failed to initialize application: {0}")]
Initialization(String),
#[error("Platform error: {0}")]
Other(String),
}
pub type PlatformResult<T> = Result<T, PlatformError>;