Working on getting WKWebView working again - close
This commit is contained in:
parent
c1da7b1b37
commit
e4f96b4ab5
|
@ -69,13 +69,13 @@ pub mod prelude {
|
|||
pub use crate::networking::URLRequest;
|
||||
|
||||
pub use crate::window::{
|
||||
Window, /*WindowController,*/ WindowDelegate
|
||||
Window, WindowConfig, WindowDelegate
|
||||
};
|
||||
|
||||
#[cfg(feature = "webview")]
|
||||
pub use crate::webview::{
|
||||
WebView, WebViewConfig, WebViewController
|
||||
WebView, WebViewConfig, WebViewDelegate
|
||||
};
|
||||
|
||||
//pub use crate::view::{View, ViewController, ViewDelegate};
|
||||
pub use crate::view::{View, ViewDelegate};
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use objc_id::Id;
|
|||
|
||||
use crate::foundation::{id, NSString};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct URLRequest {
|
||||
pub inner: Id<Object>
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::rc::Rc;
|
|||
use std::cell::RefCell;
|
||||
|
||||
use objc_id::ShareId;
|
||||
use objc::runtime::Object;
|
||||
use objc::runtime::{Class, Object};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, YES, NO, NSArray, NSString};
|
||||
|
@ -28,6 +28,16 @@ pub use traits::ViewDelegate;
|
|||
|
||||
pub(crate) static VIEW_DELEGATE_PTR: &str = "rstViewDelegatePtr";
|
||||
|
||||
/// A helper method for instantiating view classes and applying default settings to them.
|
||||
fn allocate_view(registration_fn: fn() -> *const Class) -> id {
|
||||
unsafe {
|
||||
let view: id = msg_send![registration_fn(), new];
|
||||
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
let _: () = msg_send![view, setWantsLayer:YES];
|
||||
view
|
||||
}
|
||||
}
|
||||
|
||||
/// A clone-able handler to a `ViewController` reference in the Objective C runtime. We use this
|
||||
/// instead of a stock `View` for easier recordkeeping, since it'll need to hold the `View` on that
|
||||
/// side anyway.
|
||||
|
@ -77,12 +87,7 @@ impl Default for View {
|
|||
impl View {
|
||||
/// Returns a default `View`, suitable for
|
||||
pub fn new() -> Self {
|
||||
let view: id = unsafe {
|
||||
let view: id = msg_send![register_view_class(), new];
|
||||
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
let _: () = msg_send![view, setWantsLayer:YES];
|
||||
view
|
||||
};
|
||||
let view = allocate_view(register_view_class);
|
||||
|
||||
View {
|
||||
internal_callback_ptr: None,
|
||||
|
@ -111,11 +116,11 @@ impl<T> View<T> where T: ViewDelegate + 'static {
|
|||
Rc::into_raw(cloned)
|
||||
};
|
||||
|
||||
let view = unsafe {
|
||||
let view: id = msg_send![register_view_class_with_delegate::<T>(), new];
|
||||
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
let view = allocate_view(register_view_class_with_delegate::<T>);
|
||||
unsafe {
|
||||
//let view: id = msg_send![register_view_class_with_delegate::<T>(), new];
|
||||
//let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
(&mut *view).set_ivar(VIEW_DELEGATE_PTR, internal_callback_ptr as usize);
|
||||
view
|
||||
};
|
||||
|
||||
let mut view = View {
|
||||
|
|
|
@ -4,6 +4,7 @@ use objc::{msg_send, sel, sel_impl};
|
|||
|
||||
use crate::foundation::{id, BOOL, YES, NO, NSInteger};
|
||||
use crate::networking::URLRequest;
|
||||
use crate::webview::enums::NavigationType;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NavigationAction {
|
||||
|
@ -42,7 +43,6 @@ impl NavigationResponse {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct OpenPanelParameters {
|
||||
pub allows_directories: bool,
|
||||
|
|
|
@ -12,51 +12,13 @@ use objc::runtime::{Class, Object, Sel};
|
|||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, CGRect, NSString, NSArray, NSInteger};
|
||||
use crate::constants::{WEBVIEW_VAR, WEBVIEW_CONFIG_VAR, WEBVIEW_CONTROLLER_PTR};
|
||||
use crate::geometry::Rect;
|
||||
use crate::view::traits::ViewController;
|
||||
use crate::webview::action::{NavigationAction, NavigationResponse};
|
||||
use crate::webview::traits::WebViewController;
|
||||
|
||||
/// Loads and configures ye old WKWebView/View for this controller.
|
||||
extern fn load_view<T: ViewController + WebViewController>(this: &mut Object, _: Sel) {
|
||||
unsafe {
|
||||
let configuration: id = *this.get_ivar(WEBVIEW_CONFIG_VAR);
|
||||
|
||||
// Technically private!
|
||||
#[cfg(feature = "webview-downloading")]
|
||||
let process_pool: id = msg_send![configuration, processPool];
|
||||
#[cfg(feature = "webview-downloading")]
|
||||
let _: () = msg_send![process_pool, _setDownloadDelegate:&*this];
|
||||
|
||||
let zero: CGRect = Rect::zero().into();
|
||||
let webview_alloc: id = msg_send![class!(WKWebView), alloc];
|
||||
let webview: id = msg_send![webview_alloc, initWithFrame:zero configuration:configuration];
|
||||
let _: () = msg_send![webview, setWantsLayer:YES];
|
||||
let _: () = msg_send![webview, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
// Provide an easy way to grab this later
|
||||
(*this).set_ivar(WEBVIEW_VAR, webview);
|
||||
|
||||
// Clean this up to be safe, as WKWebView makes a copy and we don't need it anymore.
|
||||
(*this).set_ivar(WEBVIEW_CONFIG_VAR, nil);
|
||||
|
||||
let _: () = msg_send![this, setView:webview];
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to connect delegates - doing this in `loadView` can be... bug-inducing.
|
||||
extern fn view_did_load<T: ViewController + WebViewController>(this: &Object, _: Sel) {
|
||||
unsafe {
|
||||
let webview: id = *this.get_ivar(WEBVIEW_VAR);
|
||||
let _: () = msg_send![webview, setNavigationDelegate:&*this];
|
||||
let _: () = msg_send![webview, setUIDelegate:&*this];
|
||||
}
|
||||
}
|
||||
use crate::webview::{WEBVIEW_DELEGATE_PTR, WebViewDelegate};
|
||||
use crate::webview::actions::{NavigationAction, NavigationResponse, OpenPanelParameters};
|
||||
use crate::webview::enums::{NavigationPolicy, NavigationResponsePolicy};
|
||||
|
||||
/// Called when an `alert()` from the underlying `WKWebView` is fired. Will call over to your
|
||||
/// `WebViewController`, where you should handle the event.
|
||||
extern fn alert<T: WebViewController + 'static>(_: &Object, _: Sel, _: id, s: id, _: id, complete: id) {
|
||||
extern fn alert<T: WebViewDelegate>(_: &Object, _: Sel, _: id, s: id, _: id, complete: id) {
|
||||
let alert = NSString::wrap(s).to_str();
|
||||
println!("Alert: {}", alert);
|
||||
|
||||
|
@ -68,7 +30,7 @@ extern fn alert<T: WebViewController + 'static>(_: &Object, _: Sel, _: id, s: id
|
|||
}
|
||||
|
||||
/*unsafe {
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_CONTROLLER_PTR);
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_DELEGATE_PTR);
|
||||
let webview = ptr as *const T;
|
||||
(*webview).alert(alert);
|
||||
}*/
|
||||
|
@ -81,21 +43,21 @@ extern fn alert<T: WebViewController + 'static>(_: &Object, _: Sel, _: id, s: id
|
|||
}
|
||||
|
||||
/// Fires when a message has been passed from the underlying `WKWebView`.
|
||||
extern fn on_message<T: WebViewController + 'static>(this: &Object, _: Sel, _: id, script_message: id) {
|
||||
extern fn on_message<T: WebViewDelegate>(this: &Object, _: Sel, _: id, script_message: id) {
|
||||
unsafe {
|
||||
let name = NSString::wrap(msg_send![script_message, name]).to_str();
|
||||
let body = NSString::wrap(msg_send![script_message, body]).to_str();
|
||||
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_CONTROLLER_PTR);
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_DELEGATE_PTR);
|
||||
let webview = ptr as *const T;
|
||||
(*webview).on_message(name, body);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fires when deciding a navigation policy - i.e, should something be allowed or not.
|
||||
extern fn decide_policy_for_action<T: WebViewController + 'static>(this: &Object, _: Sel, _: id, action: id, handler: usize) {
|
||||
extern fn decide_policy_for_action<T: WebViewDelegate>(this: &Object, _: Sel, _: id, action: id, handler: usize) {
|
||||
let webview = unsafe {
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_CONTROLLER_PTR);
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_DELEGATE_PTR);
|
||||
let webview = ptr as *const T;
|
||||
&*webview
|
||||
};
|
||||
|
@ -111,9 +73,9 @@ extern fn decide_policy_for_action<T: WebViewController + 'static>(this: &Object
|
|||
}
|
||||
|
||||
/// Fires when deciding a navigation policy - i.e, should something be allowed or not.
|
||||
extern fn decide_policy_for_response<T: WebViewController + 'static>(this: &Object, _: Sel, _: id, response: id, handler: usize) {
|
||||
extern fn decide_policy_for_response<T: WebViewDelegate>(this: &Object, _: Sel, _: id, response: id, handler: usize) {
|
||||
let webview = unsafe {
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_CONTROLLER_PTR);
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_DELEGATE_PTR);
|
||||
let webview = ptr as *const T;
|
||||
&*webview
|
||||
};
|
||||
|
@ -126,9 +88,9 @@ extern fn decide_policy_for_response<T: WebViewController + 'static>(this: &Obje
|
|||
}
|
||||
|
||||
/// Fires when deciding a navigation policy - i.e, should something be allowed or not.
|
||||
extern fn run_open_panel<T: WebViewController + 'static>(this: &Object, _: Sel, _: id, params: id, _: id, handler: usize) {
|
||||
extern fn run_open_panel<T: WebViewDelegate>(this: &Object, _: Sel, _: id, params: id, _: id, handler: usize) {
|
||||
let webview = unsafe {
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_CONTROLLER_PTR);
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_DELEGATE_PTR);
|
||||
let webview = ptr as *const T;
|
||||
&*webview
|
||||
};
|
||||
|
@ -139,7 +101,7 @@ extern fn run_open_panel<T: WebViewController + 'static>(this: &Object, _: Sel,
|
|||
match urls {
|
||||
Some(u) => {
|
||||
let nsurls: NSArray = u.iter().map(|s| {
|
||||
let s = NSString::new(&s);
|
||||
let s = NSString::new(s);
|
||||
msg_send![class!(NSURL), URLWithString:s]
|
||||
}).collect::<Vec<id>>().into();
|
||||
|
||||
|
@ -155,9 +117,9 @@ extern fn run_open_panel<T: WebViewController + 'static>(this: &Object, _: Sel,
|
|||
/// response is upgraded to BecomeDownload. Only called when explicitly linked since it's a private
|
||||
/// API.
|
||||
#[cfg(feature = "webview-downloading")]
|
||||
extern fn handle_download<T: WebViewController + 'static>(this: &Object, _: Sel, download: id, suggested_filename: id, handler: usize) {
|
||||
extern fn handle_download<T: WebViewDelegate>(this: &Object, _: Sel, download: id, suggested_filename: id, handler: usize) {
|
||||
let webview = unsafe {
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_CONTROLLER_PTR);
|
||||
let ptr: usize = *this.get_ivar(WEBVIEW_DELEGATE_PTR);
|
||||
let webview = ptr as *const T;
|
||||
&*webview
|
||||
};
|
||||
|
@ -179,27 +141,34 @@ extern fn handle_download<T: WebViewController + 'static>(this: &Object, _: Sel,
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/// 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
|
||||
/// varieties of delegates needed there).
|
||||
pub fn register_controller_class<
|
||||
T: ViewController + WebViewController + 'static,
|
||||
>() -> *const Class {
|
||||
pub fn register_webview_class() -> *const Class {
|
||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
INIT.call_once(|| unsafe {
|
||||
let superclass = Class::get("NSViewController").unwrap();
|
||||
let mut decl = ClassDecl::new("RSTWebViewController", superclass).unwrap();
|
||||
let superclass = class!(WKWebView);
|
||||
let decl = ClassDecl::new("RSTWebView", superclass).unwrap();
|
||||
VIEW_CLASS = decl.register();
|
||||
});
|
||||
|
||||
decl.add_ivar::<id>(WEBVIEW_CONFIG_VAR);
|
||||
decl.add_ivar::<id>(WEBVIEW_VAR);
|
||||
decl.add_ivar::<usize>(WEBVIEW_CONTROLLER_PTR);
|
||||
unsafe { VIEW_CLASS }
|
||||
}
|
||||
|
||||
// NSViewController
|
||||
decl.add_method(sel!(loadView), load_view::<T> as extern fn(&mut Object, _));
|
||||
decl.add_method(sel!(viewDidLoad), view_did_load::<T> as extern fn(&Object, _));
|
||||
/// 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
|
||||
/// varieties of delegates needed there).
|
||||
pub fn register_webview_delegate_class<T: WebViewDelegate>() -> *const Class {
|
||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||
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);
|
||||
|
||||
// WKNavigationDelegate
|
||||
decl.add_method(sel!(webView:decidePolicyForNavigationAction:decisionHandler:), decide_policy_for_action::<T> as extern fn(&Object, _, _, id, usize));
|
||||
|
@ -214,7 +183,7 @@ pub fn register_controller_class<
|
|||
|
||||
// WKDownloadDelegate is a private class on macOS that handles downloading (saving) files.
|
||||
// It's absurd that this is still private in 2020. This probably couldn't get into the app
|
||||
// store, so... screw it, fine for now.
|
||||
// store, so... screw it, feature-gate it.
|
||||
#[cfg(feature = "webview-downloading")]
|
||||
decl.add_method(sel!(_download:decideDestinationWithSuggestedFilename:completionHandler:), handle_download::<T> as extern fn(&Object, _, id, id, usize));
|
||||
|
|
@ -5,7 +5,7 @@ use objc_id::Id;
|
|||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, YES, NSString};
|
||||
use crate::foundation::{id, YES, NO, NSString};
|
||||
use crate::webview::enums::InjectAt;
|
||||
|
||||
/// A wrapper for `WKWebViewConfiguration`. Holds (retains) pointers for the Objective-C runtime
|
||||
|
@ -63,4 +63,12 @@ impl WebViewConfig {
|
|||
let _: () = msg_send![preferences, setValue:yes forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn attach_handlers(&self, target: id) {
|
||||
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> id {
|
||||
&mut *self.objc
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use crate::foundation::NSInteger;
|
||||
|
||||
/// Describes a navigation type from within the `WebView`.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum NavigationType {
|
||||
/// A user activated a link.
|
||||
LinkActivated,
|
||||
|
@ -23,21 +24,21 @@ pub enum NavigationType {
|
|||
Other
|
||||
}
|
||||
|
||||
impl From<NSInteger> for NavigationType {
|
||||
fn from(i: NSInteger) -> Self {
|
||||
match i {
|
||||
-1 => NavigationType::Other,
|
||||
0 => NavigationType::LinkActivated,
|
||||
1 => NavigationType::FormSubmitted,
|
||||
2 => NavigationType::BackForward,
|
||||
3 => NavigationType::Reload,
|
||||
4 => NavigationType::FormResubmitted,
|
||||
e => { panic!("Unsupported navigation type: {}", e); }
|
||||
impl From<NavigationType> for NSInteger {
|
||||
fn from(nav_type: NavigationType) -> Self {
|
||||
match nav_type {
|
||||
NavigationType::Other => -1,
|
||||
NavigationType::LinkActivated => 0,
|
||||
NavigationType::FormSubmitted => 1,
|
||||
NavigationType::BackForward => 2,
|
||||
NavigationType::Reload => 3,
|
||||
NavigationType::FormResubmitted => 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the policy for a given navigation.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum NavigationPolicy {
|
||||
/// Should be canceled.
|
||||
Cancel,
|
||||
|
@ -47,8 +48,8 @@ pub enum NavigationPolicy {
|
|||
}
|
||||
|
||||
impl From<NavigationPolicy> for NSInteger {
|
||||
fn into(self) -> Self {
|
||||
match self {
|
||||
fn from(policy: NavigationPolicy) -> Self {
|
||||
match policy {
|
||||
NavigationPolicy::Cancel => 0,
|
||||
NavigationPolicy::Allow => 1
|
||||
}
|
||||
|
@ -56,6 +57,7 @@ impl From<NavigationPolicy> for NSInteger {
|
|||
}
|
||||
|
||||
/// Describes a response policy for a given navigation.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum NavigationResponsePolicy {
|
||||
/// Should be canceled.
|
||||
Cancel,
|
||||
|
@ -70,8 +72,8 @@ pub enum NavigationResponsePolicy {
|
|||
}
|
||||
|
||||
impl From<NavigationResponsePolicy> for NSInteger {
|
||||
fn into(self) -> Self {
|
||||
match self {
|
||||
fn from(policy: NavigationResponsePolicy) -> Self {
|
||||
match policy {
|
||||
NavigationResponsePolicy::Cancel => 0,
|
||||
NavigationResponsePolicy::Allow => 1,
|
||||
|
||||
|
@ -82,6 +84,7 @@ impl From<NavigationResponsePolicy> for NSInteger {
|
|||
}
|
||||
|
||||
/// Dictates where a given user script should be injected.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum InjectAt {
|
||||
/// Inject at the start of the document.
|
||||
Start = 0,
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
//! A `WebViewHandle` represents an underlying `WKWebView`. You're passed a reference to one during your
|
||||
//! `WebViewController::did_load()` method. This method is safe to store and use, however as it's
|
||||
//! UI-specific it's not thread safe.
|
||||
//!
|
||||
//! You can use this struct to configure how a view should look and layout. It implements
|
||||
//! AutoLayout - for more information, see the AutoLayout tutorial.
|
||||
|
||||
use objc_id::ShareId;
|
||||
use objc::runtime::Object;
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, YES, NSArray, NSString};
|
||||
use crate::color::Color;
|
||||
use crate::constants::BACKGROUND_COLOR;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::pasteboard::PasteboardType;
|
||||
|
||||
/// A clone-able handler to a `ViewController` reference in the Objective C runtime. We use this
|
||||
/// instead of a stock `WKWebView` for easier recordkeeping, since it'll need to hold the `WKWebView` on that
|
||||
/// side anyway.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct WebViewHandle {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: Option<ShareId<Object>>,
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
pub top: LayoutAnchorY,
|
||||
|
||||
/// A pointer to the Objective-C runtime leading layout constraint.
|
||||
pub leading: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime trailing layout constraint.
|
||||
pub trailing: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime bottom layout constraint.
|
||||
pub bottom: LayoutAnchorY,
|
||||
|
||||
/// A pointer to the Objective-C runtime width layout constraint.
|
||||
pub width: LayoutAnchorDimension,
|
||||
|
||||
/// A pointer to the Objective-C runtime height layout constraint.
|
||||
pub height: LayoutAnchorDimension,
|
||||
|
||||
/// A pointer to the Objective-C runtime center X layout constraint.
|
||||
pub center_x: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime center Y layout constraint.
|
||||
pub center_y: LayoutAnchorY
|
||||
}
|
||||
|
||||
impl WebViewHandle {
|
||||
pub(crate) fn new(object: ShareId<Object>) -> Self {
|
||||
let view: id = unsafe {
|
||||
msg_send![&*object, view]
|
||||
};
|
||||
|
||||
WebViewHandle {
|
||||
objc: Some(object),
|
||||
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
|
||||
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
|
||||
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }),
|
||||
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }),
|
||||
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }),
|
||||
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }),
|
||||
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }),
|
||||
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,133 +4,222 @@
|
|||
//! - `WKWebView`
|
||||
//! - `WKUIDelegate`
|
||||
//! - `WKScriptMessageHandler`
|
||||
//! - `NSViewController`
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use objc_id::ShareId;
|
||||
use objc::runtime::Object;
|
||||
use objc::runtime::{Class, Object};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, NSString};
|
||||
use crate::constants::WEBVIEW_CONTROLLER_PTR;
|
||||
use crate::webview::controller::register_controller_class;
|
||||
use crate::foundation::{id, nil, YES, NO, CGRect, NSString};
|
||||
use crate::geometry::Rect;
|
||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
|
||||
pub mod actions;
|
||||
pub mod enums;
|
||||
|
||||
pub(crate) mod controller;
|
||||
pub(crate) mod class;
|
||||
use class::{register_webview_class, register_webview_class_with_delegate};
|
||||
//pub(crate) mod process_pool;
|
||||
|
||||
pub mod traits;
|
||||
pub use traits::WebViewController;
|
||||
pub use traits::WebViewDelegate;
|
||||
|
||||
pub mod config;
|
||||
pub use config::WebViewConfig;
|
||||
|
||||
/// A `View` wraps two different controllers - one on the Objective-C/Cocoa side, which forwards
|
||||
/// calls into your supplied `ViewController` trait object. This involves heap allocation, but all
|
||||
/// of Cocoa is essentially Heap'd, so... well, enjoy.
|
||||
#[derive(Clone)]
|
||||
pub struct WebView<T> {
|
||||
internal_callback_ptr: *const RefCell<T>,
|
||||
pub objc_controller: WebViewHandle,
|
||||
pub controller: Rc<RefCell<T>>
|
||||
pub(crate) static WEBVIEW_DELEGATE_PTR: &str = "rstWebViewDelegatePtr";
|
||||
|
||||
fn allocate_webview(
|
||||
config: WebViewConfig,
|
||||
delegate: Option<&Object>
|
||||
) -> id {
|
||||
unsafe {
|
||||
let configuration = config.into_inner();
|
||||
|
||||
if let Some(delegate) = objc_delegate {
|
||||
// Technically private!
|
||||
#[cfg(feature = "webview-downloading")]
|
||||
let process_pool: id = msg_send![configuration, processPool];
|
||||
#[cfg(feature = "webview-downloading")]
|
||||
let _: () = msg_send![process_pool, _setDownloadDelegate:*delegate];
|
||||
|
||||
// add handlers
|
||||
for
|
||||
}
|
||||
|
||||
let zero: CGRect = Rect::zero().into();
|
||||
let webview_alloc: id = msg_send![register_webview_class(), alloc];
|
||||
let webview: id = msg_send![webview_alloc, initWithFrame:zero configuration:configuration];
|
||||
|
||||
let _: () = msg_send![webview, setWantsLayer:YES];
|
||||
let _: () = msg_send![webview, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
let _: () = msg_send![webview, setNavigationDelegate:webview];
|
||||
let _: () = msg_send![webview, setUIDelegate:webview];
|
||||
|
||||
webview
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WebView<T> where T: WebViewController + 'static {
|
||||
/// Allocates and configures a `ViewController` in the Objective-C/Cocoa runtime that maps over
|
||||
/// to your supplied view controller.
|
||||
pub fn new(controller: T) -> Self {
|
||||
let config = controller.config();
|
||||
let controller = Rc::new(RefCell::new(controller));
|
||||
pub struct WebView<T = ()> {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: ShareId<Object>,
|
||||
|
||||
/// We need to store the underlying delegate separately from the `WKWebView` - this is a where
|
||||
/// we do so.
|
||||
pub objc_delegate: Option<ShareId<Object>>,
|
||||
|
||||
/// An internal callback pointer that we use in delegate loopbacks. Default implementations
|
||||
/// don't require this.
|
||||
pub(crate) internal_callback_ptr: Option<*const RefCell<T>>,
|
||||
|
||||
/// A pointer to the delegate for this view.
|
||||
pub delegate: Option<Rc<RefCell<T>>>,
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
pub top: LayoutAnchorY,
|
||||
|
||||
/// A pointer to the Objective-C runtime leading layout constraint.
|
||||
pub leading: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime trailing layout constraint.
|
||||
pub trailing: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime bottom layout constraint.
|
||||
pub bottom: LayoutAnchorY,
|
||||
|
||||
/// A pointer to the Objective-C runtime width layout constraint.
|
||||
pub width: LayoutAnchorDimension,
|
||||
|
||||
/// A pointer to the Objective-C runtime height layout constraint.
|
||||
pub height: LayoutAnchorDimension,
|
||||
|
||||
/// A pointer to the Objective-C runtime center X layout constraint.
|
||||
pub center_x: LayoutAnchorX,
|
||||
|
||||
/// A pointer to the Objective-C runtime center Y layout constraint.
|
||||
pub center_y: LayoutAnchorY
|
||||
}
|
||||
|
||||
impl Default for WebView {
|
||||
fn default() -> Self {
|
||||
WebView::new(WebViewConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl WebView {
|
||||
pub fn new(config: WebViewConfig) -> Self {
|
||||
let view = allocate_webview(register_webview_class, config, None);
|
||||
|
||||
WebView {
|
||||
internal_callback_ptr: None,
|
||||
delegate: None,
|
||||
objc_delegate: None,
|
||||
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
|
||||
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
|
||||
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }),
|
||||
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }),
|
||||
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }),
|
||||
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }),
|
||||
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }),
|
||||
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WebView<T> where T: WebViewDelegate + 'static {
|
||||
/// 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.
|
||||
pub fn with(config: WebViewConfig, delegate: T) -> WebView<T> {
|
||||
let delegate = Rc::new(RefCell::new(delegate));
|
||||
|
||||
let internal_callback_ptr = {
|
||||
let cloned = Rc::clone(&controller);
|
||||
let cloned = Rc::clone(&delegate);
|
||||
Rc::into_raw(cloned)
|
||||
};
|
||||
|
||||
let handle = WebViewHandle::new(unsafe {
|
||||
let view_controller: id = msg_send![register_controller_class::<T>(), new];
|
||||
(&mut *view_controller).set_ivar(WEBVIEW_CONTROLLER_PTR, internal_callback_ptr as usize);
|
||||
|
||||
// WKWebView isn't really great to subclass, so we don't bother here unlike other
|
||||
// widgets in this framework. Just set and forget.
|
||||
let frame: CGRect = Rect::zero().into();
|
||||
let alloc: id = msg_send![class!(WKWebView), alloc];
|
||||
let view: id = msg_send![alloc, initWithFrame:frame configuration:&*config.0];
|
||||
let _: () = msg_send![&*view_controller, setView:view];
|
||||
|
||||
ShareId::from_ptr(view_controller)
|
||||
});
|
||||
let objc_delegate = unsafe {
|
||||
let objc_delegate: id = msg_send![register_webview_delegate_class::<T>, new];
|
||||
(&mut *objc_delegate).set_ivar(WEBVIEW_DELEGATE_PTR, internal_callback_ptr as usize);
|
||||
ShareId::from_ptr(objc_delegate)
|
||||
};
|
||||
|
||||
let view = allocate_webview(config, Some(&objc_delegate));
|
||||
|
||||
let mut view = WebView {
|
||||
internal_callback_ptr: Some(internal_callback_ptr),
|
||||
delegate: None,
|
||||
objc_delegate: Some(objc_delegate),
|
||||
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
|
||||
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
|
||||
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }),
|
||||
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }),
|
||||
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }),
|
||||
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }),
|
||||
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }),
|
||||
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }),
|
||||
objc: unsafe { ShareId::from_ptr(view) },
|
||||
};
|
||||
|
||||
{
|
||||
let mut vc = controller.borrow_mut();
|
||||
(*vc).did_load(handle.clone());
|
||||
let mut delegate = delegate.borrow_mut();
|
||||
(*delegate).did_load(view.clone_as_handle());
|
||||
}
|
||||
|
||||
WebView {
|
||||
internal_callback_ptr: internal_callback_ptr,
|
||||
objc_controller: handle,
|
||||
controller: controller
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_background_color(&self, color: Color) {
|
||||
self.objc_controller.set_background_color(color);
|
||||
}
|
||||
|
||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||
self.objc_controller.register_for_dragged_types(types);
|
||||
}
|
||||
|
||||
pub fn top(&self) -> &LayoutAnchorY {
|
||||
&self.objc_controller.top
|
||||
}
|
||||
|
||||
pub fn leading(&self) -> &LayoutAnchorX {
|
||||
&self.objc_controller.leading
|
||||
}
|
||||
|
||||
pub fn trailing(&self) -> &LayoutAnchorX {
|
||||
&self.objc_controller.trailing
|
||||
}
|
||||
|
||||
pub fn bottom(&self) -> &LayoutAnchorY {
|
||||
&self.objc_controller.bottom
|
||||
}
|
||||
|
||||
pub fn width(&self) -> &LayoutAnchorDimension {
|
||||
&self.objc_controller.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> &LayoutAnchorDimension {
|
||||
&self.objc_controller.height
|
||||
view.delegate = Some(delegate);
|
||||
view
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout for View<T> {
|
||||
impl<T> WebView<T> {
|
||||
/// An internal method that returns a clone of this object, sans references to the delegate or
|
||||
/// callback pointer. We use this in calling `did_load()` - implementing delegates get a way to
|
||||
/// reference, customize and use the view but without the trickery of holding pieces of the
|
||||
/// delegate - the `View` is the only true holder of those.
|
||||
pub(crate) fn clone_as_handle(&self) -> WebView {
|
||||
WebView {
|
||||
internal_callback_ptr: None,
|
||||
delegate: None,
|
||||
top: self.top.clone(),
|
||||
leading: self.leading.clone(),
|
||||
trailing: self.trailing.clone(),
|
||||
bottom: self.bottom.clone(),
|
||||
width: self.width.clone(),
|
||||
height: self.height.clone(),
|
||||
center_x: self.center_x.clone(),
|
||||
center_y: self.center_y.clone(),
|
||||
objc: self.objc.clone(),
|
||||
objc_delegate: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Layout for WebView<T> {
|
||||
/// Returns the Objective-C object used for handling the view heirarchy.
|
||||
fn get_backing_node(&self) -> Option<ShareId<Object>> {
|
||||
self.objc_controller.objc.clone()
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
self.objc.clone()
|
||||
}
|
||||
|
||||
fn add_subview<V: Layout>(&self, subview: &V) {
|
||||
self.objc_controller.add_subview(subview);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Debug for View<T> {
|
||||
impl<T> std::fmt::Debug for WebView<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "View ({:p})", self)
|
||||
write!(f, "WebView ({:p})", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for View<T> {
|
||||
impl<T> Drop for WebView<T> {
|
||||
/// A bit of extra cleanup for delegate callback pointers.
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = Rc::from_raw(self.internal_callback_ptr);
|
||||
if let Some(ptr) = self.internal_callback_ptr {
|
||||
let _ = Rc::from_raw(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,27 +2,16 @@
|
|||
//! `WKWebView`. It allows you to do things such as handle opening a file (for uploads or
|
||||
//! in-browser-processing), handling navigation actions or JS message callbacks, and so on.
|
||||
|
||||
use crate::webview::config::WebViewConfig;
|
||||
use crate::webview::enums::{
|
||||
NavigationAction, NavigationPolicy,
|
||||
NavigationResponse, NavigationResponsePolicy,
|
||||
OpenPanelParameters
|
||||
};
|
||||
use crate::webview::handle::WebViewHandle;
|
||||
use crate::webview::WebView;
|
||||
use crate::webview::actions::{NavigationAction, NavigationResponse, OpenPanelParameters};
|
||||
use crate::webview::enums::{NavigationPolicy, NavigationResponsePolicy};
|
||||
|
||||
/// You can implement this on structs to handle callbacks from the underlying `WKWebView`.
|
||||
pub trait WebViewController {
|
||||
/// Due to a quirk in how the underlying `WKWebView` works, the configuration object must be
|
||||
/// set up before initializing anything. To enable this, you can implement this method and
|
||||
/// return whatever `WebViewConfig` object you want.
|
||||
///
|
||||
/// By default, this returns `WebViewConfig::default()`.
|
||||
fn configure(&mut self) -> WebViewConfig { WebViewConfig::default() }
|
||||
|
||||
pub trait WebViewDelegate {
|
||||
/// 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
|
||||
/// main thread!
|
||||
fn did_load(&mut self, _view: WebViewHandle) {}
|
||||
fn did_load(&mut self, _webview: WebView) {}
|
||||
|
||||
/// Called when this is about to be added to the view heirarchy.
|
||||
fn will_appear(&self) {}
|
||||
|
|
Loading…
Reference in a new issue