Merge branch 'trunk' into fix-ios-simulator-boot
This commit is contained in:
commit
2840b7f70f
|
@ -23,7 +23,7 @@ block = "0.1.6"
|
||||||
core-foundation = "0.9"
|
core-foundation = "0.9"
|
||||||
core-graphics = "0.22"
|
core-graphics = "0.22"
|
||||||
dispatch = "0.2.0"
|
dispatch = "0.2.0"
|
||||||
infer = { version = "0.11", optional = true }
|
infer = { version = "0.13", optional = true }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
objc = "0.2.7"
|
objc = "0.2.7"
|
||||||
|
@ -99,3 +99,6 @@ required-features = ["appkit"]
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "window_controller"
|
name = "window_controller"
|
||||||
required-features = ["appkit"]
|
required-features = ["appkit"]
|
||||||
|
[[exmaple]]
|
||||||
|
name = "safe_area"
|
||||||
|
required-features = ["appkit"]
|
|
@ -91,7 +91,9 @@ impl Dispatcher for BasicApp {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct WebViewInstance;
|
pub struct WebViewInstance;
|
||||||
|
|
||||||
impl WebViewDelegate for WebViewInstance {}
|
impl WebViewDelegate for WebViewInstance {
|
||||||
|
const NAME: &'static str = "BrowserWebViewDelegate";
|
||||||
|
}
|
||||||
|
|
||||||
struct AppWindow {
|
struct AppWindow {
|
||||||
toolbar: Toolbar<BrowserToolbar>,
|
toolbar: Toolbar<BrowserToolbar>,
|
||||||
|
|
186
examples/popover.rs
Normal file
186
examples/popover.rs
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
//! This example showcases how to use a `Popover`.
|
||||||
|
//! This requires multiple types:
|
||||||
|
//! - A Window with a Controller / View
|
||||||
|
//! - A Popover
|
||||||
|
//! - Another Controller / View
|
||||||
|
|
||||||
|
use cacao::appkit::menu::{Menu, MenuItem};
|
||||||
|
use cacao::appkit::window::{Window, WindowConfig, WindowController, WindowDelegate};
|
||||||
|
use cacao::appkit::{App, AppDelegate};
|
||||||
|
use cacao::button::Button;
|
||||||
|
use cacao::geometry::{Edge, Rect};
|
||||||
|
use cacao::layout::{Layout, LayoutConstraint};
|
||||||
|
use cacao::notification_center::Dispatcher;
|
||||||
|
use cacao::text::{Font, Label};
|
||||||
|
use cacao::view::{Popover, PopoverConfig, View, ViewController, ViewDelegate};
|
||||||
|
|
||||||
|
struct BasicApp {
|
||||||
|
window: WindowController<MyWindow>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppDelegate for BasicApp {
|
||||||
|
fn did_finish_launching(&self) {
|
||||||
|
App::set_menu(vec![
|
||||||
|
Menu::new("", vec![
|
||||||
|
MenuItem::Services,
|
||||||
|
MenuItem::Separator,
|
||||||
|
MenuItem::Hide,
|
||||||
|
MenuItem::HideOthers,
|
||||||
|
MenuItem::ShowAll,
|
||||||
|
MenuItem::Separator,
|
||||||
|
MenuItem::Quit,
|
||||||
|
]),
|
||||||
|
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||||
|
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||||
|
Menu::new("Window", vec![
|
||||||
|
MenuItem::Minimize,
|
||||||
|
MenuItem::Zoom,
|
||||||
|
MenuItem::Separator,
|
||||||
|
MenuItem::new("Bring All to Front"),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
App::activate();
|
||||||
|
|
||||||
|
self.window.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_terminate_after_last_window_closed(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct MyWindow {
|
||||||
|
controller: Option<ViewController<PopoverExampleContentView>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowDelegate for MyWindow {
|
||||||
|
const NAME: &'static str = "MyWindow";
|
||||||
|
|
||||||
|
fn did_load(&mut self, window: Window) {
|
||||||
|
window.set_minimum_content_size(400., 400.);
|
||||||
|
window.set_title("A Basic Window!?");
|
||||||
|
let view = PopoverExampleContentView::new();
|
||||||
|
let controller = ViewController::new(view);
|
||||||
|
window.set_content_view_controller(&controller);
|
||||||
|
self.controller = Some(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn will_close(&self) {
|
||||||
|
println!("Closing now!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyWindow {
|
||||||
|
pub fn on_message(&self, message: Msg) {
|
||||||
|
if let Some(delegate) = self.controller.as_ref().map(|e| &e.view).and_then(|v| v.delegate.as_ref()) {
|
||||||
|
delegate.on_message(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new("com.test.window-delegate", BasicApp {
|
||||||
|
window: WindowController::with(WindowConfig::default(), MyWindow::default())
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Msg {
|
||||||
|
Click
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct PopoverExampleContentView {
|
||||||
|
view: Option<View>,
|
||||||
|
button: Option<Button>,
|
||||||
|
popover: Option<Popover<PopoverExampleContentViewController>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopoverExampleContentView {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
view: None,
|
||||||
|
button: None,
|
||||||
|
popover: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_message(&self, message: Msg) {
|
||||||
|
match message {
|
||||||
|
Msg::Click => {
|
||||||
|
let Some(ref popover) = self.popover else { return };
|
||||||
|
let Some(ref button) = self.button else { return };
|
||||||
|
popover.show_popover(Rect::zero(), button, Edge::MaxY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewDelegate for PopoverExampleContentView {
|
||||||
|
const NAME: &'static str = "PopoverExampleContentView";
|
||||||
|
|
||||||
|
fn did_load(&mut self, view: cacao::view::View) {
|
||||||
|
let mut button = Button::new("Show");
|
||||||
|
button.set_action(|| dispatch_ui(Msg::Click));
|
||||||
|
|
||||||
|
let controller = PopoverExampleContentViewController::new();
|
||||||
|
let config = PopoverConfig {
|
||||||
|
animates: false,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let popover = Popover::new(controller, config);
|
||||||
|
self.popover = Some(popover);
|
||||||
|
|
||||||
|
view.add_subview(&button);
|
||||||
|
|
||||||
|
LayoutConstraint::activate(&[
|
||||||
|
button.center_x.constraint_equal_to(&view.center_x),
|
||||||
|
button.center_y.constraint_equal_to(&view.center_y)
|
||||||
|
]);
|
||||||
|
|
||||||
|
self.view = Some(view);
|
||||||
|
self.button = Some(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_ui(message: Msg) {
|
||||||
|
println!("Dispatching UI message: {:?}", message);
|
||||||
|
App::<BasicApp, Msg>::dispatch_main(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatcher for BasicApp {
|
||||||
|
type Message = Msg;
|
||||||
|
|
||||||
|
// Handles a message that came over on the main (UI) thread.
|
||||||
|
fn on_ui_message(&self, message: Self::Message) {
|
||||||
|
if let Some(d) = &self.window.window.delegate {
|
||||||
|
d.on_message(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PopoverExampleContentViewController {
|
||||||
|
pub label: Label
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopoverExampleContentViewController {
|
||||||
|
fn new() -> Self {
|
||||||
|
let label = Label::new();
|
||||||
|
let font = Font::system(20.);
|
||||||
|
label.set_font(&font);
|
||||||
|
label.set_text("Hello");
|
||||||
|
Self { label }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewDelegate for PopoverExampleContentViewController {
|
||||||
|
const NAME: &'static str = "PopoverExampleContentViewController";
|
||||||
|
|
||||||
|
fn did_load(&mut self, view: View) {
|
||||||
|
view.add_subview(&self.label);
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,11 @@ This example showcases text input, and logs it to the underlying console. It's m
|
||||||
|
|
||||||
`cargo run --example text_input`
|
`cargo run --example text_input`
|
||||||
|
|
||||||
|
## Safe Area
|
||||||
|
This example showcases A window containg the text "Hello, world!" while keeping everything in the safearea to keep it visible.
|
||||||
|
|
||||||
|
`cargo run --example safe_area`
|
||||||
|
|
||||||
## Calculator
|
## Calculator
|
||||||
A Rust-rendition of the macOS Calculator app.
|
A Rust-rendition of the macOS Calculator app.
|
||||||
|
|
||||||
|
|
63
examples/safe_area.rs
Normal file
63
examples/safe_area.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
//! This example showcases setting up a basic application and view with safe area constraints.
|
||||||
|
|
||||||
|
use cacao::appkit::window::Window;
|
||||||
|
use cacao::appkit::{App, AppDelegate};
|
||||||
|
use cacao::layout::{Layout, LayoutConstraint};
|
||||||
|
use cacao::text::{Font, Label};
|
||||||
|
use cacao::view::{View, ViewDelegate};
|
||||||
|
|
||||||
|
struct BasicApp {
|
||||||
|
window: Window,
|
||||||
|
content_view: View<ContentView>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppDelegate for BasicApp {
|
||||||
|
fn did_finish_launching(&self) {
|
||||||
|
App::activate();
|
||||||
|
|
||||||
|
self.window.set_minimum_content_size(400., 400.);
|
||||||
|
self.window.set_title("Safe Area Demo");
|
||||||
|
self.window.set_content_view(&self.content_view);
|
||||||
|
self.window.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_terminate_after_last_window_closed(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ContentView {
|
||||||
|
content: View,
|
||||||
|
label: Label
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewDelegate for ContentView {
|
||||||
|
const NAME: &'static str = "SafeAreaView";
|
||||||
|
|
||||||
|
fn did_load(&mut self, view: View) {
|
||||||
|
let font = Font::system(30.);
|
||||||
|
self.label.set_font(&font);
|
||||||
|
self.label.set_text("Hello World");
|
||||||
|
self.label.set_text_color(cacao::color::Color::rgb(255, 255, 255));
|
||||||
|
|
||||||
|
self.content.add_subview(&self.label);
|
||||||
|
view.add_subview(&self.content);
|
||||||
|
|
||||||
|
// Add layout constraints to be 100% excluding the safe area
|
||||||
|
// Do last because it will crash because the view needs to be inside the hierarchy
|
||||||
|
cacao::layout::LayoutConstraint::activate(&[
|
||||||
|
self.content.top.constraint_equal_to(&view.safe_layout_guide.top),
|
||||||
|
self.content.leading.constraint_equal_to(&view.safe_layout_guide.leading),
|
||||||
|
self.content.trailing.constraint_equal_to(&view.safe_layout_guide.trailing),
|
||||||
|
self.content.bottom.constraint_equal_to(&view.safe_layout_guide.bottom),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new("com.test.window", BasicApp {
|
||||||
|
window: Window::default(),
|
||||||
|
content_view: View::with(ContentView::default())
|
||||||
|
}).run();
|
||||||
|
}
|
|
@ -23,6 +23,8 @@ impl AppDelegate for BasicApp {
|
||||||
pub struct WebViewInstance;
|
pub struct WebViewInstance;
|
||||||
|
|
||||||
impl WebViewDelegate for WebViewInstance {
|
impl WebViewDelegate for WebViewInstance {
|
||||||
|
const NAME: &'static str = "CustomWebViewDelegate";
|
||||||
|
|
||||||
fn on_custom_protocol_request(&self, path: &str) -> Option<Vec<u8>> {
|
fn on_custom_protocol_request(&self, path: &str) -> Option<Vec<u8>> {
|
||||||
let requested_asset_path = path.replace("cacao://", "");
|
let requested_asset_path = path.replace("cacao://", "");
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,11 @@
|
||||||
//! creates a custom `NSApplication` subclass that currently does nothing; this is meant as a hook
|
//! creates a custom `NSApplication` subclass that currently does nothing; this is meant as a hook
|
||||||
//! for potential future use.
|
//! for potential future use.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
use objc::class;
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
use objc::runtime::Class;
|
use objc::runtime::Class;
|
||||||
|
|
||||||
|
use crate::foundation::load_or_register_class;
|
||||||
|
|
||||||
/// Used for injecting a custom NSApplication. Currently does nothing.
|
/// Used for injecting a custom NSApplication. Currently does nothing.
|
||||||
pub(crate) fn register_app_class() -> *const Class {
|
pub(crate) fn register_app_class() -> *const Class {
|
||||||
static mut APP_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSApplication", "RSTApplication", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSApplication);
|
|
||||||
let decl = ClassDecl::new("RSTApplication", superclass).unwrap();
|
|
||||||
APP_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { APP_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,19 @@
|
||||||
//! for potential future use.
|
//! for potential future use.
|
||||||
|
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
use block::Block;
|
use block::Block;
|
||||||
|
|
||||||
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::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::appkit::app::{AppDelegate, APP_PTR};
|
use crate::appkit::app::{AppDelegate, APP_PTR};
|
||||||
use crate::appkit::printing::PrintSettings;
|
use crate::appkit::printing::PrintSettings;
|
||||||
use crate::error::Error;
|
|
||||||
use crate::foundation::{id, nil, to_bool, NSArray, NSString, NSUInteger, BOOL, NO, YES};
|
|
||||||
use crate::user_activity::UserActivity;
|
|
||||||
|
|
||||||
#[cfg(feature = "cloudkit")]
|
#[cfg(feature = "cloudkit")]
|
||||||
use crate::cloudkit::share::CKShareMetaData;
|
use crate::cloudkit::share::CKShareMetaData;
|
||||||
|
use crate::error::Error;
|
||||||
|
use crate::foundation::{id, load_or_register_class, nil, to_bool, NSArray, NSString, NSUInteger, BOOL, NO, YES};
|
||||||
|
use crate::user_activity::UserActivity;
|
||||||
|
|
||||||
/// A handy method for grabbing our `AppDelegate` from the pointer. This is different from our
|
/// A handy method for grabbing our `AppDelegate` from the pointer. This is different from our
|
||||||
/// standard `utils` version as this doesn't require `RefCell` backing.
|
/// standard `utils` version as this doesn't require `RefCell` backing.
|
||||||
|
@ -295,13 +290,7 @@ extern "C" fn delegate_handles_key<T: AppDelegate>(this: &Object, _: Sel, _: id,
|
||||||
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
||||||
/// pointers we need to have.
|
/// pointers we need to have.
|
||||||
pub(crate) fn register_app_delegate_class<T: AppDelegate + AppDelegate>() -> *const Class {
|
pub(crate) fn register_app_delegate_class<T: AppDelegate + AppDelegate>() -> *const Class {
|
||||||
static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSObject", "RSTAppDelegate", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSObject);
|
|
||||||
let mut decl = ClassDecl::new("RSTAppDelegate", superclass).unwrap();
|
|
||||||
|
|
||||||
decl.add_ivar::<usize>(APP_PTR);
|
decl.add_ivar::<usize>(APP_PTR);
|
||||||
|
|
||||||
// Launching Applications
|
// Launching Applications
|
||||||
|
@ -461,9 +450,5 @@ pub(crate) fn register_app_delegate_class<T: AppDelegate + AppDelegate>() -> *co
|
||||||
sel!(application:delegateHandlesKey:),
|
sel!(application:delegateHandlesKey:),
|
||||||
delegate_handles_key::<T> as extern "C" fn(&Object, _, _, id) -> BOOL
|
delegate_handles_key::<T> as extern "C" fn(&Object, _, _, id) -> BOOL
|
||||||
);
|
);
|
||||||
|
})
|
||||||
DELEGATE_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { DELEGATE_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,13 +63,15 @@ pub use enums::*;
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::AppDelegate;
|
pub use traits::AppDelegate;
|
||||||
|
|
||||||
|
use super::window::Window;
|
||||||
|
|
||||||
pub(crate) static APP_PTR: &str = "rstAppPtr";
|
pub(crate) static APP_PTR: &str = "rstAppPtr";
|
||||||
|
|
||||||
/// A handler to make some boilerplate less annoying.
|
/// A handler to make some boilerplate less annoying.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn shared_application<F: Fn(id)>(handler: F) {
|
fn shared_application<T, F: Fn(id) -> T>(handler: F) -> T {
|
||||||
let app: id = unsafe { msg_send![register_app_class(), sharedApplication] };
|
let app: id = unsafe { msg_send![register_app_class(), sharedApplication] };
|
||||||
handler(app);
|
handler(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper for `NSApplication` in AppKit/Cocoa, and `UIApplication` in UIKit/Cocoa Touch.
|
/// A wrapper for `NSApplication` in AppKit/Cocoa, and `UIApplication` in UIKit/Cocoa Touch.
|
||||||
|
@ -298,6 +300,13 @@ impl App {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn main_window() -> Window {
|
||||||
|
shared_application(|app| unsafe {
|
||||||
|
let window: id = msg_send![app, mainWindow];
|
||||||
|
Window::existing(window)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Terminates the application, firing the requisite cleanup delegate methods in the process.
|
/// Terminates the application, firing the requisite cleanup delegate methods in the process.
|
||||||
///
|
///
|
||||||
/// This is typically called when the user chooses to quit via the App menu.
|
/// This is typically called when the user chooses to quit via the App menu.
|
||||||
|
|
|
@ -3,16 +3,13 @@
|
||||||
//! now.
|
//! now.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
use block::ConcreteBlock;
|
|
||||||
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 objc_id::Id;
|
use objc_id::Id;
|
||||||
|
|
||||||
use crate::events::EventModifierFlag;
|
use crate::events::EventModifierFlag;
|
||||||
use crate::foundation::{id, nil, NSString, NSUInteger};
|
use crate::foundation::{id, load_or_register_class, NSString, NSUInteger};
|
||||||
|
|
||||||
static BLOCK_PTR: &'static str = "cacaoMenuItemBlockPtr";
|
static BLOCK_PTR: &'static str = "cacaoMenuItemBlockPtr";
|
||||||
|
|
||||||
|
@ -314,19 +311,10 @@ extern "C" fn fire_block_action(this: &Object, _: Sel, _item: id) {
|
||||||
/// In general, we do not want to do more than we need to here - menus are one of the last areas
|
/// In general, we do not want to do more than we need to here - menus are one of the last areas
|
||||||
/// where Carbon still lurks, and subclassing things can get weird.
|
/// where Carbon still lurks, and subclassing things can get weird.
|
||||||
pub(crate) fn register_menu_item_class() -> *const Class {
|
pub(crate) fn register_menu_item_class() -> *const Class {
|
||||||
static mut APP_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSMenuItem", "CacaoMenuItem", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSMenuItem);
|
|
||||||
let mut decl = ClassDecl::new("CacaoMenuItem", superclass).unwrap();
|
|
||||||
decl.add_ivar::<usize>(BLOCK_PTR);
|
decl.add_ivar::<usize>(BLOCK_PTR);
|
||||||
|
|
||||||
decl.add_method(sel!(dealloc), dealloc_cacao_menuitem as extern "C" fn(&Object, _));
|
decl.add_method(sel!(dealloc), dealloc_cacao_menuitem as extern "C" fn(&Object, _));
|
||||||
decl.add_method(sel!(fireBlockAction:), fire_block_action as extern "C" fn(&Object, _, id));
|
decl.add_method(sel!(fireBlockAction:), fire_block_action as extern "C" fn(&Object, _, id));
|
||||||
|
})
|
||||||
APP_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { APP_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,15 @@
|
||||||
//! Everything useful for the `WindowController`. Handles injecting an `NSWindowController` subclass
|
//! Everything useful for the `WindowController`. Handles injecting an `NSWindowController` subclass
|
||||||
//! into the Objective C runtime, which loops back to give us lifecycle methods.
|
//! into the Objective C runtime, which loops back to give us lifecycle methods.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
use objc::class;
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
use objc::runtime::Class;
|
use objc::runtime::Class;
|
||||||
|
|
||||||
use crate::appkit::window::{WindowDelegate, WINDOW_DELEGATE_PTR};
|
use crate::appkit::window::{WindowDelegate, WINDOW_DELEGATE_PTR};
|
||||||
|
use crate::foundation::load_or_register_class;
|
||||||
|
|
||||||
/// Injects an `NSWindowController` subclass, with some callback and pointer ivars for what we
|
/// Injects an `NSWindowController` subclass, with some callback and pointer ivars for what we
|
||||||
/// need to do.
|
/// need to do.
|
||||||
pub(crate) fn register_window_controller_class<T: WindowDelegate>() -> *const Class {
|
pub(crate) fn register_window_controller_class<T: WindowDelegate>() -> *const Class {
|
||||||
static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSWindowController", "RSTWindowController", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSWindowController);
|
|
||||||
let mut decl = ClassDecl::new("RSTWindowController", superclass).unwrap();
|
|
||||||
decl.add_ivar::<usize>(WINDOW_DELEGATE_PTR);
|
decl.add_ivar::<usize>(WINDOW_DELEGATE_PTR);
|
||||||
DELEGATE_CLASS = decl.register();
|
})
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { DELEGATE_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ use crate::foundation::{id, nil, to_bool, NSInteger, NSString, NSUInteger, NO, Y
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
use crate::objc_access::ObjcAccess;
|
use crate::objc_access::ObjcAccess;
|
||||||
use crate::utils::{os, Controller};
|
use crate::utils::{os, Controller};
|
||||||
|
use crate::view::View;
|
||||||
|
|
||||||
mod class;
|
mod class;
|
||||||
use class::register_window_class_with_delegate;
|
use class::register_window_class_with_delegate;
|
||||||
|
@ -112,6 +113,13 @@ impl Window {
|
||||||
delegate: None
|
delegate: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn existing(window: *mut Object) -> Window {
|
||||||
|
Window {
|
||||||
|
objc: ShareId::from_ptr(window),
|
||||||
|
delegate: None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Window<T>
|
impl<T> Window<T>
|
||||||
|
@ -197,6 +205,19 @@ impl<T> Window<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the subtitle (smaller text bellow the title on unified and expanded title bars) on the
|
||||||
|
/// underlying window. When this property is an empty string, the system removes the subtitle
|
||||||
|
/// from the window layout. Allocates and passes an `NSString` over to the Objective C runtime.
|
||||||
|
/// Does nothing when less than version 11.
|
||||||
|
pub fn set_subtittle(&self, subtitle: &str) {
|
||||||
|
if !os::is_minimum_version(11) { return; }
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let subtitle = NSString::new(subtitle);
|
||||||
|
let _: () = msg_send![&*self.objc, setSubtitle: subtitle];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the title visibility for the underlying window.
|
/// Sets the title visibility for the underlying window.
|
||||||
pub fn set_title_visibility(&self, visibility: TitleVisibility) {
|
pub fn set_title_visibility(&self, visibility: TitleVisibility) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -291,6 +312,11 @@ impl<T> Window<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the objc ContentView from the window
|
||||||
|
pub(crate) unsafe fn content_view(&self) -> id {
|
||||||
|
let id: *mut Object = msg_send![&*self.objc, contentView];
|
||||||
|
id
|
||||||
|
}
|
||||||
/// Given a view, sets it as the content view for this window.
|
/// Given a view, sets it as the content view for this window.
|
||||||
pub fn set_content_view<L: Layout + 'static>(&self, view: &L) {
|
pub fn set_content_view<L: Layout + 'static>(&self, view: &L) {
|
||||||
view.with_backing_obj_mut(|backing_node| unsafe {
|
view.with_backing_obj_mut(|backing_node| unsafe {
|
||||||
|
|
|
@ -21,36 +21,28 @@
|
||||||
//! my_view.add_subview(&button);
|
//! my_view.add_subview(&button);
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::fmt;
|
use objc::runtime::{Class, Object};
|
||||||
use std::sync::Once;
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
use objc::runtime::{Class, Object, Sel};
|
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
pub use enums::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "appkit")]
|
||||||
|
use crate::appkit::FocusRingType;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::control::Control;
|
use crate::control::Control;
|
||||||
use crate::foundation::{id, nil, NSString, NSUInteger, BOOL, NO, YES};
|
use crate::foundation::{id, load_or_register_class, nil, NSString, NSUInteger, NO, YES};
|
||||||
use crate::image::Image;
|
use crate::image::Image;
|
||||||
use crate::invoker::TargetActionHandler;
|
use crate::invoker::TargetActionHandler;
|
||||||
use crate::keys::Key;
|
use crate::keys::Key;
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
use crate::objc_access::ObjcAccess;
|
|
||||||
use crate::text::{AttributedString, Font};
|
|
||||||
use crate::utils::{load, properties::ObjcProperty};
|
|
||||||
|
|
||||||
#[cfg(feature = "autolayout")]
|
#[cfg(feature = "autolayout")]
|
||||||
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
||||||
|
use crate::objc_access::ObjcAccess;
|
||||||
#[cfg(feature = "appkit")]
|
use crate::text::{AttributedString, Font};
|
||||||
use crate::appkit::FocusRingType;
|
use crate::utils::properties::ObjcProperty;
|
||||||
|
|
||||||
mod enums;
|
mod enums;
|
||||||
pub use enums::*;
|
|
||||||
|
|
||||||
/// Wraps `NSButton` on appkit, and `UIButton` on iOS and tvOS.
|
/// Wraps `NSButton` on appkit, and `UIButton` on iOS and tvOS.
|
||||||
///
|
///
|
||||||
|
@ -313,6 +305,7 @@ impl ObjcAccess for Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for Button {}
|
impl Layout for Button {}
|
||||||
|
|
||||||
impl Control for Button {}
|
impl Control for Button {}
|
||||||
|
|
||||||
impl ObjcAccess for &Button {
|
impl ObjcAccess for &Button {
|
||||||
|
@ -326,6 +319,7 @@ impl ObjcAccess for &Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for &Button {}
|
impl Layout for &Button {}
|
||||||
|
|
||||||
impl Control for &Button {}
|
impl Control for &Button {}
|
||||||
|
|
||||||
impl Drop for Button {
|
impl Drop for Button {
|
||||||
|
@ -343,14 +337,5 @@ impl Drop for Button {
|
||||||
/// Registers an `NSButton` subclass, and configures it to hold some ivars
|
/// Registers an `NSButton` subclass, and configures it to hold some ivars
|
||||||
/// for various things we need to store.
|
/// for various things we need to store.
|
||||||
fn register_class() -> *const Class {
|
fn register_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSButton", "RSTButton", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSButton);
|
|
||||||
let decl = ClassDecl::new("RSTButton", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,11 @@
|
||||||
//! that enables this functionality, we want to be able to provide this with some level of
|
//! that enables this functionality, we want to be able to provide this with some level of
|
||||||
//! backwards compatibility for Mojave, as that's still a supported OS.
|
//! backwards compatibility for Mojave, as that's still a supported OS.
|
||||||
|
|
||||||
use std::os::raw::c_void;
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
use core_graphics::base::CGFloat;
|
use core_graphics::base::CGFloat;
|
||||||
|
use objc::runtime::{Class, Object, Sel};
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
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::foundation::{id, nil, NSArray, NSInteger, NSString, NSUInteger, NO, YES};
|
use crate::foundation::{id, load_or_register_class, nil, NSArray, NSInteger};
|
||||||
use crate::utils::os;
|
use crate::utils::os;
|
||||||
|
|
||||||
pub(crate) const AQUA_LIGHT_COLOR_NORMAL_CONTRAST: &'static str = "AQUA_LIGHT_COLOR_NORMAL_CONTRAST";
|
pub(crate) const AQUA_LIGHT_COLOR_NORMAL_CONTRAST: &'static str = "AQUA_LIGHT_COLOR_NORMAL_CONTRAST";
|
||||||
|
@ -259,13 +254,7 @@ extern "C" fn color_with_system_effect(this: &Object, _: Sel, effect: NSInteger)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn register_class() -> *const Class {
|
pub(crate) fn register_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSColor", "CacaoDynamicColor", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSColor);
|
|
||||||
let mut decl = ClassDecl::new("CacaoDynamicColor", superclass).unwrap();
|
|
||||||
|
|
||||||
// These methods all need to be forwarded, so let's hook them up.
|
// These methods all need to be forwarded, so let's hook them up.
|
||||||
decl.add_method(sel!(colorSpace), color_space as extern "C" fn(&Object, _) -> id);
|
decl.add_method(sel!(colorSpace), color_space as extern "C" fn(&Object, _) -> id);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
|
@ -359,9 +348,5 @@ pub(crate) fn register_class() -> *const Class {
|
||||||
decl.add_ivar::<id>(AQUA_LIGHT_COLOR_HIGH_CONTRAST);
|
decl.add_ivar::<id>(AQUA_LIGHT_COLOR_HIGH_CONTRAST);
|
||||||
decl.add_ivar::<id>(AQUA_DARK_COLOR_NORMAL_CONTRAST);
|
decl.add_ivar::<id>(AQUA_DARK_COLOR_NORMAL_CONTRAST);
|
||||||
decl.add_ivar::<id>(AQUA_DARK_COLOR_HIGH_CONTRAST);
|
decl.add_ivar::<id>(AQUA_DARK_COLOR_HIGH_CONTRAST);
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
use objc::declare::ClassDecl;
|
||||||
use objc::runtime::{objc_getClass, Class};
|
use objc::runtime::{objc_getClass, Class};
|
||||||
|
|
||||||
|
@ -11,98 +15,98 @@ lazy_static! {
|
||||||
static ref CLASSES: ClassMap = ClassMap::new();
|
static ref CLASSES: ClassMap = ClassMap::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
/// A very simple RNG seed that we use in constructing unique subclass names.
|
||||||
|
///
|
||||||
|
/// Why are we doing this? Mainly because I just don't want to bring in another
|
||||||
|
/// crate for something that can be done like this; we don't need cryptographically
|
||||||
|
/// secure generation or anything fancy, as we're just after a unique dangling bit
|
||||||
|
/// for class names.
|
||||||
|
static RNG_SEED: Cell<u64> = Cell::new({
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
Instant::now().hash(&mut hasher);
|
||||||
|
thread::current().id().hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
(hash << 1) | 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents an entry in a `ClassMap`. We store an optional superclass_name for debugging
|
||||||
|
/// purposes; it's an `Option` to make the logic of loading a class type where we don't need to
|
||||||
|
/// care about the superclass type simpler.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ClassEntry {
|
||||||
|
pub superclass_name: Option<&'static str>,
|
||||||
|
pub ptr: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a key in a `ClassMap`.
|
||||||
|
type ClassKey = (&'static str, Option<&'static str>);
|
||||||
|
|
||||||
/// A ClassMap is a general cache for our Objective-C class lookup and registration. Rather than
|
/// A ClassMap is a general cache for our Objective-C class lookup and registration. Rather than
|
||||||
/// constantly calling into the runtime, we store pointers to Class types here after first lookup
|
/// constantly calling into the runtime, we store pointers to Class types here after first lookup
|
||||||
/// and/or creation. The general store format is (roughly speaking) as follows:
|
/// and/or creation.
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// {
|
|
||||||
/// "subclass_type": {
|
|
||||||
/// "superclass_type": *const Class as usize
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// The reasoning behind the double map is that it allows for lookup without allocating a `String`
|
|
||||||
/// on each hit; allocations are only required when creating a Class to inject, purely for naming
|
|
||||||
/// and debugging reasons.
|
|
||||||
///
|
///
|
||||||
/// There may be a way to do this without using HashMaps and avoiding the heap, but working and
|
/// There may be a way to do this without using HashMaps and avoiding the heap, but working and
|
||||||
/// usable beats ideal for now. Open to suggestions.
|
/// usable beats ideal for now. Open to suggestions.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ClassMap(RwLock<HashMap<&'static str, HashMap<&'static str, usize>>>);
|
pub(crate) struct ClassMap(RwLock<HashMap<ClassKey, ClassEntry>>);
|
||||||
|
|
||||||
impl ClassMap {
|
impl ClassMap {
|
||||||
/// Returns a new ClassMap.
|
/// Returns a new ClassMap.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
ClassMap(RwLock::new({
|
ClassMap(RwLock::new(HashMap::new()))
|
||||||
let mut map = HashMap::new();
|
|
||||||
|
|
||||||
// Top-level classes, like `NSView`, we cache here. The reasoning is that if a subclass
|
|
||||||
// is being created, we can avoid querying the runtime for the superclass - i.e, many
|
|
||||||
// subclasses will have `NSView` as their superclass.
|
|
||||||
map.insert("_supers", HashMap::new());
|
|
||||||
|
|
||||||
map
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to load a previously registered subclass.
|
/// Attempts to load a previously registered class.
|
||||||
pub fn load_subclass(&self, subclass_name: &'static str, superclass_name: &'static str) -> Option<*const Class> {
|
///
|
||||||
let reader = self.0.read().unwrap();
|
/// This checks our internal map first, and then calls out to the Objective-C runtime to ensure
|
||||||
|
/// we're not missing anything.
|
||||||
if let Some(inner) = (*reader).get(subclass_name) {
|
pub fn load(&self, class_name: &'static str, superclass_name: Option<&'static str>) -> Option<*const Class> {
|
||||||
if let Some(class) = inner.get(superclass_name) {
|
|
||||||
return Some(*class as *const Class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Store a newly created subclass type.
|
|
||||||
pub fn store_subclass(&self, subclass_name: &'static str, superclass_name: &'static str, class: *const Class) {
|
|
||||||
let mut writer = self.0.write().unwrap();
|
|
||||||
|
|
||||||
if let Some(map) = (*writer).get_mut(subclass_name) {
|
|
||||||
map.insert(superclass_name, class as usize);
|
|
||||||
} else {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
map.insert(superclass_name, class as usize);
|
|
||||||
(*writer).insert(subclass_name, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to load a Superclass. This first checks for the cached pointer; if not present, it
|
|
||||||
/// will load the superclass from the Objective-C runtime and cache it for future lookup. This
|
|
||||||
/// assumes that the class is one that should *already* and *always* exist in the runtime, and
|
|
||||||
/// by design will panic if it can't load the correct superclass, as that would lead to very
|
|
||||||
/// invalid behavior.
|
|
||||||
pub fn load_superclass(&self, name: &'static str) -> Option<*const Class> {
|
|
||||||
{
|
{
|
||||||
let reader = self.0.read().unwrap();
|
let reader = self.0.read().unwrap();
|
||||||
if let Some(superclass) = (*reader)["_supers"].get(name) {
|
if let Some(entry) = (*reader).get(&(class_name, superclass_name)) {
|
||||||
return Some(*superclass as *const Class);
|
let ptr = &entry.ptr;
|
||||||
|
return Some(*ptr as *const Class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let objc_superclass_name = CString::new(name).unwrap();
|
// If we don't have an entry for the class_name in our internal map, we should still check
|
||||||
let superclass = unsafe { objc_getClass(objc_superclass_name.as_ptr() as *const _) };
|
// if we can load it from the Objective-C runtime directly. The reason we need to do this
|
||||||
|
// is that there's a use-case where someone might have multiple bundles attempting to
|
||||||
|
// use or register the same subclass; Rust doesn't see the same pointers unlike the Objective-C
|
||||||
|
// runtime, and we can wind up in a situation where we're attempting to register a Class
|
||||||
|
// that already exists but we can't see.
|
||||||
|
let objc_class_name = CString::new(class_name).unwrap();
|
||||||
|
let class = unsafe { objc_getClass(objc_class_name.as_ptr() as *const _) };
|
||||||
|
|
||||||
// This should not happen, for our use-cases, but it's conceivable that this could actually
|
// This should not happen for our use-cases, but it's conceivable that this could actually
|
||||||
// be expected, so just return None and let the caller panic if so desired.
|
// be expected, so just return None and let the caller panic if so desired.
|
||||||
if superclass.is_null() {
|
if class.is_null() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we got here, then this class exists in the Objective-C runtime but is not known to
|
||||||
|
// us. For consistency's sake, we'll add this to our store and return that.
|
||||||
{
|
{
|
||||||
let mut writer = self.0.write().unwrap();
|
let mut writer = self.0.write().unwrap();
|
||||||
if let Some(supers) = (*writer).get_mut("_supers") {
|
writer.insert((class_name, superclass_name), ClassEntry {
|
||||||
supers.insert(name, superclass as usize);
|
superclass_name,
|
||||||
}
|
ptr: class as usize
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(superclass)
|
Some(class)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store a newly created subclass type.
|
||||||
|
pub fn store(&self, class_name: &'static str, superclass_name: Option<&'static str>, class: *const Class) {
|
||||||
|
let mut writer = self.0.write().unwrap();
|
||||||
|
|
||||||
|
writer.insert((class_name, superclass_name), ClassEntry {
|
||||||
|
superclass_name,
|
||||||
|
ptr: class as usize
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,19 +128,37 @@ pub fn load_or_register_class<F>(superclass_name: &'static str, subclass_name: &
|
||||||
where
|
where
|
||||||
F: Fn(&mut ClassDecl) + 'static
|
F: Fn(&mut ClassDecl) + 'static
|
||||||
{
|
{
|
||||||
if let Some(subclass) = CLASSES.load_subclass(subclass_name, superclass_name) {
|
if let Some(subclass) = CLASSES.load(subclass_name, Some(superclass_name)) {
|
||||||
return subclass;
|
return subclass;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(superclass) = CLASSES.load_superclass(superclass_name) {
|
// If we can't find the class anywhere, then we'll attempt to load the superclass and register
|
||||||
let objc_subclass_name = format!("{}_{}", subclass_name, superclass_name);
|
// our new class type.
|
||||||
|
if let Some(superclass) = CLASSES.load(superclass_name, None) {
|
||||||
|
// When we're generating a new Subclass name, we need to append a random-ish component
|
||||||
|
// due to some oddities that can come up in certain scenarios (e.g, various bundler
|
||||||
|
// situations appear to have odd rules about subclass name usage/registration, this simply
|
||||||
|
// guarantees that we almost always have a unique name to register with the ObjC runtime).
|
||||||
|
//
|
||||||
|
// For more context, see: https://github.com/ryanmcgrath/cacao/issues/63
|
||||||
|
let objc_subclass_name = format!(
|
||||||
|
"{}_{}_{}",
|
||||||
|
subclass_name,
|
||||||
|
superclass_name,
|
||||||
|
RNG_SEED.with(|rng| {
|
||||||
|
rng.set(rng.get().wrapping_add(0xa0761d6478bd642f));
|
||||||
|
let s = rng.get();
|
||||||
|
let t = u128::from(s) * (u128::from(s ^ 0xe7037ed1a0b428db));
|
||||||
|
((t >> 64) as u64) ^ (t as u64)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
match ClassDecl::new(&objc_subclass_name, unsafe { &*superclass }) {
|
match ClassDecl::new(&objc_subclass_name, unsafe { &*superclass }) {
|
||||||
Some(mut decl) => {
|
Some(mut decl) => {
|
||||||
config(&mut decl);
|
config(&mut decl);
|
||||||
|
|
||||||
let class = decl.register();
|
let class = decl.register();
|
||||||
CLASSES.store_subclass(subclass_name, superclass_name, class);
|
CLASSES.store(subclass_name, Some(superclass_name), class);
|
||||||
return class;
|
return class;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,14 @@ impl Rect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Edge {
|
||||||
|
MinX = 0,
|
||||||
|
MinY = 1,
|
||||||
|
MaxX = 2,
|
||||||
|
MaxY = 3
|
||||||
|
}
|
||||||
impl From<Rect> for CGRect {
|
impl From<Rect> for CGRect {
|
||||||
fn from(rect: Rect) -> CGRect {
|
fn from(rect: Rect) -> CGRect {
|
||||||
CGRect::new(&CGPoint::new(rect.left, rect.top), &CGSize::new(rect.width, rect.height))
|
CGRect::new(&CGPoint::new(rect.left, rect.top), &CGSize::new(rect.width, rect.height))
|
||||||
|
|
|
@ -7,33 +7,15 @@
|
||||||
//! for in the modern era. It also implements a few helpers for things like setting a background
|
//! for in the modern era. It also implements a few helpers for things like setting a background
|
||||||
//! color, and enforcing layer backing by default.
|
//! color, and enforcing layer backing by default.
|
||||||
|
|
||||||
use std::sync::Once;
|
use objc::runtime::Class;
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
use crate::foundation::load_or_register_class;
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL};
|
|
||||||
use objc::{class, sel, sel_impl};
|
|
||||||
use objc_id::Id;
|
|
||||||
|
|
||||||
use crate::dragdrop::DragInfo;
|
|
||||||
use crate::foundation::{id, NSUInteger, NO, YES};
|
|
||||||
use crate::utils::load;
|
|
||||||
use crate::view::{ViewDelegate, VIEW_DELEGATE_PTR};
|
|
||||||
|
|
||||||
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
||||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
/// used if there's no delegates.
|
/// used if there's no delegates.
|
||||||
pub(crate) fn register_image_view_class() -> *const Class {
|
pub(crate) fn register_image_view_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSImageView", "RSTImageView", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSImageView);
|
|
||||||
let decl = ClassDecl::new("RSTImageView", superclass).unwrap();
|
|
||||||
|
|
||||||
//decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
//decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,10 @@
|
||||||
use std::sync::Once;
|
use objc::runtime::Class;
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
use crate::foundation::load_or_register_class;
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL};
|
|
||||||
use objc::{class, sel, sel_impl};
|
|
||||||
use objc_id::Id;
|
|
||||||
|
|
||||||
use crate::foundation::{id, NSUInteger, NO, YES};
|
|
||||||
use crate::utils::load;
|
|
||||||
use crate::view::{ViewDelegate, VIEW_DELEGATE_PTR};
|
|
||||||
|
|
||||||
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
||||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
/// used if there's no delegates.
|
/// used if there's no delegates.
|
||||||
pub(crate) fn register_image_view_class() -> *const Class {
|
pub(crate) fn register_image_view_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("UIImageView", "RSTImageView", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(UIImageView);
|
|
||||||
let mut decl = ClassDecl::new("RSTImageView", superclass).expect("Failed to get RSTVIEW");
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
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::{msg_send, sel, sel_impl};
|
||||||
use objc_id::Id;
|
|
||||||
|
|
||||||
use crate::dragdrop::DragInfo;
|
use crate::foundation::{id, load_or_register_class, NSString, NO, YES};
|
||||||
use crate::foundation::{id, load_or_register_class, NSString, NSUInteger, NO, YES};
|
|
||||||
use crate::input::{TextFieldDelegate, TEXTFIELD_DELEGATE_PTR};
|
use crate::input::{TextFieldDelegate, TEXTFIELD_DELEGATE_PTR};
|
||||||
use crate::utils::load;
|
use crate::utils::load;
|
||||||
|
|
||||||
|
@ -52,16 +47,7 @@ extern "C" fn text_should_end_editing<T: TextFieldDelegate>(this: &mut Object, _
|
||||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
/// used if there's no delegates.
|
/// used if there's no delegates.
|
||||||
pub(crate) fn register_view_class() -> *const Class {
|
pub(crate) fn register_view_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSTextField", "RSTTextInputField", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSTextField);
|
|
||||||
let decl = ClassDecl::new("RSTTextInputField", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Injects an `NSTextField` subclass, with some callback and pointer ivars for what we
|
/// Injects an `NSTextField` subclass, with some callback and pointer ivars for what we
|
||||||
|
|
|
@ -9,15 +9,12 @@
|
||||||
//! is going away.
|
//! is going away.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::{Arc, Mutex, Once};
|
|
||||||
|
|
||||||
use block::{Block, ConcreteBlock, RcBlock};
|
|
||||||
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::{msg_send, sel, sel_impl};
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::foundation::{id, nil, NSString};
|
use crate::foundation::{id, load_or_register_class};
|
||||||
use crate::utils::load;
|
use crate::utils::load;
|
||||||
|
|
||||||
pub static ACTION_CALLBACK_PTR: &str = "rstTargetActionPtr";
|
pub static ACTION_CALLBACK_PTR: &str = "rstTargetActionPtr";
|
||||||
|
@ -95,18 +92,8 @@ extern "C" fn perform<F: Fn() + 'static>(this: &mut Object, _: Sel, _sender: id)
|
||||||
/// on drop. We handle the heap copy on the Rust side, so setting the block
|
/// on drop. We handle the heap copy on the Rust side, so setting the block
|
||||||
/// is just an ivar.
|
/// is just an ivar.
|
||||||
pub(crate) fn register_invoker_class<F: Fn() + 'static>() -> *const Class {
|
pub(crate) fn register_invoker_class<F: Fn() + 'static>() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSObject", "RSTTargetActionHandler", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSObject);
|
|
||||||
let mut decl = ClassDecl::new("RSTTargetActionHandler", superclass).unwrap();
|
|
||||||
|
|
||||||
decl.add_ivar::<usize>(ACTION_CALLBACK_PTR);
|
decl.add_ivar::<usize>(ACTION_CALLBACK_PTR);
|
||||||
decl.add_method(sel!(perform:), perform::<F> as extern "C" fn(&mut Object, _, id));
|
decl.add_method(sel!(perform:), perform::<F> as extern "C" fn(&mut Object, _, id));
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ pub mod layout;
|
||||||
pub mod listview;
|
pub mod listview;
|
||||||
pub mod networking;
|
pub mod networking;
|
||||||
pub mod notification_center;
|
pub mod notification_center;
|
||||||
pub(crate) mod objc_access;
|
pub mod objc_access;
|
||||||
|
|
||||||
#[cfg(feature = "appkit")]
|
#[cfg(feature = "appkit")]
|
||||||
pub mod pasteboard;
|
pub mod pasteboard;
|
||||||
|
|
|
@ -7,16 +7,13 @@
|
||||||
//! for in the modern era. It also implements a few helpers for things like setting a background
|
//! for in the modern era. It also implements a few helpers for things like setting a background
|
||||||
//! color, and enforcing layer backing by default.
|
//! color, and enforcing layer backing by default.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
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::{msg_send, sel, sel_impl};
|
||||||
use objc_id::Id;
|
use objc_id::Id;
|
||||||
|
|
||||||
use crate::appkit::menu::{Menu, MenuItem};
|
use crate::appkit::menu::Menu;
|
||||||
use crate::dragdrop::DragInfo;
|
use crate::dragdrop::DragInfo;
|
||||||
use crate::foundation::{id, load_or_register_class, nil, NSArray, NSInteger, NSUInteger, NO, YES};
|
use crate::foundation::{id, load_or_register_class, NSArray, NSInteger, NSUInteger, NO, YES};
|
||||||
use crate::listview::{ListViewDelegate, RowEdge, LISTVIEW_DELEGATE_PTR};
|
use crate::listview::{ListViewDelegate, RowEdge, LISTVIEW_DELEGATE_PTR};
|
||||||
use crate::utils::load;
|
use crate::utils::load;
|
||||||
|
|
||||||
|
@ -185,16 +182,7 @@ extern "C" fn dragging_exited<T: ListViewDelegate>(this: &mut Object, _: Sel, in
|
||||||
/// `UITableView` semantics; if `NSTableView`'s multi column behavior is needed, then it can
|
/// `UITableView` semantics; if `NSTableView`'s multi column behavior is needed, then it can
|
||||||
/// be added in.
|
/// be added in.
|
||||||
pub(crate) fn register_listview_class() -> *const Class {
|
pub(crate) fn register_listview_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSTableView", "RSTListView", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSTableView);
|
|
||||||
let decl = ClassDecl::new("RSTListView", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Injects an `NSTableView` subclass, with some callback and pointer ivars for what we
|
/// Injects an `NSTableView` subclass, with some callback and pointer ivars for what we
|
||||||
|
|
|
@ -7,15 +7,12 @@
|
||||||
//! for in the modern era. It also implements a few helpers for things like setting a background
|
//! for in the modern era. It also implements a few helpers for things like setting a background
|
||||||
//! color, and enforcing layer backing by default.
|
//! color, and enforcing layer backing by default.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
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 objc_id::Id;
|
use objc_id::Id;
|
||||||
|
|
||||||
use crate::dragdrop::DragInfo;
|
use crate::dragdrop::DragInfo;
|
||||||
use crate::foundation::{id, nil, NSUInteger, NO, YES};
|
use crate::foundation::{id, load_or_register_class, nil, NSUInteger, NO, YES};
|
||||||
use crate::listview::row::{ViewDelegate, BACKGROUND_COLOR, LISTVIEW_ROW_DELEGATE_PTR};
|
use crate::listview::row::{ViewDelegate, BACKGROUND_COLOR, LISTVIEW_ROW_DELEGATE_PTR};
|
||||||
use crate::utils::load;
|
use crate::utils::load;
|
||||||
|
|
||||||
|
@ -107,31 +104,15 @@ extern "C" fn dealloc<T: ViewDelegate>(this: &Object, _: Sel) {
|
||||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
/// used if there's no delegates.
|
/// used if there's no delegates.
|
||||||
pub(crate) fn register_listview_row_class() -> *const Class {
|
pub(crate) fn register_listview_row_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSView", "RSTTableViewRow", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSView);
|
|
||||||
let mut decl = ClassDecl::new("RSTTableViewRow", superclass).unwrap();
|
|
||||||
|
|
||||||
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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_listview_row_class_with_delegate<T: ViewDelegate>() -> *const Class {
|
pub(crate) fn register_listview_row_class_with_delegate<T: ViewDelegate>() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSView", "RSTableViewRowWithDelegate", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSView);
|
|
||||||
let mut decl = ClassDecl::new("RSTableViewRowWithDelegate", superclass).unwrap();
|
|
||||||
|
|
||||||
// A pointer to the "view controller" on the Rust side. It's expected that this doesn't
|
// A pointer to the "view controller" on the Rust side. It's expected that this doesn't
|
||||||
// move.
|
// move.
|
||||||
decl.add_ivar::<usize>(LISTVIEW_ROW_DELEGATE_PTR);
|
decl.add_ivar::<usize>(LISTVIEW_ROW_DELEGATE_PTR);
|
||||||
|
@ -164,9 +145,5 @@ pub(crate) fn register_listview_row_class_with_delegate<T: ViewDelegate>() -> *c
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
decl.add_method(sel!(dealloc), dealloc::<T> as extern "C" fn(&Object, _));
|
decl.add_method(sel!(dealloc), dealloc::<T> as extern "C" fn(&Object, _));
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +1,24 @@
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
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, sel, sel_impl};
|
use objc::{class, sel, sel_impl};
|
||||||
use objc_id::Id;
|
use objc_id::Id;
|
||||||
|
|
||||||
use crate::foundation::{id, YES, NO, NSUInteger};
|
|
||||||
use crate::dragdrop::DragInfo;
|
use crate::dragdrop::DragInfo;
|
||||||
use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate};
|
use crate::foundation::{id, NSUInteger, NO, YES};
|
||||||
use crate::utils::load;
|
use crate::utils::load;
|
||||||
|
use crate::view::{ViewDelegate, VIEW_DELEGATE_PTR};
|
||||||
|
|
||||||
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
||||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
/// used if there's no delegates.
|
/// used if there's no delegates.
|
||||||
pub(crate) fn register_view_class() -> *const Class {
|
pub(crate) fn register_view_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("UIView", "RSTView", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(UIView);
|
|
||||||
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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_with_delegate<T: ViewDelegate>() -> *const Class {
|
pub(crate) fn register_view_class_with_delegate<T: ViewDelegate>() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("UIView", "RSTViewWithDelegate", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(UIView);
|
|
||||||
let mut decl = ClassDecl::new("RSTViewWithDelegate", superclass).unwrap();
|
|
||||||
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
|
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
|
||||||
VIEW_CLASS = decl.register();
|
})
|
||||||
});
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
VIEW_CLASS
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,12 @@
|
||||||
//! for in the modern era. It also implements a few helpers for things like setting a background
|
//! for in the modern era. It also implements a few helpers for things like setting a background
|
||||||
//! color, and enforcing layer backing by default.
|
//! color, and enforcing layer backing by default.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL};
|
use objc::runtime::{Class, Object, Sel, BOOL};
|
||||||
use objc::{class, sel, sel_impl};
|
use objc::{sel, sel_impl};
|
||||||
use objc_id::Id;
|
use objc_id::Id;
|
||||||
|
|
||||||
use crate::dragdrop::DragInfo;
|
use crate::dragdrop::DragInfo;
|
||||||
use crate::foundation::{id, NSUInteger, NO, YES};
|
use crate::foundation::{id, load_or_register_class, NSUInteger, NO, YES};
|
||||||
use crate::scrollview::{ScrollViewDelegate, SCROLLVIEW_DELEGATE_PTR};
|
use crate::scrollview::{ScrollViewDelegate, SCROLLVIEW_DELEGATE_PTR};
|
||||||
use crate::utils::load;
|
use crate::utils::load;
|
||||||
|
|
||||||
|
@ -77,28 +74,13 @@ extern "C" fn dragging_exited<T: ScrollViewDelegate>(this: &mut Object, _: Sel,
|
||||||
|
|
||||||
/// Injects an `NSScrollView` subclass.
|
/// Injects an `NSScrollView` subclass.
|
||||||
pub(crate) fn register_scrollview_class() -> *const Class {
|
pub(crate) fn register_scrollview_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSScrollView", "RSTScrollView", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSScrollView);
|
|
||||||
let decl = ClassDecl::new("RSTScrollView", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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_scrollview_class_with_delegate<T: ScrollViewDelegate>() -> *const Class {
|
pub(crate) fn register_scrollview_class_with_delegate<T: ScrollViewDelegate>() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSScrollView", "RSTScrollViewWithDelegate", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSScrollView);
|
|
||||||
let mut decl = ClassDecl::new("RSTScrollViewWithDelegate", superclass).unwrap();
|
|
||||||
|
|
||||||
// A pointer to the "view controller" on the Rust side. It's expected that this doesn't
|
// A pointer to the "view controller" on the Rust side. It's expected that this doesn't
|
||||||
// move.
|
// move.
|
||||||
decl.add_ivar::<usize>(SCROLLVIEW_DELEGATE_PTR);
|
decl.add_ivar::<usize>(SCROLLVIEW_DELEGATE_PTR);
|
||||||
|
@ -126,9 +108,5 @@ pub(crate) fn register_scrollview_class_with_delegate<T: ScrollViewDelegate>() -
|
||||||
sel!(draggingExited:),
|
sel!(draggingExited:),
|
||||||
dragging_exited::<T> as extern "C" fn(&mut Object, _, _)
|
dragging_exited::<T> as extern "C" fn(&mut Object, _, _)
|
||||||
);
|
);
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
//! Implements a Select-style dropdown. By default this uses NSPopupSelect on macOS.
|
//! Implements a Select-style dropdown. By default this uses NSPopupSelect on macOS.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
use core_graphics::geometry::CGRect;
|
use core_graphics::geometry::CGRect;
|
||||||
|
use objc::runtime::{Class, Object};
|
||||||
use objc::declare::ClassDecl;
|
use objc::{msg_send, sel, sel_impl};
|
||||||
use objc::runtime::{Class, Object, Sel};
|
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::control::Control;
|
use crate::control::Control;
|
||||||
use crate::foundation::{id, nil, NSInteger, NSString, NO, YES};
|
use crate::foundation::{id, load_or_register_class, nil, NSInteger, NSString, NO, YES};
|
||||||
use crate::geometry::Rect;
|
use crate::geometry::Rect;
|
||||||
use crate::invoker::TargetActionHandler;
|
use crate::invoker::TargetActionHandler;
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
use crate::objc_access::ObjcAccess;
|
|
||||||
use crate::utils::properties::ObjcProperty;
|
|
||||||
|
|
||||||
#[cfg(feature = "autolayout")]
|
#[cfg(feature = "autolayout")]
|
||||||
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
||||||
|
use crate::objc_access::ObjcAccess;
|
||||||
|
use crate::utils::properties::ObjcProperty;
|
||||||
|
|
||||||
/// Wraps `NSPopUpSelect` on AppKit. Not currently implemented for iOS.
|
/// Wraps `NSPopUpSelect` on AppKit. Not currently implemented for iOS.
|
||||||
///
|
///
|
||||||
|
@ -268,14 +263,5 @@ impl Drop for Select {
|
||||||
/// Registers an `NSSelect` subclass, and configures it to hold some ivars
|
/// Registers an `NSSelect` subclass, and configures it to hold some ivars
|
||||||
/// for various things we need to store.
|
/// for various things we need to store.
|
||||||
fn register_class() -> *const Class {
|
fn register_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSPopUpButton", "CacaoSelect", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSPopUpButton);
|
|
||||||
let decl = ClassDecl::new("CacaoSelect", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
//! A wrapper for NSSwitch. Currently the epitome of jank - if you're poking around here, expect
|
//! A wrapper for NSSwitch. Currently the epitome of jank - if you're poking around here, expect
|
||||||
//! that this will change at some point.
|
//! that this will change at some point.
|
||||||
|
|
||||||
use std::fmt;
|
use objc::runtime::{Class, Object};
|
||||||
use std::sync::Once;
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
use objc::runtime::{Class, Object, Sel};
|
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::foundation::{id, nil, NSString, BOOL, NO, YES};
|
use crate::foundation::{id, load_or_register_class, nil, NSString, NO};
|
||||||
use crate::invoker::TargetActionHandler;
|
use crate::invoker::TargetActionHandler;
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
use crate::objc_access::ObjcAccess;
|
|
||||||
use crate::utils::{load, properties::ObjcProperty};
|
|
||||||
|
|
||||||
#[cfg(feature = "autolayout")]
|
#[cfg(feature = "autolayout")]
|
||||||
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
||||||
|
use crate::objc_access::ObjcAccess;
|
||||||
|
use crate::utils::properties::ObjcProperty;
|
||||||
|
|
||||||
/// A wrapper for `NSSwitch`. Holds (retains) pointers for the Objective-C runtime
|
/// A wrapper for `NSSwitch`. Holds (retains) pointers for the Objective-C runtime
|
||||||
/// where our `NSSwitch` lives.
|
/// where our `NSSwitch` lives.
|
||||||
|
@ -178,14 +173,5 @@ impl Drop for Switch {
|
||||||
/// Registers an `NSButton` subclass, and configures it to hold some ivars
|
/// Registers an `NSButton` subclass, and configures it to hold some ivars
|
||||||
/// for various things we need to store.
|
/// for various things we need to store.
|
||||||
fn register_class() -> *const Class {
|
fn register_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSButton", "RSTSwitch", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSButton);
|
|
||||||
let decl = ClassDecl::new("RSTSwitch", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use objc::{class, msg_send, sel, sel_impl};
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::foundation::{id, nil, NSArray, NSString, NO, YES};
|
use crate::foundation::{id, nil, NSArray, NSString, NO, YES};
|
||||||
|
use crate::utils::os;
|
||||||
|
|
||||||
/// A `Font` can be constructed and applied to supported controls to control things like text
|
/// A `Font` can be constructed and applied to supported controls to control things like text
|
||||||
/// appearance and size.
|
/// appearance and size.
|
||||||
|
@ -40,6 +41,25 @@ impl Font {
|
||||||
|
|
||||||
Font(unsafe { ShareId::from_ptr(msg_send![class!(NSFont), boldSystemFontOfSize: size]) })
|
Font(unsafe { ShareId::from_ptr(msg_send![class!(NSFont), boldSystemFontOfSize: size]) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates and returns a monospace system font at the specified size and weight
|
||||||
|
///
|
||||||
|
/// # Support
|
||||||
|
///
|
||||||
|
/// The `monospace` font feature is available from version `10.15`.
|
||||||
|
///
|
||||||
|
/// If the current system is using an older version the `monospacedSystemFontOfSize`
|
||||||
|
/// option will be omitted.
|
||||||
|
pub fn monospace(size: f64, weight: f64) -> Self {
|
||||||
|
let size = size as CGFloat;
|
||||||
|
let weight = weight as CGFloat;
|
||||||
|
|
||||||
|
if os::is_minimum_semversion(10, 15, 0) {
|
||||||
|
Font(unsafe { ShareId::from_ptr(msg_send![class!(NSFont), monospacedSystemFontOfSize: size weight: weight]) })
|
||||||
|
} else {
|
||||||
|
Font(unsafe { ShareId::from_ptr(msg_send![class!(NSFont), systemFontOfSize: size weight: weight ]) })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Font {
|
impl Deref for Font {
|
||||||
|
|
|
@ -7,50 +7,24 @@
|
||||||
//! for in the modern era. It also implements a few helpers for things like setting a background
|
//! for in the modern era. It also implements a few helpers for things like setting a background
|
||||||
//! color, and enforcing layer backing by default.
|
//! color, and enforcing layer backing by default.
|
||||||
|
|
||||||
use std::sync::Once;
|
use objc::runtime::Class;
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
use crate::foundation::load_or_register_class;
|
||||||
use objc::runtime::{Class, Object, Sel, BOOL};
|
|
||||||
use objc::{class, sel, sel_impl};
|
|
||||||
use objc_id::Id;
|
|
||||||
|
|
||||||
use crate::dragdrop::DragInfo;
|
|
||||||
use crate::foundation::{id, NSUInteger, NO, YES};
|
|
||||||
use crate::text::label::{LabelDelegate, LABEL_DELEGATE_PTR};
|
use crate::text::label::{LabelDelegate, LABEL_DELEGATE_PTR};
|
||||||
use crate::utils::load;
|
|
||||||
|
|
||||||
/// Injects an `NSTextField` subclass. This is used for the default views that don't use delegates - we
|
/// Injects an `NSTextField` subclass. This is used for the default views that don't use delegates - we
|
||||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
/// used if there's no delegates.
|
/// used if there's no delegates.
|
||||||
pub(crate) fn register_view_class() -> *const Class {
|
pub(crate) fn register_view_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSTextField", "RSTTextField", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSTextField);
|
|
||||||
let decl = ClassDecl::new("RSTTextField", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Injects an `NSTextField` subclass, with some callback and pointer ivars for what we
|
/// Injects an `NSTextField` subclass, with some callback and pointer ivars for what we
|
||||||
/// need to do.
|
/// need to do.
|
||||||
pub(crate) fn register_view_class_with_delegate<T: LabelDelegate>() -> *const Class {
|
pub(crate) fn register_view_class_with_delegate<T: LabelDelegate>() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSView", "RSTTextFieldWithDelegate", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSView);
|
|
||||||
let mut decl = ClassDecl::new("RSTTextFieldWithDelegate", superclass).unwrap();
|
|
||||||
|
|
||||||
// A pointer to the "view controller" on the Rust side. It's expected that this doesn't
|
// A pointer to the "view controller" on the Rust side. It's expected that this doesn't
|
||||||
// move.
|
// move.
|
||||||
decl.add_ivar::<usize>(LABEL_DELEGATE_PTR);
|
decl.add_ivar::<usize>(LABEL_DELEGATE_PTR);
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,11 @@
|
||||||
//! creates a custom `UIApplication` subclass that currently does nothing; this is meant as a hook
|
//! creates a custom `UIApplication` subclass that currently does nothing; this is meant as a hook
|
||||||
//! for potential future use.
|
//! for potential future use.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
use objc::class;
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
use objc::runtime::Class;
|
use objc::runtime::Class;
|
||||||
|
|
||||||
|
use crate::foundation::load_or_register_class;
|
||||||
|
|
||||||
/// Used for injecting a custom UIApplication. Currently does nothing.
|
/// Used for injecting a custom UIApplication. Currently does nothing.
|
||||||
pub(crate) fn register_app_class() -> *const Class {
|
pub(crate) fn register_app_class() -> *const Class {
|
||||||
static mut APP_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("UIApplication", "RSTApplication", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(UIApplication);
|
|
||||||
let decl = ClassDecl::new("RSTApplication", superclass).unwrap();
|
|
||||||
APP_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { APP_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,20 @@
|
||||||
//! creates a custom `UIApplication` subclass that currently does nothing; this is meant as a hook
|
//! creates a custom `UIApplication` subclass that currently does nothing; this is meant as a hook
|
||||||
//! for potential future use.
|
//! for potential future use.
|
||||||
|
|
||||||
//use std::ffi::c_void;
|
use objc::runtime::{Class, Object, Sel};
|
||||||
use std::sync::Once;
|
use objc::{sel, sel_impl};
|
||||||
|
|
||||||
|
//use crate::error::Error;
|
||||||
|
use crate::foundation::{id, load_or_register_class, BOOL, YES};
|
||||||
|
use crate::uikit::app::{AppDelegate, APP_DELEGATE};
|
||||||
|
use crate::uikit::scene::{SceneConnectionOptions, SceneSession};
|
||||||
|
|
||||||
//use std::unreachable;
|
//use std::unreachable;
|
||||||
|
|
||||||
//use block::Block;
|
//use block::Block;
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
use objc::runtime::{Class, Object, Sel};
|
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
|
||||||
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
//use crate::error::Error;
|
|
||||||
use crate::foundation::{id, nil, NSArray, NSString, NSUInteger, BOOL, NO, YES};
|
|
||||||
//use crate::user_activity::UserActivity;
|
//use crate::user_activity::UserActivity;
|
||||||
|
|
||||||
use crate::uikit::app::{AppDelegate, APP_DELEGATE};
|
|
||||||
use crate::uikit::scene::{SceneConfig, SceneConnectionOptions, SceneSession};
|
|
||||||
|
|
||||||
#[cfg(feature = "cloudkit")]
|
|
||||||
use crate::cloudkit::share::CKShareMetaData;
|
|
||||||
|
|
||||||
/// A handy method for grabbing our `AppDelegate` from the pointer. This is different from our
|
/// A handy method for grabbing our `AppDelegate` from the pointer. This is different from our
|
||||||
/// standard `utils` version as this doesn't require `RefCell` backing.
|
/// standard `utils` version as this doesn't require `RefCell` backing.
|
||||||
fn app<T>(this: &Object) -> &T {
|
fn app<T>(this: &Object) -> &T {
|
||||||
|
@ -49,13 +41,7 @@ extern "C" fn configuration_for_scene_session<T: AppDelegate>(this: &Object, _:
|
||||||
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
||||||
/// pointers we need to have.
|
/// pointers we need to have.
|
||||||
pub(crate) fn register_app_delegate_class<T: AppDelegate>() -> *const Class {
|
pub(crate) fn register_app_delegate_class<T: AppDelegate>() -> *const Class {
|
||||||
static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSObject", "RSTAppDelegate", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSObject);
|
|
||||||
let mut decl = ClassDecl::new("RSTAppDelegate", superclass).unwrap();
|
|
||||||
|
|
||||||
// Launching Applications
|
// Launching Applications
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(application:didFinishLaunchingWithOptions:),
|
sel!(application:didFinishLaunchingWithOptions:),
|
||||||
|
@ -71,9 +57,5 @@ pub(crate) fn register_app_delegate_class<T: AppDelegate>() -> *const Class {
|
||||||
sel!(application:didDiscardSceneSessions:),
|
sel!(application:didDiscardSceneSessions:),
|
||||||
did_discard_scene_sessions::<T> as extern "C" fn(&Object, _, _, id)
|
did_discard_scene_sessions::<T> as extern "C" fn(&Object, _, _, id)
|
||||||
);*/
|
);*/
|
||||||
|
})
|
||||||
DELEGATE_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { DELEGATE_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,10 @@
|
||||||
use std::ffi::c_void;
|
use objc::runtime::{Class, Object, Protocol, Sel};
|
||||||
use std::sync::Once;
|
|
||||||
use std::unreachable;
|
|
||||||
|
|
||||||
use block::Block;
|
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
|
||||||
use objc::runtime::{Class, Object, Sel};
|
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use url::Url;
|
use crate::foundation::{id, load_or_register_class};
|
||||||
|
|
||||||
use crate::error::Error;
|
|
||||||
use crate::foundation::{id, nil, NSArray, NSString, NSUInteger, BOOL, NO, YES};
|
|
||||||
use crate::user_activity::UserActivity;
|
|
||||||
use crate::utils::load;
|
|
||||||
|
|
||||||
use crate::uikit::app::SCENE_DELEGATE_VENDOR;
|
use crate::uikit::app::SCENE_DELEGATE_VENDOR;
|
||||||
use crate::uikit::scene::{Scene, SceneConfig, SceneConnectionOptions, SceneSession, WindowSceneDelegate};
|
use crate::uikit::scene::{Scene, SceneConnectionOptions, SceneSession, WindowSceneDelegate};
|
||||||
|
use crate::utils::load;
|
||||||
#[cfg(feature = "cloudkit")]
|
|
||||||
use crate::cloudkit::share::CKShareMetaData;
|
|
||||||
|
|
||||||
pub(crate) static WINDOW_SCENE_PTR: &str = "rstWindowSceneDelegatePtr";
|
pub(crate) static WINDOW_SCENE_PTR: &str = "rstWindowSceneDelegatePtr";
|
||||||
|
|
||||||
|
@ -60,14 +45,7 @@ extern "C" fn scene_will_connect_to_session_with_options<T: WindowSceneDelegate>
|
||||||
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
||||||
/// pointers we need to have.
|
/// pointers we need to have.
|
||||||
pub(crate) fn register_window_scene_delegate_class<T: WindowSceneDelegate, F: Fn() -> Box<T>>() -> *const Class {
|
pub(crate) fn register_window_scene_delegate_class<T: WindowSceneDelegate, F: Fn() -> Box<T>>() -> *const Class {
|
||||||
static mut DELEGATE_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("UIResponder", "RSTWindowSceneDelegate", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
use objc::runtime::{class_addProtocol, Protocol};
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(UIResponder);
|
|
||||||
let mut decl = ClassDecl::new("RSTWindowSceneDelegate", superclass).unwrap();
|
|
||||||
|
|
||||||
let p = Protocol::get("UIWindowSceneDelegate").unwrap();
|
let p = Protocol::get("UIWindowSceneDelegate").unwrap();
|
||||||
|
|
||||||
// A spot to hold a pointer to
|
// A spot to hold a pointer to
|
||||||
|
@ -82,10 +60,5 @@ pub(crate) fn register_window_scene_delegate_class<T: WindowSceneDelegate, F: Fn
|
||||||
sel!(scene:willConnectToSession:options:),
|
sel!(scene:willConnectToSession:options:),
|
||||||
scene_will_connect_to_session_with_options::<T> as extern "C" fn(&Object, _, _, _, _)
|
scene_will_connect_to_session_with_options::<T> as extern "C" fn(&Object, _, _, _, _)
|
||||||
);
|
);
|
||||||
|
})
|
||||||
// Launching Applications
|
|
||||||
DELEGATE_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { DELEGATE_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
//! for in the modern era. It also implements a few helpers for things like setting a background
|
//! for in the modern era. It also implements a few helpers for things like setting a background
|
||||||
//! color, and enforcing layer backing by default.
|
//! color, and enforcing layer backing by default.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
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};
|
||||||
|
@ -92,23 +90,13 @@ extern "C" fn update_layer(this: &Object, _: Sel) {
|
||||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
/// used if there's no delegates.
|
/// used if there's no delegates.
|
||||||
pub(crate) fn register_view_class() -> *const Class {
|
pub(crate) fn register_view_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSView", "RSTView", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSView);
|
|
||||||
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();
|
|
||||||
|
|
||||||
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
||||||
decl.add_method(sel!(updateLayer), update_layer as extern "C" fn(&Object, _));
|
decl.add_method(sel!(updateLayer), update_layer as extern "C" fn(&Object, _));
|
||||||
decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
||||||
|
|
||||||
decl.add_ivar::<id>(BACKGROUND_COLOR);
|
decl.add_ivar::<id>(BACKGROUND_COLOR);
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
|
|
@ -77,6 +77,8 @@ mod splitviewcontroller;
|
||||||
#[cfg(feature = "appkit")]
|
#[cfg(feature = "appkit")]
|
||||||
pub use splitviewcontroller::SplitViewController;
|
pub use splitviewcontroller::SplitViewController;
|
||||||
|
|
||||||
|
mod popover;
|
||||||
|
pub use popover::*;
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::ViewDelegate;
|
pub use traits::ViewDelegate;
|
||||||
|
|
||||||
|
|
95
src/view/popover/mod.rs
Normal file
95
src/view/popover/mod.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use core_graphics::geometry::CGRect;
|
||||||
|
use objc::runtime::Object;
|
||||||
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
use crate::appkit::toolbar::ToolbarItem;
|
||||||
|
use crate::appkit::window::Window;
|
||||||
|
use crate::appkit::App;
|
||||||
|
use crate::foundation::{id, nil, NSString};
|
||||||
|
use crate::geometry::{Edge, Rect};
|
||||||
|
use crate::layout::Layout;
|
||||||
|
use crate::utils::{os, CGSize, Controller};
|
||||||
|
use crate::view::{View, ViewController, ViewDelegate};
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
#[repr(i64)]
|
||||||
|
pub enum PopoverBehaviour {
|
||||||
|
/// Your application assumes responsibility for closing the popover.
|
||||||
|
ApplicationDefined = 0,
|
||||||
|
/// The system will close the popover when the user interacts with a user interface element outside the popover.
|
||||||
|
Transient = 1,
|
||||||
|
/// The system will close the popover when the user interacts with user interface elements in the window containing the popover's positioning view.
|
||||||
|
Semitransient = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PopoverConfig {
|
||||||
|
pub content_size: CGSize,
|
||||||
|
pub animates: bool,
|
||||||
|
pub behaviour: PopoverBehaviour
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PopoverConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
content_size: CGSize {
|
||||||
|
width: 320.0,
|
||||||
|
height: 320.0
|
||||||
|
},
|
||||||
|
animates: true,
|
||||||
|
behaviour: PopoverBehaviour::Transient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Popover<Content> {
|
||||||
|
/// A reference to the underlying Objective-C NSPopover
|
||||||
|
pub objc: ShareId<Object>,
|
||||||
|
|
||||||
|
/// The wrapped ViewController.
|
||||||
|
pub view_controller: ViewController<Content>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Content> Popover<Content>
|
||||||
|
where
|
||||||
|
Content: ViewDelegate + 'static
|
||||||
|
{
|
||||||
|
pub fn new(content: Content, config: PopoverConfig) -> Self {
|
||||||
|
let view_controller = ViewController::new(content);
|
||||||
|
let objc = unsafe {
|
||||||
|
let pop: id = msg_send![class!(NSPopover), new];
|
||||||
|
let _: () = msg_send![pop, setContentSize: config.content_size];
|
||||||
|
let _: () = msg_send![pop, setBehavior: config.behaviour as i64];
|
||||||
|
let _: () = msg_send![pop, setAnimates: config.animates];
|
||||||
|
let _: () = msg_send![pop, setContentViewController: &*view_controller.objc];
|
||||||
|
|
||||||
|
ShareId::from_ptr(pop)
|
||||||
|
};
|
||||||
|
|
||||||
|
Popover { objc, view_controller }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Content> Popover<Content> {
|
||||||
|
/// Show a popover relative to a view
|
||||||
|
pub fn show_popover<V: Layout>(&self, relative_to: Rect, view: &V, edge: Edge) {
|
||||||
|
let rect: CGRect = relative_to.into();
|
||||||
|
unsafe {
|
||||||
|
view.with_backing_obj_mut(|obj| {
|
||||||
|
let _: () = msg_send![&*self.objc, showRelativeToRect:rect ofView: &*obj preferredEdge: edge as u32];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show the popover relative to the content view of the main window
|
||||||
|
pub fn show_popover_main(&self, rect: Rect, edge: Edge) {
|
||||||
|
let window = App::main_window();
|
||||||
|
unsafe {
|
||||||
|
let content_view = window.content_view();
|
||||||
|
let rect: CGRect = rect.into();
|
||||||
|
let _: () = msg_send![&*self.objc, showRelativeToRect:rect ofView: content_view preferredEdge: edge as u32];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,10 @@
|
||||||
use std::sync::Once;
|
|
||||||
use std::unreachable;
|
|
||||||
|
|
||||||
use objc::declare::ClassDecl;
|
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::foundation::{BOOL};
|
use crate::foundation::BOOL;
|
||||||
use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate};
|
use crate::utils::{as_bool, load};
|
||||||
use crate::utils::{load, as_bool};
|
use crate::view::{ViewDelegate, VIEW_DELEGATE_PTR};
|
||||||
|
|
||||||
/// Called when the view controller receives a `viewWillAppear:` message.
|
/// Called when the view controller receives a `viewWillAppear:` message.
|
||||||
extern "C" fn will_appear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BOOL) {
|
extern "C" fn will_appear<T: ViewDelegate>(this: &mut Object, _: Sel, animated: BOOL) {
|
||||||
|
@ -51,22 +48,18 @@ extern "C" fn did_disappear<T: ViewDelegate>(this: &mut Object, _: Sel, animated
|
||||||
|
|
||||||
/// Registers an `NSViewDelegate`.
|
/// Registers an `NSViewDelegate`.
|
||||||
pub(crate) fn register_view_controller_class<T: ViewDelegate + 'static>() -> *const Class {
|
pub(crate) fn register_view_controller_class<T: ViewDelegate + 'static>() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("UIViewController", "RSTViewController", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(UIViewController);
|
|
||||||
let mut decl = ClassDecl::new("RSTViewController", superclass).unwrap();
|
|
||||||
|
|
||||||
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
|
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
|
||||||
|
|
||||||
decl.add_method(sel!(viewWillAppear:), will_appear::<T> as extern "C" fn(&mut Object, _, BOOL));
|
decl.add_method(sel!(viewWillAppear:), will_appear::<T> as extern "C" fn(&mut Object, _, BOOL));
|
||||||
decl.add_method(sel!(viewDidAppear:), did_appear::<T> as extern "C" fn(&mut Object, _, BOOL));
|
decl.add_method(sel!(viewDidAppear:), did_appear::<T> as extern "C" fn(&mut Object, _, BOOL));
|
||||||
decl.add_method(sel!(viewWillDisappear:), will_disappear::<T> as extern "C" fn(&mut Object, _, BOOL));
|
decl.add_method(
|
||||||
decl.add_method(sel!(viewDidDisappear:), did_disappear::<T> as extern "C" fn(&mut Object, _, BOOL));
|
sel!(viewWillDisappear:),
|
||||||
|
will_disappear::<T> as extern "C" fn(&mut Object, _, BOOL)
|
||||||
VIEW_CLASS = decl.register();
|
);
|
||||||
});
|
decl.add_method(
|
||||||
|
sel!(viewDidDisappear:),
|
||||||
unsafe { VIEW_CLASS }
|
did_disappear::<T> as extern "C" fn(&mut Object, _, BOOL)
|
||||||
|
);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::sync::Once;
|
|
||||||
|
|
||||||
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, sel, sel_impl};
|
use objc::{class, sel, sel_impl};
|
||||||
|
@ -14,16 +12,7 @@ use crate::view::{ViewDelegate, VIEW_DELEGATE_PTR};
|
||||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
/// used if there's no delegates.
|
/// used if there's no delegates.
|
||||||
pub(crate) fn register_view_class() -> *const Class {
|
pub(crate) fn register_view_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("UIView", "RSTView", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(UIView);
|
|
||||||
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Injects a `UIView` subclass, with some callback and pointer ivars for what we
|
/// Injects a `UIView` subclass, with some callback and pointer ivars for what we
|
||||||
|
|
|
@ -12,7 +12,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::foundation::{id, nil, NSArray, NSInteger, NSString, NO, YES};
|
use crate::foundation::{id, load_or_register_class, nil, NSArray, NSInteger, NSString, NO, YES};
|
||||||
use crate::webview::actions::{NavigationAction, NavigationResponse};
|
use crate::webview::actions::{NavigationAction, NavigationResponse};
|
||||||
use crate::webview::{mimetype::MimeType, WebViewDelegate, WEBVIEW_DELEGATE_PTR}; //, OpenPanelParameters};
|
use crate::webview::{mimetype::MimeType, WebViewDelegate, WEBVIEW_DELEGATE_PTR}; //, OpenPanelParameters};
|
||||||
//use crate::webview::enums::{NavigationPolicy, NavigationResponsePolicy};
|
//use crate::webview::enums::{NavigationPolicy, NavigationResponsePolicy};
|
||||||
|
@ -170,29 +170,14 @@ extern "C" fn handle_download<T: WebViewDelegate>(this: &Object, _: Sel, downloa
|
||||||
/// both a subclass of `NSViewController` and a delegate of the held `WKWebView` (for the various
|
/// both a subclass of `NSViewController` and a delegate of the held `WKWebView` (for the various
|
||||||
/// varieties of delegates needed there).
|
/// varieties of delegates needed there).
|
||||||
pub fn register_webview_class() -> *const Class {
|
pub fn register_webview_class() -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("WKWebView", "CacaoWebView", |decl| unsafe {})
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(WKWebView);
|
|
||||||
let decl = ClassDecl::new("RSTWebView", superclass).unwrap();
|
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers an `NSViewController` that we effectively turn into a `WebViewController`. Acts as
|
/// Registers an `NSViewController` that we effectively turn into a `WebViewController`. Acts as
|
||||||
/// both a subclass of `NSViewController` and a delegate of the held `WKWebView` (for the various
|
/// both a subclass of `NSViewController` and a delegate of the held `WKWebView` (for the various
|
||||||
/// varieties of delegates needed there).
|
/// varieties of delegates needed there).
|
||||||
pub fn register_webview_delegate_class<T: WebViewDelegate>() -> *const Class {
|
pub fn register_webview_delegate_class<T: WebViewDelegate>(instance: &T) -> *const Class {
|
||||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
load_or_register_class("NSObject", instance.subclass_name(), |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = class!(NSObject);
|
|
||||||
let mut decl = ClassDecl::new("RSTWebViewDelegate", superclass).unwrap();
|
|
||||||
|
|
||||||
decl.add_ivar::<usize>(WEBVIEW_DELEGATE_PTR);
|
decl.add_ivar::<usize>(WEBVIEW_DELEGATE_PTR);
|
||||||
|
|
||||||
// WKNavigationDelegate
|
// WKNavigationDelegate
|
||||||
|
@ -239,9 +224,5 @@ pub fn register_webview_delegate_class<T: WebViewDelegate>() -> *const Class {
|
||||||
sel!(_download:decideDestinationWithSuggestedFilename:completionHandler:),
|
sel!(_download:decideDestinationWithSuggestedFilename:completionHandler:),
|
||||||
handle_download::<T> as extern "C" fn(&Object, _, id, id, usize)
|
handle_download::<T> as extern "C" fn(&Object, _, id, id, usize)
|
||||||
);
|
);
|
||||||
|
})
|
||||||
VIEW_CLASS = decl.register();
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { VIEW_CLASS }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,10 +230,11 @@ where
|
||||||
/// Initializes a new WebView with a given `WebViewDelegate`. This enables you to respond to events
|
/// Initializes a new WebView with a given `WebViewDelegate`. This enables you to respond to events
|
||||||
/// and customize the view as a module, similar to class-based systems.
|
/// and customize the view as a module, similar to class-based systems.
|
||||||
pub fn with(config: WebViewConfig, delegate: T) -> WebView<T> {
|
pub fn with(config: WebViewConfig, delegate: T) -> WebView<T> {
|
||||||
|
let delegate_class = register_webview_delegate_class(&delegate);
|
||||||
let mut delegate = Box::new(delegate);
|
let mut delegate = Box::new(delegate);
|
||||||
|
|
||||||
let objc_delegate = unsafe {
|
let objc_delegate = unsafe {
|
||||||
let objc_delegate: id = msg_send![register_webview_delegate_class::<T>(), new];
|
let objc_delegate: id = msg_send![delegate_class, new];
|
||||||
let ptr: *const T = &*delegate;
|
let ptr: *const T = &*delegate;
|
||||||
(&mut *objc_delegate).set_ivar(WEBVIEW_DELEGATE_PTR, ptr as usize);
|
(&mut *objc_delegate).set_ivar(WEBVIEW_DELEGATE_PTR, ptr as usize);
|
||||||
ShareId::from_ptr(objc_delegate)
|
ShareId::from_ptr(objc_delegate)
|
||||||
|
|
|
@ -4,41 +4,23 @@
|
||||||
//!
|
//!
|
||||||
//! If you use that feature, there are no guarantees you'll be accepted into the App Store.
|
//! If you use that feature, there are no guarantees you'll be accepted into the App Store.
|
||||||
|
|
||||||
use std::sync::Once;
|
|
||||||
use std::ffi::c_void;
|
|
||||||
|
|
||||||
use block::Block;
|
use block::Block;
|
||||||
|
use cocoa::foundation::{NSArray, NSInteger, NSPoint, NSRect, NSSize, NSString};
|
||||||
use cocoa::foundation::{NSRect, NSPoint, NSSize, NSString, NSArray, NSInteger};
|
|
||||||
|
|
||||||
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::foundation::{id, nil, YES, NO};
|
use crate::foundation::{id, nil, NO, YES};
|
||||||
use crate::webview::traits::WebViewController;
|
use crate::webview::traits::WebViewController;
|
||||||
|
|
||||||
extern "C" fn download_delegate(this: &Object, _: Sel) -> id {
|
extern "C" fn download_delegate(this: &Object, _: Sel) -> id {
|
||||||
println!("YO!");
|
println!("YO!");
|
||||||
unsafe {
|
unsafe { NSString::alloc(nil).init_str("") }
|
||||||
NSString::alloc(nil).init_str("")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_process_pool() -> *const Object {
|
pub fn register_process_pool() -> *const Object {
|
||||||
static mut PROCESS_POOL: *const Object = 0 as *const Object;
|
load_or_register_class("WKProcessPool", "RSTWebViewProcessPool", |decl| unsafe {
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
INIT.call_once(|| unsafe {
|
|
||||||
let superclass = Class::get("WKProcessPool").unwrap();
|
|
||||||
let mut decl = ClassDecl::new("RSTWebViewProcessPool", superclass).unwrap();
|
|
||||||
|
|
||||||
//decl.add_ivar::<id>(DOWNLOAD_DELEGATE_PTR);
|
//decl.add_ivar::<id>(DOWNLOAD_DELEGATE_PTR);
|
||||||
decl.add_method(sel!(_downloadDelegate), download_delegate as extern "C" fn(&Object, _) -> id);
|
decl.add_method(sel!(_downloadDelegate), download_delegate as extern "C" fn(&Object, _) -> id);
|
||||||
|
})
|
||||||
//PROCESS_POOL = decl.register();
|
|
||||||
PROCESS_POOL = msg_send![decl.register(), new];
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe { PROCESS_POOL }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,17 @@ use crate::webview::WebView;
|
||||||
|
|
||||||
/// You can implement this on structs to handle callbacks from the underlying `WKWebView`.
|
/// You can implement this on structs to handle callbacks from the underlying `WKWebView`.
|
||||||
pub trait WebViewDelegate {
|
pub trait WebViewDelegate {
|
||||||
|
/// Used to cache subclass creations on the Objective-C side.
|
||||||
|
/// You can just set this to be the name of your delegate type. This
|
||||||
|
/// value *must* be unique per-type.
|
||||||
|
const NAME: &'static str;
|
||||||
|
|
||||||
|
/// You should rarely (read: probably never) need to implement this yourself.
|
||||||
|
/// It simply acts as a getter for the associated `NAME` const on this trait.
|
||||||
|
fn subclass_name(&self) -> &'static str {
|
||||||
|
Self::NAME
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when the View is ready to work with. You're passed a `ViewHandle` - this is safe to
|
/// Called when the View is ready to work with. You're passed a `ViewHandle` - this is safe to
|
||||||
/// store and use repeatedly, but it's not thread safe - any UI calls must be made from the
|
/// store and use repeatedly, but it's not thread safe - any UI calls must be made from the
|
||||||
/// main thread!
|
/// main thread!
|
||||||
|
|
Loading…
Reference in a new issue