Ongoing efforts, experimenting with stack/heap issues (ObjC/NSObject/AppKit are... mostly heap already), further work on Pasteboard support
This commit is contained in:
parent
d68cbdc450
commit
80ba209413
|
@ -16,6 +16,7 @@ lazy_static = "1"
|
|||
objc = "0.2.7"
|
||||
objc_id = "0.1.1"
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
url = "2.1.1"
|
||||
|
||||
[features]
|
||||
enable-webview-downloading = []
|
||||
|
|
|
@ -5,8 +5,11 @@
|
|||
use cocoa::foundation::NSUInteger;
|
||||
|
||||
use objc::runtime::Object;
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::pasteboard::Pasteboard;
|
||||
|
||||
/// Represents operations that can happen for a given drag/drop scenario.
|
||||
pub enum DragOperation {
|
||||
/// No drag operations are allowed.
|
||||
|
@ -56,5 +59,13 @@ pub struct DragInfo {
|
|||
}
|
||||
|
||||
impl DragInfo {
|
||||
|
||||
/// Returns a wrapped Pasteboard instance, enabling you to get the contents of whatever is
|
||||
/// being pasted/dragged/dropped/etc.
|
||||
///
|
||||
/// Note: in general, you should not store pasteboards.
|
||||
pub fn get_pasteboard(&self) -> Pasteboard {
|
||||
unsafe {
|
||||
Pasteboard::with(msg_send![&*self.info, draggingPasteboard])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
51
appkit/src/error.rs
Normal file
51
appkit/src/error.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
//! A wrapper for `NSError`, which can be (and is) bubbled up for certain calls in this library. It
|
||||
//! attempts to be thread safe where possible, and extract the "default" usable information out of
|
||||
//! an `NSError`. This might not be what you need, though, so if it's missing something... well,
|
||||
//! it's up for discussion.
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
use cocoa::base::id;
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use crate::utils::str_from;
|
||||
|
||||
/// A wrapper around pieces of data extracted from `NSError`. This could be improved: right now, it
|
||||
/// allocates `String` instances when theoretically it could be avoided, and we might be erasing
|
||||
/// certain parts of the `NSError` object that are useful.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AppKitError {
|
||||
pub code: usize,
|
||||
pub domain: String,
|
||||
pub description: String
|
||||
}
|
||||
|
||||
impl AppKitError {
|
||||
/// Given an `NSError` (i.e, an id reference) we'll pull out the relevant information and
|
||||
/// configure this. We pull out the information as it makes the error thread safe this way,
|
||||
/// which is... easier, in some cases.
|
||||
pub fn new(error: id) -> Box<Self> {
|
||||
let (code, domain, description) = unsafe {
|
||||
let code: usize = msg_send![error, code];
|
||||
let domain: id = msg_send![error, domain];
|
||||
let description: id = msg_send![error, localizedDescription];
|
||||
|
||||
(code, domain, description)
|
||||
};
|
||||
|
||||
Box::new(AppKitError {
|
||||
code: code,
|
||||
domain: str_from(domain).to_string(),
|
||||
description: str_from(description).to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AppKitError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.description)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for AppKitError {}
|
|
@ -1,69 +1,92 @@
|
|||
//! A wrapper for `NSFileManager`, which is necessary for macOS/iOS (the sandbox makes things
|
||||
//! tricky, and this transparently handles it for you).
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use cocoa::base::{id, nil, NO};
|
||||
use cocoa::foundation::NSUInteger;
|
||||
|
||||
use cocoa::foundation::{NSString, NSUInteger};
|
||||
use objc_id::Id;
|
||||
use objc::runtime::Object;
|
||||
use objc::runtime::{BOOL, Object};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use url::Url;
|
||||
|
||||
use crate::error::AppKitError;
|
||||
use crate::filesystem::enums::{SearchPathDirectory, SearchPathDomainMask};
|
||||
use crate::utils::str_from;
|
||||
|
||||
pub struct FileManagerInner {
|
||||
pub manager: Id<Object>
|
||||
pub struct FileManager {
|
||||
pub manager: RwLock<Id<Object>>
|
||||
}
|
||||
|
||||
impl Default for FileManagerInner {
|
||||
impl Default for FileManager {
|
||||
/// Returns a default file manager, which maps to the default system file manager. For common
|
||||
/// and simple tasks, with no callbacks, you might want this.
|
||||
fn default() -> Self {
|
||||
FileManagerInner {
|
||||
manager: unsafe {
|
||||
FileManager {
|
||||
manager: RwLock::new(unsafe {
|
||||
let manager: id = msg_send![class!(NSFileManager), defaultManager];
|
||||
Id::from_ptr(manager)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileManagerInner {
|
||||
pub fn get_path(&self, directory: SearchPathDirectory, in_domain: SearchPathDomainMask) -> Result<String, Box<dyn std::error::Error>> {
|
||||
impl FileManager {
|
||||
/// Returns a new FileManager that opts in to delegate methods.
|
||||
pub fn new() -> Self {
|
||||
FileManager {
|
||||
manager: RwLock::new(unsafe {
|
||||
let manager: id = msg_send![class!(NSFileManager), new];
|
||||
Id::from_ptr(manager)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a directory/domain combination, will attempt to get the directory that matches.
|
||||
/// Returns a PathBuf that wraps the given location. If there's an error on the Objective-C
|
||||
/// side, we attempt to catch it and bubble it up.
|
||||
pub fn get_directory(&self, directory: SearchPathDirectory, in_domain: SearchPathDomainMask) -> Result<Url, Box<dyn Error>> {
|
||||
let dir: NSUInteger = directory.into();
|
||||
let mask: NSUInteger = in_domain.into();
|
||||
|
||||
unsafe {
|
||||
let dir: id = msg_send![&*self.manager, URLForDirectory:dir
|
||||
let directory = unsafe {
|
||||
let manager = self.manager.read().unwrap();
|
||||
let dir: id = msg_send![&**manager, URLForDirectory:dir
|
||||
inDomain:mask
|
||||
appropriateForURL:nil
|
||||
create:NO
|
||||
error:nil];
|
||||
|
||||
let s: id = msg_send![dir, path];
|
||||
Ok(str_from(s).to_string())
|
||||
let s: id = msg_send![dir, absoluteString];
|
||||
str_from(s)
|
||||
};
|
||||
|
||||
Url::parse(directory).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Given two paths, moves file (`from`) to the location specified in `to`. This can result in
|
||||
/// 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 s2 = NSString::alloc(nil).init_str(to.as_str());
|
||||
let to_url: id = msg_send![class!(NSURL), URLWithString:s2];
|
||||
|
||||
// This should potentially be write(), but the backing class handles this logic
|
||||
// already, so... going to leave it as read.
|
||||
let manager = self.manager.read().unwrap();
|
||||
|
||||
let error: id = nil;
|
||||
let result: BOOL = msg_send![&**manager, moveItemAtURL:from_url toURL:to_url error:&error];
|
||||
if result == NO {
|
||||
return Err(AppKitError::new(error));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FileManager(Rc<RefCell<FileManagerInner>>);
|
||||
|
||||
impl FileManager {
|
||||
pub fn new() -> Self {
|
||||
FileManager(Rc::new(RefCell::new(FileManagerInner {
|
||||
manager: unsafe {
|
||||
let manager: id = msg_send![class!(NSFileManager), new];
|
||||
Id::from_ptr(manager)
|
||||
Ok(())
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn get_path(&self, directory: SearchPathDirectory, in_domain: SearchPathDomainMask) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let manager = self.0.borrow();
|
||||
manager.get_path(directory, in_domain)
|
||||
}
|
||||
|
||||
//pub fn contents_of(directory: &str, properties: &[
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ pub mod color;
|
|||
pub mod collection_view;
|
||||
pub mod constants;
|
||||
pub mod dragdrop;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod filesystem;
|
||||
pub mod geometry;
|
||||
|
@ -40,6 +41,9 @@ pub mod view;
|
|||
pub mod webview;
|
||||
pub mod window;
|
||||
|
||||
// We re-export these so that they can be used without increasing build times.
|
||||
pub use url;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::app::{App, AppDelegate};
|
||||
|
||||
|
@ -60,6 +64,6 @@ pub mod prelude {
|
|||
pub use crate::view::{View, ViewController, ViewWrapper};
|
||||
|
||||
pub use appkit_derive::{
|
||||
WindowWrapper
|
||||
WindowWrapper, ViewWrapper
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,13 +2,18 @@
|
|||
//! (think: drag and drop between applications). It exposes a Rust interface that tries to be
|
||||
//! complete, but might not cover everything 100% right now - feel free to pull request.
|
||||
|
||||
use cocoa::base::id;
|
||||
use std::error::Error;
|
||||
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSArray};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
use url::Url;
|
||||
|
||||
use crate::pasteboard::types::PasteboardName;
|
||||
use crate::error::AppKitError;
|
||||
use crate::pasteboard::types::{PasteboardName, PasteboardType};
|
||||
use crate::utils::str_from;
|
||||
|
||||
/// Represents an `NSPasteboard`, enabling you to handle copy/paste/drag and drop.
|
||||
pub struct Pasteboard {
|
||||
|
@ -32,7 +37,7 @@ impl Pasteboard {
|
|||
}
|
||||
}
|
||||
|
||||
/// Should be pasteboardname enum!
|
||||
/// Retrieves the system Pasteboard for the given name/type.
|
||||
pub fn named(name: PasteboardName) -> Self {
|
||||
Pasteboard {
|
||||
inner: unsafe {
|
||||
|
@ -64,4 +69,60 @@ impl Pasteboard {
|
|||
let _: () = msg_send![&*self.inner, clearContents];
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks inside the pasteboard contents and extracts what FileURLs are there, if any.
|
||||
pub fn get_file_urls(&self) -> Result<Vec<Url>, Box<dyn Error>> {
|
||||
unsafe {
|
||||
let mut i = 0;
|
||||
|
||||
let class: id = msg_send![class!(NSURL), class];
|
||||
let classes: id = NSArray::arrayWithObjects(nil, &[class]);
|
||||
let contents: id = msg_send![&*self.inner, readObjectsForClasses:classes options:nil];
|
||||
|
||||
// This can happen if the Pasteboard server has an error in returning items.
|
||||
// In our case, we'll bubble up an error by checking the pasteboard.
|
||||
if contents == nil {
|
||||
// This error is not necessarily "correct", but in the event of an error in
|
||||
// Pasteboard server retrieval I'm not sure where to check... and this stuff is
|
||||
// kinda ancient and has conflicting docs in places. ;P
|
||||
return Err(Box::new(AppKitError {
|
||||
code: 666,
|
||||
domain: "com.appkit-rs.pasteboard".to_string(),
|
||||
description: "Pasteboard server returned no data.".to_string()
|
||||
}));
|
||||
}
|
||||
|
||||
let count: usize = msg_send![contents, count];
|
||||
let mut urls: Vec<Url> = Vec::with_capacity(count);
|
||||
|
||||
loop {
|
||||
let nsurl: id = msg_send![contents, objectAtIndex:i];
|
||||
let path: id = msg_send![nsurl, path];
|
||||
let s = str_from(path);
|
||||
urls.push(Url::parse(&format!("file://{}", s))?);
|
||||
|
||||
i += 1;
|
||||
if i == count { break; }
|
||||
}
|
||||
|
||||
Ok(urls)
|
||||
}
|
||||
}
|
||||
/*
|
||||
/// Retrieves the pasteboard contents as a string. This can be `None` (`nil` on the Objective-C
|
||||
/// side) if the pasteboard data doesn't match the requested type, so check accordingly.
|
||||
///
|
||||
/// Note: In macOS 10.6 and later, if the receiver contains multiple items that can provide string,
|
||||
/// RTF, or RTFD data, the text data from each item is returned as a combined result separated by newlines.
|
||||
/// This Rust wrapper is a quick pass, and could be improved. ;P
|
||||
pub fn contents_for(&self, pasteboard_type: PasteboardType) -> Option<String> {
|
||||
unsafe {
|
||||
let contents: id = msg_send![&*self.inner, stringForType:pasteboard_type.to_nsstring()];
|
||||
if contents != nil {
|
||||
return Some(str_from(contents).to_string());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -7,10 +7,19 @@ use cocoa::foundation::NSString;
|
|||
/// Constants for the standard system pasteboard names.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum PasteboardName {
|
||||
/// The dragging/dropping pasteboard.
|
||||
Drag,
|
||||
|
||||
/// The find pasteboard.
|
||||
Find,
|
||||
|
||||
/// The font pasteboard.
|
||||
Font,
|
||||
|
||||
/// The general pasteboard.
|
||||
General,
|
||||
|
||||
/// The ruler pasteboard.
|
||||
Ruler
|
||||
}
|
||||
|
||||
|
@ -19,11 +28,11 @@ impl PasteboardName {
|
|||
pub fn to_nsstring(&self) -> id {
|
||||
unsafe {
|
||||
NSString::alloc(nil).init_str(match self {
|
||||
PasteboardName::Drag => "",
|
||||
PasteboardName::Find => "",
|
||||
PasteboardName::Font => "",
|
||||
PasteboardName::General => "",
|
||||
PasteboardName::Ruler => ""
|
||||
PasteboardName::Drag => "Apple CFPasteboard drag",
|
||||
PasteboardName::Find => "Apple CFPasteboard find",
|
||||
PasteboardName::Font => "Apple CFPasteboard font",
|
||||
PasteboardName::General => "Apple CFPasteboard general",
|
||||
PasteboardName::Ruler => "Apple CFPasteboard ruler"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,10 @@ 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::constants::{BACKGROUND_COLOR, VIEW_CONTROLLER_PTR};
|
||||
use crate::dragdrop::DragInfo;
|
||||
use crate::view::traits::ViewController;
|
||||
|
||||
/// Enforces normalcy, or: a needlessly cruel method in terms of the name. You get the idea though.
|
||||
|
@ -37,21 +39,25 @@ extern fn update_layer(this: &Object, _: Sel) {
|
|||
}
|
||||
|
||||
/// Called when a drag/drop operation has entered this view.
|
||||
extern fn dragging_entered<T: ViewController>(this: &mut Object, _: Sel, _: id) -> NSUInteger {
|
||||
extern fn dragging_entered<T: ViewController>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
|
||||
unsafe {
|
||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||
let view = ptr as *const T;
|
||||
(*view).dragging_entered().into()
|
||||
(*view).dragging_entered(DragInfo {
|
||||
info: Id::from_ptr(info)
|
||||
}).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when a drag/drop operation has entered this view.
|
||||
extern fn prepare_for_drag_operation<T: ViewController>(this: &mut Object, _: Sel, _: id) -> BOOL {
|
||||
extern fn prepare_for_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) -> BOOL {
|
||||
unsafe {
|
||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||
let view = ptr as *const T;
|
||||
|
||||
match (*view).prepare_for_drag_operation() {
|
||||
match (*view).prepare_for_drag_operation(DragInfo {
|
||||
info: Id::from_ptr(info)
|
||||
}) {
|
||||
true => YES,
|
||||
false => NO
|
||||
}
|
||||
|
@ -59,12 +65,14 @@ extern fn prepare_for_drag_operation<T: ViewController>(this: &mut Object, _: Se
|
|||
}
|
||||
|
||||
/// Called when a drag/drop operation has entered this view.
|
||||
extern fn perform_drag_operation<T: ViewController>(this: &mut Object, _: Sel, _: id) -> BOOL {
|
||||
extern fn perform_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) -> BOOL {
|
||||
unsafe {
|
||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||
let view = ptr as *const T;
|
||||
|
||||
match (*view).perform_drag_operation() {
|
||||
match (*view).perform_drag_operation(DragInfo {
|
||||
info: Id::from_ptr(info)
|
||||
}) {
|
||||
true => YES,
|
||||
false => NO
|
||||
}
|
||||
|
@ -72,11 +80,25 @@ extern fn perform_drag_operation<T: ViewController>(this: &mut Object, _: Sel, _
|
|||
}
|
||||
|
||||
/// Called when a drag/drop operation has entered this view.
|
||||
extern fn dragging_exited<T: ViewController>(this: &mut Object, _: Sel, _: id) {
|
||||
extern fn conclude_drag_operation<T: ViewController>(this: &mut Object, _: Sel, info: id) {
|
||||
unsafe {
|
||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||
let view = ptr as *const T;
|
||||
(*view).dragging_exited();
|
||||
|
||||
(*view).conclude_drag_operation(DragInfo {
|
||||
info: Id::from_ptr(info)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when a drag/drop operation has entered this view.
|
||||
extern fn dragging_exited<T: ViewController>(this: &mut Object, _: Sel, info: id) {
|
||||
unsafe {
|
||||
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||
let view = ptr as *const T;
|
||||
(*view).dragging_exited(DragInfo {
|
||||
info: Id::from_ptr(info)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,6 +125,7 @@ pub(crate) fn register_view_class<T: ViewController>() -> *const Class {
|
|||
decl.add_method(sel!(draggingEntered:), dragging_entered::<T> as extern fn (&mut Object, _, _) -> NSUInteger);
|
||||
decl.add_method(sel!(prepareForDragOperation:), prepare_for_drag_operation::<T> as extern fn (&mut Object, _, _) -> BOOL);
|
||||
decl.add_method(sel!(performDragOperation:), perform_drag_operation::<T> as extern fn (&mut Object, _, _) -> BOOL);
|
||||
decl.add_method(sel!(concludeDragOperation:), conclude_drag_operation::<T> as extern fn (&mut Object, _, _));
|
||||
decl.add_method(sel!(draggingExited:), dragging_exited::<T> as extern fn (&mut Object, _, _));
|
||||
|
||||
VIEW_CLASS = decl.register();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use std::sync::Once;
|
||||
|
||||
use cocoa::base::{id, nil, YES, NO};
|
||||
use cocoa::base::{id, NO};
|
||||
use cocoa::foundation::{NSRect};
|
||||
|
||||
use objc::declare::ClassDecl;
|
||||
|
|
|
@ -4,7 +4,7 @@ use objc::runtime::Object;
|
|||
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::dragdrop::DragOperation;
|
||||
use crate::dragdrop::{DragInfo, DragOperation};
|
||||
|
||||
pub trait ViewWrapper {
|
||||
fn get_handle(&self) -> Option<ShareId<Object>>;
|
||||
|
@ -13,8 +13,19 @@ pub trait ViewWrapper {
|
|||
pub trait ViewController {
|
||||
fn did_load(&self);
|
||||
|
||||
fn dragging_entered(&self) -> DragOperation { DragOperation::None }
|
||||
fn prepare_for_drag_operation(&self) -> bool { false }
|
||||
fn perform_drag_operation(&self) -> bool { false }
|
||||
fn dragging_exited(&self) {}
|
||||
/// Invoked when the dragged image enters destination bounds or frame; returns dragging operation to perform.
|
||||
fn dragging_entered(&self, _info: DragInfo) -> DragOperation { DragOperation::None }
|
||||
|
||||
/// Invoked when the image is released, allowing the receiver to agree to or refuse drag operation.
|
||||
fn prepare_for_drag_operation(&self, _info: DragInfo) -> bool { false }
|
||||
|
||||
/// Invoked after the released image has been removed from the screen, signaling the receiver to import the pasteboard data.
|
||||
fn perform_drag_operation(&self, _info: DragInfo) -> bool { false }
|
||||
|
||||
/// Invoked when the dragging operation is complete, signaling the receiver to perform any necessary clean-up.
|
||||
fn conclude_drag_operation(&self, _info: DragInfo) {}
|
||||
|
||||
/// Invoked when the dragged image exits the destination’s bounds rectangle (in the case of a view) or its frame
|
||||
/// rectangle (in the case of a window object).
|
||||
fn dragging_exited(&self, _info: DragInfo) {}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use cocoa::base::{id, nil, YES, NO};
|
||||
use cocoa::base::{id, nil, YES};
|
||||
use cocoa::foundation::NSArray;
|
||||
|
||||
use objc_id::ShareId;
|
||||
|
@ -86,3 +86,9 @@ impl View {
|
|||
view.set_background_color(color);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for View {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "View ({:p})", self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ impl Default for WindowConfig {
|
|||
let dimensions = NSRect::new(NSPoint::new(0., 0.), NSSize::new(800., 600.));
|
||||
|
||||
let style = WindowStyle::Resizable | WindowStyle::Miniaturizable | WindowStyle::UnifiedTitleAndToolbar |
|
||||
WindowStyle::Closable | WindowStyle::Titled;
|
||||
WindowStyle::Closable | WindowStyle::Titled | WindowStyle::FullSizeContentView;
|
||||
|
||||
let alloc: id = msg_send![class!(NSWindow), alloc];
|
||||
let window: id = msg_send![alloc, initWithContentRect:dimensions styleMask:style backing:2 as NSUInteger defer:YES];
|
||||
|
|
|
@ -7,13 +7,13 @@ use crate::proc_macro::TokenStream;
|
|||
use quote::quote;
|
||||
use syn::{DeriveInput, parse_macro_input};
|
||||
|
||||
/// Derivces an `appkit::prelude::WinWrapper` block, which implements forwarding methods for things
|
||||
/// Derivces an `appkit::prelude::WindowWrapper` block, which implements forwarding methods for things
|
||||
/// like setting the window title, or showing and closing it. It currently expects that the wrapped
|
||||
/// struct has `window` as the field holding the `Window` from `appkit-rs`.
|
||||
///
|
||||
/// Note that this expects that pointers to Window(s) should not move once created.
|
||||
#[proc_macro_derive(WindowWrapper)]
|
||||
pub fn impl_window_controller(input: TokenStream) -> TokenStream {
|
||||
pub fn impl_window_wrapper(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = &input.ident;
|
||||
|
@ -30,3 +30,24 @@ pub fn impl_window_controller(input: TokenStream) -> TokenStream {
|
|||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
/// Derives an `appkit::prelude::ViewWrapper` block, which implements some necessary bits and
|
||||
/// pieces for View handling.
|
||||
#[proc_macro_derive(ViewWrapper)]
|
||||
pub fn impl_view_wrapper(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = &input.ident;
|
||||
let generics = input.generics;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let expanded = quote! {
|
||||
impl #impl_generics appkit::prelude::ViewWrapper for #name #ty_generics #where_clause {
|
||||
fn get_handle(&self) -> Option<appkit::ShareId<appkit::Object>> {
|
||||
self.view.get_handle()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue