macOS Platform Guide
The macOS backend uses Cocoa/AppKit (Objective-C++) for all native UI components. It provides a
true Mac experience with native widgets, dark mode support via NSAppearanceNameDarkAqua,
and full integration with the macOS windowing system.
| Language | Objective-C++ |
|---|---|
| Lines of code | ~3,800 |
| HB_FUNC functions | ~158 |
| Native API | Cocoa / AppKit (NSView, NSWindow) |
| Scintilla | 5.5.3 (static library) |
| Supported OS | macOS 12 (Monterey) and later |
| Architectures | x86_64, ARM64 (Apple Silicon) |
Architecture Overview
#xcommand preprocessor"] B --> C["TForm, TControl
Harbour OOP classes"] C --> D["HB_FUNC Bridge
~158 functions"] D --> E["Cocoa Backend
Objective-C++ / NSView"] E --> F["AppKit
NSButton, NSTextField, etc."] E --> G["Core Graphics
Drawing"] E --> H["Scintilla 5.5.3
Static lib"] style A fill:#58a6ff,stroke:#388bfd,color:#0d1117 style B fill:#8b5cf6,stroke:#7c3aed,color:#fff style C fill:#f59e0b,stroke:#d97706,color:#0d1117 style D fill:#f59e0b,stroke:#d97706,color:#0d1117 style E fill:#555,stroke:#333,color:#fff style F fill:#555,stroke:#333,color:#fff style G fill:#555,stroke:#333,color:#fff style H fill:#555,stroke:#333,color:#fff
Native Window Creation
Every HarbourBuilder form is a native NSWindow with an NSView content view.
Controls are Cocoa subclasses of NSView:
// cocoa_backend.mm - Creating a form window HB_FUNC( UI_FORMNEW ) { const char * cTitle = hb_parc( 1 ); NSInteger nWidth = hb_parni( 2 ); NSInteger nHeight = hb_parni( 3 ); NSRect frame = NSMakeRect( 0, 0, nWidth, nHeight ); NSWindowStyleMask style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; NSWindow * window = [[NSWindow alloc] initWithContentRect:frame styleMask:style backing:NSBackingStoreBuffered defer:NO]; [window setTitle:[NSString stringWithUTF8String:cTitle]]; [window center]; // Set up the content view (NSView for child controls) NSView * contentView = [[NSView alloc] initWithFrame:frame]; [window setContentView:contentView]; // Store handle in Harbour-side object table hb_retnint( (NSInteger)(intptr_t) window ); }
Controls map to native Cocoa classes:
| HarbourBuilder Control | Cocoa Class |
|---|---|
| Button | NSButton (NSButtonTypeMomentaryPushIn) |
| Label | NSTextField (label style, non-editable) |
| Edit | NSTextField (editable, single-line) |
| Memo | NSTextView (in NSScrollView) |
| CheckBox | NSButton (NSButtonTypeSwitch or NSButtonTypeCheck) |
| ComboBox | NSComboBox |
| ListBox | NSScrollView with NSListView |
| GroupBox | NSBox (NSBoxTypeCustom) |
| ProgressBar | NSProgressIndicator (NSProgressIndicatorStyleBar) |
| Slider/TrackBar | NSSlider |
| Image | NSImageView |
| TabControl | NSTabView |
| TableView | NSTableView (in NSScrollView) |
| Timer | NSTimer |
Event Handling
The macOS backend uses Cocoa's target-action pattern and notification system. Each control registers an action target that calls into the Harbour event system:
// cocoa_backend.mm - Button event binding HB_FUNC( UI_ONEVENT ) { NSInteger nHandle = hb_parnint( 1 ); NSInteger nEventId = hb_parnint( 2 ); PHB_ITEM pBlock = hb_param( 3, HB_IT_BLOCK ); NSButton * btn = (NSButton *)(intptr_t) nHandle; // Store the Harbour code block in a side table StoreEventBlock( nHandle, nEventId, pBlock ); // Set up the Cocoa action target EventTarget * target = [[EventTarget alloc] initWithHandle:nHandle eventId:nEventId]; [btn setTarget:target]; [btn setAction:@selector( fire: )]; } // EventTarget fires the Harbour code block @implementation EventTarget - (void) fire:(id)sender { FireEvent( self.handle, self.eventId ); } @end
Dark Mode
macOS dark mode is handled natively by AppKit:
NSAppearanceNameDarkAqua— The backend sets the application appearance toNSAppearanceNameDarkAquawhen the system is in dark mode. All standard Cocoa controls automatically adapt their colors.- Automatic control colors —
NSColorprovides semantic colors (labelColor,controlColor,windowBackgroundColor) that automatically switch between light and dark variants. - System theme observation — The backend registers for
NSApplicationDidChangeScreenParametersNotificationto detect theme changes at runtime.
// cocoa_backend.mm - Dark mode setup void SetupAppearance(void) { // Follow the system appearance (light or dark) [[NSApplication sharedApplication] setAppearance:nil]; // To force dark mode regardless of system setting: // NSAppearance * dark = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; // [[NSApplication sharedApplication] setAppearance:dark]; }
HarbourBuilder follows the macOS system appearance setting. If your Mac is set to Dark mode (System Settings → Appearance), HarbourBuilder and all apps built with it will automatically use the dark theme. No configuration needed.
Scintilla Integration
On macOS, Scintilla 5.5.3 is built as a static library and linked directly into the HarbourBuilder binary:
| File | Type | Purpose |
|---|---|---|
libscintilla.a | Static library | Scintilla core editing component |
liblexilla.a | Static library | Lexer library |
The Scintilla view is embedded as an SCIView (Scintilla's Cocoa view class) within
the code editor tab. It communicates via the Scintilla direct function interface.
// Embedding Scintilla in the Cocoa view hierarchy @interface HBCodeEditorView : NSView @property (strong) SCIView * scintillaView; @end // Create and configure SCIView * sciView = [[SCIView alloc] initWithFrame:frame]; Scintilla_SendMessage( sciView, SCI_SETLEXER, SCLEX_CPP, 0 ); Scintilla_SendMessage( sciView, SCI_SETSTYLEBITS, 8, 0 );
Build Process
Prerequisites
- Xcode Command Line Tools — This is the only prerequisite. Install with:
xcode-select --install
This providesclang,make, and the macOS SDK headers. - No separate Harbour install needed — The build script downloads and compiles Harbour automatically if it's not already present.
Build Script (build_mac.sh)
The script performs these steps:
- Check for Harbour — If
$HBDIRis not set or Harbour is not found at~/harbour, it clones and compiles Harbour from github.com/harbour/core. - Build Scintilla — Downloads Scintilla 5.5.3 source and builds
libscintilla.aandliblexilla.aas static libraries. - Compile the IDE — Compiles HarbourBuilder source using the Harbour compiler and clang for the Objective-C++ backend.
- Link — Links against the macOS frameworks:
-framework Cocoa,-framework Foundation,-framework AppKit, plus the Harbour runtime and Scintilla static libs.
# build_mac.sh - Typical build session cd HarbourBuilder/samples ./build_mac.sh # Copy to bin/ and run cp HbBuilder ../bin/ ../bin/HbBuilder
If you already have Harbour installed elsewhere: HBDIR=/path/to/harbour ./build_mac.sh
Apple Silicon Considerations
The default build targets x86_64 for compatibility. On Apple Silicon Macs (M1/M2/M3/M4),
the binary runs under Rosetta 2 translation. For native ARM64 builds:
- Recompile Harbour for
darwin/clang/arm64 - Update
HBDIRinbuild_mac.shto point to the ARM64 Harbour build - Rebuild Scintilla with
-arch arm64 - Build HarbourBuilder with
clang -arch arm64
# Building for Apple Silicon (ARM64) export CFLAGS="-arch arm64" export LDFLAGS="-arch arm64" HBDIR=~/harbour_arm64 ./build_mac.sh
Rosetta 2 translation is very efficient on Apple Silicon. Most applications run with negligible performance impact. However, for the best performance — especially with CPU-intensive operations like compiling — a native ARM64 build is recommended.
.app Bundle Creation
To distribute a HarbourBuilder application as a standard macOS application bundle:
MyApp.app/ ├── Contents/ │ ├── Info.plist // Bundle metadata │ ├── MacOS/ │ │ └── MyApp // The compiled binary │ ├── Resources/ │ │ ├── icon.icns // Application icon │ │ └── MainMenu.nib // (optional) MainMenu │ └── PkgInfo // APPL????
The Info.plist must include at minimum:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleExecutable</key> <string>MyApp</string> <key>CFBundleIconFile</key> <string>icon</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>NSHighResolutionCapable</key> <true/> </dict> </plist>
Platform-Specific Features
| Feature | macOS Implementation |
|---|---|
| Native menus | NSMenu / NSMenuItem in the system menu bar |
| Open/Save dialogs | NSOpenPanel / NSSavePanel |
| Font picker | NSFontPanel |
| Color picker | NSColorPanel |
| Alert dialogs | NSAlert |
| Notifications | NSUserNotificationCenter |
| File events | FSEvents API |
| Pasteboard | NSPasteboard |
| Dark mode | NSAppearanceNameDarkAqua |
| Retina support | Automatic via backing:NSBackingStoreBuffered |
Known Limitations
- Gatekeeper — Unsigned applications may be blocked by Gatekeeper. Right-click → Open, or remove quarantine:
xattr -d com.apple.quarantine HbBuilder. - Notarization — For distribution to other Macs, the binary should be notarized through Apple's Developer ID program.
- Scintilla version — The macOS backend uses Scintilla 5.5.3 (vs 5.6.1 on Windows/Linux) due to Cocoa-specific compatibility requirements in the newer version.
- ARM64 — Native Apple Silicon builds require manual recompilation of Harbour and Scintilla for
arm64.
Dependencies
| Framework/Library | Purpose |
|---|---|
-framework Cocoa | Complete Cocoa/AppKit/Foundation umbrella framework |
-framework AppKit | UI controls, windows, views, events |
-framework Foundation | NSString, NSArray, NSDate, etc. |
libscintilla.a | Code editing component (static) |
liblexilla.a | Lexer component (static) |
libharbour.a | Harbour runtime |