Working on getting WKWebView working again - close

This commit is contained in:
Ryan McGrath 2020-03-20 18:37:00 -07:00
parent c1da7b1b37
commit e4f96b4ab5
No known key found for this signature in database
GPG key ID: 811674B62B666830
10 changed files with 259 additions and 264 deletions

View file

@ -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};
}

View file

@ -7,6 +7,7 @@ use objc_id::Id;
use crate::foundation::{id, NSString};
#[derive(Debug)]
pub struct URLRequest {
pub inner: Id<Object>
}

View file

@ -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 {

View file

@ -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,

View file

@ -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));

View file

@ -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
}
}

View file

@ -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,

View file

@ -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] }),
}
}
}

View file

@ -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
}
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));
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
}
}
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);
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)
};
// 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];
let view = allocate_webview(config, Some(&objc_delegate));
ShareId::from_ptr(view_controller)
});
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());
}
view.delegate = Some(delegate);
view
}
}
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: internal_callback_ptr,
objc_controller: handle,
controller: controller
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
}
}
}
pub fn set_background_color(&self, color: Color) {
self.objc_controller.set_background_color(color);
}
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
self.objc_controller.register_for_dragged_types(types);
}
pub fn top(&self) -> &LayoutAnchorY {
&self.objc_controller.top
}
pub fn leading(&self) -> &LayoutAnchorX {
&self.objc_controller.leading
}
pub fn trailing(&self) -> &LayoutAnchorX {
&self.objc_controller.trailing
}
pub fn bottom(&self) -> &LayoutAnchorY {
&self.objc_controller.bottom
}
pub fn width(&self) -> &LayoutAnchorDimension {
&self.objc_controller.width
}
pub fn height(&self) -> &LayoutAnchorDimension {
&self.objc_controller.height
}
}
impl<T> Layout for View<T> {
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);
}
}
}
}

View file

@ -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) {}