Ongoing changes for the core.
- Implemented basic support for `NSPasteBoardType` and drag/drop events. Not complete, but close. - `geometry.rs` implements a basic `Rect`, with some conversion logic for `NSRect`. Nothing special. - Changes to `Window` to remove some of the forced delegate pattern. Autosave name is now just another setter. - `WebView` no longer has a backing `View` by default, so implementors need to handle that on their own. - Beginning to wrap `NSFileManager`, which is kind of important given that this supports both Sandboxed and non-Sandboxed apps. - Documentation work ongoing.
This commit is contained in:
parent
5cd3a53681
commit
0446227a8d
0
appkit/src/collection_view/mod.rs
Normal file
0
appkit/src/collection_view/mod.rs
Normal file
47
appkit/src/dragdrop.rs
Normal file
47
appkit/src/dragdrop.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//! This module contains various bits and pieces for drag and drop operations. They're shared
|
||||||
|
//! across the codebase, hence why they're here - they're not currently exhaustive, so feel free to
|
||||||
|
//! tinker and pull request.
|
||||||
|
|
||||||
|
use cocoa::foundation::NSUInteger;
|
||||||
|
|
||||||
|
/// Represents operations that can happen for a given drag/drop scenario.
|
||||||
|
pub enum DragOperation {
|
||||||
|
/// No drag operations are allowed.
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// The data represented by the image can be copied.
|
||||||
|
Copy,
|
||||||
|
|
||||||
|
/// The data can be shared.
|
||||||
|
Link,
|
||||||
|
|
||||||
|
/// The operation can be defined by the destination.
|
||||||
|
Generic,
|
||||||
|
|
||||||
|
/// The operation is negotiated privately between the source and the destination.
|
||||||
|
Private,
|
||||||
|
|
||||||
|
/// The data can be moved.
|
||||||
|
Move,
|
||||||
|
|
||||||
|
/// The data can be deleted.
|
||||||
|
Delete,
|
||||||
|
|
||||||
|
// All of the above.
|
||||||
|
// @TODO: NSUIntegerMax, a tricky beast
|
||||||
|
// Every
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DragOperation> for NSUInteger {
|
||||||
|
fn from(op: DragOperation) -> Self {
|
||||||
|
match op {
|
||||||
|
DragOperation::None => 0,
|
||||||
|
DragOperation::Copy => 1,
|
||||||
|
DragOperation::Link => 2,
|
||||||
|
DragOperation::Generic => 4,
|
||||||
|
DragOperation::Private => 8,
|
||||||
|
DragOperation::Move => 16,
|
||||||
|
DragOperation::Delete => 32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
//! Certain enums that are useful (response types, etc).
|
|
||||||
|
|
||||||
use cocoa::foundation::{NSInteger};
|
|
||||||
|
|
||||||
pub enum ModalResponse {
|
|
||||||
Ok,
|
|
||||||
Continue,
|
|
||||||
Canceled,
|
|
||||||
Stopped,
|
|
||||||
Aborted,
|
|
||||||
FirstButtonReturned,
|
|
||||||
SecondButtonReturned,
|
|
||||||
ThirdButtonReturned
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NSInteger> for ModalResponse {
|
|
||||||
fn from(i: NSInteger) -> Self {
|
|
||||||
match i {
|
|
||||||
1 => ModalResponse::Ok,
|
|
||||||
0 => ModalResponse::Canceled,
|
|
||||||
1000 => ModalResponse::FirstButtonReturned,
|
|
||||||
1001 => ModalResponse::SecondButtonReturned,
|
|
||||||
1002 => ModalResponse::ThirdButtonReturned,
|
|
||||||
-1000 => ModalResponse::Stopped,
|
|
||||||
-1001 => ModalResponse::Aborted,
|
|
||||||
-1002 => ModalResponse::Continue,
|
|
||||||
e => { panic!("Unknown NSModalResponse sent back! {}", e); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
115
appkit/src/filesystem/enums.rs
Normal file
115
appkit/src/filesystem/enums.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
//! Certain enums that are useful (response types, etc).
|
||||||
|
|
||||||
|
use cocoa::foundation::{NSInteger, NSUInteger};
|
||||||
|
|
||||||
|
pub enum ModalResponse {
|
||||||
|
Ok,
|
||||||
|
Continue,
|
||||||
|
Canceled,
|
||||||
|
Stopped,
|
||||||
|
Aborted,
|
||||||
|
FirstButtonReturned,
|
||||||
|
SecondButtonReturned,
|
||||||
|
ThirdButtonReturned
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NSInteger> for ModalResponse {
|
||||||
|
fn from(i: NSInteger) -> Self {
|
||||||
|
match i {
|
||||||
|
1 => ModalResponse::Ok,
|
||||||
|
0 => ModalResponse::Canceled,
|
||||||
|
1000 => ModalResponse::FirstButtonReturned,
|
||||||
|
1001 => ModalResponse::SecondButtonReturned,
|
||||||
|
1002 => ModalResponse::ThirdButtonReturned,
|
||||||
|
-1000 => ModalResponse::Stopped,
|
||||||
|
-1001 => ModalResponse::Aborted,
|
||||||
|
-1002 => ModalResponse::Continue,
|
||||||
|
e => { panic!("Unknown NSModalResponse sent back! {}", e); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SearchPathDomainMask {
|
||||||
|
User,
|
||||||
|
Local,
|
||||||
|
Network,
|
||||||
|
Domain,
|
||||||
|
AllDomains
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SearchPathDomainMask> for NSUInteger {
|
||||||
|
fn from(mask: SearchPathDomainMask) -> Self {
|
||||||
|
match mask {
|
||||||
|
SearchPathDomainMask::User => 1,
|
||||||
|
SearchPathDomainMask::Local => 2,
|
||||||
|
SearchPathDomainMask::Network => 4,
|
||||||
|
SearchPathDomainMask::Domain => 8,
|
||||||
|
SearchPathDomainMask::AllDomains => 0x0ffff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SearchPathDirectory {
|
||||||
|
Applications,
|
||||||
|
DemoApplications,
|
||||||
|
DeveloperApplications,
|
||||||
|
AdminApplications,
|
||||||
|
Library,
|
||||||
|
Developer,
|
||||||
|
User,
|
||||||
|
Documentation,
|
||||||
|
Documents,
|
||||||
|
CoreServices,
|
||||||
|
AutosavedInformation,
|
||||||
|
Desktop,
|
||||||
|
Caches,
|
||||||
|
ApplicationSupport,
|
||||||
|
Downloads,
|
||||||
|
InputMethods,
|
||||||
|
Movies,
|
||||||
|
Music,
|
||||||
|
Pictures,
|
||||||
|
PrinterDescription,
|
||||||
|
SharedPublic,
|
||||||
|
PreferencePanes,
|
||||||
|
ApplicationScripts,
|
||||||
|
ItemReplacement,
|
||||||
|
AllApplications,
|
||||||
|
AllLibraries,
|
||||||
|
Trash
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SearchPathDirectory> for NSUInteger {
|
||||||
|
fn from(directory: SearchPathDirectory) -> Self {
|
||||||
|
match directory {
|
||||||
|
SearchPathDirectory::Applications => 1,
|
||||||
|
SearchPathDirectory::DemoApplications => 2,
|
||||||
|
SearchPathDirectory::DeveloperApplications => 3,
|
||||||
|
SearchPathDirectory::AdminApplications => 4,
|
||||||
|
SearchPathDirectory::Library => 5,
|
||||||
|
SearchPathDirectory::Developer => 6,
|
||||||
|
SearchPathDirectory::User => 7,
|
||||||
|
SearchPathDirectory::Documentation => 8,
|
||||||
|
SearchPathDirectory::Documents => 9,
|
||||||
|
SearchPathDirectory::CoreServices => 10,
|
||||||
|
SearchPathDirectory::AutosavedInformation => 11,
|
||||||
|
SearchPathDirectory::Desktop => 12,
|
||||||
|
SearchPathDirectory::Caches => 13,
|
||||||
|
SearchPathDirectory::ApplicationSupport => 14,
|
||||||
|
SearchPathDirectory::Downloads => 15,
|
||||||
|
SearchPathDirectory::InputMethods => 16,
|
||||||
|
SearchPathDirectory::Movies => 17,
|
||||||
|
SearchPathDirectory::Music => 18,
|
||||||
|
SearchPathDirectory::Pictures => 19,
|
||||||
|
SearchPathDirectory::PrinterDescription => 20,
|
||||||
|
SearchPathDirectory::SharedPublic => 21,
|
||||||
|
SearchPathDirectory::PreferencePanes => 22,
|
||||||
|
SearchPathDirectory::ApplicationScripts => 23,
|
||||||
|
|
||||||
|
SearchPathDirectory::ItemReplacement => 99,
|
||||||
|
SearchPathDirectory::AllApplications => 100,
|
||||||
|
SearchPathDirectory::AllLibraries => 101,
|
||||||
|
SearchPathDirectory::Trash => 102
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
appkit/src/filesystem/manager.rs
Normal file
69
appkit/src/filesystem/manager.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//! 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 cocoa::base::{id, nil, NO};
|
||||||
|
use cocoa::foundation::{NSInteger, NSUInteger};
|
||||||
|
|
||||||
|
use objc_id::Id;
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
|
use crate::filesystem::enums::{SearchPathDirectory, SearchPathDomainMask};
|
||||||
|
use crate::utils::str_from;
|
||||||
|
|
||||||
|
pub struct FileManagerInner {
|
||||||
|
pub manager: Id<Object>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FileManagerInner {
|
||||||
|
fn default() -> Self {
|
||||||
|
FileManagerInner {
|
||||||
|
manager: 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>> {
|
||||||
|
let dir: NSUInteger = directory.into();
|
||||||
|
let mask: NSUInteger = in_domain.into();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let dir: id = msg_send![&*self.manager, URLForDirectory:dir
|
||||||
|
inDomain:mask
|
||||||
|
appropriateForURL:nil
|
||||||
|
create:NO
|
||||||
|
error:nil];
|
||||||
|
|
||||||
|
let s: id = msg_send![dir, path];
|
||||||
|
Ok(str_from(s).to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
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: &[
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
|
|
||||||
|
|
||||||
pub mod enums;
|
pub mod enums;
|
||||||
pub use enums::*;
|
pub use enums::*;
|
||||||
|
|
||||||
|
pub mod manager;
|
||||||
|
pub use manager::FileManager;
|
||||||
|
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
pub use traits::OpenSaveController;
|
pub use traits::*;
|
||||||
|
|
||||||
pub mod save;
|
pub mod save;
|
||||||
pub use save::FileSavePanel;
|
pub use save::FileSavePanel;
|
|
@ -11,7 +11,7 @@ use objc::{class, msg_send, sel, sel_impl};
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::file_panel::enums::ModalResponse;
|
use crate::filesystem::enums::ModalResponse;
|
||||||
use crate::utils::str_from;
|
use crate::utils::str_from;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
|
@ -1,6 +1,5 @@
|
||||||
//! A trait that you can implement to handle open and save file dialogs. This more or less maps
|
//! A trait that you can implement to handle open and save file dialogs. This more or less maps
|
||||||
//! over to `NSOpenPanel` and `NSSavePanel` handling.
|
//! over to `NSOpenPanel` and `NSSavePanel` handling.
|
||||||
|
|
||||||
pub trait OpenSaveController {
|
pub trait OpenSaveController {
|
||||||
/// Called when the user has entered a filename (typically, during saving). `confirmed`
|
/// Called when the user has entered a filename (typically, during saving). `confirmed`
|
||||||
/// indicates whether or not they hit the save button.
|
/// indicates whether or not they hit the save button.
|
||||||
|
@ -19,3 +18,10 @@ pub trait OpenSaveController {
|
||||||
/// Determine whether the specified URL should be enabled in the Open panel.
|
/// Determine whether the specified URL should be enabled in the Open panel.
|
||||||
fn should_enable_url(&self, _url: &str) -> bool { true }
|
fn should_enable_url(&self, _url: &str) -> bool { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait you can implement for working with the underlying filesystem. This is important,
|
||||||
|
/// notably, because sandboxed applications have different working restrictions surrounding what
|
||||||
|
/// they can access.
|
||||||
|
pub trait FileManagerController {
|
||||||
|
|
||||||
|
}
|
39
appkit/src/geometry.rs
Normal file
39
appkit/src/geometry.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//! Wrapper methods for various geometry types (rects, sizes, ec).
|
||||||
|
|
||||||
|
use cocoa::foundation::{NSRect, NSPoint, NSSize};
|
||||||
|
|
||||||
|
/// A struct that represents a box - top, left, width and height.
|
||||||
|
pub struct Rect {
|
||||||
|
/// Distance from the top, in points.
|
||||||
|
pub top: f64,
|
||||||
|
|
||||||
|
/// Distance from the left, in points.
|
||||||
|
pub left: f64,
|
||||||
|
|
||||||
|
/// Width, in points.
|
||||||
|
pub width: f64,
|
||||||
|
|
||||||
|
/// Height, in points.
|
||||||
|
pub height: f64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rect {
|
||||||
|
/// Returns a zero'd out Rect, with f64 (32-bit is mostly dead on Cocoa, so... this is "okay").
|
||||||
|
pub fn zero() -> Rect {
|
||||||
|
Rect {
|
||||||
|
top: 0.0,
|
||||||
|
left: 0.0,
|
||||||
|
width: 0.0,
|
||||||
|
height: 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rect> for NSRect {
|
||||||
|
fn from(rect: Rect) -> NSRect {
|
||||||
|
NSRect::new(
|
||||||
|
NSPoint::new(rect.top, rect.left),
|
||||||
|
NSSize::new(rect.width, rect.height)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,27 +20,23 @@ pub use objc_id::ShareId;
|
||||||
pub use objc::runtime::Object;
|
pub use objc::runtime::Object;
|
||||||
pub use cocoa::base::id;
|
pub use cocoa::base::id;
|
||||||
|
|
||||||
pub trait ViewWrapper {
|
|
||||||
fn get_handle(&self) -> Option<ShareId<Object>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ViewController {
|
|
||||||
fn did_load(&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod alert;
|
pub mod alert;
|
||||||
pub mod app;
|
pub mod app;
|
||||||
pub mod events;
|
|
||||||
pub mod menu;
|
|
||||||
pub mod button;
|
pub mod button;
|
||||||
pub mod file_panel;
|
pub mod collection_view;
|
||||||
pub mod toolbar;
|
pub mod dragdrop;
|
||||||
pub mod notifications;
|
pub mod events;
|
||||||
pub mod webview;
|
pub mod filesystem;
|
||||||
pub mod view;
|
pub mod geometry;
|
||||||
pub mod window;
|
pub mod menu;
|
||||||
pub mod networking;
|
pub mod networking;
|
||||||
|
pub mod notifications;
|
||||||
|
pub mod pasteboard;
|
||||||
|
pub mod toolbar;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
pub mod view;
|
||||||
|
pub mod webview;
|
||||||
|
pub mod window;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::app::{App, AppDelegate};
|
pub use crate::app::{App, AppDelegate};
|
||||||
|
@ -59,7 +55,7 @@ pub mod prelude {
|
||||||
WebView, WebViewConfig, WebViewController
|
WebView, WebViewConfig, WebViewController
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::{ViewController, ViewWrapper};
|
pub use crate::view::{View, ViewController, ViewWrapper};
|
||||||
|
|
||||||
pub use appkit_derive::{
|
pub use appkit_derive::{
|
||||||
WindowWrapper
|
WindowWrapper
|
||||||
|
|
79
appkit/src/pasteboard.rs
Normal file
79
appkit/src/pasteboard.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
//! This module provides some basic wrappers for PasteBoard functionality. It's currently not an
|
||||||
|
//! exhaustive clone, but feel free to pull request accordingly!
|
||||||
|
|
||||||
|
use cocoa::base::{id, nil};
|
||||||
|
use cocoa::foundation::NSString;
|
||||||
|
|
||||||
|
/// Represents different PasteBoard types that can be referred to.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum PasteBoardType {
|
||||||
|
/// URL data for one file or resource.
|
||||||
|
URL,
|
||||||
|
|
||||||
|
/// Color data.
|
||||||
|
Color,
|
||||||
|
|
||||||
|
/// A file URL.
|
||||||
|
FileURL,
|
||||||
|
|
||||||
|
/// Font and character information.
|
||||||
|
Font,
|
||||||
|
|
||||||
|
/// Type for HTML content.
|
||||||
|
HTML,
|
||||||
|
|
||||||
|
/// Multiple text selection.
|
||||||
|
MultipleTextSelection,
|
||||||
|
|
||||||
|
/// PDF data.
|
||||||
|
PDF,
|
||||||
|
|
||||||
|
/// PNG image data.
|
||||||
|
PNG,
|
||||||
|
|
||||||
|
/// Rich Text Format (RTF) data.
|
||||||
|
RTF,
|
||||||
|
|
||||||
|
/// RTFD formatted file contents.
|
||||||
|
RTFD,
|
||||||
|
|
||||||
|
/// Paragraph formatting information.
|
||||||
|
Ruler,
|
||||||
|
|
||||||
|
/// Sound data.
|
||||||
|
Sound,
|
||||||
|
|
||||||
|
/// String data.
|
||||||
|
String,
|
||||||
|
|
||||||
|
/// Tab-separated fields of text.
|
||||||
|
TabularText,
|
||||||
|
|
||||||
|
/// Tag Image File Format (TIFF) data.
|
||||||
|
TIFF
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PasteBoardType {
|
||||||
|
/// Creates an `NSString` out of the underlying type.
|
||||||
|
pub fn to_nsstring(&self) -> id {
|
||||||
|
unsafe {
|
||||||
|
NSString::alloc(nil).init_str(match self {
|
||||||
|
PasteBoardType::URL => "public.url",
|
||||||
|
PasteBoardType::Color => "com.apple.cocoa.pasteboard.color",
|
||||||
|
PasteBoardType::FileURL => "public.file-url",
|
||||||
|
PasteBoardType::Font => "com.apple.cocoa.pasteboard.character-formatting",
|
||||||
|
PasteBoardType::HTML => "public.html",
|
||||||
|
PasteBoardType::MultipleTextSelection => "com.apple.cocoa.pasteboard.multiple-text-selection",
|
||||||
|
PasteBoardType::PDF => "com.adobe.pdf",
|
||||||
|
PasteBoardType::PNG => "public.png",
|
||||||
|
PasteBoardType::RTF => "public.rtf",
|
||||||
|
PasteBoardType::RTFD => "com.apple.flat-rtfd",
|
||||||
|
PasteBoardType::Ruler => "com.apple.cocoa.pasteboard.paragraph-formatting",
|
||||||
|
PasteBoardType::Sound => "com.apple.cocoa.pasteboard.sound",
|
||||||
|
PasteBoardType::String => "public.utf8-plain-text",
|
||||||
|
PasteBoardType::TabularText => "public.utf8-tab-separated-values-text",
|
||||||
|
PasteBoardType::TIFF => "public.tiff",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,12 +9,16 @@
|
||||||
|
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
use cocoa::base::{id, nil, YES};
|
use cocoa::base::{id, nil, YES, NO};
|
||||||
|
use cocoa::foundation::{NSUInteger};
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
use objc::declare::ClassDecl;
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL};
|
use objc::runtime::{Class, Object, Sel, BOOL};
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
|
use crate::view::VIEW_CONTROLLER_PTR;
|
||||||
|
use crate::view::traits::ViewController;
|
||||||
|
|
||||||
/// Enforces normalcy, or: a needlessly cruel method in terms of the name. You get the idea though.
|
/// Enforces normalcy, or: a needlessly cruel method in terms of the name. You get the idea though.
|
||||||
extern fn enforce_normalcy(_: &Object, _: Sel) -> BOOL {
|
extern fn enforce_normalcy(_: &Object, _: Sel) -> BOOL {
|
||||||
return YES;
|
return YES;
|
||||||
|
@ -31,9 +35,53 @@ 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 {
|
||||||
|
unsafe {
|
||||||
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
|
let view = ptr as *const T;
|
||||||
|
(*view).dragging_entered().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 {
|
||||||
|
unsafe {
|
||||||
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
|
let view = ptr as *const T;
|
||||||
|
|
||||||
|
match (*view).prepare_for_drag_operation() {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when a drag/drop operation has entered this view.
|
||||||
|
extern fn perform_drag_operation<T: ViewController>(this: &mut Object, _: Sel, _: id) -> BOOL {
|
||||||
|
unsafe {
|
||||||
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
|
let view = ptr as *const T;
|
||||||
|
|
||||||
|
match (*view).perform_drag_operation() {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when a drag/drop operation has entered this view.
|
||||||
|
extern fn dragging_exited<T: ViewController>(this: &mut Object, _: Sel, _: id) {
|
||||||
|
unsafe {
|
||||||
|
let ptr: usize = *this.get_ivar(VIEW_CONTROLLER_PTR);
|
||||||
|
let view = ptr as *const T;
|
||||||
|
(*view).dragging_exited();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Injects an `NSView` subclass, with some callback and pointer ivars for what we
|
/// Injects an `NSView` subclass, with some callback and pointer ivars for what we
|
||||||
/// need to do.
|
/// need to do.
|
||||||
pub(crate) fn register_view_class() -> *const Class {
|
pub(crate) fn register_view_class<T: ViewController>() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||||
static INIT: Once = Once::new();
|
static INIT: Once = Once::new();
|
||||||
|
|
||||||
|
@ -41,11 +89,20 @@ pub(crate) fn register_view_class() -> *const Class {
|
||||||
let superclass = Class::get("NSView").unwrap();
|
let superclass = Class::get("NSView").unwrap();
|
||||||
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();
|
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();
|
||||||
|
|
||||||
|
// A pointer to the "view controller" on the Rust side. It's expected that this doesn't
|
||||||
|
// move.
|
||||||
|
decl.add_ivar::<usize>(VIEW_CONTROLLER_PTR);
|
||||||
|
|
||||||
decl.add_method(sel!(isFlipped), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
decl.add_method(sel!(isFlipped), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
||||||
decl.add_method(sel!(requiresConstraintBasedLayout), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
|
||||||
decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
||||||
decl.add_method(sel!(updateLayer), update_layer as extern fn(&Object, _));
|
decl.add_method(sel!(updateLayer), update_layer as extern fn(&Object, _));
|
||||||
|
|
||||||
|
// Drag and drop operations (e.g, accepting files)
|
||||||
|
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!(draggingExited:), dragging_exited::<T> as extern fn (&mut Object, _, _));
|
||||||
|
|
||||||
VIEW_CLASS = decl.register();
|
VIEW_CLASS = decl.register();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,69 +1,43 @@
|
||||||
//! Hoists a basic NSView. In our current particular use case,
|
//! Hoists a basic `NSViewController`. We use `NSViewController` rather than plain `NSView` as
|
||||||
//! this is primarily used as the ContentView for a window. From there,
|
//! we're interested in the lifecycle methods and events.
|
||||||
//! we configure an NSToolbar and WKWebview on top of them.
|
|
||||||
|
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
use cocoa::base::{id, YES};
|
use cocoa::base::{id, nil, YES, NO};
|
||||||
use cocoa::foundation::{NSRect, NSPoint, NSSize};
|
use cocoa::foundation::{NSRect, NSUInteger};
|
||||||
|
|
||||||
use objc_id::Id;
|
|
||||||
use objc::declare::ClassDecl;
|
use objc::declare::ClassDecl;
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL};
|
use objc::runtime::{Class, Object, Sel};
|
||||||
use objc::{msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
/// A trait for handling the view lifecycle.
|
use crate::geometry::Rect;
|
||||||
pub trait View {
|
use crate::view::{VIEW_CONTROLLER_PTR, ViewController};
|
||||||
fn did_load(&mut self) {}
|
use crate::view::class::register_view_class;
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper for `NSWindow`. Holds (retains) pointers for the Objective-C runtime
|
/// Loads and configures ye old NSView for this controller.
|
||||||
/// where our `NSWindow` and associated delegate live.
|
extern fn load_view<T: ViewController>(this: &mut Object, _: Sel) {
|
||||||
pub struct View {
|
unsafe {
|
||||||
pub inner: Id<Object>
|
let zero: NSRect = Rect::zero().into();
|
||||||
}
|
let view: id = msg_send![register_view_class::<T>(), new];
|
||||||
|
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||||
impl View {
|
let _: () = msg_send![view, setFrame:zero];
|
||||||
/// Creates a new `NSWindow` instance, configures it appropriately (e.g, titlebar appearance),
|
let _: () = msg_send![this, setView:view];
|
||||||
/// injects an `NSObject` delegate wrapper, and retains the necessary Objective-C runtime
|
|
||||||
/// pointers.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let inner = unsafe {
|
|
||||||
let rect_zero = NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.));
|
|
||||||
let alloc: id = msg_send![register_class(), alloc];
|
|
||||||
let view: id = msg_send![alloc, initWithFrame:rect_zero];
|
|
||||||
let _: () = msg_send![view, setWantsLayer:YES];
|
|
||||||
let _: () = msg_send![view, setLayerContentsRedrawPolicy:1];
|
|
||||||
Id::from_ptr(view)
|
|
||||||
};
|
|
||||||
|
|
||||||
View {
|
|
||||||
inner: inner
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is used for some specific calls, where macOS NSView needs to be
|
/// Registers an `NSViewController`.
|
||||||
/// forcefully dragged into the modern age (e.g, position coordinates from top left...).
|
pub fn register_controller_class<T: ViewController + 'static>() -> *const Class {
|
||||||
extern fn enforce_normalcy(_: &Object, _: Sel) -> BOOL {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers an `NSView` subclass, and configures it to hold some ivars for various things we need
|
|
||||||
/// to store.
|
|
||||||
fn register_class() -> *const Class {
|
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||||
static INIT: Once = Once::new();
|
static INIT: Once = Once::new();
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
INIT.call_once(|| unsafe {
|
||||||
let superclass = Class::get("NSView").unwrap();
|
let superclass = Class::get("NSViewController").unwrap();
|
||||||
let mut decl = ClassDecl::new("SBAView", superclass).unwrap();
|
let mut decl = ClassDecl::new("RSTViewController", superclass).unwrap();
|
||||||
|
|
||||||
// Force NSView to render from the top-left, not bottom-left
|
decl.add_ivar::<usize>(VIEW_CONTROLLER_PTR);
|
||||||
decl.add_method(sel!(isFlipped), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
|
||||||
|
|
||||||
// Request optimized backing layers
|
// NSViewController
|
||||||
decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
decl.add_method(sel!(loadView), load_view::<T> as extern fn(&mut Object, _));
|
||||||
|
|
||||||
VIEW_CLASS = decl.register();
|
VIEW_CLASS = decl.register();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,2 +1,11 @@
|
||||||
|
|
||||||
|
pub(crate) static VIEW_CONTROLLER_PTR: &str = "rstViewControllerPtr";
|
||||||
|
|
||||||
pub(crate) mod class;
|
pub(crate) mod class;
|
||||||
|
pub(crate) mod controller;
|
||||||
|
|
||||||
|
pub mod traits;
|
||||||
|
pub use traits::*;
|
||||||
|
|
||||||
|
pub mod view;
|
||||||
|
pub use view::View;
|
||||||
|
|
20
appkit/src/view/traits.rs
Normal file
20
appkit/src/view/traits.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//! Various traits used for Views.
|
||||||
|
|
||||||
|
use objc::runtime::Object;
|
||||||
|
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
use crate::dragdrop::DragOperation;
|
||||||
|
|
||||||
|
pub trait ViewWrapper {
|
||||||
|
fn get_handle(&self) -> Option<ShareId<Object>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {}
|
||||||
|
}
|
71
appkit/src/view/view.rs
Normal file
71
appkit/src/view/view.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
//! A wrapper for `NSViewController`. Uses interior mutability to
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use cocoa::base::{id, nil, YES, NO};
|
||||||
|
use cocoa::foundation::{NSString, NSArray};
|
||||||
|
|
||||||
|
use objc_id::ShareId;
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
|
use crate::pasteboard::PasteBoardType;
|
||||||
|
use crate::view::{VIEW_CONTROLLER_PTR, ViewController};
|
||||||
|
use crate::view::controller::register_controller_class;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ViewInner {
|
||||||
|
pub controller: Option<ShareId<Object>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewInner {
|
||||||
|
pub fn configure<T: ViewController + 'static>(&mut self, controller: &T) {
|
||||||
|
self.controller = Some(unsafe {
|
||||||
|
let view_controller: id = msg_send![register_controller_class::<T>(), new];
|
||||||
|
(&mut *view_controller).set_ivar(VIEW_CONTROLLER_PTR, controller as *const T as usize);
|
||||||
|
|
||||||
|
let view: id = msg_send![view_controller, view];
|
||||||
|
(&mut *view).set_ivar(VIEW_CONTROLLER_PTR, controller as *const T as usize);
|
||||||
|
|
||||||
|
ShareId::from_ptr(view_controller)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_for_dragged_types(&self, types: &[PasteBoardType]) {
|
||||||
|
if let Some(controller) = &self.controller {
|
||||||
|
unsafe {
|
||||||
|
let types = NSArray::arrayWithObjects(nil, &types.iter().map(|t| {
|
||||||
|
t.to_nsstring()
|
||||||
|
}).collect::<Vec<id>>());
|
||||||
|
|
||||||
|
let view: id = msg_send![*controller, view];
|
||||||
|
let _: () = msg_send![view, registerForDraggedTypes:types];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]) {
|
||||||
|
let view = self.0.borrow();
|
||||||
|
view.register_for_dragged_types(types);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ use objc::declare::ClassDecl;
|
||||||
use objc::runtime::{Class, Object, Sel};
|
use objc::runtime::{Class, Object, Sel};
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::ViewController;
|
use crate::view::ViewController;
|
||||||
use crate::view::class::register_view_class;
|
use crate::view::class::register_view_class;
|
||||||
use crate::webview::action::{NavigationAction, NavigationResponse};
|
use crate::webview::action::{NavigationAction, NavigationResponse};
|
||||||
use crate::webview::{WEBVIEW_VAR, WEBVIEW_CONFIG_VAR, WEBVIEW_CONTROLLER_PTR};
|
use crate::webview::{WEBVIEW_VAR, WEBVIEW_CONFIG_VAR, WEBVIEW_CONTROLLER_PTR};
|
||||||
|
@ -27,7 +27,9 @@ extern fn load_view<T: ViewController + WebViewController>(this: &mut Object, _:
|
||||||
let configuration: id = *this.get_ivar(WEBVIEW_CONFIG_VAR);
|
let configuration: id = *this.get_ivar(WEBVIEW_CONFIG_VAR);
|
||||||
|
|
||||||
// Technically private!
|
// Technically private!
|
||||||
|
#[cfg(feature = "enable-webview-downloading")]
|
||||||
let process_pool: id = msg_send![configuration, processPool];
|
let process_pool: id = msg_send![configuration, processPool];
|
||||||
|
#[cfg(feature = "enable-webview-downloading")]
|
||||||
let _: () = msg_send![process_pool, _setDownloadDelegate:&*this];
|
let _: () = msg_send![process_pool, _setDownloadDelegate:&*this];
|
||||||
|
|
||||||
let zero = NSRect::new(NSPoint::new(0., 0.), NSSize::new(1000., 600.));
|
let zero = NSRect::new(NSPoint::new(0., 0.), NSSize::new(1000., 600.));
|
||||||
|
@ -42,23 +44,7 @@ extern fn load_view<T: ViewController + WebViewController>(this: &mut Object, _:
|
||||||
// Clean this up to be safe, as WKWebView makes a copy and we don't need it anymore.
|
// Clean this up to be safe, as WKWebView makes a copy and we don't need it anymore.
|
||||||
(*this).set_ivar(WEBVIEW_CONFIG_VAR, nil);
|
(*this).set_ivar(WEBVIEW_CONFIG_VAR, nil);
|
||||||
|
|
||||||
// Note that we put this in a backing NSView to handle an edge case - if someone sets the
|
let _: () = msg_send![this, setView:webview];
|
||||||
// WKWebView as the content view of a window, and the inspector is able to be activated,
|
|
||||||
// it'll try to (at first) add the inspector to the parent view of the WKWebView... which
|
|
||||||
// is undefined behavior.
|
|
||||||
let view: id = msg_send![register_view_class(), new];
|
|
||||||
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
|
||||||
let _: () = msg_send![view, setFrame:zero];
|
|
||||||
let _: () = msg_send![view, addSubview:webview];
|
|
||||||
|
|
||||||
let constraint = class!(NSLayoutConstraint);
|
|
||||||
let constraints = NSArray::arrayWithObjects(nil, &vec![
|
|
||||||
msg_send![constraint, constraintWithItem:webview attribute:7 relatedBy:0 toItem:view attribute:7 multiplier:1.0 constant:0.0],
|
|
||||||
msg_send![constraint, constraintWithItem:webview attribute:8 relatedBy:0 toItem:view attribute:8 multiplier:1.0 constant:0.0],
|
|
||||||
]);
|
|
||||||
|
|
||||||
let _: () = msg_send![constraint, activateConstraints:constraints];
|
|
||||||
let _: () = msg_send![this, setView:view];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,9 @@ use objc_id::ShareId;
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::ViewController;
|
use crate::view::ViewController;
|
||||||
use crate::webview::{WEBVIEW_VAR, WEBVIEW_CONFIG_VAR, WEBVIEW_CONTROLLER_PTR};
|
use crate::webview::{WEBVIEW_VAR, WEBVIEW_CONFIG_VAR, WEBVIEW_CONTROLLER_PTR, WebViewController};
|
||||||
use crate::webview::WebViewController;
|
|
||||||
use crate::webview::controller::register_controller_class;
|
use crate::webview::controller::register_controller_class;
|
||||||
|
|
||||||
use crate::webview::config::{WebViewConfig, InjectAt};
|
use crate::webview::config::{WebViewConfig, InjectAt};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
@ -59,12 +59,6 @@ pub trait WindowWrapper {
|
||||||
/// 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.
|
||||||
pub trait WindowController {
|
pub trait WindowController {
|
||||||
/// `NSWindow` has a lovely usability feature wherein it'll cache the position in
|
|
||||||
/// `UserDefaults` when a window closes. This is generally nice for a lot of cases (e.g,
|
|
||||||
/// documents) but needs a key to work with. A blank key, the default, will not cache - so
|
|
||||||
/// you can implement this and return your own key per window delegate to cache accordingly.
|
|
||||||
fn autosave_name(&self) -> &str { "" }
|
|
||||||
|
|
||||||
/// The framework offers a standard, modern `NSWindow` by default - but sometimes you want
|
/// The framework offers a standard, modern `NSWindow` by default - but sometimes you want
|
||||||
/// something else. Implement this and return your desired Window configuration.
|
/// something else. Implement this and return your desired Window configuration.
|
||||||
fn config(&self) -> WindowConfig { WindowConfig::default() }
|
fn config(&self) -> WindowConfig { WindowConfig::default() }
|
||||||
|
|
|
@ -5,13 +5,13 @@ use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use cocoa::base::{id, nil, YES, NO};
|
use cocoa::base::{id, nil, YES, NO};
|
||||||
use cocoa::foundation::NSString;
|
use cocoa::foundation::{NSSize, NSString};
|
||||||
|
|
||||||
use objc_id::Id;
|
use objc_id::Id;
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc::{msg_send, sel, sel_impl};
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::{ViewController, ViewWrapper};
|
use crate::view::{ViewController, ViewWrapper};
|
||||||
use crate::toolbar::{Toolbar, ToolbarDelegate};
|
use crate::toolbar::{Toolbar, ToolbarDelegate};
|
||||||
use crate::window::WindowController;
|
use crate::window::WindowController;
|
||||||
use crate::window::controller::{register_window_controller_class};
|
use crate::window::controller::{register_window_controller_class};
|
||||||
|
@ -55,8 +55,6 @@ impl WindowInner {
|
||||||
///
|
///
|
||||||
/// APPKIT!
|
/// APPKIT!
|
||||||
pub fn configure<T: WindowController + 'static>(&mut self, window_controller: &T) {
|
pub fn configure<T: WindowController + 'static>(&mut self, window_controller: &T) {
|
||||||
let autosave_name = window_controller.autosave_name();
|
|
||||||
|
|
||||||
let window = window_controller.config().0;
|
let window = window_controller.config().0;
|
||||||
|
|
||||||
self.controller = Some(unsafe {
|
self.controller = Some(unsafe {
|
||||||
|
@ -68,12 +66,6 @@ impl WindowInner {
|
||||||
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];
|
||||||
|
|
||||||
// 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(autosave_name);
|
|
||||||
let _: () = msg_send![window, setFrameAutosaveName:autosave];
|
|
||||||
|
|
||||||
Id::from_ptr(controller)
|
Id::from_ptr(controller)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -126,6 +118,31 @@ impl WindowInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
/// 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
|
/// `ToolbarDelegate` you pass! The underlying `NSToolbar` is a bit... old, and it's just
|
||||||
/// easier to do things this way.
|
/// easier to do things this way.
|
||||||
|
@ -229,6 +246,18 @@ impl Window {
|
||||||
window.set_titlebar_appears_transparent(transparent);
|
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!
|
/// 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) {
|
pub fn set_toolbar<T: ToolbarDelegate + 'static>(&self, identifier: &str, toolbar: T) {
|
||||||
let mut window = self.0.borrow_mut();
|
let mut window = self.0.borrow_mut();
|
||||||
|
|
Loading…
Reference in a new issue