Cocoa Internals/Application

From Free Pascal wiki
Jump to: navigation, search

Cocoa's Application API pretty much matches what LCL provides. It could be possible to simply call NSApp apis, that correspond to LCL APIs. However, there's a major difference, that makes a simple solution not possible. LCL Application provides a methods HandleMessages and ProcessMessagess allowing a manual control over the event-loop.

NSApp doesn't provide any corresponding counter parts, thus it's necessary to process events loops manually.

Manual Event Loop

Cocoa Widgetset implements even loops manually. Apple documentation advises that it's possible, though could be complicated.

The actual event processing code could be found at AppWaitMessage and AppProcessMessage.

procedure TCocoaWidgetSet.AppWaitMessage;
var
  event : NSEvent;
  pool:NSAutoReleasePool;
begin
  pool := NSAutoreleasePool.alloc.init;
  event := NSApp.nextEventMatchingMask_untilDate_inMode_dequeue(NSAnyEventMask, NSDate.distantFuture, NSDefaultRunLoopMode, true);
  if event <> nil then
  begin
    if (event.type_ = NSApplicationDefined) and (event.subtype = LCLEventSubTypeMessage) and (event.data1 = LM_NULL) and (event.data2 = AppHandle) then
      CheckSynchronize
    else
      NSApp.sendEvent(event);
    NSApp.updateWindows;
  end;
  pool.release;
end;

Sub-classing NSApplication

With a manual event loop implementation, there's another problem showed up.

NSWindows are behaving differently depending if NSApplication has started or not. (running property returning TRUE or FALSE). The Apple documentation doesn't mention that specifically, except for the following note:

Important
Many AppKit classes rely on the NSApplication class and may not work properly until this class is fully initialized.
As a result, you should not, for example, attempt to invoke methods of other AppKit classes from an initialization method of an NSApplication subclass.

If NSApplication is not running, then NSWindows would not pass the focus to another Window with the application. Instead they would keep the focus (if hidden) or make no window a focused window (if closed). (see #32177)

There are two options to make NSApplication running flag to set to true:

  • use NSApp default run method (which could not be used, due to the need of manual event processing)
  • subclass NSApplication, override "isRunning" (getter of "running" property)

The sub-classing option is used, introducing TCocoaApplication class. It overrides two methods:

  • run - the code is actually LCL Loop procedure, which would process ObjC events loop manually
  • isRunning - returns true, if run method was actually called.

See Also