PasCocoa
PasCocoa is the project to build object oriented bindings to use Cocoa in Pascal.
Objective-C to Pascal Bindings
There are 2 main versions of Objective-C, and thus of Objective-C headers: 1.0 and 2.0
Version 2.0 is only available on Mac OS X 10.5 or superior, making it too restrictive at the moment, and thus the available headers are a translation of 1.0 headers from a Mac OS X 10.4.9 machine.
Objective-C 1.0 Bindings
- Reference page: http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef1/Reference/reference.html
- Available on: Mac OS X 10.0 and superior
Objective-C 2.0 Bindings
- Available on: Mac OS X 10.5 and superior
Example
<delphi> {
simpleform.pas
This example shows how to use the PasCocoa bindings to create a NSAutoreleasePool, initialize the application global variable, create a simple window without contents and attach a close handler to it that exits the application.
Compilation of this example requires the following options: -k-framework -kcocoa -k-lobjc
This example project is released under public domain
AUTHORS: Felipe Monteiro de Carvalho
} program simplewindow;
{$ifdef fpc}{$mode delphi}{$endif}
uses
objc, ctypes, FPCMacOSAll, AppKit, Foundation;
const
Str_Window_Title = 'This is the title'; Str_Window_Message = 'This is the message';
var
{ classes } pool: NSAutoreleasePool; MainWindow: NSWindow; MainWindowView: NSView; TextField: NSTextField; { strings } CFTitle, CFMessage: CFStringRef; { sizes } MainWindowRect, TextFieldRect: NSRect;
begin
{ Creates a AutoreleasePool for this thread. Every thread must have one } pool := NSAutoreleasePool.Create;
{ Creates the application NSApp object } NSApp := NSApplication.sharedApplication;
{ Creates a simple window } MainWindowRect.origin.x := 300.0; MainWindowRect.origin.y := 300.0; MainWindowRect.size.width := 300.0; MainWindowRect.size.height := 500.0;
MainWindow := NSWindow.initWithContentRect(MainWindowRect, NSTitledWindowMask or NSClosableWindowMask or NSMiniaturizableWindowMask or NSResizableWindowMask, NSBackingStoreBuffered, NO);
{ Initializes the title of the window }
CFTitle := CFStringCreateWithPascalString(nil, Str_Window_Title, kCFStringEncodingUTF8); MainWindow.setTitle(CFTitle);
{ Adds a NSTextField with a string } CFMessage := CFStringCreateWithPascalString(nil, Str_Window_Message, kCFStringEncodingUTF8); TextFieldRect.origin.x := 0.0; TextFieldRect.origin.y := 200.0; TextFieldRect.size.width := 300.0; TextFieldRect.size.height := 100.0; TextField := NSTextField.initWithFrame(TextFieldRect); TextField.setStringValue(CFMessage); MainWindowView := NSView.CreateWithHandle(MainWindow.contentView); MainWindowView.addSubview(TextField);
{ Put's the window on the front z-order }
MainWindow.orderFrontRegardless;
{ Enters main message loop }
NSApp.run;
{ Releases the AutoreleasePool for this thread } pool.Free;
end. </delphi>
Subversion
svn co https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/bindings/objc objc svn co https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/bindings/pascocoa pascocoa
Documentation
Creating a class which inherits from a Cocoa class
To create a class which inherits from a Cocoa class, one needs to create a normal class which inherits from any of the classes on the PasCocoa bindings and use the NSObject.CreateClassDefinition method to register that class on the Objective-C runtime before calling the NSObject constructor.
Example:
<delphi> uses foundation;
type
{ TMyController }
TMyController = class(NSObject) public { Extra binding functions } constructor Create; override; function getClass: objc.id; override; ... end;
const
Str_TMyController = 'TMyController'; ...
implementation
constructor TMyController.Create; begin
{ The class is registered on the Objective-C runtime before the NSObject constructor is called } if not CreateClassDefinition(Str_TMyController, Str_NSObject) then WriteLn('Failed to create objc class');
inherited Create;
{ Insert other desired initialization here }
end;
{ Called by the NSObject constructor to get the definition of our class.
At this point the class is simply identifyed by it's name and must be already registered on the Objective-C runtime. }
function TMyController.getClass: objc.id; begin
Result := objc_getClass(Str_TMyController);
end; </delphi>
Registering Pascal methods as Objective-c methods
Pascal methods aren't automatically available to Objective-C. They need to be first registered on the Objective-C runtime by overriding the AddMethods method and calling NSObject.AddMethod method for every method to be registered.
Example:
<delphi> </delphi>
Roadmap
General Roadmap
Component | Status | Details |
---|---|---|
Objective-C 1.0 Runtime Headers | Working | Mac OS X 10.0 or superior |
Objective-C 2.0 Runtime Headers | Not Implemented | Mac OS X 10.5 or superior only |
Automatic headers Parser | Partially Implemented | |
Foundation | Partially Implemented | |
AppKit | Partially Implemented |
Foundation Classes Roadmap
Component | Status |
---|---|
NSAutoreleasePool | Partially Implemented |
NSGeometry | Partially Implemented |
NSObject | Partially Implemented |
AppKit Classes Roadmap
Component | Status |
---|---|
NSActionCell | Partially Implemented |
NSAlert | Partially Implemented |
NSApplication | Partially Implemented |
NSButton | Partially Implemented |
NSButtonCell | Partially Implemented |
NSCell | Partially Implemented |
NSControl | Partially Implemented |
NSGraphics | Partially Implemented |
NSImage | Partially Implemented |
NSPanel | Partially Implemented |
NSTextField | Partially Implemented |
NSView | Partially Implemented |
NSWindow | Partially Implemented |
NSStatusBar | Partially Implemented |
NSStatusItem | Partially Implemented |
Implementation Details
Overview
The Cocoa headers contain several peculiarities that lead us to need a special structure for the bindings. First, several forward declarations of classes are done in a very unordently way. Pascal only accepts forward declarations of classes if they are all declared on the same type clause, and therefore a special session CLASSES is created on the include file.
The include file has a total of 3 separate sessions: HEADER, CLASSES and IMPLEMENTATION
A tipical include file should have this structure:
<delphi> {%mainunit appkit.pas} {
NSStatusBar.h Application Kit Copyright (c) 1997-2005, Apple Computer, Inc. All rights reserved.
}
{$ifdef HEADER} {$ifndef NSSTATUSBAR_PAS_H} {$define NSSTATUSBAR_PAS_H}
//insert constants, records and other interface declarations with the exception of classes
{$endif} {$endif}
{$ifdef FORWARD} {$ifndef NSSTATUSBAR_FORWARD} {$define NSSTATUSBAR_FORWARD}
//forward declarations of classes
{$endif} {$endif}
{$ifdef CLASSES} {$ifndef NSSTATUSBAR_PAS_H} {$define NSSTATUSBAR_PAS_H}
//declaration of classes
{$endif} {$endif} {$ifdef IMPLEMENTATION}
//insert implementation declarations here. The order is not important on the implementation, so don't use extra ifdefs.
{$endif} </delphi>
HEADERS
Guidelines:
- Inclusion of headers in other frameworks should be removed, because the each framework is mapped to a pascal unit, and therefore all include files of a used framework are already accessible. Inclusion of headers of the same file framework should become include clauses which will used a C-styled ifdef mechanism to resolve in which order the include files will be added. This mechanism works fairly well and keeps the structure of the include files similar to the original one.
CLASSES
Guidelines:
- Should include the same include files as the HEADERS section
- All private members of classes should be removed
IMPLEMENTATION
- Method should send message using one of the functions:
objc_msgSend
objc_msgSend_frep (float and double types for i386, not for PPC)
objc_msgSend_stret (records)
Reference Page: http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html
- Class methods must be called for ClassID
- Instance methods must be called for Handle
Notes
Objects allocation
Objective-C language does not provide any special syntax to define, if class method is construtor method or not. However, Apple provides some kind of methods naming convention, you can learn more about it here: http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_13_section_3.html
Following Objective-C methods, should be constructors for Pascal objects:
- class methods named +(id) alloc... (Obj-C construct methods)
- class methods +(NSClassName *) anyMethodName... (Obj-C convenience constructors)
- class methods +(id) anyMethodName (Obj-C convenience constructors)
- instance methods name - init... (Obj-C initialization methods).
Though object allocation and initialization are separated in time for Objective-C. Pascal rarely uses the same style, providing larger number of constructors, so - init... methods should become constructors, though it's questionable.
Categories
Objective-C provides mechanism to extend class methods dynamicly without need of class iheritance. These are known as categories and extensions. Reference page: http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_4_section_1.html This feature is not supported by Pascal, there's no way to extend methods of the class without inheritance.
Categories are used very often in Cocoa headers. They are used to group methods logically. It's common, that Base class is extended with several categories within a single header file. The good example is NSString.h, there base NSString class has only 2 method, while it's categories declares dozens of new methods.
In this case (if base class and it's categories are declared in the single header) all methods of the categories must be declared the base class. So convertede Pascal class NSString, would contain all methods that were declared in NSString.h categories.
The same way, might be used to generate Pascal classes, if base Obj-C class and it's categories are declared in different headers, but discouraged.
Carbon-Cocoa integration
Some Obj-C classes can be casted to Carbon types, such types are also known as toll-free bridged. It's recommended to use Carbon types (all of them declared at FPCMacOSALL unit) for parameters and method result types.
<delphi> function GetName: NSString; procedure SetStrParam(value: NSString); </delphi>
should be <delphi> function GetName: CGStringRef; procedure SetStrParam(value: CGStringRef); </delphi>
List of toll-free bridged types, add additional information on Carbon-Cocoa integration can be found at this page: http://developer.apple.com/documentation/Cocoa/Conceptual/CarbonCocoaDoc/Articles/InterchangeableDataTypes.html
Unnamed enums
There're great number of unnamed enums declared in Cocoa headers. These enums are used to describe the constant values for methods parameters. They must be converted as Pascal constants, rather than enumeration, to avoid parameter size conflicts.