Retired Document
Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.
Using a Cocoa User Interface in a Carbon Application
You can use a Cocoa user interface in a Carbon application starting with Mac OS X version 10.2. The system provides code that allows Cocoa and Carbon to communicate user events to each other, so there are only a few tasks you must perform to enable the Cocoa user interface to work properly in Carbon. Most tasks are similar to what you would do to use non-UI Cocoa functionality in a Carbon application—that is, writing C-callable wrapper functions and calling them. (See Using Cocoa Functionality in a Carbon Application.)
It is important to recognize that embedding a Cocoa NSView inside a Carbon window is not supported in Mac OS X version 10.4 and earlier. For more information, see HICocoaView: Using Cocoa Views in Carbon Windows.
To use a Cocoa user interface in a Carbon application, you need to perform two major tasks:
Write a Cocoa source file that contains the interface you want to use, the Cocoa methods that support the interface, and C-callable wrapper functions that allow access to the Cocoa functionality needed by the Carbon application. This is described in detail in Writing the Cocoa Source Files.
Write Carbon code that provides handlers, as appropriate, for the interface. See Setting Up the Carbon Application to Use the Cocoa Interface for details.
The tasks described in the following sections are illustrated using sample code taken from a working application called CocoaInCarbon. See About the CocoaInCarbon Application for a description of the application. You can download the code for CocoaInCarbon.
Keep in mind that many parts of the CocoaInCarbon application are specific to the sample application; you need to customize the code for your own purposes. Although most of the code from the CocoaInCarbon application is shown in the listings in this article, not all of the code is included.
About the CocoaInCarbon Application
When you look at the code in the subsequent sections, it may be helpful to have an idea of how the CocoaInCarbon application behaves and what the user interface looks like. When the application is launched, an empty window appears. This window, shown in Figure 1, is a Carbon window defined in a nib file created with Interface Builder. The application provides a Test menu, that contains one command, Open Cocoa Window.
When the user chooses Open Cocoa Window from the Test menu, the application calls the C-wrapper function that opens the Cocoa window (shown in Figure 2) and makes the window active.
When the user clicks the button shown in Figure 2, the mouse event is received by the Carbon application, which automatically dispatches the event to Cocoa. Specifically, the button receives the mouse event and sends its action method. The action method triggers a button command handler provided by the Carbon application. The button command handler calls a C-wrapper function that sends the text “button pressed!” to the text field below the button, as shown in Figure 2.
Although the sample application is obviously a contrived example, it illustrates the extent to which Carbon and Cocoa can communicate with each other.
Writing the Cocoa Source Files
Writing the Cocoa source requires performing the tasks described in the following sections:
Creating a Cocoa Source File Using Xcode
You need to make a Cocoa source file that contains all the Cocoa functionality needed by your Carbon application. Follow these steps to create a Cocoa source file:
Open your Carbon project in Xcode.
Choose File > New File.
Select Empty File in Project in the New File window and click the Next button.
Name the file so it has the appropriate
.m
extension. The sample code filename isController.m
.Recall from Preprocessing Mixed-Language Code that the
.m
extension indicates to the compiler that the code is Objective-C.Create any other files you need for the application. For example, the Cocoa source created for the CocoaInCarbon application has an interface file,
Controller.h
. You must create this file and import it to theController.m
file by adding an import statement to theController.m
file.
As long as you create your source file using Xcode, you should not need to modify build settings and property list values.
Writing a Common Header File
Both the Cocoa and the Carbon sources need to have a copy of the header file that declares any constants used to communicate events between them. The constant that defines the button-press event in the CocoaInCarbon application is one such constant. The C-callable wrapper function declarations could also be in this shared file. Listing 1 shows the contents of the header file (CocoaStuff.h
) that must be included in both the Cocoa and the Carbon source for the CocoaInCarbon project.
Listing 1 The contents of the common header for Cocoa and Carbon
enum { |
kEventButtonPressed = 1 |
}; |
//Declare the wrapper functions |
OSStatus initializeCocoa(OSStatus (*callBack)(int)); |
OSStatus orderWindowFront(void); |
OSStatus changeText(CFStringRef message); |
Implementing the Controller
The code to implement the controller for the Cocoa source in the CocoaInCarbon application is shown in Listing 2. The critical item in this code is the use of the NSApplicationLoad
function (in the line numbered 2). A Carbon application cannot access the Cocoa interface unless the application includes a call to NSApplicationLoad
. This function is not needed for Cocoa applications, but it is mandatory for Carbon applications that use Cocoa. The NSApplicationLoad
function is available starting with Mac OS X v10.2. NSApplicationLoad
should be called after Carbon is initialized.
A detailed explanation of each numbered line of code in Listing 2 follows the listing.
Listing 2 Implementing the controller in the Cocoa source file
#import "Controller.h" |
static Controller *sharedController; |
@implementation Controller |
+ (Controller *) sharedController |
{ |
return sharedController; |
} |
- (id) init // 1 |
{ |
self = [super init]; |
NSApplicationLoad(); // 2 |
if (![NSBundle loadNibNamed:@"MyWindow" owner:self]) { |
NSLog(@"failed to load MyWindow nib"); |
} |
sharedController = self; |
return self; |
} |
- (void) setCallBack:(CallBackType) callBack // 3 |
{ |
_callBack = callBack; |
} |
- (void) showWindow // 4 |
{ |
[window makeKeyAndOrderFront:nil]; |
} |
- (void) changeText:(NSString *)text // 5 |
{ |
[textField setStringValue:text]; |
} |
- (IBAction) buttonPressed:(id)sender // 6 |
{ |
(*_callBack) (kEventButtonPressed); |
} |
@end |
Here’s what the code does:
Defines the method that initializes Cocoa. You’ll write a C-callable wrapper function for this method later. See Writing C-Callable Wrapper Functions.
Calls
NSApplicationLoad
as required. This entry point is needed for Carbon applications using Cocoa API but is a no-op for Cocoa applications.Sets a callback for the controller. The callback (as you’ll see later) is provided by the Carbon application to handle the button-press event.
Shows the Cocoa window and makes it active and frontmost. You’ll write a C-callable wrapper function for this method later. See Writing C-Callable Wrapper Functions.
Displays a string in the text field of the Cocoa window. You’ll write a C-callable wrapper function for this method later. See Writing C-Callable Wrapper Functions.
Defines the Interface Builder action associated with the button in the Cocoa window. It invokes the callback (provided by Carbon) that handles the button-press event. You’ll connect the action method to its button target later. See Creating the Cocoa Window in Interface Builder.
Declaring the Controller Interface
The code to declare the interface for the controller (Controller.h
) is shown in Listing 3. The item of note in this listing is the _callBack
instance variable. The Carbon application provides the callback function that’s assigned to this variable.
Listing 3 Declaring the interface for the controller
#import <Cocoa/Cocoa.h> |
#import "CocoaStuff.h" |
typedef OSStatus (*CallBackType)(int); |
@interface Controller : NSObject |
{ |
id window; |
id textField; |
CallBackType _callBack; |
} |
- (void)setCallBack:(CallBackType)callBack; |
- (void)showWindow; |
+ (id)sharedController; |
@end |
Writing C-Callable Wrapper Functions
Listing 4 shows the C-callable wrapper functions that are part of the Cocoa source file. These functions are in the Controller.m
file in the CocoaInCarbon project. Each function in Listing 4 wraps around one of the methods shown in Listing 2.
One of the critical items in this code is the use of and NSAutoreleasePool object. For Cocoa API used by a Carbon application, you must set up autorelease pools as needed. A detailed explanation of each numbered line of code appears following the listing.
Listing 4 C-callable wrapper functions for the Cocoa API
OSStatus initializeCocoa (OSStatus (*callBack)(int)) // 1 |
{ |
Controller *controller; |
NSAutoreleasePool *localPool; |
localPool = [[NSAutoreleasePool alloc] init]; // 2 |
controller = [[Controller alloc] init]; // 3 |
[controller setCallBack:callBack]; // 4 |
[localPool release]; // 5 |
return noErr; |
} |
OSStatus orderWindowFront(void) // 6 |
{ |
NSAutoreleasePool *localPool; |
localPool = [[NSAutoreleasePool alloc] init]; |
[[Controller sharedController] showWindow]; |
[localPool release]; |
return noErr; |
} |
OSStatus changeText (CFStringRef message) // 7 |
{ |
NSAutoreleasePool *localPool; |
localPool = [[NSAutoreleasePool alloc] init]; |
[[Controller sharedController] changeText:(NSString *)message]; // 8 |
[localPool release]; |
return noErr; |
} |
Here’s what the code does:
Defines a C-callable wrapper function that must be called by the Carbon application to initialize Cocoa.
Allocates and initializes an autorelease pool, as required. You must do this for each C- callable wrapper function. See Using Carbon and Cocoa in the Same Application for more information.
Calls the
init
method. Recall that this method makes the required call toNSApplicationLoad
.Assigns the callback function provided by the Carbon application to the controller’s callback instance variable.
Releases the local autorelease pool.
Defines a C-callable wrapper function for the
showWindow
method.Defines a C-callable wrapper function for the
changeText:
method.Casts a
CFStringRef
value to NSString *. Recall from Interchangeable Data Types that theCFString
data type is toll-free bridged to the NSString class.
Creating the Cocoa Window in Interface Builder
You must use Interface Builder to add the Cocoa window to the appropriate nib file and connect targets to the appropriate actions. A nib file contains the definition of a set of user-interface elements. When the button in the sample application is pressed, it triggers the buttonPressed
action method (see Figure 3). Recall from Listing 2 that the buttonPressed
method calls the callback function assigned to the controller. In the CocoaInCarbon application, the callback is provided by the Carbon application.
For more information on creating a Cocoa window in Interface Builder, see Cocoa Application Tutorial.
Setting Up the Carbon Application to Use the Cocoa Interface
Your Carbon application must perform a number of tasks to use the interface provided by the Cocoa. These tasks are described in the following sections:
Including the Common Header File
Both the Cocoa and the Carbon source files need to have a copy of the header file that declares any constants used to communicate events. Listing 1 shows the header file (CocoaStuff.h
) that needs to be included with both the Cocoa and the Carbon source files for the CocoaInCarbon project. See Writing a Common Header File for details.
Writing a Command Handler
The Carbon code in the CocoaInCarbon application provides a function (handleCommand
) that is passed as a parameter to the C-wrapper function that initializes Cocoa. When the user presses the button in the Cocoa window, the action method associated with the button invokes the handleCommand
function. In the CocoaInCarbon application, this function just sends a string back to the Cocoa method, so it is not really necessary. However, the handleCommand
function does illustrate how a more complex application could be notified of user actions in the Cocoa source.
The handleCommand
function is shown in Listing 5. A detailed explanation of each numbered line of code appears following the listing.
Listing 5 A Carbon function that handles a Cocoa button press
static OSStatus handleCommand (int commandID) |
{ |
OSStatus osStatus = noErr; |
if (commandID == kEventButtonPressed) // 1 |
{ |
osStatus = changeText (CFSTR("button pressed!")); // 2 |
require_noerr (osStatus, CantCallFunction); |
} |
CantCallFunction: |
return osStatus; |
} |
Here’s what the code does:
Copyright © 2002, 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-10-31