Cocoa Internals/Input

From Lazarus wiki
Revision as of 06:05, 29 September 2018 by Skalogryz (talk | contribs) (→‎See Also)
Jump to navigationJump to search

Mouse

The biggest difference between Apple and LCL ideology is the way of handling MouseMove events.

According to Apple - mouse events are easily to flood the event queue and thus are disabled by default, unless a use presses and hold the button.

Apple provides a special NSWindow mode to allow MouseMove events to come through even with buttons unreleased. However, such MouseMove events would be reported to the focused controls (aka FirstResponder), rather than the control immediately under the cursor.

(MouseDown, MouseUp events are reported directly to the control under the cursor, similar to LCL).

Because of that, Widgetset mouse event processing routine first checks, if cursor is actually above the focused control, and if it's not, it would let Cocoa propagate the mouse event, down (from child to parent) the controls hierarchy.

Such propagation happens, when NSView inherited mouseMove event is called.

If the actual control is found, the propagation of the event is stopped.

Implemenation

The whole set of methods should be overridden for each sub-classed Objective-C control

   function acceptsFirstMouse(event: NSEvent): Boolean; override;
   procedure mouseDown(event: NSEvent); override;
   procedure mouseUp(event: NSEvent); override;
   procedure rightMouseDown(event: NSEvent); override;
   procedure rightMouseUp(event: NSEvent); override;
   procedure rightMouseDragged(event: NSEvent); override;
   procedure otherMouseDown(event: NSEvent); override;
   procedure otherMouseUp(event: NSEvent); override;
   procedure otherMouseDragged(event: NSEvent); override;
   procedure mouseDragged(event: NSEvent); override;
   procedure mouseMoved(event: NSEvent); override;
   procedure scrollWheel(event: NSEvent); override;

Even if a target LCL control doesn't publish the events (i.e. TScrollBar doesn't have any mouse events published), all those methods should still be overriden. The purpose is to give LCL control over that. For example, if a control is used at the design time, ALL of cocoa default handling should be blocked.

Typical implementation for mouseDown/mouseUp events (including "right" and "other" versions) should look like this:

procedure TCocoaControl.mouseDown(event: NSEvent);
begin
  if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
    inherited mouseDown(event);
end;

procedure TCocoaControl.mouseUp(event: NSEvent);
begin
  if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
    inherited mouseUp(event);
end;

Such implementation notifies LCL first (via callback) about a mouse event. If LCL doesn't block it (by returning "true" from MouseUpdownEvent) the event is passed further to Cocoa. Actually there's no harm in non-blocking mouseUp and always inheriting it.

However, for some controls, what either implementing drag and drop functionality or some sort of dragging action (i.e. ScrollBar) requires mouseDown to be implemented in the following manner:

procedure TCocoaScrollBar.mouseDown(event: NSEvent);
begin
  if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
  begin
    inherited mouseDown(event);
 
    callback.MouseUpDownEvent(event, true); // forced mouse-up event
  end;
end;

The reason for that is within Cocoa (and Apple in genernal) implementation of mouse-hold button loops. Once inherited mouseDown is called, the procedure would not return until the mouse button is released or the action is cancelled. It's quite typical to do an event processing loop, until mouse-up event received .

The issue is that the actual mouseUp() selector would never be called, and must be emulated via callback.MouseUpDownEvent(, true) call.

The implementation of mouseUp method should still remain.

Also, such approach doesn't apply to rightMouseDown otherMouseDown. Apple doesn't make any event loops for those.

Special Keys

Some controls wants special keys, such as Tabs and Arrows to handle. (for example TMemo with WantsTab set to true, or TTrackBar to change thumb position).

Both tabs and arrows are keys used for switch focus within the application itself.

Thus, it's critical for a control to let LCL know, that they would handle those keys and focus would not be switched.

For this purpose an additional method has been introduced:

lclExpectedKeys:::

the method accepts variable 3 parameters:

  • wantTabs (default value false), should be set to true, if control expects to process tab key
  • wantArrows (default value false), should be set to true, if control expects to process arrow keys (all of them!)
  • wantAllKeys (default value true?)... not sure how it's being used.

See Also