Cocoa Internals: what changed
Revision 3 to 4. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | The page is about the 64 bit Cocoa Widgetset internal implementation. The page should be useful for Cocoa widgetset developers (maintainers and/or contributors) as well as any developers who need to use the Cocoa-specific API. | |
| 2 | 2 | ||
| 3 | 3 | ## Minimum Version | |
| 4 | 4 | ||
| 5 | 5 | - Some parts of Cocoa are written using Objective C 2.0 features. Objective C 2.0 features used: | |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | 9 | [Enumeration in Objective-C classes](https://www.freepascal.org/docs-html/ref/refse76.html#x138-16000011.10) | |
| 10 | 10 | ||
| 11 | 11 | - Minimal macOS version is: | |
| 12 | 12 | ||
| 13 | 13 | :* 10.5 - spiritual | |
| 14 | 14 | ||
| 15 | 15 | :* 10.6 - de facto (a lot of 10.6 only APIs are used without any actual verification of the proper macOS version) | |
| 16 | 16 | ||
| 17 | 17 | There's a reason to keep the backwards compatibility for 10.6. This is the last system that supports Rosetta applications to allow running [PowerPC](https://en.wikipedia.org/wiki/PowerPC) applications on an Intel processor. | |
| 18 | 18 | ||
| 19 | 19 | ### Compiler Support | |
| 20 | 20 | ||
| 21 | 21 | APIs that not (yet) provided by a corresponding version of FPC (delivered with Lazarus), should be declared at **cocoa_extra.pas** header | |
| 22 | 22 | ||
| 23 | 23 | It's desired that the legacy version (i.e. deprecated APIs) would be wrapped into the [[FPC New Features 2.6.2|minimum version defines]]. | |
| 24 | 24 | ||
| 25 | 25 | If an application using LCL to be deployed to App Store, Apple would verify the used APIs and ban, if any older APIs are used. | |
| 26 | 26 | ||
| 27 | 27 | ## Recompilation | |
| 28 | 28 | ||
| 29 | 29 | If you tried to modify Cocoa-Widget and the next attempt at the project or its Cocoa compilation throws an unexpected error like "cocoawscommon.pas(y:x) Error: identifier idents no member "lclFrame"", you need to delete all Cocoa compiled units. | |
| 30 | 30 | ||
| 31 | - | ||
| 31 | + | ```text | |
| 32 | 32 | ./lazarus/lcl/units/i386-darwin/cocoa | |
| 33 | 33 | ``` | |
| 34 | 34 | ||
| 35 | 35 | or | |
| 36 | 36 | ||
| 37 | - | ||
| 37 | + | ```text | |
| 38 | 38 | ./lazarus/lcl/units/x86_64-darwin/cocoa | |
| 39 | 39 | ``` | |
| 40 | 40 | ||
| 41 | 41 | (to be reported - compiler issue with ignoring extensions) | |
| 42 | 42 | ||
| 43 | 43 | ### Reconfigure LCL package | |
| 44 | 44 | ||
| 45 | 45 |  | |
| 46 | 46 | ||
| 47 | 47 | Another way is to configure the LCL package to always remove .o files before recompilation of the package itself. (The same effect as removing it from command line): | |
| 48 | 48 | ||
| 49 | 49 | - Open LCL package ( Package -\> Open Loaded Package ... -\>LCL ) | |
| 50 | 50 | - Select (package) "Options" | |
| 51 | 51 | - Select "Compiler Command" | |
| 52 | 52 | - specify command for "Execute before" | |
| 53 | 53 | ||
| 54 | - | ||
| 54 | + | ```text | |
| 55 | 55 | sh -c "rm -f ../units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)/*" | |
| 56 | 56 | ``` | |
| 57 | 57 | ||
| 58 | 58 | Note that use of **sh** is necessary, because Lazarus IDE doesn't process * (asterisk) | |
| 59 | 59 | ||
| 60 | 60 | ## LCL specific ObjC classes | |
| 61 | 61 | ||
| 62 | 62 | In order to control and handle an NSView's behavior LCL uses descendent classes from standard Cocoa controls. That is, for NSWindow, TCocoaWindow is introduced. The descedants are used for the purpose of "overriding" default class implementation, where it is needed. In some cases using delegate classes is not enough. | |
| 63 | 63 | ||
| 64 | 64 | There are two ways, in order to provide a common LCL(friendly) API interface for all Cocoa classes or sub-classes: | |
| 65 | 65 | ||
| 66 | 66 | - use **protocol** (similar to Interfaces in Object Pascal form). Any class, sub-classing from standard Cocoa classes, would have to implement the **protocol**. The downside of this approach is - a lot of code would be duplicated. | |
| 67 | 67 | - use **categories** (similar to Javascript prototypes, not available in Object Pascal. Class-helpers looks alike, but are not the same). Categories allow to add methods (only methods! adding variables is not allowed) to any base class of Objective-C. Any descendants of this class automatically acquire the method, and it's default implementation. Also any subclass, can override the default implementation with its own. Apple doesn't recommend the use of categories (due to ambiguity and possible conflicts among different libraries introducing their own extensions). See more at the [[Cocoa Internals/Extensions|Cocoa Internals Extensions]] page | |
| 68 | 68 | ||
| 69 | 69 | ## Code Style | |
| 70 | 70 | ||
| 71 | 71 | The following requirement applies for Cocoa widgetset. Other widgetset may be following some other rules (historically), but in general LCL follows the same requirements. | |
| 72 | 72 | ||
| 73 | 73 | - **Operators** Keep the operator separated by spaces between operands | |
| 74 | 74 | ||
| 75 | 75 | ``` pascal | |
| 76 | 76 | A := B; | |
| 77 | 77 | A := B * C; | |
| 78 | 78 | ``` | |
| 79 | 79 | ||
| 80 | 80 | - **Blocks** The main rule is to make 2 character spacing from the code block start. Begin / Else should start on a new line. | |
| 81 | 81 | ||
| 82 | 82 | ``` pascal | |
| 83 | 83 | procedure B; | |
| 84 | 84 | begin | |
| 85 | 85 | if A then // 2 character spacing from begin | |
| 86 | 86 | begin | |
| 87 | 87 | Start Here // 2 character spacing from begin | |
| 88 | 88 | Next Line | |
| 89 | 89 | end | |
| 90 | 90 | else | |
| 91 | 91 | begin | |
| 92 | 92 | Another Line | |
| 93 | 93 | end; | |
| 94 | 94 | end; | |
| 95 | 95 | ``` | |
| 96 | 96 | ||
| 97 | 97 | - **Standard Function Name** please keep naming in ProperCase. Reserved words should be lower case. Name of (global/local) variables and fields should match declaration. Local variables should start with lower case. | |
| 98 | 98 | ||
| 99 | 99 | ``` pascal | |
| 100 | 100 | if not Assigned(A) then | |
| 101 | 101 | begin | |
| 102 | 102 | Result := nil; | |
| 103 | 103 | end; | |
| 104 | 104 | ``` | |
| 105 | 105 | ||
| 106 | 106 | ## Using for-in instead of for | |
| 107 | 107 | ||
| 108 | 108 | When dealing with arrays, it's common for Pascal to write the code using "Integer" type array index like: | |
| 109 | 109 | ||
| 110 | 110 | ```delphi | |
| 111 | 111 | var | |
| 112 | 112 | i : integer; | |
| 113 | 113 | l : TList; | |
| 114 | 114 | ... | |
| 115 | 115 | for i:=0 to l.Count-1 do | |
| 116 | 116 | ... | |
| 117 | 117 | ``` | |
| 118 | 118 | ||
| 119 | 119 | Cocoa provides it's own implementation of arrays using NSArray class. Writing the the loop in the same manner: | |
| 120 | 120 | ||
| 121 | 121 | ```delphi | |
| 122 | 122 | var | |
| 123 | 123 | i : integer; | |
| 124 | 124 | l : NSArray; | |
| 125 | 125 | ... | |
| 126 | 126 | for i:=0 to l.count-1 do | |
| 127 | 127 | ... | |
| 128 | 128 | ``` | |
| 129 | 129 | ||
| 130 | 130 | is possible, though not desired. | |
| 131 | 131 | ||
| 132 | 132 | The reason for that, is that NSArray "count" is an NSUInteger type for Cocoa. This type has a different size on 32 and 64 platforms. (While "Integer" type is always 32 bit in FPC.) | |
| 133 | 133 | ||
| 134 | 134 | Thus while the code written above might work just fine on 32-platform, it might cause a range check error (if such check is enabled) on a 64 bit machine. For example, when count is zero. Here's a simplest (no ObjC) example of the problem: | |
| 135 | 135 | ||
| 136 | 136 | ```delphi | |
| 137 | 137 | {$R+} | |
| 138 | 138 | function Count: qword; | |
| 139 | 139 | begin | |
| 140 | 140 | Result := 0; | |
| 141 | 141 | end; | |
| 142 | 142 | var | |
| 143 | 143 | i: Integer; | |
| 144 | 144 | begin | |
| 145 | 145 | for i := 0 to Count-1 do // <- throws a range check error, because QWord result doesn't fit for Integer index | |
| 146 | 146 | WriteLn(i); | |
| 147 | 147 | end. | |
| 148 | 148 | ``` | |
| 149 | 149 | ||
| 150 | 150 | However, running a loop from elements of an array is quite a common task. For example - changing attributes of all children controls, due to some action. | |
| 151 | 151 | ||
| 152 | 152 | Instead of using a higher size Index variable (i.e. PtrInt or PtrUInt), it's handy to use for-in loop. Most common Objective C classes provide a special feature (NSFastEnumerator protocol) to do enumeration through its member. It works for the 32 bit and well as the 64 bit platform, in the same manner, but requires the Objective C 2.0 runtime version (with objectivec2 modeswitch enabled). | |
| 153 | 153 | ||
| 154 | 154 | An iteration for-in loop might look like this: | |
| 155 | 155 | ||
| 156 | 156 | ```delphi | |
| 157 | 157 | var | |
| 158 | 158 | obj : NSObject; | |
| 159 | 159 | arr : NSArray; | |
| 160 | 160 | ... | |
| 161 | 161 | for obj in arr do | |
| 162 | 162 | ... | |
| 163 | 163 | ``` | |
| 164 | 164 | ||
| 165 | 165 | ## Writing a Cocoa app without the LCL | |
| 166 | 166 | ||
| 167 | 167 | This is useful for testing hard to debug bugs in the LCL. Here is an example program written in Pascal-Cocoa which creates a window with 2 buttons and a simple set of menus: | |
| 168 | 168 | ||
| 169 | 169 | ```delphi | |
| 170 | 170 | program cocoa_without_lcl; | |
| 171 | 171 | ||
| 172 | 172 | {$mode objfpc}{$H+} | |
| 173 | 173 | {$modeswitch objectivec1} | |
| 174 | 174 | ||
| 175 | 175 | uses | |
| 176 | 176 | CocoaAll, classes, sysutils; | |
| 177 | 177 | ||
| 178 | 178 | type | |
| 179 | 179 | { TMyDelegate } | |
| 180 | 180 | TMyDelegate = objcclass(NSObject) | |
| 181 | 181 | public | |
| 182 | 182 | procedure HandleButtonClick_A(sender: id); message 'HandleButtonClick_A:'; | |
| 183 | 183 | procedure HandleButtonClick_B(sender: id); message 'HandleButtonClick_B:'; | |
| 184 | 184 | procedure HandleMenuClick_A(sender: id); message 'HandleMenuClick_A:'; | |
| 185 | 185 | procedure HandleMenuClick_B(sender: id); message 'HandleMenuClick_B:'; | |
| 186 | 186 | end; | |
| 187 | 187 | ||
| 188 | 188 | var | |
| 189 | 189 | appName: NSString; | |
| 190 | 190 | window: NSWindow; | |
| 191 | 191 | pool: NSAutoreleasePool; | |
| 192 | 192 | lText: NSTextField; | |
| 193 | 193 | lButton: NSButton; | |
| 194 | 194 | lDelegate: TMyDelegate; | |
| 195 | 195 | // | |
| 196 | 196 | MainMenu_A: NSMenu; | |
| 197 | 197 | TopItem_A: NSMenuItem; | |
| 198 | 198 | TopMenu_A: NSMenu; | |
| 199 | 199 | MenuItem_A: NSMenuItem; | |
| 200 | 200 | // | |
| 201 | 201 | MainMenu_B: NSMenu; | |
| 202 | 202 | TopItem_B: NSMenuItem; | |
| 203 | 203 | TopMenu_B: NSMenu; | |
| 204 | 204 | MenuItem_B: NSMenuItem; | |
| 205 | 205 | ||
| 206 | 206 | procedure TMyDelegate.HandleButtonClick_A(sender: id); | |
| 207 | 207 | begin | |
| 208 | 208 | NSApp.setMainMenu(MainMenu_A); | |
| 209 | 209 | end; | |
| 210 | 210 | ||
| 211 | 211 | procedure TMyDelegate.HandleButtonClick_B(sender: id); | |
| 212 | 212 | begin | |
| 213 | 213 | NSApp.setMainMenu(MainMenu_B); | |
| 214 | 214 | end; | |
| 215 | 215 | ||
| 216 | 216 | procedure TMyDelegate.HandleMenuClick_A(sender: id); | |
| 217 | 217 | var | |
| 218 | 218 | Str: string; | |
| 219 | 219 | begin | |
| 220 | 220 | Str := 'A '+inttostr(random(888)); | |
| 221 | 221 | window.setTitle(NSSTR(@Str[1])); | |
| 222 | 222 | end; | |
| 223 | 223 | ||
| 224 | 224 | procedure TMyDelegate.HandleMenuClick_B(sender: id); | |
| 225 | 225 | var | |
| 226 | 226 | Str: string; | |
| 227 | 227 | begin | |
| 228 | 228 | Str := 'B '+inttostr(random(888)); | |
| 229 | 229 | window.setTitle(NSSTR(@Str[1])); | |
| 230 | 230 | end; | |
| 231 | 231 | ||
| 232 | 232 | procedure CreateMenus; | |
| 233 | 233 | var | |
| 234 | 234 | nsTitle: NSString; | |
| 235 | 235 | nsKey : NSString; | |
| 236 | 236 | ||
| 237 | 237 | function CreateMenuItem(AName, AShortcut: string; ASelector: SEL): NSMenuItem; | |
| 238 | 238 | begin | |
| 239 | 239 | nsKey := NSSTR(PChar(AShortcut)); | |
| 240 | 240 | nsTitle := NSSTR(PChar(AName)); | |
| 241 | 241 | Result := NSMenuItem.alloc.initWithTitle_action_keyEquivalent(nsTitle, ASelector, nsKey); | |
| 242 | 242 | Result.setKeyEquivalentModifierMask(NSCommandKeyMask); | |
| 243 | 243 | nsTitle.release; | |
| 244 | 244 | nsKey.release; | |
| 245 | 245 | Result.setKeyEquivalentModifierMask(NSCommandKeyMask); | |
| 246 | 246 | Result.setTarget(lDelegate); | |
| 247 | 247 | end; | |
| 248 | 248 | ||
| 249 | 249 | begin | |
| 250 | 250 | MainMenu_A := NSMenu.alloc.initWithTitle(NSSTR('Menu A')); | |
| 251 | 251 | TopItem_A := CreateMenuItem('TopItem A', '', nil); | |
| 252 | 252 | MainMenu_A.insertItem_atIndex(TopItem_A, 0); | |
| 253 | 253 | TopMenu_A := NSMenu.alloc.initWithTitle(NSSTR('TopMenu A')); | |
| 254 | 254 | TopItem_A.setSubmenu(TopMenu_A); | |
| 255 | 255 | MenuItem_A := CreateMenuItem('MenuItem A', 'A', objcselector('HandleMenuClick_A:')); | |
| 256 | 256 | MenuItem_A.setTarget(lDelegate); | |
| 257 | 257 | TopMenu_A.insertItem_atIndex(MenuItem_A, 0); | |
| 258 | 258 | ||
| 259 | 259 | MainMenu_B := NSMenu.alloc.initWithTitle(NSSTR('Menu B')); | |
| 260 | 260 | TopItem_B := CreateMenuItem('TopItem B', '', nil); | |
| 261 | 261 | MainMenu_B.insertItem_atIndex(TopItem_B, 0); | |
| 262 | 262 | TopMenu_B := NSMenu.alloc.initWithTitle(NSSTR('TopMenu B')); | |
| 263 | 263 | TopItem_B.setSubmenu(TopMenu_B); | |
| 264 | 264 | MenuItem_B := CreateMenuItem('MenuItem B', 'B', objcselector('HandleMenuClick_B:')); | |
| 265 | 265 | MenuItem_B.setTarget(lDelegate); | |
| 266 | 266 | TopMenu_B.insertItem_atIndex(MenuItem_B, 0); | |
| 267 | 267 | end; | |
| 268 | 268 | ||
| 269 | 269 | begin | |
| 270 | 270 | // Autorelease pool, app and window creation | |
| 271 | 271 | pool := NSAutoreleasePool.new; | |
| 272 | 272 | NSApp := NSApplication.sharedApplication; | |
| 273 | 273 | NSApp.setActivationPolicy(NSApplicationActivationPolicyRegular); | |
| 274 | 274 | appName := NSProcessInfo.processInfo.processName; | |
| 275 | 275 | window := NSWindow.alloc.initWithContentRect_styleMask_backing_defer(NSMakeRect(0, 0, 200, 200), | |
| 276 | 276 | NSTitledWindowMask or NSClosableWindowMask or NSMiniaturizableWindowMask, | |
| 277 | 277 | NSBackingStoreBuffered, False).autorelease; | |
| 278 | 278 | lDelegate := TMyDelegate.alloc.init; | |
| 279 | 279 | ||
| 280 | 280 | // text label | |
| 281 | 281 | lText := NSTextField.alloc.initWithFrame(NSMakeRect(50, 50, 120, 50)).autorelease; | |
| 282 | 282 | lText.setBezeled(False); | |
| 283 | 283 | lText.setDrawsBackground(False); | |
| 284 | 284 | lText.setEditable(False); | |
| 285 | 285 | lText.setSelectable(False); | |
| 286 | 286 | lText.setStringValue(NSSTR('NSTextField')); | |
| 287 | 287 | window.contentView.addSubview(lText); | |
| 288 | 288 | ||
| 289 | 289 | // button | |
| 290 | 290 | lButton := NSButton.alloc.initWithFrame(NSMakeRect(50, 100, 120, 50)).autorelease; | |
| 291 | 291 | window.contentView.addSubview(lButton); | |
| 292 | 292 | lButton.setTitle(NSSTR('Button A!')); | |
| 293 | 293 | lButton.setButtonType(NSMomentaryLightButton); | |
| 294 | 294 | lButton.setBezelStyle(NSRoundedBezelStyle); | |
| 295 | 295 | // button event handler setting | |
| 296 | 296 | lButton.setTarget(lDelegate); | |
| 297 | 297 | lButton.setAction(ObjCSelector(lDelegate.HandleButtonClick_A)); | |
| 298 | 298 | ||
| 299 | 299 | // button B | |
| 300 | 300 | lButton := NSButton.alloc.initWithFrame(NSMakeRect(50, 150, 120, 50)).autorelease; | |
| 301 | 301 | window.contentView.addSubview(lButton); | |
| 302 | 302 | lButton.setTitle(NSSTR('Button B!')); | |
| 303 | 303 | lButton.setButtonType(NSMomentaryLightButton); | |
| 304 | 304 | lButton.setBezelStyle(NSRoundedBezelStyle); | |
| 305 | 305 | // button event handler setting | |
| 306 | 306 | lButton.setTarget(lDelegate); | |
| 307 | 307 | lButton.setAction(ObjCSelector(lDelegate.HandleButtonClick_B)); | |
| 308 | 308 | ||
| 309 | 309 | // Menus | |
| 310 | 310 | CreateMenus(); | |
| 311 | 311 | NSApp.setMainMenu(MainMenu_A); | |
| 312 | 312 | ||
| 313 | 313 | // Window showing and app running | |
| 314 | 314 | window.center; | |
| 315 | 315 | window.setTitle(appName); | |
| 316 | 316 | window.makeKeyAndOrderFront(nil); | |
| 317 | 317 | NSApp.activateIgnoringOtherApps(true); | |
| 318 | 318 | NSApp.run; | |
| 319 | 319 | end. | |
| 320 | 320 | ``` | |
| 321 | 321 | ||
| 322 | 322 | ## Units | |
| 323 | 323 | ||
| 324 | 324 | The units should use the ".pas" extension, not ".pp" for consistency. Use of "include" is not welcome and is kept there for legacy reasons only (not to lose the history, if a file is merged). | |
| 325 | 325 | ||
| 326 | 326 | **Unit** | |
| 327 | 327 | ||
| 328 | 328 | **Description** | |
| 329 | 329 | ||
| 330 | 330 | CocoaPrivate | |
| 331 | 331 | ||
| 332 | 332 | The unit contains most of the Cocoa classes overrides. The unit should stay LCL-classes free as much as possible (i.e. should not use Forms, StdCtrls, etc. Though using LCLTypes or LCLProc is ok). | |
| 333 | 333 | ||
| 334 | 334 | CocoaUtils | |
| 335 | 335 | ||
| 336 | 336 | The set of utility functions mostly to convert types (between LCL and Cocoa types) | |
| 337 | 337 | ||
| 338 | 338 | Input constant conversion utilities are also placed here. | |
| 339 | 339 | ||
| 340 | 340 | Cocoa_Extra | |
| 341 | 341 | ||
| 342 | 342 | The purpose of the unit is to full-fill missing FPC headers declarations for macOS. (whatever is missing at CocoaAll). | |
| 343 | 343 | ||
| 344 | 344 | Should not contain any additional functions, except for external and constant declarations. | |
| 345 | 345 | ||
| 346 | 346 | CocoaButtons | |
| 347 | 347 | ||
| 348 | 348 | Controls that are considered buttons (except for NSPopupButton) are declared in this unit. | |
| 349 | 349 | ||
| 350 | 350 | CocoaCarent | |
| 351 | 351 | ||
| 352 | 352 | The unit provides the code to implement caret for Cocoa. Cocoa itself doesn't have any "caret" specific API. | |
| 353 | 353 | ||
| 354 | 354 | CocoaDatepicker | |
| 355 | 355 | ||
| 356 | 356 | The unit contains the implementation of cocoa calendar control | |
| 357 | 357 | ||
| 358 | 358 | CocoaGDIObjects | |
| 359 | 359 | ||
| 360 | 360 | Highlever wrappers over Cocoa object to implement LCL GDI handles. | |
| 361 | 361 | ||
| 362 | 362 | CocoaScrollers | |
| 363 | 363 | ||
| 364 | 364 | All controls related to scrolling: TScrollBar and variants of ScrollView are implemented here | |
| 365 | 365 | ||
| 366 | 366 | CocoaTabControls | |
| 367 | 367 | ||
| 368 | 368 | Classes that are used to implement tabcontrol (and tabpage) are declared here | |
| 369 | 369 | ||
| 370 | 370 | CocoaTables | |
| 371 | 371 | ||
| 372 | 372 | The master table class is declared here. (used for TListView and TListBox). | |
| 373 | 373 | ||
| 374 | 374 | Supplemental classes for NSCell and NSView based tables are also in this unit | |
| 375 | 375 | ||
| 376 | 376 | CocoaTextEdits | |
| 377 | 377 | ||
| 378 | 378 | Text editting controls are implemented here. Including Combobox and NSpopupBox (readonly combobox) | |
| 379 | 379 | ||
| 380 | 380 | CocoaTheme | |
| 381 | 381 | ||
| 382 | 382 | Theme drawing class.(todo: get rid of "customdrawing" parts) | |
| 383 | 383 | ||
| 384 | 384 | CocoaWindows | |
| 385 | 385 | ||
| 386 | 386 | Classes related to the window and its contents are implemented here | |
| 387 | 387 | ||
| 388 | 388 | CocoaInt | |
| 389 | 389 | ||
| 390 | 390 | cocoawinapih.inc / cocoawinapi.inc | |
| 391 | 391 | ||
| 392 | 392 | cocoalclapih.inc /cocoalclapi.inc | |
| 393 | 393 | ||
| 394 | 394 | The Widgetset class API (winApi-style) are implemented here. | |
| 395 | 395 | ||
| 396 | 396 | **TCocoaApplication** class is also declared and implemented in this unit. Both Widgetset object and code of TCocoaApplication class are mutually using each other. | |
| 397 | 397 | ||
| 398 | 398 | Widgetset interfacing units | |
| 399 | 399 | ||
| 400 | 400 | CocoaWSCommon | |
| 401 | 401 | ||
| 402 | 402 | The unit contains common bindings between native Cocoa (obcjclasses implemented in CocoaPrivate) and LCL. The actual callbacks interfaces are implemented here. Most of the "messaging" to LCL is also implemented here | |
| 403 | 403 | ||
| 404 | 404 | ## Callback Objects | |
| 405 | 405 | ||
| 406 | 406 | - **ICommonCallback** is the primary object for Cocoa objects to report back actions taken. It prevents "the handlers" from dealing with Objective-C APIs over all. | |
| 407 | 407 | - Some controls might introduce an additional interface to process more events. They all should inherit from ICommonCallback | |
| 408 | 408 | - For each HANDLE created there should be exactly one CallBack object allocated, because | |
| 409 | 409 | - Callback object is released on Destruction of the HANDLE (from LCL perspective). (Even though the actual ObjC object can be released later.) | |
| 410 | 410 | ||
| 411 | 411 | ## Why so Boolean? | |
| 412 | 412 | ||
| 413 | 413 | Cocoa Widgetset introduces two ObjC-specific boolean types: | |
| 414 | 414 | ||
| 415 | 415 | - LCLObjCBoolean - declared at CocoaPrivate and is commonly used for any Cocoa overridden methods | |
| 416 | 416 | - ObjCBool - declared at Cocoa_Extra and is used under BOOL8FIX. | |
| 417 | 417 | ||
| 418 | 418 | Both types were introduced to handle the issues with the Boolean type parameter. (Resulting Boolean value seems to be working as expected on x86_64). | |
| 419 | 419 | ||
| 420 | 420 | However, the intent of each type is different: | |
| 421 | 421 | ||
| 422 | 422 | - LCLObjCBoolean - is to compensate for FPC backwards incompatible change (targeted for 3.2.0 release) that's changing all "Boolean" type parameters to "Boolean8". Thus any overridden methods in LCL would stop compile. (Boolean8 is not an alias for Boolean.) Unfortunatelly the type is expected to stay likely forever. | |
| 423 | 423 | - ObjCBool - is a need to resolve Boolean parameter problem with 3.0.4. For any call to boolean parameter method, additional methods were introduced to pass the parameter as byte parameter (**unsigned char**), and pass the actual value property. The type can be dropped as soon as LCL support for 3.0.4 is dropped. The type should not be used in any 3rd party components. | |
| 424 | 424 | ||
| 425 | 425 | ## See also | |
| 426 | 426 | ||
| 427 | 427 | - [[Cocoa Interface]] - general information regarding the Cocoa Interface. | |
| 428 | 428 | - [[Cocoa Internals/Application]] - details about use of NSApplication and clipboard. | |
| 429 | 429 | - [[Cocoa Internals/Buttons]] - provides the information about internal implementation of Button controls. | |
| 430 | 430 | - [[Cocoa Internals/Dialogs]] - implementation of common dialogs | |
| 431 | 431 | - [[Cocoa Internals/Extensions]] - additional method introduced to ObjC classes, to prevent code duplication and if-conditions. | |
| 432 | 432 | - [[Cocoa Internals/Forms]] - provides the information about internal implementation of Forms (Windows) | |
| 433 | 433 | - [[Cocoa Internals/Graphics]] - graphics, fonts related topics. | |
| 434 | 434 | - [[Cocoa Internals/Input]] - covers topics of Cocoa specific input processing implementation. Cocoa input processing is somewhat foreign to LCL (or Windows style) processing. | |
| 435 | 435 | - [[Cocoa Internals/Memory Management]] - guidelines of memory management tactics, whenever writing/understanding Cocoa code. (provides a lot of general Cocoa information (also applicable for UIKit)) | |
| 436 | 436 | - [[Cocoa Internals/Menu]] - macOS menu bar | |
| 437 | 437 | - [[Cocoa Internals/OS Versions]] - suggestions around macOS API deprecations. | |
| 438 | 438 | - [[Cocoa Internals/Scroll]] - Scroll controls. | |
| 439 | 439 | - [[Cocoa Internals/Text Controls]] - Cocoa text controls. | |
| 440 | 440 | - [[Cocoa Internals/Theme]] - Cocoa Themes. | |
| 441 | 441 | - [[Cocoa Internals/Widgetset]] - information about Handle implementation and focus change. | |
| 442 | 442 | ||
| 443 | 443 | --- | |
| 444 | 444 | *From the Free Pascal wiki, page "Cocoa Internals", as it stood on 2022-01-15 after a change by Skalogryz, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 445 | 445 | Category: Cocoa | |
| 446 | 446 | Category: Lazarus internals | |
| 447 | 447 | Category: macOS |
