Added hello_world example, expanded appkit

This commit adds a Hello World program, which creates and displays a
minimalist Cocoa app with a Menu Bar, a Quit menu, and a Window.

It also wraps the Cocoa methods used in hello_world in static functions
in appkit, inside separate modules for different classes. Later on,
Cocoa methods could be implemented as traits on `id` if that turns out
to be easier.
This commit is contained in:
Alex Chandel
2014-09-17 15:18:57 -04:00
parent cfbf6d9d5a
commit be0f6bd4d7
5 changed files with 349 additions and 15 deletions
+58
View File
@@ -0,0 +1,58 @@
extern crate cocoa;
use cocoa::base::{NSUInteger, nil, ObjCSelector};
use cocoa::appkit::{NSApp, NSRect, NSPoint, NSSize,
NSProcessInfo,
NSApplication, NSApplicationActivationPolicyRegular,
NSWindow, NSTitledWindowMask, NSBackingStoreBuffered,
NSString,
NSMenu, NSMenuItem};
fn main() {
unsafe {
let app = NSApp();
NSApplication::setActivationPolicy_(app, NSApplicationActivationPolicyRegular);
// create Menu Bar
let menubar = NSMenu::new();
let app_menu_item = NSMenuItem::new();
NSMenu::addItem_(menubar, app_menu_item);
NSApplication::setMainMenu_(app, menubar);
// create Application menu
let app_menu = NSMenu::new();
let app_name = NSProcessInfo::processName(NSProcessInfo::processInfo());
let quit_prefix = NSString::from_str("Quit \0");
let quit_title = NSString::stringByAppendingString_(
quit_prefix,
app_name
);
let quit_action = "terminate:".as_selector();
let quit_key = NSString::from_str("q\0");
let quit_item = NSMenuItem::initWithTitle_action_keyEquivalent_(
NSMenuItem::alloc(),
quit_title,
quit_action,
quit_key
);
NSMenu::addItem_(app_menu, quit_item);
NSMenuItem::setSubmenu_(app_menu_item, app_menu);
// create Window
let window = NSWindow::initWithContentRect_styleMask_backing_defer_(
NSWindow::alloc(),
NSRect::new(NSPoint::new(0., 0.), NSSize::new(200., 200.)),
NSTitledWindowMask as NSUInteger,
NSBackingStoreBuffered,
false
);
// segfaults in invoke_msg_NSPoint_NSPoint():
// NSWindow::cascadeTopLeftFromPoint_(window, NSPoint::new(20., 20.));
let title = NSString::from_str("Hello World!\0");
NSWindow::setTitle_(window, title);
NSWindow::makeKeyAndOrderFront_(window, nil);
NSApplication::activateIgnoringOtherApps_(app, true);
NSApplication::run(app);
}
}
+162 -6
View File
@@ -11,13 +11,14 @@ use base::{ObjCMethodCall, id};
pub type CGFloat = f32;
#[allow(dead_code)]
#[repr(C)]
pub struct NSPoint {
x: f64,
y: f64,
pub x: f64,
pub y: f64,
}
impl NSPoint {
#[inline]
pub fn new(x: f64, y: f64) -> NSPoint {
NSPoint {
x: x,
@@ -26,13 +27,14 @@ impl NSPoint {
}
}
#[allow(dead_code)]
#[repr(C)]
pub struct NSSize {
width: f64,
height: f64,
pub width: f64,
pub height: f64,
}
impl NSSize {
#[inline]
pub fn new(width: f64, height: f64) -> NSSize {
NSSize {
width: width,
@@ -41,11 +43,22 @@ impl NSSize {
}
}
#[repr(C)]
pub struct NSRect {
pub origin: NSPoint,
pub size: NSSize,
}
impl NSRect {
#[inline]
pub fn new(origin: NSPoint, size: NSSize) -> NSRect {
NSRect {
origin: origin,
size: size
}
}
}
#[link(name = "AppKit", kind = "framework")]
extern {}
@@ -53,3 +66,146 @@ pub unsafe fn NSApp() -> id {
"NSApplication".send("sharedApplication", ())
}
#[repr(i64)]
pub enum NSApplicationActivationPolicy {
NSApplicationActivationPolicyRegular = 0,
NSApplicationActivationPolicyERROR = -1
}
#[repr(u64)]
pub enum NSWindowMask {
NSBorderlessWindowMask = 0,
NSTitledWindowMask = 1 << 0,
NSClosableWindowMask = 1 << 1,
NSMiniaturizableWindowMask = 1 << 2,
NSResizableWindowMask = 1 << 3,
NSTexturedBackgroundWindowMask = 1 << 8,
NSUnifiedTitleAndToolbarWindowMask = 1 << 12,
NSFullScreenWindowMask = 1 << 14
}
#[repr(u64)]
pub enum NSBackingStoreType {
NSBackingStoreRetained = 0,
NSBackingStoreNonretained = 1,
NSBackingStoreBuffered = 2
}
pub mod NSProcessInfo {
use base::{id, ObjCMethodCall};
pub unsafe fn processInfo() -> id {
"NSProcessInfo".send("processInfo", ())
}
pub unsafe fn processName(this: id) -> id {
this.send("processName", ())
}
}
pub mod NSApplication {
use base::{id, NSInteger, ObjCMethodCall};
use appkit::NSApplicationActivationPolicy;
pub unsafe fn setActivationPolicy_(this: id, policy: NSApplicationActivationPolicy) -> bool {
this.send_bool("setActivationPolicy:", policy as NSInteger)
}
pub unsafe fn setMainMenu_(this: id, menu: id) {
this.send_void("setMainMenu:", menu)
}
pub unsafe fn activateIgnoringOtherApps_(this: id, ignore: bool) {
this.send_void("activateIgnoringOtherApps:", ignore);
}
pub unsafe fn run(this: id) {
this.send_void("run", ());
}
}
pub mod NSMenu {
use base::{id, ObjCMethodCall};
pub unsafe fn new() -> id {
"NSMenu".send("new", ())
}
pub unsafe fn addItem_(this: id, menu_item: id) {
this.send_void("addItem:", menu_item)
}
}
pub mod NSMenuItem {
use base::{id, ObjCMethodCall, SEL};
pub unsafe fn alloc() -> id {
"NSMenuItem".send("alloc", ())
}
pub unsafe fn new() -> id {
"NSMenuItem".send("new", ())
}
pub unsafe fn initWithTitle_action_keyEquivalent_(this: id, title: id, action: SEL, key: id) -> id {
this.send("initWithTitle:action:keyEquivalent:", (title, action, key))
}
pub unsafe fn setSubmenu_(this: id, submenu: id) {
this.send_void("setSubmenu:", submenu)
}
}
pub mod NSWindow {
use base::{id, NSUInteger, ObjCMethodCall};
use appkit::{NSRect, NSPoint, NSBackingStoreType};
pub unsafe fn alloc() -> id {
"NSWindow".send("alloc", ())
}
pub unsafe fn initWithContentRect_styleMask_backing_defer_(this: id,
rect: NSRect,
style: NSUInteger,
backing: NSBackingStoreType,
defer: bool) -> id {
this.send("initWithContentRect:styleMask:backing:defer:",
(rect, style, backing as NSUInteger, defer))
}
pub unsafe fn cascadeTopLeftFromPoint_(this: id, top_left: NSPoint) -> NSPoint {
this.send_point("cascadeTopLeftFromPoint:", top_left)
}
pub unsafe fn setTitle_(this: id, title: id) {
this.send_void("setTitle:", title);
}
pub unsafe fn makeKeyAndOrderFront_(this: id, sender: id) {
this.send_void("makeKeyAndOrderFront:", sender)
}
}
pub mod NSString {
use base::{id, ObjCMethodCall};
pub unsafe fn alloc() -> id {
"NSString".send("alloc", ())
}
pub unsafe fn initWithUTF8String_(this: id, c_string: *const u8) -> id {
this.send("initWithUTF8String:", c_string as id)
}
pub unsafe fn stringByAppendingString_(this: id, other: id) -> id {
this.send("stringByAppendingString:", other)
}
pub unsafe fn from_str(string: &str) -> id {
initWithUTF8String_(alloc(), string.as_ptr())
}
}
+106 -8
View File
@@ -7,9 +7,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use appkit::NSRect;
use appkit::{NSRect, NSPoint};
use libc::{c_double, c_long};
use libc::{c_double, c_long, c_ulong, c_char};
use libc;
pub type Class = libc::intptr_t;
@@ -19,6 +19,16 @@ pub type SEL = libc::intptr_t;
#[allow(non_camel_case_types)]
pub type id = libc::intptr_t;
#[cfg(target_word_size = "32")]
pub type NSInteger = libc::c_int;
#[cfg(target_word_size = "32")]
pub type NSUInteger = libc::c_uint;
#[cfg(target_word_size = "64")]
pub type NSInteger = libc::c_long;
#[cfg(target_word_size = "64")]
pub type NSUInteger = libc::c_ulong;
pub static nil: id = 0 as id;
extern {
@@ -57,50 +67,81 @@ pub fn selector(name: &str) -> SEL {
/// A trait that allows syntax like:
///
/// let string = "NSString".send("alloc").send("initWithCString:", "Hello world!");
/// let string = "NSString".send("alloc").send("initWithUTF8String:", "Hello world!");
pub trait ObjCMethodCall {
unsafe fn send<S:ObjCSelector,A:ObjCMethodArgs>(self, selector: S, args: A) -> id;
unsafe fn send_double<S:ObjCSelector,A:ObjCMethodDoubleArgs>(self, selector: S, args: A)
-> c_double;
unsafe fn send_long<S:ObjCSelector,A:ObjCMethodLongArgs>(self, selector: S, args: A) -> c_long;
unsafe fn send_void<S:ObjCSelector,A:ObjCMethodVoidArgs>(self, selector: S, args: A);
unsafe fn send_bool<S:ObjCSelector,A:ObjCMethodBoolArgs>(self, selector: S, args: A) -> bool;
unsafe fn send_point<S:ObjCSelector,A:ObjCMethodPointArgs>(self, selector: S, args: A) -> NSPoint;
}
impl ObjCMethodCall for id {
#[inline]
unsafe fn send<S:ObjCSelector,A:ObjCMethodArgs>(self, selector: S, args: A) -> id {
args.send_args(self, selector.as_selector())
}
#[inline]
unsafe fn send_double<S:ObjCSelector,A:ObjCMethodDoubleArgs>(self, selector: S, args: A)
-> c_double {
args.send_double_args(self, selector.as_selector())
}
#[inline]
unsafe fn send_long<S:ObjCSelector,A:ObjCMethodLongArgs>(self, selector: S, args: A)
-> c_long {
args.send_long_args(self, selector.as_selector())
}
#[inline]
unsafe fn send_void<S:ObjCSelector,A:ObjCMethodVoidArgs>(self, selector: S, args: A) {
args.send_void_args(self, selector.as_selector())
}
#[inline]
unsafe fn send_bool<S:ObjCSelector,A:ObjCMethodBoolArgs>(self, selector: S, args: A)
-> bool {
args.send_bool_args(self, selector.as_selector())
}
#[inline]
unsafe fn send_point<S:ObjCSelector,A:ObjCMethodPointArgs>(self, selector: S, args: A)
-> NSPoint {
args.send_point_args(self, selector.as_selector())
}
}
/// A convenience implementation that allows methods on class names to be called directly, as in:
///
///
/// "NSString".send("alloc")
impl<'a> ObjCMethodCall for &'a str {
#[inline]
unsafe fn send<S:ObjCSelector,A:ObjCMethodArgs>(self, selector: S, args: A) -> id {
args.send_args(class(self), selector.as_selector())
}
#[inline]
unsafe fn send_double<S:ObjCSelector,A:ObjCMethodDoubleArgs>(self, selector: S, args: A)
-> c_double {
args.send_double_args(class(self), selector.as_selector())
}
#[inline]
unsafe fn send_long<S:ObjCSelector,A:ObjCMethodLongArgs>(self, selector: S, args: A)
-> c_long {
args.send_long_args(class(self), selector.as_selector())
}
#[inline]
unsafe fn send_void<S:ObjCSelector,A:ObjCMethodVoidArgs>(self, selector: S, args: A) {
args.send_void_args(class(self), selector.as_selector())
}
#[inline]
unsafe fn send_bool<S:ObjCSelector,A:ObjCMethodBoolArgs>(self, selector: S, args: A)
-> bool {
args.send_bool_args(class(self), selector.as_selector())
}
#[inline]
unsafe fn send_point<S:ObjCSelector,A:ObjCMethodPointArgs>(self, selector: S, args: A)
-> NSPoint {
args.send_point_args(class(self), selector.as_selector())
}
}
/// A trait that allows C strings to be used as selectors without having to convert them first.
@@ -138,6 +179,12 @@ trait ObjCMethodLongArgs {
trait ObjCMethodVoidArgs {
unsafe fn send_void_args(self, receiver: id, selector: SEL);
}
trait ObjCMethodBoolArgs {
unsafe fn send_bool_args(self, receiver: id, selector: SEL) -> bool;
}
trait ObjCMethodPointArgs {
unsafe fn send_point_args(self, receiver: id, selector: SEL) -> NSPoint;
}
impl ObjCMethodArgs for () {
#[inline]
@@ -146,11 +193,10 @@ impl ObjCMethodArgs for () {
}
}
impl ObjCMethodArgs for (id, id, id, id, id) {
impl ObjCMethodArgs for id {
#[inline]
unsafe fn send_args(self, receiver: id, selector: SEL) -> id {
let (first, second, third, fourth, fifth) = self;
invoke_msg_id_id_id_id_id_id(receiver, selector, first, second, third, fourth, fifth)
invoke_msg_id_id(receiver, selector, self)
}
}
@@ -161,6 +207,30 @@ impl ObjCMethodArgs for NSRect {
}
}
impl ObjCMethodArgs for (id, SEL, id) {
#[inline]
unsafe fn send_args(self, receiver: id, selector: SEL) -> id {
let (first, second, third) = self;
invoke_msg_id_id_SEL_id(receiver, selector, first, second, third)
}
}
impl ObjCMethodArgs for (NSRect, c_ulong, c_ulong, bool) {
#[inline]
unsafe fn send_args(self, receiver: id, selector: SEL) -> id {
let (first, second, third, fourth) = self;
invoke_msg_id_NSRect_ulong_ulong_bool(receiver, selector, first, second, third, fourth)
}
}
impl ObjCMethodArgs for (id, id, id, id, id) {
#[inline]
unsafe fn send_args(self, receiver: id, selector: SEL) -> id {
let (first, second, third, fourth, fifth) = self;
invoke_msg_id_id_id_id_id_id(receiver, selector, first, second, third, fourth, fifth)
}
}
impl ObjCMethodDoubleArgs for () {
#[inline]
unsafe fn send_double_args(self, receiver: id, selector: SEL) -> f64 {
@@ -196,6 +266,24 @@ impl ObjCMethodVoidArgs for id {
}
}
impl ObjCMethodBoolArgs for c_long {
#[inline]
unsafe fn send_bool_args(self, receiver: id, selector: SEL) -> bool {
invoke_msg_bool_long(receiver, selector, self)
}
}
impl ObjCMethodPointArgs for NSPoint {
#[inline]
unsafe fn send_point_args(self, receiver: id, selector: SEL) -> NSPoint {
println!("before");
let stret: NSPoint = NSPoint::new(0., 0.);
invoke_msg_NSPoint_NSPoint(&stret, receiver, selector, self);
println!("after");
stret
}
}
/// A trait that simulates variadic parameters for method calls.
#[cfg(test)]
@@ -238,6 +326,15 @@ mod test {
extern {
pub fn invoke_msg_double(theReceiver: id, theSelector: SEL) -> f64;
pub fn invoke_msg_id(theReceiver: id, theSelector: SEL) -> id;
pub fn invoke_msg_id_id(theReceiver: id, theSelector: SEL, a: id) -> id;
pub fn invoke_msg_id_NSRect(theReceiver: id, theSelector: SEL, a: &NSRect) -> id;
pub fn invoke_msg_id_id_SEL_id(theReceiver: id, theSelector: SEL, a: id, b: SEL, c: id) -> id;
pub fn invoke_msg_id_NSRect_ulong_ulong_bool(theReceiver: id,
theSelector: SEL,
a: NSRect,
b: c_ulong,
c: c_ulong,
d: bool) -> id;
pub fn invoke_msg_id_id_id_id_id_id(theReceiver: id,
theSelector: SEL,
a: id,
@@ -246,10 +343,11 @@ extern {
d: id,
e: id)
-> id;
pub fn invoke_msg_id_NSRect(theReceiver: id, theSelector: SEL, a: &NSRect) -> id;
pub fn invoke_msg_long(theReceiver: id, theSelector: SEL) -> c_long;
pub fn invoke_msg_void(theReceiver: id, theSelector: SEL);
pub fn invoke_msg_void_bool(theReceiver: id, theSelector: SEL, a: bool);
pub fn invoke_msg_void_id(theReceiver: id, theSelector: SEL, a: id);
pub fn invoke_msg_bool_long(theReceiver: id, theSelector: SEL, a: c_long) -> bool;
pub fn invoke_msg_NSPoint_NSPoint(stretAddr: &NSPoint, theReceiver: id, theSelector: SEL, a: NSPoint);
}
+1 -1
View File
@@ -15,7 +15,7 @@
#![feature(globs)]
#![allow(non_snake_case_functions)]
#![allow(non_snake_case)]
extern crate libc;
+22
View File
@@ -22,14 +22,26 @@ id invoke_msg_id(id theReceiver, SEL theSelector) {
return objc_msgSend(theReceiver, theSelector);
}
id invoke_msg_id_id(id theReceiver, SEL theSelector, id a) {
return objc_msgSend(theReceiver, theSelector, a);
}
id invoke_msg_id_NSRect(id theReceiver, SEL theSelector, NSRect *rect) {
return objc_msgSend(theReceiver, theSelector, *rect);
}
id invoke_msg_id_id_SEL_id(id theReceiver, SEL theSelector, id a, SEL b, id c) {
return objc_msgSend(theReceiver, theSelector, a, b, c);
}
id invoke_msg_id_id_id_id_id_id(id theReceiver, SEL theSelector, id a, id b, id c, id d, id e) {
return objc_msgSend(theReceiver, theSelector, a, b, c, d, e);
}
id invoke_msg_id_NSRect_ulong_ulong_bool(id theReceiver, SEL theSelector, NSRect a, unsigned long b, unsigned long c, bool d) {
return objc_msgSend(theReceiver, theSelector, a, b, c, d);
}
long invoke_msg_long(id theReceiver, SEL theSelector) {
return (long)objc_msgSend(theReceiver, theSelector);
}
@@ -46,3 +58,13 @@ void invoke_msg_void_id(id theReceiver, SEL theSelector, id id) {
objc_msgSend(theReceiver, theSelector, id);
}
char invoke_msg_bool_long(id theReceiver, SEL theSelector, long a) {
return objc_msgSend(theReceiver, theSelector, a);
}
/** segfaults */
void invoke_msg_NSPoint_NSPoint(NSPoint *stretAddr, id theReceiver, SEL theSelector, NSPoint point) {
void (*s)(void *ret, id self, SEL op, ...) = (void *)objc_msgSend_stret;
return s((void *)stretAddr, theReceiver, theSelector, point);
}