diff --git a/appkit/build.rs b/appkit/build.rs index 5dad65c..0f04424 100644 --- a/appkit/build.rs +++ b/appkit/build.rs @@ -8,6 +8,7 @@ fn main() { println!("cargo:rustc-link-lib=framework=Foundation"); println!("cargo:rustc-link-lib=framework=Cocoa"); println!("cargo:rustc-link-lib=framework=CoreGraphics"); + println!("cargo:rustc-link-lib=framework=QuartzCore"); println!("cargo:rustc-link-lib=framework=Security"); diff --git a/appkit/lib.rs b/appkit/lib.rs index 24fb23e..c4d2806 100644 --- a/appkit/lib.rs +++ b/appkit/lib.rs @@ -44,7 +44,7 @@ pub mod printing; pub mod toolbar; pub mod user_activity; pub mod utils; -//pub mod view; +pub mod view; #[cfg(feature = "webview")] pub mod webview; diff --git a/appkit/view/class.rs b/appkit/view/class.rs index 0e7d2ec..3e2dc69 100644 --- a/appkit/view/class.rs +++ b/appkit/view/class.rs @@ -134,8 +134,8 @@ pub(crate) fn register_view_class() -> *const Class { decl.add_ivar::(BACKGROUND_COLOR); decl.add_method(sel!(isFlipped), enforce_normalcy as extern fn(&Object, _) -> BOOL); - decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern fn(&Object, _) -> BOOL); - decl.add_method(sel!(updateLayer), update_layer as extern fn(&Object, _)); + //decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern fn(&Object, _) -> BOOL); + //decl.add_method(sel!(updateLayer), update_layer as extern fn(&Object, _)); VIEW_CLASS = decl.register(); }); diff --git a/appkit/view/controller/mod.rs b/appkit/view/controller/mod.rs index 85570c8..81b23d3 100644 --- a/appkit/view/controller/mod.rs +++ b/appkit/view/controller/mod.rs @@ -16,7 +16,7 @@ use class::register_view_controller_class; //#[derive(Debug)] pub struct ViewController { pub objc: ShareId, - pub view: Box + pub view: Box } impl ViewController { diff --git a/appkit/view/mod.rs b/appkit/view/mod.rs index abec299..ad6c1c9 100644 --- a/appkit/view/mod.rs +++ b/appkit/view/mod.rs @@ -79,6 +79,7 @@ impl View { 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 }; @@ -93,45 +94,9 @@ impl View { 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: ShareId::from_ptr(view), + objc: unsafe { ShareId::from_ptr(view) }, } } - - /// Call this to set the background color for the backing layer. - pub fn set_background_color(&self, color: Color) { - unsafe { - //let view: id = msg_send![*self.objc, view]; - //(*view).set_ivar(BACKGROUND_COLOR, color.into_platform_specific_color()); - //let _: () = msg_send![view, setNeedsDisplay:YES]; - } - } - - /// Register this view for drag and drop operations. - pub fn register_for_dragged_types(&self, types: &[PasteboardType]) { - unsafe { - let types: NSArray = types.into_iter().map(|t| { - // This clone probably doesn't need to be here, but it should also be cheap as - // this is just an enum... and this is not an oft called method. - let x: NSString = t.clone().into(); - x.into_inner() - }).collect::>().into(); - - let _: () = msg_send![&*self.objc, registerForDraggedTypes:types.into_inner()]; - } - } - - /// Given a subview, adds it to this view. - pub fn add_subview(&self, subview: &T) { - /*if let Some(subview_controller) = subview.get_backing_node() { - unsafe { - let _: () = msg_send![*this, addChildViewController:&*subview_controller]; - - let subview: id = msg_send![&*subview_controller, view]; - let view: id = msg_send![*this, view]; - let _: () = msg_send![view, addSubview:subview]; - } - }*/ - } } impl View where T: ViewDelegate + 'static { @@ -152,9 +117,9 @@ impl View where T: ViewDelegate + 'static { view }; - let view = View { + let mut view = View { internal_callback_ptr: Some(internal_callback_ptr), - delegate: Some(delegate), + 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] }), @@ -163,7 +128,7 @@ impl View where T: ViewDelegate + 'static { 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: ShareId::from_ptr(view), + objc: unsafe { ShareId::from_ptr(view) }, }; { @@ -183,16 +148,65 @@ impl View where T: ViewDelegate + 'static { }); } + view.delegate = Some(delegate); view } } -impl Layout for View { +impl View { + /// Call this to set the background color for the backing layer. + pub fn set_background_color(&self, color: Color) { + let bg = color.into_platform_specific_color(); + + unsafe { + let cg: id = msg_send![bg, CGColor]; + let layer: id = msg_send![&*self.objc, layer]; + let _: () = msg_send![layer, setBackgroundColor:cg]; + //let view: id = msg_send![*self.objc, view]; + //(*view).set_ivar(BACKGROUND_COLOR, color.into_platform_specific_color()); + //let _: () = msg_send![view, setNeedsDisplay:YES]; + } + } + + /// Register this view for drag and drop operations. + pub fn register_for_dragged_types(&self, types: &[PasteboardType]) { + unsafe { + let types: NSArray = types.into_iter().map(|t| { + // This clone probably doesn't need to be here, but it should also be cheap as + // this is just an enum... and this is not an oft called method. + let x: NSString = t.clone().into(); + x.into_inner() + }).collect::>().into(); + + let _: () = msg_send![&*self.objc, registerForDraggedTypes:types.into_inner()]; + } + } + + //pub fn add_subview(&self, subview: &L) { + /*if let Some(subview_controller) = subview.get_backing_node() { + unsafe { + let _: () = msg_send![*this, addChildViewController:&*subview_controller]; + + let subview: id = msg_send![&*subview_controller, view]; + let view: id = msg_send![*this, view]; + let _: () = msg_send![view, addSubview:subview]; + } + }*/ + //} +} + +impl Layout for View { fn get_backing_node(&self) -> ShareId { self.objc.clone() } - fn add_subview(&self, _: &V) {} + fn add_subview(&self, view: &V) { + let backing_node = view.get_backing_node(); + + unsafe { + let _: () = msg_send![&*self.objc, addSubview:backing_node]; + } + } } impl Drop for View { diff --git a/appkit/window/mod.rs b/appkit/window/mod.rs index 50885a7..deed6b0 100644 --- a/appkit/window/mod.rs +++ b/appkit/window/mod.rs @@ -155,6 +155,15 @@ impl Window { //} } + /// Given a view, sets it as the content view for this window. + pub fn set_content_view(&self, view: &L) { + let backing_node = view.get_backing_node(); + + unsafe { + let _: () = msg_send![&*self.objc, setContentView:&*backing_node]; + } + } + /// Shows the window. pub fn show(&self) { unsafe { diff --git a/examples/autolayout/Cargo.toml b/examples/autolayout/Cargo.toml new file mode 100644 index 0000000..cbbf7cf --- /dev/null +++ b/examples/autolayout/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "autolayout" +version = "0.1.0" +authors = ["Ryan McGrath "] +edition = "2018" + +[dependencies] +appkit = { path = "../../appkit" } diff --git a/examples/autolayout/src/main.rs b/examples/autolayout/src/main.rs new file mode 100644 index 0000000..07d929e --- /dev/null +++ b/examples/autolayout/src/main.rs @@ -0,0 +1,69 @@ +//! This example showcases setting up a basic application and window, and setting up some views to +//! work with autolayout. + +use appkit::app::{App, AppDelegate}; +use appkit::color::rgb; +use appkit::layout::{Layout, LayoutConstraint}; +use appkit::view::View; +use appkit::window::{Window, WindowConfig, WindowDelegate}; + +struct BasicApp { + window: Window +} + +impl AppDelegate for BasicApp { + fn did_finish_launching(&self) { + self.window.show(); + } +} + +#[derive(Default)] +struct AppWindow { + content: View, + blue: View, + red: View, + green: View, + window: Window +} + +impl WindowDelegate for AppWindow { + fn did_load(&mut self, window: Window) { + window.set_title("AutoLayout Example"); + window.set_minimum_content_size(300., 300.); + self.window = window; + + self.blue.set_background_color(rgb(105, 162, 176)); + self.content.add_subview(&self.blue); + + self.red.set_background_color(rgb(224, 82, 99)); + self.content.add_subview(&self.red); + + self.green.set_background_color(rgb(161, 192, 132)); + self.content.add_subview(&self.green); + + self.window.set_content_view(&self.content); + + LayoutConstraint::activate(&[ + self.blue.top.constraint_equal_to(&self.content.top).offset(16.), + self.blue.leading.constraint_equal_to(&self.content.leading).offset(16.), + self.blue.bottom.constraint_equal_to(&self.content.bottom).offset(-16.), + self.blue.width.constraint_equal_to_constant(100.), + + self.red.top.constraint_equal_to(&self.content.top).offset(16.), + self.red.leading.constraint_equal_to(&self.blue.trailing).offset(16.), + self.red.bottom.constraint_equal_to(&self.content.bottom).offset(-16.), + + self.green.top.constraint_equal_to(&self.content.top).offset(16.), + self.green.leading.constraint_equal_to(&self.red.trailing).offset(16.), + self.green.trailing.constraint_equal_to(&self.content.trailing).offset(-16.), + self.green.bottom.constraint_equal_to(&self.content.bottom).offset(-16.), + self.green.width.constraint_equal_to_constant(100.), + ]); + } +} + +fn main() { + App::new("com.test.window", BasicApp { + window: Window::with(WindowConfig::default(), AppWindow::default()) + }).run(); +}