use block::ConcreteBlock;
use core_graphics::base::CGFloat;
use core_graphics::geometry::{CGRect, CGSize};
use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use objc_id::ShareId;
use crate::appkit::toolbar::{Toolbar, ToolbarDelegate};
use crate::color::Color;
use crate::foundation::{id, nil, to_bool, NSInteger, NSString, NSUInteger, NO, YES};
use crate::layout::Layout;
use crate::objc_access::ObjcAccess;
use crate::utils::{os, Controller};
mod class;
use class::register_window_class_with_delegate;
mod config;
pub use config::WindowConfig;
mod controller;
pub use controller::WindowController;
mod enums;
pub use enums::*;
mod traits;
pub use traits::WindowDelegate;
pub(crate) static WINDOW_DELEGATE_PTR: &str = "rstWindowDelegate";
#[derive(Debug)]
pub struct Window<T = ()> {
pub objc: ShareId<Object>,
pub delegate: Option<Box<T>>
}
impl Default for Window {
fn default() -> Self {
Window::new(WindowConfig::default())
}
}
impl Window {
pub fn new(config: WindowConfig) -> Window {
let objc = unsafe {
let _: () = msg_send![class!(NSWindow), setAllowsAutomaticWindowTabbing: NO];
let alloc: id = msg_send![class!(NSWindow), alloc];
let buffered: NSUInteger = 2;
let dimensions: CGRect = config.initial_dimensions.into();
let window: id = msg_send![alloc, initWithContentRect:dimensions
styleMask:config.style
backing:buffered
defer:match config.defer {
true => YES,
false => NO
}
];
let _: () = msg_send![window, autorelease];
let _: () = msg_send![window, setReleasedWhenClosed: NO];
let _: () = msg_send![window, setRestorable: NO];
if os::is_minimum_version(11) {
let toolbar_style: NSUInteger = config.toolbar_style.into();
let _: () = msg_send![window, setToolbarStyle: toolbar_style];
}
ShareId::from_ptr(window)
};
Window {
objc: objc,
delegate: None
}
}
}
impl<T> Window<T>
where
T: WindowDelegate + 'static
{
pub fn with(config: WindowConfig, delegate: T) -> Self {
let class = register_window_class_with_delegate::<T>(&delegate);
let mut delegate = Box::new(delegate);
let objc = unsafe {
let _: () = msg_send![class!(NSWindow), setAllowsAutomaticWindowTabbing: NO];
let alloc: id = msg_send![class, alloc];
let buffered: NSUInteger = 2;
let dimensions: CGRect = config.initial_dimensions.into();
let window: id = msg_send![alloc, initWithContentRect:dimensions
styleMask:config.style
backing:buffered
defer:match config.defer {
true => YES,
false => NO
}
];
let delegate_ptr: *const T = &*delegate;
(&mut *window).set_ivar(WINDOW_DELEGATE_PTR, delegate_ptr as usize);
let _: () = msg_send![window, autorelease];
let _: () = msg_send![window, setReleasedWhenClosed: NO];
let _: () = msg_send![window, setDelegate: window];
let _: () = msg_send![window, setRestorable: NO];
if os::is_minimum_version(11) {
let toolbar_style: NSUInteger = config.toolbar_style.into();
let _: () = msg_send![window, setToolbarStyle: toolbar_style];
}
ShareId::from_ptr(window)
};
{
(&mut delegate).did_load(Window {
delegate: None,
objc: objc.clone()
});
}
Window {
objc: objc,
delegate: Some(delegate)
}
}
}
impl<T> Window<T> {
pub fn set_title(&self, title: &str) {
unsafe {
let title = NSString::new(title);
let _: () = msg_send![&*self.objc, setTitle: title];
}
}
pub fn set_title_visibility(&self, visibility: TitleVisibility) {
unsafe {
let v = NSInteger::from(visibility);
let _: () = msg_send![&*self.objc, setTitleVisibility: v];
}
}
pub fn set_movable_by_background(&self, movable: bool) {
unsafe {
let _: () = msg_send![&*self.objc, setMovableByWindowBackground:match movable {
true => YES,
false => NO
}];
}
}
pub fn set_titlebar_appears_transparent(&self, transparent: bool) {
unsafe {
let _: () = msg_send![&*self.objc, setTitlebarAppearsTransparent:match transparent {
true => YES,
false => NO
}];
}
}
pub fn set_autosave_name(&self, name: &str) {
unsafe {
let autosave = NSString::new(name);
let _: () = msg_send![&*self.objc, setFrameAutosaveName: autosave];
}
}
pub fn set_content_size<F: Into<f64>>(&self, width: F, height: F) {
unsafe {
let size = CGSize::new(width.into(), height.into());
let _: () = msg_send![&*self.objc, setContentSize: size];
}
}
pub fn set_minimum_content_size<F: Into<f64>>(&self, width: F, height: F) {
unsafe {
let size = CGSize::new(width.into(), height.into());
let _: () = msg_send![&*self.objc, setContentMinSize: size];
}
}
pub fn set_maximum_content_size<F: Into<f64>>(&self, width: F, height: F) {
unsafe {
let size = CGSize::new(width.into(), height.into());
let _: () = msg_send![&*self.objc, setContentMaxSize: size];
}
}
pub fn set_minimum_size<F: Into<f64>>(&self, width: F, height: F) {
unsafe {
let size = CGSize::new(width.into(), height.into());
let _: () = msg_send![&*self.objc, setMinSize: size];
}
}
pub fn set_toolbar<TC: ToolbarDelegate>(&self, toolbar: &Toolbar<TC>) {
unsafe {
let _: () = msg_send![&*self.objc, setToolbar:&*toolbar.objc];
}
}
pub fn toggle_toolbar_shown(&self) {
unsafe {
let _: () = msg_send![&*self.objc, toggleToolbarShown: nil];
}
}
pub fn set_shows_toolbar_button(&self, shows: bool) {
unsafe {
let _: () = msg_send![&*self.objc, setShowsToolbarButton:match shows {
true => YES,
false => NO
}];
}
}
pub fn set_content_view<L: Layout + 'static>(&self, view: &L) {
view.with_backing_obj_mut(|backing_node| unsafe {
let _: () = msg_send![&*self.objc, setContentView:&*backing_node];
});
}
pub fn set_content_view_controller<VC: Controller + 'static>(&self, controller: &VC) {
let backing_node = controller.get_backing_node();
unsafe {
let _: () = msg_send![&*self.objc, setContentViewController:&*backing_node];
}
}
pub fn show(&self) {
unsafe {
let _: () = msg_send![&*self.objc, makeKeyAndOrderFront: nil];
}
}
pub fn close(&self) {
unsafe {
let _: () = msg_send![&*self.objc, close];
}
}
pub fn toggle_full_screen(&self) {
unsafe {
let _: () = msg_send![&*self.objc, toggleFullScreen: nil];
}
}
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
let color: id = color.as_ref().into();
unsafe {
let _: () = msg_send![&*self.objc, setBackgroundColor: color];
}
}
pub fn is_opaque(&self) -> bool {
to_bool(unsafe { msg_send![&*self.objc, isOpaque] })
}
pub fn is_miniaturized(&self) -> bool {
to_bool(unsafe { msg_send![&*self.objc, isMiniaturized] })
}
pub fn miniaturize(&self) {
unsafe {
let _: () = msg_send![&*self.objc, miniaturize];
}
}
pub fn deminiaturize(&self) {
unsafe {
let _: () = msg_send![&*self.objc, deminiaturize];
}
}
pub fn print(&self) {
unsafe {
let _: () = msg_send![&*self.objc, print];
}
}
pub fn is_on_active_space(&self) -> bool {
to_bool(unsafe { msg_send![&*self.objc, isOnActiveSpace] })
}
pub fn is_visible(&self) -> bool {
to_bool(unsafe { msg_send![&*self.objc, isVisible] })
}
pub fn is_key(&self) -> bool {
to_bool(unsafe { msg_send![&*self.objc, isKeyWindow] })
}
pub fn can_become_key(&self) -> bool {
to_bool(unsafe { msg_send![&*self.objc, canBecomeKeyWindow] })
}
pub fn make_key_window(&self) {
unsafe {
let _: () = msg_send![&*self.objc, makeKeyWindow];
}
}
pub fn make_key_and_order_front(&self) {
unsafe {
let _: () = msg_send![&*self.objc, makeKeyAndOrderFront: nil];
}
}
pub fn is_main_window(&self) -> bool {
to_bool(unsafe { msg_send![&*self.objc, isMainWindow] })
}
pub fn can_become_main_window(&self) -> bool {
to_bool(unsafe { msg_send![&*self.objc, canBecomeMainWindow] })
}
pub fn set_excluded_from_windows_menu(&self, excluded: bool) {
unsafe {
let _: () = msg_send![&*self.objc, setExcludedFromWindowsMenu:match excluded {
true => YES,
false => NO
}];
}
}
pub fn set_titlebar_separator_style(&self, style: crate::foundation::NSInteger) {
unsafe {
let _: () = msg_send![&*self.objc, setTitlebarSeparatorStyle: style];
}
}
pub fn backing_scale_factor(&self) -> f64 {
unsafe {
let scale: CGFloat = msg_send![&*self.objc, backingScaleFactor];
scale as f64
}
}
pub fn begin_sheet<F, W>(&self, window: &Window<W>, completion: F)
where
F: Fn() + Send + Sync + 'static,
W: WindowDelegate + 'static
{
let block = ConcreteBlock::new(move |_response: NSInteger| {
completion();
});
let block = block.copy();
unsafe {
let _: () = msg_send![&*self.objc, beginSheet:&*window.objc completionHandler:block];
}
}
pub fn end_sheet<W>(&self, window: &Window<W>)
where
W: WindowDelegate + 'static
{
unsafe {
let _: () = msg_send![&*self.objc, endSheet:&*window.objc];
}
}
}
impl<T> Drop for Window<T> {
fn drop(&mut self) {
if self.delegate.is_some() {
unsafe {
let _: () = msg_send![&*self.objc, setDelegate: nil];
}
}
}
}