Refactor rest of modules to use local foundation
This commit is contained in:
parent
c16dad564e
commit
aacfc81b99
|
@ -11,10 +11,10 @@ dispatch = "0.2.0"
|
|||
libc = "0.2"
|
||||
objc = "0.2.7"
|
||||
objc_id = "0.1.1"
|
||||
#uuid = { version = "0.8", features = ["v4"] }
|
||||
uuid = { version = "0.8", features = ["v4"], optional = true }
|
||||
url = "2.1.1"
|
||||
|
||||
[features]
|
||||
cloudkit = []
|
||||
user-notifications = []
|
||||
user-notifications = ["uuid"]
|
||||
enable-webview-downloading = []
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Certain enums that are useful (response types, etc).
|
||||
|
||||
use cocoa::foundation::{NSInteger, NSUInteger};
|
||||
use crate::foundation::{NSInteger, NSUInteger};
|
||||
|
||||
pub enum ModalResponse {
|
||||
Ok,
|
||||
|
|
|
@ -4,16 +4,14 @@
|
|||
use std::error::Error;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use cocoa::base::{id, nil, NO};
|
||||
use cocoa::foundation::{NSString, NSUInteger};
|
||||
use objc_id::Id;
|
||||
use objc::runtime::{BOOL, Object};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use url::Url;
|
||||
|
||||
use crate::foundation::{id, nil, NO, NSString, NSUInteger};
|
||||
use crate::error::AppKitError;
|
||||
use crate::filesystem::enums::{SearchPathDirectory, SearchPathDomainMask};
|
||||
use crate::utils::str_from;
|
||||
|
||||
pub struct FileManager {
|
||||
pub manager: RwLock<Id<Object>>
|
||||
|
@ -58,8 +56,7 @@ impl FileManager {
|
|||
create:NO
|
||||
error:nil];
|
||||
|
||||
let s: id = msg_send![dir, absoluteString];
|
||||
str_from(s)
|
||||
NSString::wrap(msg_send![dir, absoluteString]).to_str()
|
||||
};
|
||||
|
||||
Url::parse(directory).map_err(|e| e.into())
|
||||
|
@ -69,12 +66,12 @@ impl FileManager {
|
|||
/// an error on the Objective-C side, which we attempt to handle and bubble up as a result if
|
||||
/// so.
|
||||
pub fn move_item(&self, from: Url, to: Url) -> Result<(), Box<dyn Error>> {
|
||||
unsafe {
|
||||
let s = NSString::alloc(nil).init_str(from.as_str());
|
||||
let from_url: id = msg_send![class!(NSURL), URLWithString:s];
|
||||
let from = NSString::new(from.as_str());
|
||||
let to = NSString::new(to.as_str());
|
||||
|
||||
let s2 = NSString::alloc(nil).init_str(to.as_str());
|
||||
let to_url: id = msg_send![class!(NSURL), URLWithString:s2];
|
||||
unsafe {
|
||||
let from_url: id = msg_send![class!(NSURL), URLWithString:from.into_inner()];
|
||||
let to_url: id = msg_send![class!(NSURL), URLWithString:to.into_inner()];
|
||||
|
||||
// This should potentially be write(), but the backing class handles this logic
|
||||
// already, so... going to leave it as read.
|
||||
|
|
|
@ -4,14 +4,11 @@
|
|||
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use cocoa::base::{id, nil, YES, NO};
|
||||
use cocoa::foundation::{NSInteger, NSString};
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::utils::str_from;
|
||||
use crate::foundation::{id, nil, YES, NO, NSInteger, NSString};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileSavePanel {
|
||||
|
@ -54,7 +51,7 @@ impl FileSavePanel {
|
|||
|
||||
pub fn set_suggested_filename(&mut self, suggested_filename: &str) {
|
||||
unsafe {
|
||||
let filename = NSString::alloc(nil).init_str(suggested_filename);
|
||||
let filename = NSString::new(suggested_filename);
|
||||
let _: () = msg_send![&*self.panel, setNameFieldStringValue:filename];
|
||||
}
|
||||
}
|
||||
|
@ -100,11 +97,12 @@ impl FileSavePanel {
|
|||
pub fn get_url(panel: &Object) -> Option<String> {
|
||||
unsafe {
|
||||
let url: id = msg_send![&*panel, URL];
|
||||
|
||||
if url == nil {
|
||||
None
|
||||
} else {
|
||||
let path: id = msg_send![url, path];
|
||||
Some(str_from(path).to_string())
|
||||
Some(NSString::wrap(path).to_str().to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,12 @@
|
|||
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use cocoa::base::{id, YES, NO};
|
||||
use cocoa::foundation::NSInteger;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::foundation::{id, YES, NO, NSInteger, NSString};
|
||||
use crate::filesystem::enums::ModalResponse;
|
||||
use crate::utils::str_from;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileSelectPanel {
|
||||
|
@ -156,8 +153,8 @@ pub fn get_urls(panel: &Object) -> Vec<String> {
|
|||
}
|
||||
|
||||
let url: id = msg_send![urls, objectAtIndex:count-1];
|
||||
let path: id = msg_send![url, absoluteString];
|
||||
paths.push(str_from(path).to_string());
|
||||
let path = NSString::wrap(msg_send![url, absoluteString]).to_str().to_string();
|
||||
paths.push(path);
|
||||
count -= 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@ pub struct Rect {
|
|||
}
|
||||
|
||||
impl Rect {
|
||||
/// Returns a new `Rect` initialized with the values specified.
|
||||
pub fn new(top: f64, left: f64, width: f64, height: f64) -> Self {
|
||||
Rect { top: top, left: left, width: width, height: height }
|
||||
}
|
||||
|
||||
/// Returns a zero'd out Rect, with f64 (32-bit is mostly dead on Cocoa, so... this is "okay").
|
||||
pub fn zero() -> Rect {
|
||||
Rect {
|
||||
|
|
|
@ -29,7 +29,7 @@ pub mod constants;
|
|||
pub mod dragdrop;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
//pub mod filesystem;
|
||||
pub mod filesystem;
|
||||
pub mod foundation;
|
||||
pub mod geometry;
|
||||
pub mod layout;
|
||||
|
@ -44,8 +44,8 @@ pub mod printing;
|
|||
pub mod toolbar;
|
||||
pub mod user_activity;
|
||||
pub mod utils;
|
||||
/*pub mod view;
|
||||
pub mod webview;
|
||||
pub mod view;
|
||||
//pub mod webview;
|
||||
pub mod window;
|
||||
|
||||
// We re-export these so that they can be used without increasing build times.
|
||||
|
@ -69,13 +69,9 @@ pub mod prelude {
|
|||
Window, WindowController, WindowHandle
|
||||
};
|
||||
|
||||
pub use crate::webview::{
|
||||
WebView, WebViewConfig, WebViewController
|
||||
};
|
||||
//pub use crate::webview::{
|
||||
// WebView, WebViewConfig, WebViewController
|
||||
//};
|
||||
|
||||
pub use crate::view::{View, ViewHandle, ViewController};
|
||||
|
||||
pub use appkit_derive::{
|
||||
WindowWrapper, ViewWrapper
|
||||
};
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
//! Wraps UNUserNotificationCenter for macOS. Note that this uses the newer
|
||||
//! `UserNotifications.framework` API, which requires that your application be properly signed.
|
||||
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::NSString;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::notifications::Notification;
|
||||
use crate::utils::str_from;
|
||||
|
||||
#[allow(non_upper_case_globals, non_snake_case)]
|
||||
pub mod NotificationAuthOption {
|
||||
pub const Badge: i32 = 1 << 0;
|
||||
pub const Sound: i32 = 1 << 1;
|
||||
pub const Alert: i32 = 1 << 2;
|
||||
}
|
||||
|
||||
/// Acts as a central interface to the Notification Center on macOS.
|
||||
pub struct NotificationCenter;
|
||||
|
||||
impl NotificationCenter {
|
||||
/// Requests authorization from the user to send them notifications.
|
||||
pub fn request_authorization(options: i32) {
|
||||
unsafe {
|
||||
let block = ConcreteBlock::new(|_: id, error: id| {
|
||||
let msg: id = msg_send![error, localizedDescription];
|
||||
|
||||
let localized_description = str_from(msg);
|
||||
if localized_description != "" {
|
||||
println!("{:?}", localized_description);
|
||||
}
|
||||
});
|
||||
|
||||
let center: id = msg_send![class!(UNUserNotificationCenter), currentNotificationCenter];
|
||||
let _: () = msg_send![center, requestAuthorizationWithOptions:options completionHandler:block.copy()];
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues up a `Notification` to be displayed to the user.
|
||||
pub fn notify(notification: Notification) {
|
||||
let uuidentifier = format!("{}", uuid::Uuid::new_v4());
|
||||
|
||||
unsafe {
|
||||
let identifier = NSString::alloc(nil).init_str(&uuidentifier);
|
||||
let request: id = msg_send![class!(UNNotificationRequest), requestWithIdentifier:identifier content:&*notification.inner trigger:nil];
|
||||
|
||||
let center: id = msg_send![class!(UNUserNotificationCenter), currentNotificationCenter];
|
||||
let _: () = msg_send![center, addNotificationRequest:request];
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all notifications that have been delivered (e.g, in the notification center).
|
||||
pub fn remove_all_delivered_notifications() {
|
||||
unsafe {
|
||||
let center: id = msg_send![class!(UNUserNotificationCenter), currentNotificationCenter];
|
||||
let _: () = msg_send![center, removeAllDeliveredNotifications];
|
||||
}
|
||||
}
|
||||
}
|
29
appkit/src/notifications/enums.rs
Normal file
29
appkit/src/notifications/enums.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
//! Enums used in notifications - e.g, for customizing registration or appearance.
|
||||
|
||||
use crate::foundation::NSUInteger;
|
||||
|
||||
pub enum NotificationAuthOption {
|
||||
Badge,
|
||||
Sound,
|
||||
Alert
|
||||
}
|
||||
|
||||
impl From<NotificationAuthOption> for NSUInteger {
|
||||
fn from(option: NotificationAuthOption) -> Self {
|
||||
match option {
|
||||
NotificationAuthOption::Badge => 1 << 0,
|
||||
NotificationAuthOption::Sound => 1 << 1,
|
||||
NotificationAuthOption::Alert => 1 << 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&NotificationAuthOption> for NSUInteger {
|
||||
fn from(option: &NotificationAuthOption) -> Self {
|
||||
match option {
|
||||
NotificationAuthOption::Badge => 1 << 0,
|
||||
NotificationAuthOption::Sound => 1 << 1,
|
||||
NotificationAuthOption::Alert => 1 << 2
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,65 @@
|
|||
//! Hoisting.
|
||||
//! Wraps UNUserNotificationCenter for macOS. Note that this uses the newer
|
||||
//! `UserNotifications.framework` API, which requires that your application be properly signed.
|
||||
//!
|
||||
//! To use this module, you must specify the `user-notifications` feature flag in your
|
||||
//! `Cargo.toml`.
|
||||
|
||||
pub mod center;
|
||||
pub use center::*;
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::foundation::{id, nil, NSString, NSUInteger};
|
||||
|
||||
pub mod enums;
|
||||
pub use enums::NotificationAuthOption;
|
||||
|
||||
pub mod notifications;
|
||||
pub use notifications::*;
|
||||
pub use notifications::Notification;
|
||||
|
||||
/// Acts as a central interface to the Notification Center on macOS.
|
||||
pub struct NotificationCenter;
|
||||
|
||||
impl NotificationCenter {
|
||||
/// Requests authorization from the user to send them notifications.
|
||||
pub fn request_authorization(options: &[NotificationAuthOption]) {
|
||||
unsafe {
|
||||
// @TODO: Revisit.
|
||||
let block = ConcreteBlock::new(|_: id, error: id| {
|
||||
let localized_description = NSString::new(msg_send![error, localizedDescription]).to_str();
|
||||
if localized_description != "" {
|
||||
println!("{:?}", localized_description);
|
||||
}
|
||||
});
|
||||
|
||||
let mut opts: NSUInteger = 0;
|
||||
for opt in options {
|
||||
let o: NSUInteger = opt.into();
|
||||
opts = opts << o;
|
||||
}
|
||||
|
||||
let center: id = msg_send![class!(UNUserNotificationCenter), currentNotificationCenter];
|
||||
let _: () = msg_send![center, requestAuthorizationWithOptions:opts completionHandler:block.copy()];
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues up a `Notification` to be displayed to the user.
|
||||
pub fn notify(notification: Notification) {
|
||||
let uuidentifier = format!("{}", Uuid::new_v4());
|
||||
|
||||
unsafe {
|
||||
let identifier = NSString::new(&uuidentifier);
|
||||
let request: id = msg_send![class!(UNNotificationRequest), requestWithIdentifier:identifier content:&*notification.0 trigger:nil];
|
||||
let center: id = msg_send![class!(UNUserNotificationCenter), currentNotificationCenter];
|
||||
let _: () = msg_send![center, addNotificationRequest:request];
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all notifications that have been delivered (e.g, in the notification center).
|
||||
pub fn remove_all_delivered_notifications() {
|
||||
unsafe {
|
||||
let center: id = msg_send![class!(UNUserNotificationCenter), currentNotificationCenter];
|
||||
let _: () = msg_send![center, removeAllDeliveredNotifications];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +1,28 @@
|
|||
//! Acts as a (currently dumb) wrapper for `UNMutableNotificationContent`, which is what you mostly
|
||||
//! need to pass to the notification center for things to work.
|
||||
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::NSString;
|
||||
|
||||
use objc_id::Id;
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, NSString};
|
||||
|
||||
/// A wrapper for `UNMutableNotificationContent`. Retains the pointer from the Objective C side,
|
||||
/// and is ultimately dropped upon sending.
|
||||
pub struct Notification {
|
||||
pub inner: Id<Object>
|
||||
}
|
||||
pub struct Notification(pub Id<Object>);
|
||||
|
||||
impl Notification {
|
||||
/// Constructs a new `Notification`. This allocates `NSString`'s, as it has to do so for the
|
||||
/// Objective C runtime - be aware if you're slaming this (you shouldn't be slamming this).
|
||||
pub fn new(title: &str, body: &str) -> Self {
|
||||
Notification {
|
||||
inner: unsafe {
|
||||
let cls = class!(UNMutableNotificationContent);
|
||||
let content: id = msg_send![cls, new];
|
||||
let title = NSString::new(title);
|
||||
let body = NSString::new(body);
|
||||
|
||||
let title = NSString::alloc(nil).init_str(title);
|
||||
Notification(unsafe {
|
||||
let content: id = msg_send![class!(UNMutableNotificationContent), new];
|
||||
let _: () = msg_send![content, setTitle:title];
|
||||
|
||||
let body = NSString::alloc(nil).init_str(body);
|
||||
let _: () = msg_send![content, setBody:body];
|
||||
|
||||
Id::from_ptr(content)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,20 +10,17 @@
|
|||
use std::rc::Rc;
|
||||
use std::sync::Once;
|
||||
|
||||
use cocoa::base::{id, nil, YES, NO};
|
||||
use cocoa::foundation::{NSUInteger};
|
||||
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel, BOOL};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, NSUInteger};
|
||||
use crate::constants::{BACKGROUND_COLOR, VIEW_CONTROLLER_PTR};
|
||||
use crate::dragdrop::DragInfo;
|
||||
use crate::view::traits::ViewController;
|
||||
use crate::utils::load;
|
||||
|
||||
|
||||
/// Enforces normalcy, or: a needlessly cruel method in terms of the name. You get the idea though.
|
||||
extern fn enforce_normalcy(_: &Object, _: Sel) -> BOOL {
|
||||
return YES;
|
||||
|
|
|
@ -3,13 +3,11 @@
|
|||
|
||||
use std::sync::Once;
|
||||
|
||||
use cocoa::base::{id, NO};
|
||||
use cocoa::foundation::{NSRect};
|
||||
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, NO, CGRect};
|
||||
use crate::constants::VIEW_CONTROLLER_PTR;
|
||||
use crate::geometry::Rect;
|
||||
use crate::view::ViewController;
|
||||
|
@ -18,7 +16,7 @@ use crate::view::class::register_view_class;
|
|||
/// Loads and configures ye old NSView for this controller.
|
||||
extern fn load_view<T: ViewController>(this: &mut Object, _: Sel) {
|
||||
unsafe {
|
||||
let zero: NSRect = Rect::zero().into();
|
||||
let zero: CGRect = Rect::zero().into();
|
||||
let view: id = msg_send![register_view_class::<T>(), new];
|
||||
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
let _: () = msg_send![view, setFrame:zero];
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
//! A wrapper for `NSViewController`. Uses interior mutability to
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use cocoa::base::{id, nil, YES};
|
||||
use cocoa::foundation::NSArray;
|
||||
//! A `ViewHandle` represents an underlying `NSView`. You're passed a reference to one during your
|
||||
//! `ViewController::did_load()` method. This method is safe to store and use, however as it's
|
||||
//! UI-specific it's not thread safe.
|
||||
//!
|
||||
//! You can use this struct to configure how a view should look and layout. It implements
|
||||
//! AutoLayout - for more information, see the AutoLayout tutorial.
|
||||
|
||||
use objc_id::ShareId;
|
||||
use objc::runtime::Object;
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, YES, NSArray, NSString};
|
||||
use crate::color::Color;
|
||||
use crate::constants::{BACKGROUND_COLOR, VIEW_CONTROLLER_PTR};
|
||||
use crate::constants::BACKGROUND_COLOR;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::pasteboard::PasteboardType;
|
||||
use crate::view::controller::register_controller_class;
|
||||
use crate::view::traits::ViewController;
|
||||
|
||||
/// A clone-able handler to a `ViewController` reference in the Objective C runtime. We use this
|
||||
/// instead of a stock `View` for easier recordkeeping, since it'll need to hold the `View` on that
|
||||
|
@ -84,12 +82,15 @@ impl ViewHandle {
|
|||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||
if let Some(objc) = &self.objc {
|
||||
unsafe {
|
||||
let types = NSArray::arrayWithObjects(nil, &types.iter().map(|t| {
|
||||
t.to_nsstring()
|
||||
}).collect::<Vec<id>>());
|
||||
let types: NSArray = types.into_iter().map(|t| {
|
||||
// This clone probably doesn't need to be here, but it should also be cheap as
|
||||
// this is just an enum... and this is not an oft called method.
|
||||
let x: NSString = t.clone().into();
|
||||
x.into_inner()
|
||||
}).collect::<Vec<id>>().into();
|
||||
|
||||
let view: id = msg_send![*objc, view];
|
||||
let _: () = msg_send![view, registerForDraggedTypes:types];
|
||||
let _: () = msg_send![view, registerForDraggedTypes:types.into_inner()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,107 +109,3 @@ impl ViewHandle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 handle = ViewHandle::new(inner);
|
||||
|
||||
{
|
||||
let mut vc = controller.borrow_mut();
|
||||
(*vc).did_load(handle.clone());
|
||||
}
|
||||
|
||||
View {
|
||||
internal_callback_ptr: internal_callback_ptr,
|
||||
objc_controller: handle,
|
||||
controller: controller
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_background_color(&self, color: Color) {
|
||||
self.objc_controller.set_background_color(color);
|
||||
}
|
||||
|
||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||
self.objc_controller.register_for_dragged_types(types);
|
||||
}
|
||||
|
||||
pub fn top(&self) -> &LayoutAnchorY {
|
||||
&self.objc_controller.top
|
||||
}
|
||||
|
||||
pub fn leading(&self) -> &LayoutAnchorX {
|
||||
&self.objc_controller.leading
|
||||
}
|
||||
|
||||
pub fn trailing(&self) -> &LayoutAnchorX {
|
||||
&self.objc_controller.trailing
|
||||
}
|
||||
|
||||
pub fn bottom(&self) -> &LayoutAnchorY {
|
||||
&self.objc_controller.bottom
|
||||
}
|
||||
|
||||
pub fn width(&self) -> &LayoutAnchorDimension {
|
||||
&self.objc_controller.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> &LayoutAnchorDimension {
|
||||
&self.objc_controller.height
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout 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.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, subview: &V) {
|
||||
self.objc_controller.add_subview(subview);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for View<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,156 @@
|
|||
//! This module implements tooling for constructing Views. Notably, it provides the following:
|
||||
//!
|
||||
//! - A `View` type, which holds your `impl ViewController` and handles routing around platform
|
||||
//! lifecycle methods accordingly. This is a heap allocation.
|
||||
//! - A `ViewController` trait, which enables you to hook into the `NSViewController` lifecycle
|
||||
//! methods.
|
||||
//! - A `ViewHandle` struct, which wraps a platform-provided `NSView` and enables you to configure
|
||||
//! things such as appearance and layout.
|
||||
//!
|
||||
//! You can use it like the following:
|
||||
//!
|
||||
//! ```
|
||||
//! use appkit::prelude::{View, ViewController, ViewHandle};
|
||||
//! use appkit::color::rgb;
|
||||
//!
|
||||
//! #[derive(Default)]
|
||||
//! pub struct MyView {
|
||||
//! pub view: ViewHandle
|
||||
//! }
|
||||
//!
|
||||
//! impl ViewController for MyView {
|
||||
//! fn did_load(&mut self, view: ViewHandle) {
|
||||
//! self.view = view;
|
||||
//! self.view.set_background_color(rgb(0, 0, 0));
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! For more information and examples, consult the sample code distributed in the git repository.
|
||||
|
||||
pub(crate) mod class;
|
||||
pub(crate) mod controller;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use objc_id::ShareId;
|
||||
use objc::runtime::Object;
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::id;
|
||||
use crate::color::Color;
|
||||
use crate::constants::VIEW_CONTROLLER_PTR;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::pasteboard::PasteboardType;
|
||||
|
||||
mod class;
|
||||
mod controller;
|
||||
use controller::register_controller_class;
|
||||
|
||||
pub mod traits;
|
||||
pub use traits::*;
|
||||
pub use traits::ViewController;
|
||||
|
||||
pub mod view;
|
||||
pub use view::{View, ViewHandle};
|
||||
pub mod handle;
|
||||
pub use handle::ViewHandle;
|
||||
|
||||
/// 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 handle = ViewHandle::new(inner);
|
||||
|
||||
{
|
||||
let mut vc = controller.borrow_mut();
|
||||
(*vc).did_load(handle.clone());
|
||||
}
|
||||
|
||||
View {
|
||||
internal_callback_ptr: internal_callback_ptr,
|
||||
objc_controller: handle,
|
||||
controller: controller
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_background_color(&self, color: Color) {
|
||||
self.objc_controller.set_background_color(color);
|
||||
}
|
||||
|
||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||
self.objc_controller.register_for_dragged_types(types);
|
||||
}
|
||||
|
||||
pub fn top(&self) -> &LayoutAnchorY {
|
||||
&self.objc_controller.top
|
||||
}
|
||||
|
||||
pub fn leading(&self) -> &LayoutAnchorX {
|
||||
&self.objc_controller.leading
|
||||
}
|
||||
|
||||
pub fn trailing(&self) -> &LayoutAnchorX {
|
||||
&self.objc_controller.trailing
|
||||
}
|
||||
|
||||
pub fn bottom(&self) -> &LayoutAnchorY {
|
||||
&self.objc_controller.bottom
|
||||
}
|
||||
|
||||
pub fn width(&self) -> &LayoutAnchorDimension {
|
||||
&self.objc_controller.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> &LayoutAnchorDimension {
|
||||
&self.objc_controller.height
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout 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.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, subview: &V) {
|
||||
self.objc_controller.add_subview(subview);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for View<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
//! Cocoa and associated widgets. This also handles looping back
|
||||
//! lifecycle events, such as window resizing or close events.
|
||||
|
||||
use cocoa::base::{id, YES, NO};
|
||||
use cocoa::foundation::{NSRect, NSPoint, NSSize, NSUInteger};
|
||||
|
||||
use objc_id::Id;
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, YES, NO, NSUInteger, CGRect};
|
||||
use crate::geometry::Rect;
|
||||
|
||||
#[allow(non_upper_case_globals, non_snake_case)]
|
||||
pub mod WindowStyle {
|
||||
use cocoa::foundation::NSUInteger;
|
||||
use crate::foundation::NSUInteger;
|
||||
|
||||
pub const Borderless: NSUInteger = 0;
|
||||
pub const Titled: NSUInteger = 1 << 0;
|
||||
|
@ -32,7 +32,7 @@ pub struct WindowConfig(pub Id<Object>);
|
|||
impl Default for WindowConfig {
|
||||
fn default() -> Self {
|
||||
WindowConfig(unsafe {
|
||||
let dimensions = NSRect::new(NSPoint::new(0., 0.), NSSize::new(800., 600.));
|
||||
let dimensions: CGRect = Rect::new(0., 0., 800., 600.).into();
|
||||
|
||||
let style = WindowStyle::Resizable | WindowStyle::Miniaturizable | WindowStyle::UnifiedTitleAndToolbar |
|
||||
WindowStyle::Closable | WindowStyle::Titled;
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
use std::rc::Rc;
|
||||
use std::sync::Once;
|
||||
|
||||
use cocoa::base::id;
|
||||
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, sel, sel_impl};
|
||||
|
||||
use crate::foundation::id;
|
||||
use crate::constants::WINDOW_CONTROLLER_PTR;
|
||||
use crate::utils::load;
|
||||
use crate::window::WindowController;
|
||||
|
|
15
appkit/src/window/enums.rs
Normal file
15
appkit/src/window/enums.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
//! Enums used in Window construction and handling.
|
||||
|
||||
pub enum WindowTitleVisibility {
|
||||
Visible,
|
||||
Hidden
|
||||
}
|
||||
|
||||
impl From<WindowTitleVisibility> for usize {
|
||||
fn from(visibility: WindowTitleVisibility) -> usize {
|
||||
match visibility {
|
||||
WindowTitleVisibility::Visible => 0,
|
||||
WindowTitleVisibility::Hidden => 1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,15 +4,12 @@
|
|||
//! 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::foundation::{id, nil, YES, NO, CGSize, NSString};
|
||||
use crate::layout::traits::Layout;
|
||||
|
||||
use crate::toolbar::{Toolbar, ToolbarController};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -24,7 +21,7 @@ impl WindowHandle {
|
|||
pub fn set_title(&self, title: &str) {
|
||||
if let Some(controller) = &self.0 {
|
||||
unsafe {
|
||||
let title = NSString::alloc(nil).init_str(title);
|
||||
let title = NSString::new(title);
|
||||
let window: id = msg_send![*controller, window];
|
||||
let _: () = msg_send![window, setTitle:title];
|
||||
}
|
||||
|
@ -72,7 +69,7 @@ impl WindowHandle {
|
|||
if let Some(controller) = &self.0 {
|
||||
unsafe {
|
||||
let window: id = msg_send![*controller, window];
|
||||
let autosave = NSString::alloc(nil).init_str(name);
|
||||
let autosave = NSString::new(name);
|
||||
let _: () = msg_send![window, setFrameAutosaveName:autosave];
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +79,7 @@ impl WindowHandle {
|
|||
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 size = CGSize::new(width.into(), height.into());
|
||||
let window: id = msg_send![*controller, window];
|
||||
let _: () = msg_send![window, setMinSize:size];
|
||||
}
|
||||
|
|
|
@ -1,9 +1,59 @@
|
|||
//! Implements wrappers and traits for `NSWindowController` and associated types.
|
||||
//! Implements Window controls for macOS, by wrapping the various necessary moving pieces
|
||||
//! (`NSWindow`, `NSWindowController`, and `NSWindowDelegate`) into one trait that you can
|
||||
//! implement.
|
||||
//!
|
||||
//! For example:
|
||||
//!
|
||||
//! ```
|
||||
//! use appkit::prelude::{AppController, Window};
|
||||
//! use window::MyWindow;
|
||||
//!
|
||||
//! pub struct MyApp(Window<MyWindow>);
|
||||
//!
|
||||
//! impl MyApp {
|
||||
//! pub fn new() -> Self {
|
||||
//! MyApp(Window::new(MyWindow {
|
||||
//! // Your things here
|
||||
//! }))
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl AppController for MyApp {
|
||||
//! fn did_load(&self) {
|
||||
//! self.0.show();
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This simulate class-based structures well enough - you just can't subclass. Now you can do the
|
||||
//! following:
|
||||
//!
|
||||
//! ```
|
||||
//! use appkit::prelude::{WindowController, WindowHandle};
|
||||
//!
|
||||
//! pub struct MyWindow;
|
||||
//!
|
||||
//! impl WindowController for MyWindow {
|
||||
//! fn did_load(&mut self, handle: WindowHandle) {}
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
pub mod traits;
|
||||
pub use traits::WindowController;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::foundation::{id, nil};
|
||||
use crate::constants::WINDOW_CONTROLLER_PTR;
|
||||
use crate::layout::traits::Layout;
|
||||
use crate::toolbar::{Toolbar, ToolbarController};
|
||||
|
||||
mod controller;
|
||||
use controller::register_window_controller_class;
|
||||
|
||||
pub mod enums;
|
||||
pub use enums::{WindowTitleVisibility};
|
||||
|
||||
pub mod config;
|
||||
pub use config::{WindowConfig, WindowStyle};
|
||||
|
@ -11,5 +61,106 @@ pub use config::{WindowConfig, WindowStyle};
|
|||
pub mod handle;
|
||||
pub use handle::WindowHandle;
|
||||
|
||||
pub mod window;
|
||||
pub use window::{Window, WindowTitleVisibility};
|
||||
pub mod traits;
|
||||
pub use traits::WindowController;
|
||||
|
||||
/// A `Window` represents your way of interacting with an `NSWindow`. It wraps the various moving
|
||||
/// pieces to enable you to focus on reacting to lifecycle methods and doing your thing.
|
||||
#[derive(Clone, Debug)]
|
||||
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
|
||||
/// `NSWindow` in `[NSWindowController loadWindow]`?
|
||||
///
|
||||
/// This is a great question. It's because NSWindowController is... well, broken or buggy -
|
||||
/// pick a term, either works. It's optimized for loading from xib/nib files, and attempting to
|
||||
/// get loadWindow to fire properly is a pain in the rear (you're fighting a black box).
|
||||
///
|
||||
/// This is why we do this work here, but for things subclassing `NSViewController`, we go with
|
||||
/// the route of implementing `loadView`.
|
||||
///
|
||||
/// APPKIT!
|
||||
pub fn new(controller: T) -> Self {
|
||||
let window = controller.config().0;
|
||||
let controller = Rc::new(RefCell::new(controller));
|
||||
|
||||
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 controller_alloc: id = msg_send![window_controller_class, alloc];
|
||||
let controller: id = msg_send![controller_alloc, initWithWindow:window];
|
||||
(&mut *controller).set_ivar(WINDOW_CONTROLLER_PTR, internal_callback_ptr as usize);
|
||||
|
||||
let window: id = msg_send![controller, window];
|
||||
let _: () = msg_send![window, setDelegate:controller];
|
||||
|
||||
ShareId::from_ptr(controller)
|
||||
};
|
||||
|
||||
{
|
||||
let mut vc = controller.borrow_mut();
|
||||
(*vc).did_load(WindowHandle(Some(inner.clone())));
|
||||
}
|
||||
|
||||
Window {
|
||||
internal_callback_ptr: internal_callback_ptr,
|
||||
objc_controller: WindowHandle(Some(inner)),
|
||||
controller: controller
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the title for this window.
|
||||
pub fn set_title(&self, title: &str) {
|
||||
self.objc_controller.set_title(title.into());
|
||||
}
|
||||
|
||||
/// Sets the toolbar for this window.
|
||||
pub fn set_toolbar<TC: ToolbarController + 'static>(&self, toolbar: &Toolbar<TC>) {
|
||||
self.objc_controller.set_toolbar(toolbar);
|
||||
}
|
||||
|
||||
/// Sets the content view controller for the window.
|
||||
pub fn set_content_view_controller<VC: Layout + 'static>(&self, view_controller: &VC) {
|
||||
self.objc_controller.set_content_view_controller(view_controller);
|
||||
}
|
||||
|
||||
/// Shows the window, running a configuration pass if necessary.
|
||||
pub fn show(&self) {
|
||||
self.objc_controller.show();
|
||||
}
|
||||
|
||||
/// Closes the window.
|
||||
pub fn close(&self) {
|
||||
self.objc_controller.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,23 +16,9 @@ use crate::window::handle::WindowHandle;
|
|||
use crate::window::traits::WindowController;
|
||||
use crate::window::controller::register_window_controller_class;
|
||||
|
||||
pub enum WindowTitleVisibility {
|
||||
Visible,
|
||||
Hidden
|
||||
}
|
||||
|
||||
impl From<WindowTitleVisibility> for usize {
|
||||
fn from(visibility: WindowTitleVisibility) -> usize {
|
||||
match visibility {
|
||||
WindowTitleVisibility::Visible => 0,
|
||||
WindowTitleVisibility::Hidden => 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Window` represents your way of interacting with an `NSWindow`. It wraps the various moving
|
||||
/// pieces to enable you to focus on reacting to lifecycle methods and doing your thing.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Window<T> {
|
||||
internal_callback_ptr: *const RefCell<T>,
|
||||
pub objc_controller: WindowHandle,
|
||||
|
|
Loading…
Reference in a new issue