Devised a better (safer) way to handle UI/Controller/Delegate setup
This commit is contained in:
parent
80ba209413
commit
f49eff24f9
|
@ -17,14 +17,15 @@ use crate::menu::Menu;
|
||||||
mod events;
|
mod events;
|
||||||
use events::register_app_class;
|
use events::register_app_class;
|
||||||
|
|
||||||
|
pub trait Dispatcher {
|
||||||
pub trait AppDelegate {
|
|
||||||
type Message: Send + Sync;
|
type Message: Send + Sync;
|
||||||
|
|
||||||
|
fn on_message(&self, _message: Self::Message) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AppDelegate {
|
||||||
fn did_finish_launching(&self) {}
|
fn did_finish_launching(&self) {}
|
||||||
fn did_become_active(&self) {}
|
fn did_become_active(&self) {}
|
||||||
|
|
||||||
fn on_message(&self, _message: Self::Message) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper for `NSApplication`. It holds (retains) pointers for the Objective-C runtime,
|
/// A wrapper for `NSApplication`. It holds (retains) pointers for the Objective-C runtime,
|
||||||
|
@ -60,7 +61,7 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppDelegate<Message = M> {
|
impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppDelegate + Dispatcher<Message = M> {
|
||||||
/// Dispatches a message by grabbing the `sharedApplication`, getting ahold of the delegate,
|
/// Dispatches a message by grabbing the `sharedApplication`, getting ahold of the delegate,
|
||||||
/// and passing back through there. All messages are currently dispatched on the main thread.
|
/// and passing back through there. All messages are currently dispatched on the main thread.
|
||||||
pub fn dispatch(message: M) {
|
pub fn dispatch(message: M) {
|
||||||
|
|
|
@ -8,3 +8,4 @@ pub(crate) static VIEW_CONTROLLER_PTR: &str = "rstViewControllerPtr";
|
||||||
pub(crate) static WEBVIEW_CONFIG_VAR: &str = "rstWebViewConfig";
|
pub(crate) static WEBVIEW_CONFIG_VAR: &str = "rstWebViewConfig";
|
||||||
pub(crate) static WEBVIEW_VAR: &str = "rstWebView";
|
pub(crate) static WEBVIEW_VAR: &str = "rstWebView";
|
||||||
pub(crate) static WEBVIEW_CONTROLLER_PTR: &str = "rstWebViewControllerPtr";
|
pub(crate) static WEBVIEW_CONTROLLER_PTR: &str = "rstWebViewControllerPtr";
|
||||||
|
pub(crate) static WINDOW_CONTROLLER_PTR: &str = "rstWindowController";
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub mod window;
|
||||||
pub use url;
|
pub use url;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::app::{App, AppDelegate};
|
pub use crate::app::{App, AppDelegate, Dispatcher};
|
||||||
|
|
||||||
pub use crate::menu::{Menu, MenuItem};
|
pub use crate::menu::{Menu, MenuItem};
|
||||||
pub use crate::notifications::{Notification, NotificationCenter, NotificationAuthOption};
|
pub use crate::notifications::{Notification, NotificationCenter, NotificationAuthOption};
|
||||||
|
@ -54,14 +54,14 @@ pub mod prelude {
|
||||||
pub use crate::networking::URLRequest;
|
pub use crate::networking::URLRequest;
|
||||||
|
|
||||||
pub use crate::window::{
|
pub use crate::window::{
|
||||||
Window, WindowWrapper as WinWrapper, WindowController
|
Window, WindowController, WindowHandle
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::webview::{
|
pub use crate::webview::{
|
||||||
WebView, WebViewConfig, WebViewController
|
WebView, WebViewConfig, WebViewController
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::view::{View, ViewController, ViewWrapper};
|
pub use crate::view::{View, ViewHandle, ViewController};
|
||||||
|
|
||||||
pub use appkit_derive::{
|
pub use appkit_derive::{
|
||||||
WindowWrapper, ViewWrapper
|
WindowWrapper, ViewWrapper
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
//! for in the modern era. It also implements a few helpers for things like setting a background
|
//! for in the modern era. It also implements a few helpers for things like setting a background
|
||||||
//! color, and enforcing layer backing by default.
|
//! color, and enforcing layer backing by default.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
use cocoa::base::{id, nil, YES, NO};
|
use cocoa::base::{id, nil, YES, NO};
|
||||||
|
@ -42,10 +44,19 @@ extern fn update_layer(this: &Object, _: Sel) {
|
||||||
extern fn dragging_entered<T: ViewController>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
|
extern fn dragging_entered<T: ViewController>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
let view = ptr as *const T;
|
let view_ptr = ptr as *const RefCell<T>;
|
||||||
(*view).dragging_entered(DragInfo {
|
let view = Rc::from_raw(view_ptr);
|
||||||
|
|
||||||
|
let response = {
|
||||||
|
let v = view.borrow();
|
||||||
|
|
||||||
|
(*v).dragging_entered(DragInfo {
|
||||||
info: Id::from_ptr(info)
|
info: Id::from_ptr(info)
|
||||||
}).into()
|
}).into()
|
||||||
|
};
|
||||||
|
|
||||||
|
Rc::into_raw(view);
|
||||||
|
response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,14 +64,22 @@ extern fn dragging_entered<T: ViewController>(this: &mut Object, _: Sel, info: i
|
||||||
extern fn prepare_for_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) -> BOOL {
|
extern fn prepare_for_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) -> BOOL {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
let view = ptr as *const T;
|
let view_ptr = ptr as *const RefCell<T>;
|
||||||
|
let view = Rc::from_raw(view_ptr);
|
||||||
|
|
||||||
match (*view).prepare_for_drag_operation(DragInfo {
|
let response = {
|
||||||
|
let v = view.borrow();
|
||||||
|
|
||||||
|
match (*v).prepare_for_drag_operation(DragInfo {
|
||||||
info: Id::from_ptr(info)
|
info: Id::from_ptr(info)
|
||||||
}) {
|
}) {
|
||||||
true => YES,
|
true => YES,
|
||||||
false => NO
|
false => NO
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Rc::into_raw(view);
|
||||||
|
response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,14 +87,22 @@ extern fn prepare_for_drag_operation<T: ViewController>(this: &mut Object, _: Se
|
||||||
extern fn perform_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) -> BOOL {
|
extern fn perform_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) -> BOOL {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
let view = ptr as *const T;
|
let view_ptr = ptr as *const RefCell<T>;
|
||||||
|
let view = Rc::from_raw(view_ptr);
|
||||||
|
|
||||||
match (*view).perform_drag_operation(DragInfo {
|
let response = {
|
||||||
|
let v = view.borrow();
|
||||||
|
|
||||||
|
match (*v).perform_drag_operation(DragInfo {
|
||||||
info: Id::from_ptr(info)
|
info: Id::from_ptr(info)
|
||||||
}) {
|
}) {
|
||||||
true => YES,
|
true => YES,
|
||||||
false => NO
|
false => NO
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Rc::into_raw(view);
|
||||||
|
response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,11 +110,18 @@ extern fn perform_drag_operation<T: ViewController>(this: &mut Object, _: Sel, i
|
||||||
extern fn conclude_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) {
|
extern fn conclude_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
let view = ptr as *const T;
|
let view_ptr = ptr as *const RefCell<T>;
|
||||||
|
let view = Rc::from_raw(view_ptr);
|
||||||
|
|
||||||
(*view).conclude_drag_operation(DragInfo {
|
let response = {
|
||||||
|
let v = view.borrow();
|
||||||
|
(*v).conclude_drag_operation(DragInfo {
|
||||||
info: Id::from_ptr(info)
|
info: Id::from_ptr(info)
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Rc::into_raw(view);
|
||||||
|
response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,10 +129,18 @@ extern fn conclude_drag_operation<T: ViewController>(this: &mut Object, _: Sel,
|
||||||
extern fn dragging_exited<T: ViewController>(this: &mut Object, _: Sel, info: id) {
|
extern fn dragging_exited<T: ViewController>(this: &mut Object, _: Sel, info: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
let view = ptr as *const T;
|
let view_ptr = ptr as *const RefCell<T>;
|
||||||
(*view).dragging_exited(DragInfo {
|
let view = Rc::from_raw(view_ptr);
|
||||||
|
|
||||||
|
let response = {
|
||||||
|
let v = view.borrow();
|
||||||
|
(*v).dragging_exited(DragInfo {
|
||||||
info: Id::from_ptr(info)
|
info: Id::from_ptr(info)
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Rc::into_raw(view);
|
||||||
|
response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,4 @@ pub mod traits;
|
||||||
pub use traits::*;
|
pub use traits::*;
|
||||||
|
|
||||||
pub mod view;
|
pub mod view;
|
||||||
pub use view::View;
|
pub use view::{View, ViewHandle};
|
||||||
|
|
|
@ -5,13 +5,20 @@ use objc::runtime::Object;
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::dragdrop::{DragInfo, DragOperation};
|
use crate::dragdrop::{DragInfo, DragOperation};
|
||||||
|
use crate::view::ViewHandle;
|
||||||
|
|
||||||
pub trait ViewWrapper {
|
pub trait Node {
|
||||||
fn get_handle(&self) -> Option<ShareId<Object>>;
|
fn get_backing_node(&self) -> Option<ShareId<Object>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ViewController {
|
pub trait ViewController {
|
||||||
fn did_load(&self);
|
/// Where possible, we try to respect the lazy-ish-loading of macOS/iOS systems. This hook
|
||||||
|
/// notifies you when the `View` has actually loaded into memory, and you're free to do things
|
||||||
|
/// with it.
|
||||||
|
///
|
||||||
|
/// Note that you can trigger the view to load earlier, if need be... and in many cases it's
|
||||||
|
/// fine. This is a platform-specific detail you may want to read up on. :)
|
||||||
|
fn did_load(&mut self, _view: ViewHandle) {}
|
||||||
|
|
||||||
/// Invoked when the dragged image enters destination bounds or frame; returns dragging operation to perform.
|
/// Invoked when the dragged image enters destination bounds or frame; returns dragging operation to perform.
|
||||||
fn dragging_entered(&self, _info: DragInfo) -> DragOperation { DragOperation::None }
|
fn dragging_entered(&self, _info: DragInfo) -> DragOperation { DragOperation::None }
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
//! A wrapper for `NSViewController`. Uses interior mutability to
|
//! A wrapper for `NSViewController`. Uses interior mutability to
|
||||||
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use cocoa::base::{id, nil, YES};
|
use cocoa::base::{id, nil, YES};
|
||||||
use cocoa::foundation::NSArray;
|
use cocoa::foundation::NSArray;
|
||||||
|
@ -13,29 +14,30 @@ use objc::{msg_send, sel, sel_impl};
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::constants::{BACKGROUND_COLOR, VIEW_CONTROLLER_PTR};
|
use crate::constants::{BACKGROUND_COLOR, VIEW_CONTROLLER_PTR};
|
||||||
use crate::pasteboard::PasteboardType;
|
use crate::pasteboard::PasteboardType;
|
||||||
use crate::view::traits::ViewController;
|
use crate::view::traits::{Node, ViewController};
|
||||||
use crate::view::controller::register_controller_class;
|
use crate::view::controller::register_controller_class;
|
||||||
|
|
||||||
#[derive(Default)]
|
/// A clone-able handler to a `ViewController` reference in the Objective C runtime. We use this
|
||||||
pub struct ViewInner {
|
/// instead of a stock `View` for easier recordkeeping, since it'll need to hold the `View` on that
|
||||||
pub controller: Option<ShareId<Object>>
|
/// side anyway.
|
||||||
}
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct ViewHandle(Option<ShareId<Object>>);
|
||||||
impl ViewInner {
|
|
||||||
pub fn configure<T: ViewController + 'static>(&mut self, controller: &T) {
|
impl ViewHandle {
|
||||||
self.controller = Some(unsafe {
|
/// Call this to set the background color for the backing layer.
|
||||||
let view_controller: id = msg_send![register_controller_class::<T>(), new];
|
pub fn set_background_color(&self, color: Color) {
|
||||||
(&mut *view_controller).set_ivar(VIEW_CONTROLLER_PTR, controller as *const T as usize);
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
let view: id = msg_send![view_controller, view];
|
let view: id = msg_send![*controller, view];
|
||||||
(&mut *view).set_ivar(VIEW_CONTROLLER_PTR, controller as *const T as usize);
|
(*view).set_ivar(BACKGROUND_COLOR, color.into_platform_specific_color());
|
||||||
|
let _: () = msg_send![view, setNeedsDisplay:YES];
|
||||||
ShareId::from_ptr(view_controller)
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register this view for drag and drop operations.
|
||||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||||
if let Some(controller) = &self.controller {
|
if let Some(controller) = &self.0 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let types = NSArray::arrayWithObjects(nil, &types.iter().map(|t| {
|
let types = NSArray::arrayWithObjects(nil, &types.iter().map(|t| {
|
||||||
t.to_nsstring()
|
t.to_nsstring()
|
||||||
|
@ -46,49 +48,78 @@ impl ViewInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `View` wraps two different controllers - one on the Objective-C/Cocoa side, which forwards
|
||||||
|
/// calls into your supplied `ViewController` trait object. This involves heap allocation, but all
|
||||||
|
/// of Cocoa is essentially Heap'd, so... well, enjoy.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct View<T> {
|
||||||
|
internal_callback_ptr: *const RefCell<T>,
|
||||||
|
pub objc_controller: ViewHandle,
|
||||||
|
pub controller: Rc<RefCell<T>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> View<T> where T: ViewController + 'static {
|
||||||
|
/// Allocates and configures a `ViewController` in the Objective-C/Cocoa runtime that maps over
|
||||||
|
/// to your supplied view controller.
|
||||||
|
pub fn new(controller: T) -> Self {
|
||||||
|
let controller = Rc::new(RefCell::new(controller));
|
||||||
|
|
||||||
|
let internal_callback_ptr = {
|
||||||
|
let cloned = Rc::clone(&controller);
|
||||||
|
Rc::into_raw(cloned)
|
||||||
|
};
|
||||||
|
|
||||||
|
let inner = unsafe {
|
||||||
|
let view_controller: id = msg_send![register_controller_class::<T>(), new];
|
||||||
|
(&mut *view_controller).set_ivar(VIEW_CONTROLLER_PTR, internal_callback_ptr as usize);
|
||||||
|
|
||||||
|
let view: id = msg_send![view_controller, view];
|
||||||
|
(&mut *view).set_ivar(VIEW_CONTROLLER_PTR, internal_callback_ptr as usize);
|
||||||
|
|
||||||
|
ShareId::from_ptr(view_controller)
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut vc = controller.borrow_mut();
|
||||||
|
(*vc).did_load(ViewHandle(Some(inner.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
View {
|
||||||
|
internal_callback_ptr: internal_callback_ptr,
|
||||||
|
objc_controller: ViewHandle(Some(inner)),
|
||||||
|
controller: controller
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_background_color(&self, color: Color) {
|
pub fn set_background_color(&self, color: Color) {
|
||||||
if let Some(controller) = &self.controller {
|
self.objc_controller.set_background_color(color);
|
||||||
unsafe {
|
|
||||||
let view: id = msg_send![*controller, view];
|
|
||||||
(*view).set_ivar(BACKGROUND_COLOR, color.into_platform_specific_color());
|
|
||||||
let _: () = msg_send![view, setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct View(Rc<RefCell<ViewInner>>);
|
|
||||||
|
|
||||||
impl View {
|
|
||||||
pub fn configure<T: ViewController + 'static>(&self, controller: &T) {
|
|
||||||
{
|
|
||||||
let mut view = self.0.borrow_mut();
|
|
||||||
view.configure(controller);
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.did_load();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_handle(&self) -> Option<ShareId<Object>> {
|
|
||||||
let view = self.0.borrow();
|
|
||||||
view.controller.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||||
let view = self.0.borrow();
|
self.objc_controller.register_for_dragged_types(types);
|
||||||
view.register_for_dragged_types(types);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_background_color(&self, color: Color) {
|
|
||||||
let view = self.0.borrow();
|
|
||||||
view.set_background_color(color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for View {
|
impl<T> Node for View<T> {
|
||||||
|
/// Returns the Objective-C object used for handling the view heirarchy.
|
||||||
|
fn get_backing_node(&self) -> Option<ShareId<Object>> {
|
||||||
|
self.objc_controller.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for View<T> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "View ({:p})", self)
|
write!(f, "View ({:p})", self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for View<T> {
|
||||||
|
/// A bit of extra cleanup for delegate callback pointers.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let _ = Rc::from_raw(self.internal_callback_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ impl WebView {
|
||||||
webview.configure(controller);
|
webview.configure(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.did_load();
|
//controller.did_load();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_handle(&self) -> Option<ShareId<Object>> {
|
pub fn get_handle(&self) -> Option<ShareId<Object>> {
|
||||||
|
|
147
appkit/src/window/handle.rs
Normal file
147
appkit/src/window/handle.rs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
//! Implements `WindowHandle`, which wraps a lower-level `NSWindowController` and handles method
|
||||||
|
//! shuffling to call through to the window it holds.
|
||||||
|
//!
|
||||||
|
//! We use `NSWindowController` as it has lifecycle methods that are useful, in addition to the
|
||||||
|
//! standard `NSWindowDelegate` methods.
|
||||||
|
|
||||||
|
use cocoa::base::{id, nil, YES, NO};
|
||||||
|
use cocoa::foundation::{NSSize, NSString};
|
||||||
|
|
||||||
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
use crate::view::traits::Node;
|
||||||
|
|
||||||
|
//use crate::toolbar::traits::ToolbarDelegate;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct WindowHandle(pub Option<ShareId<Object>>);
|
||||||
|
|
||||||
|
impl WindowHandle {
|
||||||
|
/// Handles setting the title on the underlying window. Allocates and passes an `NSString` over
|
||||||
|
/// to the Objective C runtime.
|
||||||
|
pub fn set_title(&self, title: &str) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let title = NSString::alloc(nil).init_str(title);
|
||||||
|
let window: id = msg_send![*controller, window];
|
||||||
|
let _: () = msg_send![window, setTitle:title];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the title visibility for the underlying window.
|
||||||
|
pub fn set_title_visibility(&self, visibility: usize) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let window: id = msg_send![*controller, window];
|
||||||
|
let _: () = msg_send![window, setTitleVisibility:visibility];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used for configuring whether the window is movable via the background.
|
||||||
|
pub fn set_movable_by_background(&self, movable: bool) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let window: id = msg_send![*controller, window];
|
||||||
|
let _: () = msg_send![window, setMovableByWindowBackground:match movable {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used for setting whether this titlebar appears transparent.
|
||||||
|
pub fn set_titlebar_appears_transparent(&self, transparent: bool) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let window: id = msg_send![*controller, window];
|
||||||
|
let _: () = msg_send![window, setTitlebarAppearsTransparent:match transparent {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used for setting this Window autosave name.
|
||||||
|
pub fn set_autosave_name(&mut self, name: &str) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let window: id = msg_send![*controller, window];
|
||||||
|
let autosave = NSString::alloc(nil).init_str(name);
|
||||||
|
let _: () = msg_send![window, setFrameAutosaveName:autosave];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the minimum size this window can shrink to.
|
||||||
|
pub fn set_minimum_content_size<F: Into<f64>>(&self, width: F, height: F) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let size = NSSize::new(width.into(), height.into());
|
||||||
|
let window: id = msg_send![*controller, window];
|
||||||
|
let _: () = msg_send![window, setMinSize:size];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used for setting a toolbar on this window. Note that this takes ownership of whatever
|
||||||
|
/// `ToolbarDelegate` you pass! The underlying `NSToolbar` is a bit... old, and it's just
|
||||||
|
/// easier to do things this way.
|
||||||
|
///
|
||||||
|
/// If you find yourself in a position where you need your toolbar after the fact, you
|
||||||
|
/// probably have bigger issues.
|
||||||
|
//pub fn set_toolbar<T: ToolbarDelegate + 'static>(&mut self, identifier: &str, toolbar: T) {
|
||||||
|
/*let toolbar = Toolbar::new(identifier, toolbar);
|
||||||
|
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let window: id = msg_send![*controller, window];
|
||||||
|
let _: () = msg_send![window, setToolbar:&*toolbar.inner];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.toolbar = Some(toolbar);*/
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// Used for setting the content view controller for this window.
|
||||||
|
pub fn set_content_view_controller<T: Node + 'static>(&self, view_controller: &T) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
if let Some(vc) = view_controller.get_backing_node() {
|
||||||
|
let _: () = msg_send![*controller, setContentViewController:&*vc];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// On macOS, calling `show()` is equivalent to calling `makeKeyAndOrderFront`. This is the
|
||||||
|
/// most common use case, hence why this method was chosen - if you want or need something
|
||||||
|
/// else, feel free to open an issue to discuss.
|
||||||
|
///
|
||||||
|
/// You should never be calling this yourself, mind you - Alchemy core handles this for you.
|
||||||
|
pub fn show(&self) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![*controller, showWindow:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// On macOS, calling `close()` is equivalent to calling... well, `close`. It closes the
|
||||||
|
/// window.
|
||||||
|
///
|
||||||
|
/// I dunno what else to say here, lol.
|
||||||
|
pub fn close(&self) {
|
||||||
|
if let Some(controller) = &self.0 {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![*controller, close];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,15 @@
|
||||||
//! Implements wrappers and traits for `NSWindowController` and associated types.
|
//! Implements wrappers and traits for `NSWindowController` and associated types.
|
||||||
|
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
pub use traits::{WindowController, WindowWrapper};
|
pub use traits::WindowController;
|
||||||
|
|
||||||
mod controller;
|
mod controller;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub use config::{WindowConfig, WindowStyle};
|
pub use config::{WindowConfig, WindowStyle};
|
||||||
|
|
||||||
|
pub mod handle;
|
||||||
|
pub use handle::WindowHandle;
|
||||||
|
|
||||||
pub mod window;
|
pub mod window;
|
||||||
pub use window::{Window, WindowTitleVisibility};
|
pub use window::{Window, WindowTitleVisibility};
|
||||||
|
|
|
@ -2,59 +2,9 @@
|
||||||
//! module. There's a few different ones, and it's just... cleaner, if
|
//! module. There's a few different ones, and it's just... cleaner, if
|
||||||
//! it's organized here.
|
//! it's organized here.
|
||||||
|
|
||||||
|
use crate::window::handle::WindowHandle;
|
||||||
use crate::window::WindowConfig;
|
use crate::window::WindowConfig;
|
||||||
|
|
||||||
/// `WindowController` is a trait that handles providing higher level methods
|
|
||||||
/// that map into platform specific methods. Typically, you won't want to (or at least, won't need
|
|
||||||
/// to) implement this yourself - simply derive `WindowWrapper` and it'll work for you.
|
|
||||||
///
|
|
||||||
/// By deriving or implementing this, you get usable methods on your struct - for example:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use appkit::{AppDelegate, Window, WindowController, WindowWrapper};
|
|
||||||
///
|
|
||||||
/// #[derive(Default, WindowWrapper)]
|
|
||||||
/// struct MyWindow {
|
|
||||||
/// window: Window
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl WindowController for MyWindow {
|
|
||||||
/// // The default implementation is actually okay!
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[derive(Default)]
|
|
||||||
/// struct MyApp {
|
|
||||||
/// window: MyWindow
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl AppDelegate for MyApp {
|
|
||||||
/// fn did_finish_launching(&mut self) {
|
|
||||||
/// window.show();
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// let app = App::new("com.myapp.lol", MyApp::default());
|
|
||||||
/// app.run();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub trait WindowWrapper {
|
|
||||||
/// Sets the title for the underlying window.
|
|
||||||
fn set_title(&self, title: &str);
|
|
||||||
|
|
||||||
/// Calls through to the NSWindow show method (technically, `[NSWindowController showWindow:]`.
|
|
||||||
/// Notable, this handles passing the implementing entity as the delegate, ensuring that
|
|
||||||
/// callbacks work appropriately.
|
|
||||||
///
|
|
||||||
/// We're technically setting the delegate later than is ideal, but in practice it works fine
|
|
||||||
/// in most cases due to the underlying implementation of `NSWindow` deferring things until
|
|
||||||
/// needed.
|
|
||||||
fn show(&self);
|
|
||||||
|
|
||||||
/// Calls through to the native NSwindow close implementation.
|
|
||||||
fn close(&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lifecycle events for anything that `impl Window`'s. These map to the standard Cocoa
|
/// Lifecycle events for anything that `impl Window`'s. These map to the standard Cocoa
|
||||||
/// lifecycle methods, but mix in a few extra things to handle offering configuration tools
|
/// lifecycle methods, but mix in a few extra things to handle offering configuration tools
|
||||||
/// in lieu of subclasses.
|
/// in lieu of subclasses.
|
||||||
|
@ -67,7 +17,7 @@ pub trait WindowController {
|
||||||
/// to set up your views and what not.
|
/// to set up your views and what not.
|
||||||
///
|
///
|
||||||
/// If you're coming from the web, you can think of this as `DOMContentLoaded`.
|
/// If you're coming from the web, you can think of this as `DOMContentLoaded`.
|
||||||
fn did_load(&self) {}
|
fn did_load(&mut self, _window: WindowHandle) {}
|
||||||
|
|
||||||
/// Fires when a window is going to close. You might opt to, say, clean up things here -
|
/// Fires when a window is going to close. You might opt to, say, clean up things here -
|
||||||
/// perhaps you have a long running task, or something that should be removed.
|
/// perhaps you have a long running task, or something that should be removed.
|
||||||
|
|
|
@ -7,24 +7,15 @@ use std::cell::RefCell;
|
||||||
use cocoa::base::{id, nil, YES, NO};
|
use cocoa::base::{id, nil, YES, NO};
|
||||||
use cocoa::foundation::{NSSize, NSString};
|
use cocoa::foundation::{NSSize, NSString};
|
||||||
|
|
||||||
use objc_id::Id;
|
|
||||||
use objc::runtime::Object;
|
|
||||||
use objc::{msg_send, sel, sel_impl};
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::view::{ViewController, ViewWrapper};
|
use crate::constants::WINDOW_CONTROLLER_PTR;
|
||||||
use crate::toolbar::{Toolbar, ToolbarDelegate};
|
use crate::toolbar::{Toolbar, ToolbarDelegate};
|
||||||
use crate::window::WindowController;
|
use crate::view::traits::Node;
|
||||||
use crate::window::controller::{register_window_controller_class};
|
use crate::window::handle::WindowHandle;
|
||||||
|
use crate::window::traits::WindowController;
|
||||||
static WINDOW_CONTROLLER_PTR: &str = "rstWindowController";
|
use crate::window::controller::register_window_controller_class;
|
||||||
|
|
||||||
/// A wrapper for `NSWindow`. Holds (retains) pointers for the Objective-C runtime
|
|
||||||
/// where our `NSWindow` and associated delegate live.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct WindowInner {
|
|
||||||
pub controller: Option<Id<Object>>,
|
|
||||||
pub toolbar: Option<Toolbar>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum WindowTitleVisibility {
|
pub enum WindowTitleVisibility {
|
||||||
Visible,
|
Visible,
|
||||||
|
@ -40,8 +31,18 @@ impl From<WindowTitleVisibility> for usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowInner {
|
/// A `Window` represents your way of interacting with an `NSWindow`. It wraps the various moving
|
||||||
/// Configures the `NSWindow` to know about our delegate.
|
/// pieces to enable you to focus on reacting to lifecycle methods and doing your thing.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Window<T> {
|
||||||
|
internal_callback_ptr: *const RefCell<T>,
|
||||||
|
pub objc_controller: WindowHandle,
|
||||||
|
pub controller: Rc<RefCell<T>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Window<T> where T: WindowController + 'static {
|
||||||
|
/// Allocates and configures a `WindowController` in the Objective-C/Cocoa runtime that maps over
|
||||||
|
/// to your supplied controller.
|
||||||
///
|
///
|
||||||
/// Now, you may look at this and go "hey, the hell is going on here - why don't you make the
|
/// Now, you may look at this and go "hey, the hell is going on here - why don't you make the
|
||||||
/// `NSWindow` in `[NSWindowController loadWindow]`?
|
/// `NSWindow` in `[NSWindowController loadWindow]`?
|
||||||
|
@ -54,246 +55,74 @@ impl WindowInner {
|
||||||
/// the route of implementing `loadView`.
|
/// the route of implementing `loadView`.
|
||||||
///
|
///
|
||||||
/// APPKIT!
|
/// APPKIT!
|
||||||
pub fn configure<T: WindowController + 'static>(&mut self, window_controller: &T) {
|
pub fn new(controller: T) -> Self {
|
||||||
let window = window_controller.config().0;
|
let window = controller.config().0;
|
||||||
|
let controller = Rc::new(RefCell::new(controller));
|
||||||
|
|
||||||
self.controller = Some(unsafe {
|
let internal_callback_ptr = {
|
||||||
|
let cloned = Rc::clone(&controller);
|
||||||
|
Rc::into_raw(cloned)
|
||||||
|
};
|
||||||
|
|
||||||
|
let inner = unsafe {
|
||||||
let window_controller_class = register_window_controller_class::<T>();
|
let window_controller_class = register_window_controller_class::<T>();
|
||||||
let controller_alloc: id = msg_send![window_controller_class, alloc];
|
let controller_alloc: id = msg_send![window_controller_class, alloc];
|
||||||
let controller: id = msg_send![controller_alloc, initWithWindow:window];
|
let controller: id = msg_send![controller_alloc, initWithWindow:window];
|
||||||
(&mut *controller).set_ivar(WINDOW_CONTROLLER_PTR, window_controller as *const T as usize);
|
(&mut *controller).set_ivar(WINDOW_CONTROLLER_PTR, internal_callback_ptr as usize);
|
||||||
|
|
||||||
let window: id = msg_send![controller, window];
|
let window: id = msg_send![controller, window];
|
||||||
let _: () = msg_send![window, setDelegate:controller];
|
let _: () = msg_send![window, setDelegate:controller];
|
||||||
|
|
||||||
Id::from_ptr(controller)
|
ShareId::from_ptr(controller)
|
||||||
});
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut vc = controller.borrow_mut();
|
||||||
|
(*vc).did_load(WindowHandle(Some(inner.clone())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles setting the title on the underlying window. Allocates and passes an `NSString` over
|
Window {
|
||||||
/// to the Objective C runtime.
|
internal_callback_ptr: internal_callback_ptr,
|
||||||
pub fn set_title(&mut self, title: &str) {
|
objc_controller: WindowHandle(Some(inner)),
|
||||||
if let Some(controller) = &self.controller {
|
controller: controller
|
||||||
unsafe {
|
|
||||||
let title = NSString::alloc(nil).init_str(title);
|
|
||||||
let window: id = msg_send![*controller, window];
|
|
||||||
let _: () = msg_send![window, setTitle:title];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the title visibility for the underlying window.
|
/// Sets the title for this window.
|
||||||
pub fn set_title_visibility(&mut self, visibility: usize) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let window: id = msg_send![*controller, window];
|
|
||||||
let _: () = msg_send![window, setTitleVisibility:visibility];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used for configuring whether the window is movable via the background.
|
|
||||||
pub fn set_movable_by_background(&self, movable: bool) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let window: id = msg_send![*controller, window];
|
|
||||||
let _: () = msg_send![window, setMovableByWindowBackground:match movable {
|
|
||||||
true => YES,
|
|
||||||
false => NO
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used for setting whether this titlebar appears transparent.
|
|
||||||
pub fn set_titlebar_appears_transparent(&self, transparent: bool) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let window: id = msg_send![*controller, window];
|
|
||||||
let _: () = msg_send![window, setTitlebarAppearsTransparent:match transparent {
|
|
||||||
true => YES,
|
|
||||||
false => NO
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used for setting this Window autosave name.
|
|
||||||
pub fn set_autosave_name(&mut self, name: &str) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let window: id = msg_send![*controller, window];
|
|
||||||
|
|
||||||
// Now we need to make sure to re-apply the NSAutoSaveName, as initWithWindow
|
|
||||||
// strips it... for some reason. We want it applied as it does nice things like
|
|
||||||
// save the window position in the Defaults database, which is what users expect.
|
|
||||||
let autosave = NSString::alloc(nil).init_str(name);
|
|
||||||
let _: () = msg_send![window, setFrameAutosaveName:autosave];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_minimum_content_size<F: Into<f64>>(&self, width: F, height: F) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let size = NSSize::new(width.into(), height.into());
|
|
||||||
let window: id = msg_send![*controller, window];
|
|
||||||
let _: () = msg_send![window, setMinSize:size];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used for setting a toolbar on this window. Note that this takes ownership of whatever
|
|
||||||
/// `ToolbarDelegate` you pass! The underlying `NSToolbar` is a bit... old, and it's just
|
|
||||||
/// easier to do things this way.
|
|
||||||
///
|
|
||||||
/// If you find yourself in a position where you need your toolbar after the fact, you
|
|
||||||
/// probably have bigger issues.
|
|
||||||
pub fn set_toolbar<T: ToolbarDelegate + 'static>(&mut self, identifier: &str, toolbar: T) {
|
|
||||||
let toolbar = Toolbar::new(identifier, toolbar);
|
|
||||||
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let window: id = msg_send![*controller, window];
|
|
||||||
let _: () = msg_send![window, setToolbar:&*toolbar.inner];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.toolbar = Some(toolbar);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used for setting the content view controller for this window.
|
|
||||||
pub fn set_content_view<T: ViewController + ViewWrapper + 'static>(&mut self, view_controller: &T) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
if let Some(vc) = view_controller.get_handle() {
|
|
||||||
let _: () = msg_send![*controller, setContentViewController:&*vc];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// On macOS, calling `show()` is equivalent to calling `makeKeyAndOrderFront`. This is the
|
|
||||||
/// most common use case, hence why this method was chosen - if you want or need something
|
|
||||||
/// else, feel free to open an issue to discuss.
|
|
||||||
///
|
|
||||||
/// You should never be calling this yourself, mind you - Alchemy core handles this for you.
|
|
||||||
pub fn show(&self) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let _: () = msg_send![*controller, showWindow:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// On macOS, calling `close()` is equivalent to calling... well, `close`. It closes the
|
|
||||||
/// window.
|
|
||||||
///
|
|
||||||
/// I dunno what else to say here, lol.
|
|
||||||
pub fn close(&self) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let _: () = msg_send![*controller, close];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for WindowInner {
|
|
||||||
/// When a Window is dropped on the Rust side, we want to ensure that we break the delegate
|
|
||||||
/// link on the Objective-C side. While this shouldn't actually be an issue, I'd rather be
|
|
||||||
/// safer than sorry.
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(controller) = &self.controller {
|
|
||||||
unsafe {
|
|
||||||
let window: id = msg_send![*controller, window];
|
|
||||||
let _: () = msg_send![window, setDelegate:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Window wraps `NSWindowController`, using interior mutability to handle configuration and calling
|
|
||||||
/// through to it.
|
|
||||||
///
|
|
||||||
/// Why `NSWindowController` and not `NSWindow`, you ask? The former has lifecycle events we're
|
|
||||||
/// interested in, the latter is... well, just the window.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Window(Rc<RefCell<WindowInner>>);
|
|
||||||
|
|
||||||
impl Window {
|
|
||||||
/// Sets the window title.
|
|
||||||
pub fn set_title(&self, title: &str) {
|
pub fn set_title(&self, title: &str) {
|
||||||
let mut window = self.0.borrow_mut();
|
self.objc_controller.set_title(title.into());
|
||||||
window.set_title(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the window title visibility.
|
|
||||||
pub fn set_title_visibility(&self, visibility: usize) {
|
|
||||||
let mut window = self.0.borrow_mut();
|
|
||||||
window.set_title_visibility(visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether the window is movable by the background or not.
|
|
||||||
pub fn set_movable_by_background(&self, movable: bool) {
|
|
||||||
let window = self.0.borrow();
|
|
||||||
window.set_movable_by_background(movable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether the titlebar appears transparent or not.
|
|
||||||
pub fn set_titlebar_appears_transparent(&self, transparent: bool) {
|
|
||||||
let window = self.0.borrow();
|
|
||||||
window.set_titlebar_appears_transparent(transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the window autosave name, which preserves things like position across restarts.
|
|
||||||
pub fn set_autosave_name(&self, name: &str) {
|
|
||||||
let mut window = self.0.borrow_mut();
|
|
||||||
window.set_autosave_name(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the window's smallest size it can shrink to.
|
|
||||||
pub fn set_minimum_content_size<F: Into<f64>>(&self, width: F, height: F) {
|
|
||||||
let window = self.0.borrow_mut();
|
|
||||||
window.set_minimum_content_size(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the Toolbar for this window. Note that this takes ownership of the toolbar!
|
|
||||||
pub fn set_toolbar<T: ToolbarDelegate + 'static>(&self, identifier: &str, toolbar: T) {
|
|
||||||
let mut window = self.0.borrow_mut();
|
|
||||||
window.set_toolbar(identifier, toolbar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the content view controller for the window.
|
/// Sets the content view controller for the window.
|
||||||
pub fn set_content_view<T: ViewController + ViewWrapper + 'static>(&self, view: &T) {
|
pub fn set_content_view_controller<VC: Node + 'static>(&self, view_controller: &VC) {
|
||||||
let mut window = self.0.borrow_mut();
|
self.objc_controller.set_content_view_controller(view_controller);
|
||||||
window.set_content_view(view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows the window, running a configuration pass if necessary.
|
/// Shows the window, running a configuration pass if necessary.
|
||||||
pub fn show<T: WindowController + 'static>(&self, controller: &T) {
|
pub fn show(&self) {
|
||||||
let did_load = {
|
self.objc_controller.show();
|
||||||
let mut window = self.0.borrow_mut();
|
|
||||||
|
|
||||||
if window.controller.is_none() {
|
|
||||||
window.configure(controller);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if did_load {
|
|
||||||
controller.did_load();
|
|
||||||
}
|
|
||||||
|
|
||||||
let window = self.0.borrow();
|
|
||||||
window.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closes the window.
|
/// Closes the window.
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
let window = self.0.borrow();
|
self.objc_controller.close();
|
||||||
window.close();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for Window<T> {
|
||||||
|
/// When a Window is dropped on the Rust side, we want to ensure that we break the delegate
|
||||||
|
/// link on the Objective-C side. While this shouldn't actually be an issue, I'd rather be
|
||||||
|
/// safer than sorry.
|
||||||
|
///
|
||||||
|
/// We also clean up our loopback pointer that we use for callbacks.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(objc_controller) = &self.objc_controller.0 {
|
||||||
|
let window: id = msg_send![*objc_controller, window];
|
||||||
|
let _: () = msg_send![window, setDelegate:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = Rc::from_raw(self.internal_callback_ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue