Add in an autolayout example

This commit is contained in:
Ryan McGrath 2020-03-20 14:02:46 -07:00
parent e50bb25e9f
commit fc53848ba2
No known key found for this signature in database
GPG key ID: 811674B62B666830
8 changed files with 147 additions and 46 deletions

View file

@ -8,6 +8,7 @@ fn main() {
println!("cargo:rustc-link-lib=framework=Foundation"); println!("cargo:rustc-link-lib=framework=Foundation");
println!("cargo:rustc-link-lib=framework=Cocoa"); println!("cargo:rustc-link-lib=framework=Cocoa");
println!("cargo:rustc-link-lib=framework=CoreGraphics"); println!("cargo:rustc-link-lib=framework=CoreGraphics");
println!("cargo:rustc-link-lib=framework=QuartzCore");
println!("cargo:rustc-link-lib=framework=Security"); println!("cargo:rustc-link-lib=framework=Security");

View file

@ -44,7 +44,7 @@ pub mod printing;
pub mod toolbar; pub mod toolbar;
pub mod user_activity; pub mod user_activity;
pub mod utils; pub mod utils;
//pub mod view; pub mod view;
#[cfg(feature = "webview")] #[cfg(feature = "webview")]
pub mod webview; pub mod webview;

View file

@ -134,8 +134,8 @@ pub(crate) fn register_view_class() -> *const Class {
decl.add_ivar::<id>(BACKGROUND_COLOR); decl.add_ivar::<id>(BACKGROUND_COLOR);
decl.add_method(sel!(isFlipped), enforce_normalcy as extern fn(&Object, _) -> BOOL); decl.add_method(sel!(isFlipped), enforce_normalcy as extern fn(&Object, _) -> BOOL);
decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern fn(&Object, _) -> BOOL); //decl.add_method(sel!(wantsUpdateLayer), enforce_normalcy as extern fn(&Object, _) -> BOOL);
decl.add_method(sel!(updateLayer), update_layer as extern fn(&Object, _)); //decl.add_method(sel!(updateLayer), update_layer as extern fn(&Object, _));
VIEW_CLASS = decl.register(); VIEW_CLASS = decl.register();
}); });

View file

@ -16,7 +16,7 @@ use class::register_view_controller_class;
//#[derive(Debug)] //#[derive(Debug)]
pub struct ViewController { pub struct ViewController {
pub objc: ShareId<Object>, pub objc: ShareId<Object>,
pub view: Box<ViewDelegate> pub view: Box<dyn ViewDelegate>
} }
impl ViewController { impl ViewController {

View file

@ -79,6 +79,7 @@ impl View {
let view: id = unsafe { let view: id = unsafe {
let view: id = msg_send![register_view_class(), new]; let view: id = msg_send![register_view_class(), new];
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO]; let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
let _: () = msg_send![view, setWantsLayer:YES];
view view
}; };
@ -93,45 +94,9 @@ impl View {
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), 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::<Vec<id>>().into();
let _: () = msg_send![&*self.objc, registerForDraggedTypes:types.into_inner()];
}
}
/// Given a subview, adds it to this view.
pub fn add_subview<T: Layout>(&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<T> View<T> where T: ViewDelegate + 'static { impl<T> View<T> where T: ViewDelegate + 'static {
@ -152,9 +117,9 @@ impl<T> View<T> where T: ViewDelegate + 'static {
view view
}; };
let view = View { let mut view = View {
internal_callback_ptr: Some(internal_callback_ptr), internal_callback_ptr: Some(internal_callback_ptr),
delegate: Some(delegate), delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }), leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }), trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }),
@ -163,7 +128,7 @@ impl<T> View<T> where T: ViewDelegate + 'static {
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }), height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }),
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }), center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }),
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }), 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<T> View<T> where T: ViewDelegate + 'static {
}); });
} }
view.delegate = Some(delegate);
view view
} }
} }
impl Layout for View { impl<T> View<T> {
/// 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::<Vec<id>>().into();
let _: () = msg_send![&*self.objc, registerForDraggedTypes:types.into_inner()];
}
}
//pub fn add_subview<L: Layout>(&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<T> Layout for View<T> {
fn get_backing_node(&self) -> ShareId<Object> { fn get_backing_node(&self) -> ShareId<Object> {
self.objc.clone() self.objc.clone()
} }
fn add_subview<V: Layout>(&self, _: &V) {} fn add_subview<V: Layout>(&self, view: &V) {
let backing_node = view.get_backing_node();
unsafe {
let _: () = msg_send![&*self.objc, addSubview:backing_node];
}
}
} }
impl<T> Drop for View<T> { impl<T> Drop for View<T> {

View file

@ -155,6 +155,15 @@ impl<T> Window<T> {
//} //}
} }
/// Given a view, sets it as the content view for this window.
pub fn set_content_view<L: Layout + 'static>(&self, view: &L) {
let backing_node = view.get_backing_node();
unsafe {
let _: () = msg_send![&*self.objc, setContentView:&*backing_node];
}
}
/// Shows the window. /// Shows the window.
pub fn show(&self) { pub fn show(&self) {
unsafe { unsafe {

View file

@ -0,0 +1,8 @@
[package]
name = "autolayout"
version = "0.1.0"
authors = ["Ryan McGrath <ryan@rymc.io>"]
edition = "2018"
[dependencies]
appkit = { path = "../../appkit" }

View file

@ -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<AppWindow>
}
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();
}