1 { $Id: $}
2 {                  --------------------------------------------
3                   cocoaprivate.pp  -  Cocoa internal classes
4                   --------------------------------------------
5 
6  This unit contains the private classhierarchy for the Cocoa implemetations
7 
8  *****************************************************************************
9   This file is part of the Lazarus Component Library (LCL)
10 
11   See the file COPYING.modifiedLGPL.txt, included in this distribution,
12   for details about the license.
13  *****************************************************************************
14 }
15 unit CocoaPrivate;
16 
17 {$mode objfpc}{$H+}
18 {$modeswitch objectivec1}
19 {$modeswitch objectivec2}
20 {$interfaces corba}
21 {$include cocoadefines.inc}
22 
23 {.$DEFINE COCOA_DEBUG_SETBOUNDS}
24 {.$DEFINE COCOA_SPIN_DEBUG}
25 {.$DEFINE COCOA_SPINEDIT_INSIDE_CONTAINER}
26 {.$DEFINE COCOA_SUPERVIEW_HEIGHT}
27 
28 interface
29 
30 uses
31   // rtl+ftl
32   Types, Classes, SysUtils,
33   CGGeometry,
34   // Libs
35   MacOSAll, CocoaAll, CocoaUtils, CocoaGDIObjects,
36   cocoa_extra,
37   // LCL
38   LCLType;
39 
40 const
41   SPINEDIT_DEFAULT_STEPPER_WIDTH = 15;
42   SPINEDIT_EDIT_SPACING_FOR_SELECTION = 4;
43   STATUSBAR_DEFAULT_HEIGHT = 18;
44 
45 type
46   // Some components might be using CocoaPrivate for use of LCLObjCBoolean
47   // Thus this declaration needs to be here.
48   LCLObjCBoolean = cocoa_extra.LCLObjCBoolean;
49 
50   { ICommonCallback }
51 
52   ICommonCallback = interface
53     // mouse events
MouseUpDownEventnull54     function MouseUpDownEvent(Event: NSEvent; AForceAsMouseUp: Boolean = False; AOverrideBlock: Boolean = False): Boolean;
55     procedure MouseClick;
MouseMovenull56     function MouseMove(Event: NSEvent): Boolean;
57 
58     // KeyEvXXX methods were introduced to allow a better control
59     // over when Cocoa keys processing is being called.
60     // (The initial KeyEvent() replicates Carbon implementation, and it's not
61     // suitable for Cocoa, due to the use of OOP and the extual "inherited Key..."needs to be called
62     // where for Carbon there's a special fucntion to call the "next event handler" present)
63     //
64     // The desired use is as following:
65     // Call KeyEvPrepare and pass NSEvent object
66     // after that call KeyEvBefore and pass a flag if AllowCocoaHandle
67     //
68     // The call would populate the flag. If it's "True" you should call "inherited" method (to let Cocoa handle the key).
69     // If the flag returned "False", you should not call inherited.
70     //
71     // No matter what the flag value was you should call KeyEvAfter.
72     procedure KeyEvBefore(Event: NSEvent; out AllowCocoaHandle: boolean);
73     procedure KeyEvAfter;
74     procedure KeyEvAfterDown(out AllowCocoaHandle: boolean);
75     procedure KeyEvHandled;
76     procedure SetTabSuppress(ASuppress: Boolean);
77 
78     function scrollWheel(Event: NSEvent): Boolean;
79     function CanFocus: Boolean;
80     // size, pos events
81     procedure frameDidChange(sender: id);
82     procedure boundsDidChange(sender: id);
83     // misc events
84     procedure Draw(ctx: NSGraphicsContext; const bounds, dirty: NSRect);
85     procedure DrawBackground(ctx: NSGraphicsContext; const bounds, dirty: NSRect);
86     procedure DrawOverlay(ctx: NSGraphicsContext; const bounds, dirty: NSRect);
87     function ResetCursorRects: Boolean;
88     procedure BecomeFirstResponder;
89     procedure ResignFirstResponder;
90     procedure DidBecomeKeyNotification;
91     procedure DidResignKeyNotification;
92     procedure SendOnChange;
93     procedure SendOnTextChanged;
94     procedure scroll(isVert: Boolean; Pos: Integer; AScrollPart: NSScrollerPart = NSScrollerNoPart);
95     // non event methods
96     function DeliverMessage(Msg: Cardinal; WParam: WParam; LParam: LParam): LResult;
97     function GetPropStorage: TStringList;
98     function GetContext: TCocoaContext;
99     function GetTarget: TObject;
100     function GetHasCaret: Boolean;
101     function GetCallbackObject: TObject;
102     procedure SetHasCaret(AValue: Boolean);
103     function GetIsOpaque: Boolean;
104     procedure SetIsOpaque(AValue: Boolean);
105     function GetShouldBeEnabled: Boolean;
106     // the method is called, when handle is being destroyed.
107     // the callback object to stay alive a little longer than LCL object (Target)
108     // thus it needs to know that LCL object has been destroyed.
109     // After this called has been removed, any Cocoa events should not be
110     // forwarded to LCL target
111     procedure RemoveTarget;
112 
113     // properties
114     property HasCaret: Boolean read GetHasCaret write SetHasCaret;
115     property IsOpaque: Boolean read GetIsOpaque write SetIsOpaque;
116   end;
117 
118   { LCLObjectExtension }
119 
120   LCLObjectExtension = objccategory(NSObject)
121     function lclIsEnabled: Boolean; message 'lclIsEnabled';
122     procedure lclSetEnabled(AEnabled: Boolean); message 'lclSetEnabled:';
123     function lclIsVisible: Boolean; message 'lclIsVisible';
124     procedure lclSetVisible(AVisible: Boolean); message 'lclSetVisible:';
125     function lclWindowState: Integer; message 'lclWindowState';
126 
127     procedure lclInvalidateRect(const r: TRect); message 'lclInvalidateRect:';
128     procedure lclInvalidate; message 'lclInvalidate';
129     procedure lclUpdate; message 'lclUpdate';
130 
131     // Returns the position of the view or window, in the immediate
132     // parent (view or screen), relative to its client coordinates system
133     // Left and Top are always returned in LCL coordinate system.
134     procedure lclRelativePos(var Left, Top: Integer); message 'lclRelativePos::';
135     procedure lclLocalToScreen(var X, Y: Integer); message 'lclLocalToScreen::';
136     procedure lclScreenToLocal(var X, Y: Integer); message 'lclScreenToLocal::';
137     function lclParent: id; message 'lclParent';
138     function lclFrame: TRect; message 'lclFrame';
139     procedure lclSetFrame(const r: TRect); message 'lclSetFrame:';
140 
141     // returns rectangle describing deltas to get "Layout" rectangle from "Frame" rectangle
142     //   left, top  - return offsets from top-left corner of the control (not reversed as in Cocoa coordinates)
143     //                    (values are typically positive)
144     //   right, bottom -  offsets for bottom-right corner
145     //                    (typically negative)
146     function lclGetFrameToLayoutDelta: TRect; message 'lclGetFrameToLayoutDelta';
147 
148     function lclClientFrame: TRect; message 'lclClientFrame';
149     function lclGetCallback: ICommonCallback; message 'lclGetCallback';
150     procedure lclClearCallback; message 'lclClearCallback';
151     function lclGetPropStorage: TStringList; message 'lclGetPropStorage';
152     function lclGetTarget: TObject; message 'lclGetTarget';
153     function lclDeliverMessage(Msg: Cardinal; WParam: WParam; LParam: LParam): LResult; message 'lclDeliverMessage:::';
154     function lclContentView: NSView; message 'lclContentView';
155     procedure lclOffsetMousePos(var Point: NSPoint); message 'lclOffsetMousePos:';
156     procedure lclExpectedKeys(var wantTabs, wantArrows, wantReturn, wantAll: Boolean); message 'lclExpectedKeys::::';
157     function lclIsMouseInAuxArea(Event: NSEvent): Boolean; message 'lclMouseInAuxArea:';
158   end;
159 
160   { LCLViewExtension }
161 
162   LCLViewExtension = objccategory(NSView)
163     function lclInitWithCreateParams(const AParams: TCreateParams): id; message 'lclInitWithCreateParams:';
164     function lclIsEnabled: Boolean; message 'lclIsEnabled'; reintroduce;
165     procedure lclSetEnabled(AEnabled: Boolean); message 'lclSetEnabled:'; reintroduce;
166 
167     function lclIsVisible: Boolean; message 'lclIsVisible'; reintroduce;
168     procedure lclSetVisible(AVisible: Boolean); message 'lclSetVisible:'; reintroduce;
169     function lclIsPainting: Boolean; message 'lclIsPainting';
170     procedure lclInvalidateRect(const r: TRect); message 'lclInvalidateRect:'; reintroduce;
171     procedure lclInvalidate; message 'lclInvalidate'; reintroduce;
172     procedure lclUpdate; message 'lclUpdate'; reintroduce;
173     procedure lclRelativePos(var Left, Top: Integer); message 'lclRelativePos::'; reintroduce;
174     procedure lclLocalToScreen(var X, Y: Integer); message 'lclLocalToScreen::'; reintroduce;
175     procedure lclScreenToLocal(var X, Y: Integer); message 'lclScreenToLocal::'; reintroduce;
176     function lclParent: id; message 'lclParent'; reintroduce;
177     function lclFrame: TRect; message 'lclFrame'; reintroduce;
178     procedure lclSetFrame(const r: TRect); message 'lclSetFrame:'; reintroduce;
179     function lclClientFrame: TRect; message 'lclClientFrame'; reintroduce;
180     function lclContentView: NSView; message 'lclContentView'; reintroduce;
181     procedure lclOffsetMousePos(var Point: NSPoint); message 'lclOffsetMousePos:'; reintroduce;
182   end;
183 
184   { LCLControlExtension }
185 
186   LCLControlExtension = objccategory(NSControl)
187     function lclIsEnabled: Boolean; message 'lclIsEnabled'; reintroduce;
188     procedure lclSetEnabled(AEnabled: Boolean); message 'lclSetEnabled:'; reintroduce;
189   end;
190 
191   { TCocoaCustomControl }
192 
193   TCocoaCustomControl = objcclass(NSControl)
194   private
195     fstr : NSString;
196 
197     isdrawing   : integer;
198     faileddraw  : Boolean;
199   public
200     callback: ICommonCallback;
201     auxMouseByParent: Boolean;
202     procedure dealloc; override;
203     function acceptsFirstResponder: LCLObjCBoolean; override;
204     procedure drawRect(dirtyRect: NSRect); override;
205     function lclGetCallback: ICommonCallback; override;
206     procedure lclClearCallback; override;
207     function lclIsMouseInAuxArea(Event: NSevent): Boolean; override;
208     // mouse
209     function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
210     procedure mouseDown(event: NSEvent); override;
211     procedure mouseUp(event: NSEvent); override;
212     procedure rightMouseDown(event: NSEvent); override;
213     procedure rightMouseUp(event: NSEvent); override;
214     procedure rightMouseDragged(event: NSEvent); override;
215     procedure otherMouseDown(event: NSEvent); override;
216     procedure otherMouseUp(event: NSEvent); override;
217     procedure otherMouseDragged(event: NSEvent); override;
218     procedure mouseDragged(event: NSEvent); override;
219     procedure mouseEntered(event: NSEvent); override;
220     procedure mouseExited(event: NSEvent); override;
221     procedure mouseMoved(event: NSEvent); override;
222     procedure scrollWheel(event: NSEvent); override;
223     // nsview
224     procedure setFrame(aframe: NSRect); override;
225     // other
226     procedure resetCursorRects; override;
227     // value
228     procedure setStringValue(avalue: NSString); override;
229     function stringValue: NSString; override;
230     procedure addSubView(aview: NSView); override;
231   end;
232 
233   TStatusItemData = record
234     Text  : NSString;
235     Width : Integer;
236     Align : TAlignment;
237   end;
238 
239   TStatusItemDataArray = array of TStatusItemData;
240 
241   { TCocoaStatusBar }
242 
243   IStatusBarCallback = interface {(ICommonCallback) // not needed to inherit from ICommonCallback}
244     function GetBarsCount: Integer;
245     //todo: consider the use Cocoa native types, instead of FPC TAlignment
246     function GetBarItem(idx: Integer; var txt: String;
247       var width: Integer; var align: TAlignment): Boolean;
248     procedure DrawPanel(idx: Integer; const r: TRect);
249   end;
250 
251   TCocoaStatusBar = objcclass(TCocoaCustomControl)
252   public
253     //StatusBar : TStatusBar;
254     barcallback : IStatusBarCallback;
255     panelCell   : NSCell;
256     procedure drawRect(dirtyRect: NSRect); override;
257     procedure dealloc; override;
258   end;
259 
260   { TCocoaGroupBox }
261 
262   TCocoaGroupBox = objcclass(NSBox)
263   public
264     callback: ICommonCallback;
265     function acceptsFirstResponder: LCLObjCBoolean; override;
266     function lclGetCallback: ICommonCallback; override;
267     procedure lclClearCallback; override;
268     procedure resetCursorRects; override;
269     function lclClientFrame: TRect; override;
270     function lclContentView: NSView; override;
271     function lclGetFrameToLayoutDelta: TRect; override;
272   end;
273 
274 
275 const
276   PROGRESS_REG_HEIGHT   = 16; // no longer applies on later macOS version
277   PROGRESS_SMALL_HEIGHT = 10;
278 
279 type
280   { TCocoaProgressIndicator }
281 
282   TCocoaProgressIndicator = objcclass(NSProgressIndicator)
283     callback: ICommonCallback;
284     function acceptsFirstResponder: LCLObjCBoolean; override;
285     function lclGetCallback: ICommonCallback; override;
286     procedure lclClearCallback; override;
287     procedure resetCursorRects; override;
288     function lclGetFrameToLayoutDelta: TRect; override;
289     procedure lclSetFrame(const r: TRect); override;
290     // mouse
291     function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
292     procedure mouseDown(event: NSEvent); override;
293     procedure mouseUp(event: NSEvent); override;
294     procedure rightMouseDown(event: NSEvent); override;
295     procedure rightMouseUp(event: NSEvent); override;
296     procedure rightMouseDragged(event: NSEvent); override;
297     procedure otherMouseDown(event: NSEvent); override;
298     procedure otherMouseUp(event: NSEvent); override;
299     procedure otherMouseDragged(event: NSEvent); override;
300     procedure mouseDragged(event: NSEvent); override;
301     procedure mouseMoved(event: NSEvent); override;
302     procedure scrollWheel(event: NSEvent); override;
303   end;
304 
305   { TManualTicks }
306 
307   TManualTicks = class(TObject)
308     count : integer;
309     //todo: keep sorted and do binary search
310     ticks : array of Integer;
311     draw  : Boolean;
312     function AddTick(atick: integer): Boolean;
313   end;
314 
315   { TCocoaSlider }
316 
317   TCocoaSlider = objcclass(NSSlider)
318     callback  : ICommonCallback;
319     intval    : Integer;
320     man       : TManualTicks;
321 
322     procedure dealloc; override;
323     procedure drawRect(dirtyRect: NSRect); override;
324 
325     function acceptsFirstResponder: LCLObjCBoolean; override;
326     function lclGetCallback: ICommonCallback; override;
327     procedure lclClearCallback; override;
328     procedure resetCursorRects; override;
329     //
330     procedure keyDown(event: NSEvent); override;
331     //
332     procedure SnapToInteger(AExtraFactor: Integer = 0); message 'SnapToInteger:';
333     procedure sliderAction(sender: id); message 'sliderAction:';
334     // mouse
335     function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
336     procedure mouseDown(event: NSEvent); override;
337     procedure mouseUp(event: NSEvent); override;
338     procedure rightMouseDown(event: NSEvent); override;
339     procedure rightMouseUp(event: NSEvent); override;
340     procedure rightMouseDragged(event: NSEvent); override;
341     procedure otherMouseDown(event: NSEvent); override;
342     procedure otherMouseUp(event: NSEvent); override;
343     procedure otherMouseDragged(event: NSEvent); override;
344     procedure mouseDragged(event: NSEvent); override;
345     procedure mouseMoved(event: NSEvent); override;
346     procedure scrollWheel(event: NSEvent); override;
347 
348     procedure lclAddManTick(atick : integer); message 'lclAddManTick:';
349     procedure lclSetManTickDraw(adraw: Boolean); message 'lclSetManTickDraw:';
350     procedure lclExpectedKeys(var wantTabs, wantArrows, wantReturn, wantAll: Boolean); override;
351   end;
352 
353   TCocoaSliderCell = objcclass(NSSliderCell)
354   end;
355 
356 procedure SetViewDefaults(AView: NSView);
357 function CheckMainThread: Boolean;
358 function GetNSViewSuperViewHeight(view: NSView): CGFloat;
359 
360 procedure SetNSControlSize(ctrl: NSView; newHeight, miniHeight, smallHeight: Integer; AutoChangeFont: Boolean);
361 
362 // these constants are missing from CocoaAll for some reason
363 const
364   NSTextAlignmentLeft      = 0;
365   NSTextAlignmentRight     = 1; // it's 2 for iOS and family
366   NSTextAlignmentCenter    = 2; // it's 1 for iOS and family
367   NSTextAlignmentJustified = 3;
368   NSTextAlignmentNatural   = 4;
369 
370 var
371   // todo: this should be a threadvar
372   TrackedControl : NSObject = nil;
373 
374 function isCallbackForSameObject(cb1, cb2: ICommonCallback): Boolean;
375 
376 function NSViewIsLCLEnabled(v: NSView): Boolean;
377 function NSObjectIsLCLEnabled(obj: NSObject): Boolean;
378 function NSViewCanFocus(v: NSView): Boolean;
379 
380 implementation
381 
382 function NSObjectIsLCLEnabled(obj: NSObject): Boolean;
383 begin
384   if obj.isKindOfClass(NSView) then
385     Result := NSViewIsLCLEnabled(NSView(obj))
386   else
387     Result := obj.lclIsEnabled;
388 end;
389 
390 function NSViewIsLCLEnabled(v: NSView): Boolean;
391 begin
392   Result := true;
393   while Assigned(v) do
394   begin
395     if not v.lclIsEnabled then begin
396       Result := false;
397       break;
398     end;
399     v:=v.superview;
400   end;
401 end;
402 
403 function NSViewCanFocus(v: NSView): Boolean;
404 var
405   cb: ICommonCallback;
406 begin
407   if Assigned(v) then
408   begin
409     cb := v.lclGetCallback;
410     if Assigned(cb) then
411       Result := cb.CanFocus
412     else
413       Result := true;
414   end
415   else
416     Result := false;
417 end;
418 
419 function isCallbackForSameObject(cb1, cb2: ICommonCallback): Boolean;
420 begin
421   Result := Assigned(cb1) and Assigned(cb2);
422   if Result then
423     Result := (cb1 = cb2) or (cb1.GetTarget = cb2.GetTarget);
424 end;
425 
426 procedure SetViewDefaults(AView: NSView);
427 begin
428   if not Assigned(AView) then Exit;
429   AView.setAutoresizingMask(NSViewMinYMargin or NSViewMaxXMargin);
430 end;
431 
432 function CheckMainThread: Boolean;
433 begin
434   Result := NSThread.currentThread.isMainThread;
435 end;
436 
437 function GetNSViewSuperViewHeight(view: NSView): CGFloat;
438 begin
439   Result := -1;
440   if not Assigned(view) then Exit;
441   if not Assigned(view.superview) then Exit;
442   //if view.superview.isKindOfClass_(TCocoaTabPageView) then
443     //Result := TCocoaTabPageView(view.superview).tabview.contentRect.size.height
444   //else
445     Result := view.superview.frame.size.height;
446   {$IFDEF COCOA_SUPERVIEW_HEIGHT}
447   WriteLn(Format('GetNSViewSuperViewHeight Result=%f', [Result]));
448   {$ENDIF}
449 end;
450 
451 { TManualTicks }
452 
AddTicknull453 function TManualTicks.AddTick(atick: integer): Boolean;
454 var
455   i : integer;
456 begin
457   //todo: must be a binary search
458   for i:=0 to length(ticks)-1 do
459     if ticks[i]=atick then begin
460       Result:=false;
461       Exit;
462     end;
463 
464   // adding new tick
465   if length(ticks)=count then begin
466     if count=0 then SetLength(ticks, 8)
467     else SetLength(ticks, count * 2);
468   end;
469   ticks[count]:=atick;
470   inc(count);
471   Result := true;
472 end;
473 
474 { TCocoaGroupBox }
475 
lclClientFramenull476 function TCocoaGroupBox.lclClientFrame: TRect;
477 var
478   v : NSView;
479 begin
480   v:=contentView;
481   if not Assigned(v) then
482     Result := inherited lclClientFrame
483   else
484     if v.isFlipped then
485       Result := NSRectToRect( v.frame )
486     else
487       NSToLCLRect(v.frame, frame.size.height, Result);
488 end;
489 
lclContentViewnull490 function TCocoaGroupBox.lclContentView: NSView;
491 begin
492   Result := NSView(contentView);
493 end;
494 
lclGetFrameToLayoutDeltanull495 function TCocoaGroupBox.lclGetFrameToLayoutDelta: TRect;
496 begin
497   Result.Left := 3;
498   Result.Right := -3;
499   Result.Top := 0;
500   Result.Bottom := -4;
501 end;
502 
acceptsFirstRespondernull503 function TCocoaGroupBox.acceptsFirstResponder: LCLObjCBoolean;
504 begin
505   Result := True;
506 end;
507 
lclGetCallbacknull508 function TCocoaGroupBox.lclGetCallback: ICommonCallback;
509 begin
510   Result := callback;
511 end;
512 
513 procedure TCocoaGroupBox.lclClearCallback;
514 begin
515   callback := nil;
516 end;
517 
518 procedure TCocoaGroupBox.resetCursorRects;
519 begin
520   if not Assigned(callback) or not callback.resetCursorRects then
521     inherited resetCursorRects;
522 end;
523 
524 { TCocoaCustomControl }
525 
526 procedure TCocoaCustomControl.setStringValue(avalue: NSString);
527 begin
528   if Assigned(fstr) then fstr.release;
529   if ASsigned(avalue) then
530     fstr:=avalue.copyWithZone(nil)
531   else
532     fstr:=nil;
533   inherited setStringValue(avalue);
534 end;
535 
stringValuenull536 function TCocoaCustomControl.stringValue: NSString;
537 begin
538   Result:=fstr;
539 end;
540 
541 procedure TCocoaCustomControl.addSubView(aview: NSView);
542 begin
543   inherited addSubView(aview);
544 
545   if Assigned(aview) then
546   begin
547     // forcing LCL compatible "auto-move" mode. Sticking to left/top corner
548     if not autoresizesSubviews then
549       {$ifdef BOOLFIX}
550       setAutoresizesSubviews_(Ord(true));
551       {$else}
552       setAutoresizesSubviews(true);
553       {$endif}
554     aview.setAutoresizingMask(NSViewMaxXMargin or NSViewMinYMargin);
555   end;
556 end;
557 
558 procedure TCocoaCustomControl.dealloc;
559 begin
560   if Assigned(fstr) then fstr.release;
561   inherited dealloc;
562 end;
563 
acceptsFirstRespondernull564 function TCocoaCustomControl.acceptsFirstResponder: LCLObjCBoolean;
565 begin
566   Result := True;
567 end;
568 
acceptsFirstMousenull569 function TCocoaCustomControl.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
570 begin
571   // By default, a mouse-down event in a window that isn’t the key window
572   // simply brings the window forward and makes it key; the event isn’t sent
573   // to the NSView object over which the mouse click occurs. The NSView can
574   // claim an initial mouse-down event, however, by overriding acceptsFirstMouse: to return YES.
575   // see bug #33034
576   Result:=true;
577 end;
578 
579 procedure TCocoaCustomControl.drawRect(dirtyRect: NSRect);
580 begin
581   if isdrawing=0 then faileddraw:=false;
582   inc(isdrawing);
583   inherited drawRect(dirtyRect);
584 
585   // Implement Color property
586   if Assigned(callback) then
587     callback.DrawBackground(NSGraphicsContext.currentContext, bounds, dirtyRect);
588 
589   if CheckMainThread and Assigned(callback) then
590     callback.Draw(NSGraphicsContext.currentContext, bounds, dirtyRect);
591   dec(isdrawing);
592 
593   if (isdrawing=0) and (faileddraw) then
594   begin
595     // Similar to Carbon. Cocoa doesn't welcome changing a framerects during paint event
596     // If such thing happens, the results are pretty much inpredicatable. #32970
597     // TreeView tries to updatedScrollBars during paint event. That sometimes is causing
598     // the frame to be changed (i.e. scroll bar showed or hidden, resized the client rect)
599     // as a result, the final image is shown up-side-down.
600     //
601     // Below is an attempt to prevent graphical artifacts and to redraw
602     // the control again.
603     inherited drawRect(dirtyRect);
604 
605     if Assigned(callback) then
606       callback.DrawBackground(NSGraphicsContext.currentContext, bounds, dirtyRect);
607 
608     if CheckMainThread and Assigned(callback) then
609       callback.Draw(NSGraphicsContext.currentContext, bounds, dirtyRect);
610   end;
611 end;
612 
TCocoaCustomControl.lclGetCallbacknull613 function TCocoaCustomControl.lclGetCallback: ICommonCallback;
614 begin
615   Result := callback;
616 end;
617 
618 procedure TCocoaCustomControl.lclClearCallback;
619 begin
620   callback := nil;
621 end;
622 
TCocoaCustomControl.lclIsMouseInAuxAreanull623 function TCocoaCustomControl.lclIsMouseInAuxArea(Event: NSevent): Boolean;
624 begin
625   if auxMouseByParent and Assigned(superview) then
626     Result := superview.lclIsMouseInAuxArea(Event)
627   else
628     Result := false;
629 end;
630 
631 procedure TCocoaCustomControl.mouseDown(event: NSEvent);
632 begin
633   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
634     inherited mouseDown(event);
635 end;
636 
637 procedure TCocoaCustomControl.mouseDragged(event: NSEvent);
638 begin
639   if not Assigned(callback) or not callback.MouseMove(event) then
640     // calling inherited causes the drag event to be passed to the
641     // parent controls
642 
643     //inherited mouseDragged(event);
644     ;
645 end;
646 
647 procedure TCocoaCustomControl.mouseEntered(event: NSEvent);
648 begin
649   inherited mouseEntered(event);
650 end;
651 
652 procedure TCocoaCustomControl.mouseExited(event: NSEvent);
653 begin
654   inherited mouseExited(event);
655 end;
656 
657 procedure TCocoaCustomControl.mouseMoved(event: NSEvent);
658 begin
659   if not Assigned(callback) or not callback.MouseMove(event) then
660     inherited mouseMoved(event);
661 end;
662 
663 procedure TCocoaCustomControl.scrollWheel(event: NSEvent);
664 begin
665   if not Assigned(callback) or not callback.scrollWheel(event) then
666     inherited scrollWheel(event);
667 end;
668 
669 procedure TCocoaCustomControl.setFrame(aframe: NSRect);
670 begin
671   if NSEqualRects(aframe, frame) then Exit;
672   if isdrawing>0 then
673     faileddraw := true;
674 
675   inherited setFrame(aframe);
676   // it actually should come from a notifcation
677   if Assigned(callback) then callback.frameDidChange(self);
678 end;
679 
680 procedure TCocoaCustomControl.mouseUp(event: NSEvent);
681 begin
682   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
683     inherited mouseUp(event);
684 end;
685 
686 procedure TCocoaCustomControl.rightMouseDown(event: NSEvent);
687 begin
688   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
689     inherited rightMouseDown(event);
690 end;
691 
692 procedure TCocoaCustomControl.rightMouseUp(event: NSEvent);
693 begin
694   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
695     inherited rightMouseUp(event);
696 end;
697 
698 procedure TCocoaCustomControl.rightMouseDragged(event: NSEvent);
699 begin
700   if not Assigned(callback) or not callback.MouseMove(event) then
701     inherited rightMouseDragged(event);
702 end;
703 
704 procedure TCocoaCustomControl.otherMouseDown(event: NSEvent);
705 begin
706   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
707     inherited otherMouseDown(event);
708 end;
709 
710 procedure TCocoaCustomControl.otherMouseUp(event: NSEvent);
711 begin
712   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
713     inherited otherMouseUp(event);
714 end;
715 
716 procedure TCocoaCustomControl.otherMouseDragged(event: NSEvent);
717 begin
718   if not Assigned(callback) or not callback.MouseMove(event) then
719     inherited otherMouseDragged(event);
720 end;
721 
722 procedure TCocoaCustomControl.resetCursorRects;
723 begin
724   if not Assigned(callback) or not callback.resetCursorRects then
725     inherited resetCursorRects;
726 end;
727 
728 { LCLObjectExtension }
729 
LCLObjectExtension.lclIsEnablednull730 function LCLObjectExtension.lclIsEnabled: Boolean;
731 begin
732   Result := False;
733 end;
734 
735 procedure LCLObjectExtension.lclSetEnabled(AEnabled: Boolean);
736 begin
737 end;
738 
LCLObjectExtension.lclIsVisiblenull739 function LCLObjectExtension.lclIsVisible: Boolean;
740 begin
741   Result := False;
742 end;
743 
744 procedure LCLObjectExtension.lclSetVisible(AVisible: Boolean);
745 begin
746 end;
747 
LCLObjectExtension.lclWindowStatenull748 function LCLObjectExtension.lclWindowState: Integer;
749 begin
750   Result := SIZE_RESTORED;
751 end;
752 
753 procedure LCLObjectExtension.lclInvalidateRect(const r: TRect);
754 begin
755 end;
756 
757 procedure LCLObjectExtension.lclInvalidate;
758 begin
759 end;
760 
761 procedure LCLObjectExtension.lclUpdate;
762 begin
763 end;
764 
765 procedure LCLObjectExtension.lclRelativePos(var Left,Top: Integer);
766 begin
767 end;
768 
769 procedure LCLObjectExtension.lclLocalToScreen(var X,Y: Integer);
770 begin
771 end;
772 
773 procedure LCLObjectExtension.lclScreenToLocal(var X, Y: Integer);
774 begin
775 end;
776 
LCLObjectExtension.lclParentnull777 function LCLObjectExtension.lclParent:id;
778 begin
779   Result:=nil;
780 end;
781 
LCLObjectExtension.lclFramenull782 function LCLObjectExtension.lclFrame:TRect;
783 begin
784   FillChar(Result, sizeof(Result), 0);
785 end;
786 
787 procedure LCLObjectExtension.lclSetFrame(const r:TRect);
788 begin
789 
790 end;
791 
LCLObjectExtension.lclGetFrameToLayoutDeltanull792 function LCLObjectExtension.lclGetFrameToLayoutDelta: TRect;
793 begin
794   Result.Top := 0;
795   Result.Left := 0;
796   Result.Right := 0;
797   Result.Bottom := 0;
798 end;
799 
LCLObjectExtension.lclClientFramenull800 function LCLObjectExtension.lclClientFrame:TRect;
801 begin
802   FillChar(Result, sizeof(Result), 0);
803 end;
804 
LCLObjectExtension.lclGetCallbacknull805 function LCLObjectExtension.lclGetCallback: ICommonCallback;
806 begin
807   Result := nil;
808 end;
809 
810 procedure LCLObjectExtension.lclClearCallback;
811 begin
812 end;
813 
LCLObjectExtension.lclGetPropStoragenull814 function LCLObjectExtension.lclGetPropStorage: TStringList;
815 var
816   Callback: ICommonCallback;
817 begin
818   Callback := lclGetCallback;
819   if Assigned(Callback) then
820     Result := Callback.GetPropStorage
821   else
822     Result := nil;
823 end;
824 
LCLObjectExtension.lclGetTargetnull825 function LCLObjectExtension.lclGetTarget: TObject;
826 var
827   Callback: ICommonCallback;
828 begin
829   Callback := lclGetCallback;
830   if Assigned(Callback) then
831     Result := Callback.GetTarget
832   else
833     Result := nil;
834 end;
835 
LCLObjectExtension.lclDeliverMessagenull836 function LCLObjectExtension.lclDeliverMessage(Msg: Cardinal; WParam: WParam; LParam: LParam): LResult;
837 var
838   Callback: ICommonCallback;
839 begin
840   Callback := lclGetCallback;
841   if Assigned(Callback) then
842     Result := Callback.DeliverMessage(Msg, WParam, LParam)
843   else
844     Result := 0;
845 end;
846 
LCLObjectExtension.lclContentViewnull847 function LCLObjectExtension.lclContentView: NSView;
848 begin
849   Result := nil;
850 end;
851 
852 procedure LCLObjectExtension.lclOffsetMousePos(var Point: NSPoint);
853 begin
854 
855 end;
856 
857 procedure LCLObjectExtension.lclExpectedKeys(var wantTabs, wantArrows,
858   wantReturn, wantAll: Boolean);
859 begin
860   wantTabs := false;
861   wantArrows := false;
862   wantReturn := false;
863   wantAll := false;
864 end;
865 
866 { The method should return TRUE, if mouse is located above an auxilary area
867   of a (composited) control, and thus MOUSE MOVE event should not be propagated
868   to LCL. For example, controls with Scrollbars should not report mouse events
869   if mouse cursor is above ScrollBar and scroll bar is visible. (ScrollBar = Auxillary area)
870 
871   By default, the whole area is considered to be non-auxillary and must be
872   reported to LCL.
873   }
LCLObjectExtension.lclIsMouseInAuxAreanull874 function LCLObjectExtension.lclIsMouseInAuxArea(Event: NSEvent): Boolean;
875 begin
876   Result := false;
877 end;
878 
879 { LCLControlExtension }
880 
RectToViewCoordnull881 function RectToViewCoord(view: NSView; const r: TRect): NSRect;
882 var
883   b: NSRect;
884 begin
885   b := view.bounds;
886   Result.origin.x := r.Left;
887   Result.size.width := r.Right - r.Left;
888   Result.size.height := r.Bottom - r.Top;
889   if Assigned(view) and (view.isFlipped) then
890     Result.origin.y := r.Top
891   else
892     Result.origin.y := b.size.height - r.Bottom;
893 end;
894 
LCLControlExtension.lclIsEnablednull895 function LCLControlExtension.lclIsEnabled:Boolean;
896 begin
897   Result := IsEnabled;
898 end;
899 
900 procedure LCLControlExtension.lclSetEnabled(AEnabled:Boolean);
901 begin
902   {$ifdef BOOLFIX}
903   SetEnabled_( Ord(AEnabled and NSViewIsLCLEnabled(self.superview) ));
904   {$else}
905   SetEnabled( AEnabled and NSViewIsLCLEnabled(self.superview) );
906   {$endif}
907   inherited lclSetEnabled(AEnabled);
908 end;
909 
LCLViewExtension.lclInitWithCreateParamsnull910 function LCLViewExtension.lclInitWithCreateParams(const AParams: TCreateParams): id;
911 var
912   p: NSView;
913   ns: NSRect;
914   {$IFDEF COCOA_DEBUG_SETBOUNDS}
915   pstr: string;
916   {$ENDIF}
917 begin
918   p := nil;
919   if (AParams.WndParent <> 0) then
920     p := NSView(AParams.WndParent).lclContentView;
921 
922   if Assigned(p) then
923     LCLToNSRect(Types.Bounds(AParams.X, AParams.Y, AParams.Width, AParams.Height),
924       p.frame.size.height, ns)
925   else
926     ns := GetNSRect(AParams.X, AParams.Y, AParams.Width, AParams.Height);
927 
928   {$IFDEF COCOA_DEBUG_SETBOUNDS}
929   if Assigned(p) then
930   begin
931     pstr := NSStringToString(p.className);
932     if NSStringToString(NSObject(AParams.WndParent).className) = 'TCocoaTabPage' then
933       pstr := pstr + ' ' + NSStringToString(TCocoaTabPage(AParams.WndParent).label_);
934   end
935   else
936     pstr := '';
937   WriteLn(Format('[LCLViewExtension.lclInitWithCreateParams] Class=%s Caption=%s ParentClass=%s ParentClassView=%s rect=%d %d %d %d Visible=%d',
938     [NSStringToString(Self.className), AParams.Caption,
939      NSStringToString(NSObject(AParams.WndParent).className), pstr,
940      Round(ns.Origin.x), Round(ns.Origin.y), Round(ns.size.width), Round(ns.size.height),
941      AParams.Style and WS_VISIBLE]));
942   {$ENDIF}
943 
944   Result := initWithFrame(ns);
945   if not Assigned(Result) then
946     Exit;
947 
948   {$ifdef BOOLFIX}
949   setHidden_(Ord(AParams.Style and WS_VISIBLE = 0));
950   {$else}
951   setHidden(AParams.Style and WS_VISIBLE = 0);
952   {$endif}
953 
954   if Assigned(p) then
955     p.lclContentView.addSubview(Result);
956   SetViewDefaults(Result);
957 end;
958 
LCLViewExtension.lclIsEnablednull959 function LCLViewExtension.lclIsEnabled: Boolean;
960 begin
961   Result := true;
962 end;
963 
964 procedure LCLViewExtension.lclSetEnabled(AEnabled: Boolean);
965 var
966   cb : ICommonCallback;
967   obj : NSObject;
968 begin
969   for obj in subviews do begin
970     cb := obj.lclGetCallback;
971     obj.lclSetEnabled(AEnabled and ((not Assigned(cb)) or cb.GetShouldBeEnabled) );
972   end;
973 end;
974 
LCLViewExtension.lclIsVisiblenull975 function LCLViewExtension.lclIsVisible: Boolean;
976 begin
977   Result := not isHidden;
978 end;
979 
980 procedure LCLViewExtension.lclSetVisible(AVisible: Boolean);
981 begin
982   {$ifdef BOOLFIX}
983   setHidden_(Ord(not AVisible));
984   {$else}
985   setHidden(not AVisible);
986   {$endif}
987   {$IFDEF COCOA_DEBUG_SETBOUNDS}
988   WriteLn(Format('LCLViewExtension.lclSetVisible: %s AVisible=%d',
989     [NSStringToString(Self.ClassName), Integer(AVisible)]));
990   {$ENDIF}
991 end;
992 
LCLViewExtension.lclIsPaintingnull993 function LCLViewExtension.lclIsPainting: Boolean;
994 begin
995   Result := Assigned(lclGetCallback) and Assigned(lclGetCallback.GetContext);
996 end;
997 
998 procedure LCLViewExtension.lclInvalidateRect(const r:TRect);
999 var
1000   view : NSView;
1001 begin
1002   view:=lclContentView;
1003   if Assigned(view) then
1004     view.setNeedsDisplayInRect(RectToViewCoord(view, r))
1005   else
1006     self.setNeedsDisplayInRect(RectToViewCoord(Self, r));
1007   //todo: it might be necessary to always invalidate self
1008   //      just need to get offset of the contentView relative for self
1009 end;
1010 
1011 procedure LCLViewExtension.lclInvalidate;
1012 begin
1013   {$ifdef BOOLFIX}
1014   setNeedsDisplay__(Ord(True));
1015   {$else}
1016   setNeedsDisplay_(True);
1017   {$endif}
1018 end;
1019 
1020 procedure LCLViewExtension.lclUpdate;
1021 begin
1022   {$ifdef BOOLFIX}
1023   setNeedsDisplay__(Ord(True));
1024   {$else}
1025   setNeedsDisplay_(True);
1026   {$endif}
1027   //display;
1028 end;
1029 
1030 procedure LCLViewExtension.lclRelativePos(var Left, Top: Integer);
1031 var
1032   sv : NSView;
1033   fr : NSRect;
1034 begin
1035   Left := Round(frame.origin.x);
1036   sv := superview;
1037   if Assigned(sv) and (not sv.isFlipped) then
1038   begin
1039     fr := frame;
1040     Top := Round(sv.frame.size.height - fr.origin.y - fr.size.height);
1041   end
1042   else
1043     Top := Round(frame.origin.y);
1044 end;
1045 
1046 procedure LCLViewExtension.lclLocalToScreen(var X, Y:Integer);
1047 var
1048   P: NSPoint;
1049 
1050 begin
1051   // 1. convert to window base
1052   // Convert from View-lcl to View-cocoa
1053   P.x := X;
1054   if isFlipped then
1055     p.y := Y
1056   else
1057     P.y := frame.size.height-y;   // convert to Cocoa system
1058 
1059   // Convert from View-cocoa to Window-cocoa
1060   P := convertPoint_ToView(P, nil);
1061 
1062   // Convert from Window-cocoa to Window-lcl
1063   X := Round(P.X);
1064   Y := Round(window.frame.size.height-P.Y); // convert to LCL system
1065 
1066   // 2. convert window to screen
tonull1067   // Use window function to convert fomr Window-lcl to Screen-lcl
1068   window.lclLocalToScreen(X, Y);
1069 end;
1070 
1071 procedure LCLViewExtension.lclScreenToLocal(var X, Y: Integer);
1072 var
1073   P: NSPoint;
1074 begin
1075   // 1. convert from screen to window
tonull1076   // use window function to onvert from Screen-lcl to Window-lcl
1077   window.lclScreenToLocal(X, Y);
1078   // Convert from Window-lcl to Window-cocoa
1079   P.x := X;
1080   P.y := Round(window.frame.size.height-Y); // convert to Cocoa system
1081 
1082   // 2. convert from window to local
1083   //    Convert from Window-cocoa to View-cocoa
1084   P := convertPoint_FromView(P, nil);
1085 
1086   // Convert from View-cocoa to View-lcl
1087   X := Round(P.x);
1088   if isFlipped then
1089     Y := Round(p.y)
1090   else
1091     Y := Round(frame.size.height-P.y);   // convert to Cocoa system
1092 end;
1093 
LCLViewExtension.lclParentnull1094 function LCLViewExtension.lclParent:id;
1095 begin
1096   Result := superView;
1097 end;
1098 
LCLViewExtension.lclFramenull1099 function LCLViewExtension.lclFrame: TRect;
1100 var
1101   v: NSView;
1102 begin
1103   v := superview;
1104   if Assigned(v) and not v.isFlipped then
1105     NSToLCLRect(frame, v.frame.size.height, Result)
1106   else
1107     Result := NSRectToRect(frame);
1108   AddLayoutToFrame( lclGetFrameToLayoutDelta, Result);
1109 end;
1110 
1111 procedure LCLViewExtension.lclSetFrame(const r: TRect);
1112 var
1113   ns: NSRect;
1114   svHeight: CGFloat;
1115   rr : TRect;
1116 begin
1117   rr := r;
1118   SubLayoutFromFrame( lclGetFrameToLayoutDelta, rr);
1119 
1120   svHeight := GetNSViewSuperViewHeight(Self);
1121   if Assigned(superview) and not superview.isFlipped then
1122   begin
1123     LCLToNSRect(rr, svHeight, ns)
1124   end
1125   else
1126     ns := RectToNSRect(rr);
1127 
1128   {$IFDEF COCOA_DEBUG_SETBOUNDS}
1129   WriteLn(Format('LCLViewExtension.lclSetFrame: %s Bounds=%s height=%d ns_pos=%d %d ns_size=%d %d',
1130     [NSStringToString(Self.ClassName), dbgs(r), Round(svHeight),
1131      Round(ns.origin.x), Round(ns.origin.y), Round(ns.size.width), Round(ns.size.height)]));
1132   {$ENDIF}
1133   setFrame(ns);
1134 end;
1135 
LCLViewExtension.lclClientFramenull1136 function LCLViewExtension.lclClientFrame: TRect;
1137 begin
1138   Result := lclFrame;
1139   Types.OffsetRect(Result, -Result.Left, -Result.Top);
1140 end;
1141 
LCLViewExtension.lclContentViewnull1142 function LCLViewExtension.lclContentView: NSView;
1143 begin
1144   Result := self;
1145 end;
1146 
1147 procedure LCLViewExtension.lclOffsetMousePos(var Point: NSPoint);
1148 var
1149   es : NSScrollView;
1150   r  : NSRect;
1151   dlt : TRect;
1152 begin
1153   Point := convertPoint_fromView(Point, nil);
1154 
1155   es := enclosingScrollView;
1156   if es.documentView <> self then es := nil;
1157   if not isFlipped then
1158     Point.y := bounds.size.height - Point.y;
1159 
1160   if Assigned(es) then
1161   begin
1162     r := es.documentVisibleRect;
1163     if isFlipped then
1164       Point.y := Point.y - r.origin.y
1165     else
1166       Point.y := Point.y - (es.documentView.frame.size.height - r.size.height - r.origin.y);
1167     Point.X := Point.X - r.origin.x;
1168   end;
1169 
1170   dlt := lclGetFrameToLayoutDelta;
1171   Point.X := Point.X - dlt.Left;
1172   Point.Y := Point.Y - dlt.Top;
1173 end;
1174 
1175 { TCocoaStatusBar }
1176 
1177 procedure TCocoaStatusBar.drawRect(dirtyRect: NSRect);
1178 var
1179   R    : TRect;
1180   i    : Integer;
1181   cs   : NSString;
1182   nr   : NSRect;
1183   dr   : NSRect;
1184   al   : TAlignment;
1185   x    : Integer;
1186   txt  : string;
1187   cnt  : Integer;
1188   w    : Integer;
1189 const
1190   CocoaAlign: array [TAlignment] of Integer = (NSNaturalTextAlignment, NSRightTextAlignment, NSCenterTextAlignment);
1191 begin
1192   if not Assigned(barcallback) then Exit;
1193 
1194   if not Assigned(panelCell) then Exit;
1195 
1196   panelCell.setControlView(Self);
1197 
1198   r := lclClientFrame();
1199   nr.origin.y := 0;
1200   nr.size.height := self.lclFrame.Height;
1201 
1202   x:=0;
1203   cnt := barcallback.GetBarsCount;
1204   for i:=0 to cnt - 1 do begin
1205 
1206     txt := '';
1207     w := 0;
1208     al := taLeftJustify;
1209 
1210     if not barcallback.GetBarItem(i, txt, w, al) then Continue;
1211 
1212     if i = cnt - 1 then w := r.Right - x;
1213     nr.size.width := w;
1214     nr.origin.x := x;
1215 
1216     // dr - draw rect. should be 1 pixel wider
1217     // and 1 pixel taller, than the actual rect.
1218     // to produce a better visual effect
1219     dr := nr;
1220     dr.size.width := dr.size.width + 1;
1221     dr.size.height := dr.size.height + 1;
1222     dr.origin.y := dr.origin.y-1;
1223 
1224     cs := NSStringUtf8(txt);
1225     panelCell.setTitle(cs);
1226     panelCell.setAlignment(CocoaAlign[al]);
1227     panelCell.drawWithFrame_inView(dr, Self);
1228     cs.release;
1229     barcallback.DrawPanel(i, NSRectToRect(nr));
1230     inc(x, w);
1231     if x > r.Right then break; // no place left
1232   end;
1233 end;
1234 
1235 procedure TCocoaStatusBar.dealloc;
1236 begin
1237   if Assigned(panelCell) then panelCell.release;
1238   inherited;
1239 end;
1240 
1241 { TCocoaProgressIndicator }
1242 
TCocoaProgressIndicator.acceptsFirstRespondernull1243 function TCocoaProgressIndicator.acceptsFirstResponder: LCLObjCBoolean;
1244 begin
1245   Result:=True;
1246 end;
1247 
lclGetCallbacknull1248 function TCocoaProgressIndicator.lclGetCallback: ICommonCallback;
1249 begin
1250   Result:=callback;
1251 end;
1252 
1253 procedure TCocoaProgressIndicator.lclClearCallback;
1254 begin
1255   callback:=nil;
1256 end;
1257 
1258 procedure TCocoaProgressIndicator.resetCursorRects;
1259 begin
1260   if not callback.resetCursorRects then
1261     inherited resetCursorRects;
1262 end;
1263 
lclGetFrameToLayoutDeltanull1264 function TCocoaProgressIndicator.lclGetFrameToLayoutDelta: TRect;
1265 begin
1266   case controlSize of
1267     NSSmallControlSize, NSMiniControlSize:
1268     begin
1269       Result.Left := 1;
1270       Result.Right := -1;
1271       Result.Top := 0;
1272       Result.Bottom := -2;
1273     end;
1274   else
1275     Result.Left := 2;
1276     Result.Right := -2;
1277     Result.Top := 0;
1278     Result.Bottom := -4;
1279   end;
1280 end;
1281 
1282 procedure TCocoaProgressIndicator.lclSetFrame(const r: TRect);
1283 begin
1284   SetNSControlSize(self, r.Bottom - r.Top, 0, PROGRESS_SMALL_HEIGHT, true);
1285   inherited lclSetFrame(r);
1286 end;
1287 
TCocoaProgressIndicator.acceptsFirstMousenull1288 function TCocoaProgressIndicator.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
1289 begin
1290   Result:=true;
1291 end;
1292 
1293 procedure TCocoaProgressIndicator.mouseDown(event: NSEvent);
1294 begin
1295   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1296   begin
1297     inherited mouseDown(event);
1298 
1299     callback.MouseUpDownEvent(event, true);
1300   end;
1301 end;
1302 
1303 procedure TCocoaProgressIndicator.mouseUp(event: NSEvent);
1304 begin
1305   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1306     inherited mouseUp(event);
1307 end;
1308 
1309 procedure TCocoaProgressIndicator.rightMouseDown(event: NSEvent);
1310 begin
1311   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1312     inherited rightMouseDown(event);
1313 end;
1314 
1315 procedure TCocoaProgressIndicator.rightMouseUp(event: NSEvent);
1316 begin
1317   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1318     inherited rightMouseUp(event);
1319 end;
1320 
1321 procedure TCocoaProgressIndicator.rightMouseDragged(event: NSEvent);
1322 begin
1323   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1324     inherited rightMouseDragged(event);
1325 end;
1326 
1327 procedure TCocoaProgressIndicator.otherMouseDown(event: NSEvent);
1328 begin
1329   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1330     inherited otherMouseDown(event);
1331 end;
1332 
1333 procedure TCocoaProgressIndicator.otherMouseUp(event: NSEvent);
1334 begin
1335   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1336     inherited otherMouseUp(event);
1337 end;
1338 
1339 procedure TCocoaProgressIndicator.otherMouseDragged(event: NSEvent);
1340 begin
1341   if not Assigned(callback) or not callback.MouseMove(event) then
1342     inherited otherMouseDragged(event);
1343 end;
1344 
1345 procedure TCocoaProgressIndicator.mouseDragged(event: NSEvent);
1346 begin
1347   if not Assigned(callback) or not callback.MouseMove(event) then
1348     inherited mouseDragged(event);
1349 end;
1350 
1351 procedure TCocoaProgressIndicator.mouseMoved(event: NSEvent);
1352 begin
1353   if not Assigned(callback) or not callback.MouseMove(event) then
1354     inherited mouseMoved(event);
1355 end;
1356 
1357 procedure TCocoaProgressIndicator.scrollWheel(event: NSEvent);
1358 begin
1359   if not Assigned(callback) or not callback.scrollWheel(event) then
1360     inherited scrollWheel(event);
1361 end;
1362 
1363 
1364 { TCocoaSlider }
1365 
GetManTicksnull1366 function GetManTicks(slider: TCocoaSlider): TManualTicks;
1367 begin
1368   if not Assigned(slider.man) then
1369     slider.man := TManualTicks.Create;
1370   Result := slider.man;
1371 end;
1372 
1373 procedure TCocoaSlider.dealloc;
1374 begin
1375   man.Free;
1376   inherited dealloc;
1377 end;
1378 
1379 procedure TCocoaSlider.drawRect(dirtyRect: NSRect);
1380 var
1381   i  : integer;
1382   nr : NSRect;
1383   xr : NSRect;
1384   dr : NSRect;
1385   nm : integer;
1386   ctx : NSGraphicsContext;
1387   pth : NSBezierPath;
1388 begin
1389   if not Assigned(man) or (not man.draw) then begin
1390     inherited drawRect(dirtyRect);
1391     Exit;
1392   end;
1393 
1394   nm := round(maxValue - minValue);
1395   if nm = 0 then Exit;
1396   if numberOfTickMarks < 2 then Exit;
1397 
1398   nr := rectOfTickMarkAtIndex(0);
1399   xr := rectOfTickMarkAtIndex(1);
1400 
1401   ctx := NSGraphicsContext.currentContext;
1402   pth := NSBezierPath.bezierPath;
1403   NSColor.controlShadowColor.setFill;
1404   dr:=nr;
1405   dr.origin.y := dr.origin.y + 1;
1406   dr.size.height := dr.size.height - 1;
1407   for i := 0 to man.count - 1 do begin
1408     dr.origin.x := round(nr.origin.x + (xr.origin.x - nr.origin.x) * (man.ticks[i] - minValue) / nm);
1409     pth.fillRect(dr);
1410   end;
1411   inherited drawRect(dirtyRect);
1412 end;
1413 
TCocoaSlider.acceptsFirstRespondernull1414 function TCocoaSlider.acceptsFirstResponder: LCLObjCBoolean;
1415 begin
1416   Result := True;
1417 end;
1418 
lclGetCallbacknull1419 function TCocoaSlider.lclGetCallback: ICommonCallback;
1420 begin
1421   Result:=callback;
1422 end;
1423 
1424 procedure TCocoaSlider.lclClearCallback;
1425 begin
1426   callback := nil;
1427 end;
1428 
1429 procedure TCocoaSlider.resetCursorRects;
1430 begin
1431   if not callback.resetCursorRects then
1432     inherited resetCursorRects;
1433 end;
1434 
1435 procedure TCocoaSlider.keyDown(event: NSEvent);
1436 var
1437   KeyCode: word;
1438 begin
1439   KeyCode := Event.keyCode;
1440   case KeyCode of
1441     kVK_UpArrow    : SnapToInteger(1);
1442     kVK_DownArrow  : SnapToInteger(-1);
1443     kVK_LeftArrow  : SnapToInteger(-1);
1444     kVK_RightArrow : SnapToInteger(1);
1445   else
1446     // If this isn't done callback.KeyEvent will cause arrow left/right to change control
1447     inherited keyDown(event);
1448   end;
1449 end;
1450 
1451 procedure TCocoaSlider.SnapToInteger(AExtraFactor: Integer);
1452 begin
1453   setIntValue(Round(doubleValue() + AExtraFactor));
1454 end;
1455 
1456 procedure TCocoaSlider.sliderAction(sender: id);
1457 var
1458   newval: Integer;
1459 begin
1460   SnapToInteger();
1461   newval := intValue;
1462   if newval <> intval then begin
1463     intval := newval;
1464     // OnChange event
1465     if callback <> nil then
1466       callback.SendOnChange();
1467   end;
1468 end;
1469 
acceptsFirstMousenull1470 function TCocoaSlider.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
1471 begin
1472   Result:=true;
1473 end;
1474 
1475 procedure TCocoaSlider.mouseDown(event: NSEvent);
1476 begin
1477   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1478   begin
1479     inherited mouseDown(event);
1480 
1481     callback.MouseUpDownEvent(event, true);
1482   end;
1483 end;
1484 
1485 procedure TCocoaSlider.mouseUp(event: NSEvent);
1486 begin
1487   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1488     inherited mouseUp(event);
1489 end;
1490 
1491 procedure TCocoaSlider.rightMouseDown(event: NSEvent);
1492 begin
1493   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1494     inherited rightMouseDown(event);
1495 end;
1496 
1497 procedure TCocoaSlider.rightMouseUp(event: NSEvent);
1498 begin
1499   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1500     inherited rightMouseUp(event);
1501 end;
1502 
1503 procedure TCocoaSlider.rightMouseDragged(event: NSEvent);
1504 begin
1505   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1506     inherited rightMouseDragged(event);
1507 end;
1508 
1509 procedure TCocoaSlider.otherMouseDown(event: NSEvent);
1510 begin
1511   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1512     inherited otherMouseDown(event);
1513 end;
1514 
1515 procedure TCocoaSlider.otherMouseUp(event: NSEvent);
1516 begin
1517   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1518     inherited otherMouseUp(event);
1519 end;
1520 
1521 procedure TCocoaSlider.otherMouseDragged(event: NSEvent);
1522 begin
1523   if not Assigned(callback) or not callback.MouseMove(event) then
1524     inherited otherMouseDragged(event);
1525 end;
1526 
1527 procedure TCocoaSlider.mouseDragged(event: NSEvent);
1528 begin
1529   if not Assigned(callback) or not callback.MouseMove(event) then
1530     inherited mouseDragged(event);
1531 end;
1532 
1533 procedure TCocoaSlider.mouseMoved(event: NSEvent);
1534 begin
1535   if not Assigned(callback) or not callback.MouseMove(event) then
1536     inherited mouseMoved(event);
1537 end;
1538 
1539 procedure TCocoaSlider.scrollWheel(event: NSEvent);
1540 begin
1541   if not Assigned(callback) or not callback.scrollWheel(event) then
1542     inherited scrollWheel(event);
1543 end;
1544 
1545 procedure TCocoaSlider.lclAddManTick(atick: integer);
1546 var
1547   mn : TManualTicks;
1548 begin
1549   mn := GetManTicks(self);
1550   if mn.AddTick(atick) then
1551   begin
1552     if mn.draw then self.setNeedsDisplay_(true);
1553   end;
1554 end;
1555 
1556 procedure TCocoaSlider.lclSetManTickDraw(adraw: Boolean);
1557 var
1558   mn : TManualTicks;
1559 begin
1560   mn := GetManTicks(self);
1561   if mn.draw=adraw then Exit;
1562   mn.draw:=adraw;
1563   self.setNeedsDisplay_(true);
1564 end;
1565 
1566 procedure TCocoaSlider.lclExpectedKeys(var wantTabs, wantArrows, wantReturn,
1567   wantAll: Boolean);
1568 begin
1569   wantTabs := false;
1570   wantArrows := true;
1571   wantReturn := false;
1572   wantAll := false;
1573 end;
1574 
1575 type
1576   NSViewControlSizeExt = objccategory external (NSView)
1577     function controlSize: Integer; message 'controlSize';
1578     procedure setControlSize(ASize: Integer); message 'setControlSize:';
1579     function cell: id; message 'cell';
1580     procedure setFont(afont: NSFont); message 'setFont:';
1581   end;
1582 
1583 procedure SetNSControlSize(ctrl: NSView; newHeight, miniHeight, smallHeight: Integer; AutoChangeFont: Boolean);
1584 var
1585   sz : NSControlSize;
1586 begin
1587   if (miniHeight>0) and (newHeight<=miniHeight) then
1588     sz:=NSMiniControlSize
1589   else if (smallHeight>0) and (newHeight<=smallHeight) then
1590     sz:=NSSmallControlSize
1591   else
1592     sz:=NSRegularControlSize;
1593 
1594   if ctrl.respondsToSelector(ObjCSelector('setControlSize:')) then
1595     ctrl.setControlSize(sz)
1596   else if ctrl.respondsToSelector(ObjCSelector('cell')) then
1597   begin
1598     if NSCell(ctrl.cell).controlSize<>sz then
1599         NSCell(ctrl.cell).setControlSize(sz);
1600   end;
1601   if AutoChangeFont and (ctrl.respondsToSelector(ObjCSelector('setFont:'))) then
1602     ctrl.setFont(NSFont.systemFontOfSize(NSFont.systemFontSizeForControlSize(sz)));
1603 end;
1604 
1605 
1606 end.
1607 
1608