Difference between revisions of "Cocoa Internals/Widgetset"

From Lazarus wiki
Jump to navigationJump to search
(One intermediate revision by the same user not shown)
Line 17: Line 17:
 
HandleView are stored within Callback object that's being created for each CreateHandle() method.
 
HandleView are stored within Callback object that's being created for each CreateHandle() method.
  
===Focusing===
+
==Focusing==
 
That has important impact of FOCUS-ing. Cocoa doesn't operate the term "focused" instead it's using terms "firstRepsonder" and "key" view/window. Generally focused control corresponds to Cocoa's "firstResponder". The NSResponder object (typically NSView) is the first object that would handle key or mouse events.  
 
That has important impact of FOCUS-ing. Cocoa doesn't operate the term "focused" instead it's using terms "firstRepsonder" and "key" view/window. Generally focused control corresponds to Cocoa's "firstResponder". The NSResponder object (typically NSView) is the first object that would handle key or mouse events.  
 
However, this is not the case for composed controls. TMemo is a combination of NSScrollView and NSTextField. NSScrollView is used as a handle. If, NSTextField would be the firstRepsonder, CocoaWS needs to return NSScrollView as focused HANDLE.  
 
However, this is not the case for composed controls. TMemo is a combination of NSScrollView and NSTextField. NSScrollView is used as a handle. If, NSTextField would be the firstRepsonder, CocoaWS needs to return NSScrollView as focused HANDLE.  
Line 37: Line 37:
 
This is important for complex controls, such as a TabControl. It manages multiple child views (each view for a tab). Only one view is visible at a time. All over views are actually DETACHED from NSWindow hierarchy. (while from LCL perspective they are still children of TForm).
 
This is important for complex controls, such as a TabControl. It manages multiple child views (each view for a tab). Only one view is visible at a time. All over views are actually DETACHED from NSWindow hierarchy. (while from LCL perspective they are still children of TForm).
 
When switching between tabs a notification about a successful switch should only be delivered when a new view is in the Window hierarchy.  
 
When switching between tabs a notification about a successful switch should only be delivered when a new view is in the Window hierarchy.  
 +
==Cursors==
 +
Widgetset cursors are based on '''NSCursor''' class. Which very nice and convenient to use.
 +
Cocoa however, doesn't provide some of the cursors that do exist in windows/LCL. For example: diagonal resize. Diagonal cursors are generated by rotation of the horizontal cursors.
  
 +
==See Also==
 +
* [[Cocoa Internals]]
 
[[Category:Cocoa]]
 
[[Category:Cocoa]]

Revision as of 00:10, 19 June 2019

Handles

THANDLE - an opaque reference value that's being generated during .CreateHandle() call of a particular Widgetset class. THANDLE is the value that's used to interface with a control. Using the value a widgetset recognizes a particular control.

Most of Widget interface methods are accepting TWinControl as a parameter. It's assumed that Handle property of the control would be used to back reference the actual control. The property stores the value received from CreateHandle() call made earlier. HandleAllocated property can be checked in order to know, if handle has been previously created or not. If handle was not created it's expected that the method can fail (returning the proper failure information. Exceptions are not expected?).

Overall LCL Handles refer to a complex controls. For example HANDLE can refer to a Memo control, that's capable of editing AND scrolling text as needed.

Cocoa's composite engine operates controls on a lower level scope. With MEMO example, there's not a single control that does both: text editing and scrolling.

Instead there are two separate controls: NSTextView and NSScrollView. (Both are descendant of NSView). But LCL expects only 1 value to be returned as a handle.

The common rule is the following: the outter bounds (aka container) control is returned as the handle. Thus in case of Memo, two NSViews are allocated: NSTextView and NSScrollView. But the outter NSScrollView is returned as HANDLE. All TCustomMemoWSControl methods are aware of that and treat handle as such. For example:

  • if paste action is required, the handle would be used as NSScrollView and then it's documentview would be used to get NSTextView to call for paste.
  • if bounds changes are required (moving, resizing) then the sizes are changed directly for the NSView referenced by HANDLE. (It's assumed that the view would automatically resize its controls. In case of TMemo, NSScrollView would adjust size of NSTextView, if wordwrapping is off).
  • Forms (TForm), that generally correspond to NSWindow, also return its handle as NSView. That view represents the root NSView of a form. However, CocoaWS API's know that and are calling necessary NSWindow methods where applicable.

HandleView are stored within Callback object that's being created for each CreateHandle() method.

Focusing

That has important impact of FOCUS-ing. Cocoa doesn't operate the term "focused" instead it's using terms "firstRepsonder" and "key" view/window. Generally focused control corresponds to Cocoa's "firstResponder". The NSResponder object (typically NSView) is the first object that would handle key or mouse events. However, this is not the case for composed controls. TMemo is a combination of NSScrollView and NSTextField. NSScrollView is used as a handle. If, NSTextField would be the firstRepsonder, CocoaWS needs to return NSScrollView as focused HANDLE.

The same approach is applicable in reverse. If SetFocus is made for the HANDLE that represents NSScrollView with NSTextField inside. NSTextField should actually become Cocoa's firstResponder.

The expected order of events is:

  • LM_KILLFOCUS
  • LM_SETFOCUS

Note that unlike WinAPI WM_KILLFOCUS, LM_KILLFOCUS doesn't provide the information about "the next suggested" control to be focused. For controls that have some sort of editors (i.e. TTreeView with a node editor (TEdit) active) such approach is causing to switch focus to TForm (the hosting form), for the next focus control selection.

The focus switch notification is handled in TCocoaWindow makeFirstResponder call. Due to problems with the recursive calls of changing the focus, the current order of events is:

  • LM_SETFOCUS
  • LM_KILLFOCUS

(for Cocoa WS)

Need of Window

The "firstResponder" is a property of a window. Thus a view can become a "firstResponder" only if it's within NSWindow's view hierarchy. This is important for complex controls, such as a TabControl. It manages multiple child views (each view for a tab). Only one view is visible at a time. All over views are actually DETACHED from NSWindow hierarchy. (while from LCL perspective they are still children of TForm). When switching between tabs a notification about a successful switch should only be delivered when a new view is in the Window hierarchy.

Cursors

Widgetset cursors are based on NSCursor class. Which very nice and convenient to use. Cocoa however, doesn't provide some of the cursors that do exist in windows/LCL. For example: diagonal resize. Diagonal cursors are generated by rotation of the horizontal cursors.

See Also