1 { $Id: $}
2 { --------------------------------------------
3 cocoawindows.pas - 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 CocoaWindows;
16
17 {$mode objfpc}{$H+}
18 {$modeswitch objectivec1}
19 {$modeswitch objectivec2}
20 {$interfaces corba}
21 {$include cocoadefines.inc}
22
23 interface
24
25 uses
26 // rtl+ftl
27 Types, Classes, SysUtils,
28 CGGeometry,
29 // Libs
30 MacOSAll, CocoaAll, CocoaUtils, CocoaGDIObjects,
31 cocoa_extra, CocoaPrivate, CocoaTextEdits, CocoaScrollers,
32 // LCL
33 //Forms,
34 LCLType, LCLProc;
35
36 type
37
38 { LCLWindowExtension }
39
40 LCLWindowExtension = objccategory(NSWindow)
lclIsVisiblenull41 function lclIsVisible: Boolean; message 'lclIsVisible'; reintroduce;
42 procedure lclSetVisible(AVisible: Boolean); message 'lclSetVisible:'; reintroduce;
lclIsEnablednull43 function lclIsEnabled: Boolean; message 'lclIsEnabled'; reintroduce;
44 procedure lclSetEnabled(AEnabled: Boolean); message 'lclSetEnabled:'; reintroduce;
45
lclWindowStatenull46 function lclWindowState: Integer; message 'lclWindowState'; reintroduce;
47 procedure lclInvalidateRect(const r: TRect); message 'lclInvalidateRect:'; reintroduce;
48 procedure lclInvalidate; message 'lclInvalidate'; reintroduce;
49 procedure lclUpdate; message 'lclUpdate'; reintroduce;
50 procedure lclRelativePos(var Left, Top: Integer); message 'lclRelativePos::'; reintroduce;
51 procedure lclLocalToScreen(var X, Y: Integer); message 'lclLocalToScreen::'; reintroduce;
52 procedure lclScreenToLocal(var X, Y: Integer); message 'lclScreenToLocal::'; reintroduce;
lclFramenull53 function lclFrame: TRect; message 'lclFrame'; reintroduce;
54 procedure lclSetFrame(const r: TRect); message 'lclSetFrame:'; reintroduce;
lclClientFramenull55 function lclClientFrame: TRect; message 'lclClientFrame'; reintroduce;
lclGetTopBarHeightnull56 function lclGetTopBarHeight:integer; message 'lclGetTopBarHeight'; reintroduce;
57 procedure lclOffsetMousePos(var Point: NSPoint); message 'lclOffsetMousePos:'; reintroduce;
58 end;
59
60 { IWindowCallback }
61
62 IWindowCallback = interface(ICommonCallBack)
CanActivatenull63 function CanActivate: Boolean;
64 procedure Activate;
65 procedure Deactivate;
66 procedure CloseQuery(var CanClose: Boolean);
67 procedure Close;
68 procedure Resize;
69 procedure Move;
70 procedure WindowStateChanged;
71
GetEnablednull72 function GetEnabled: Boolean;
73 procedure SetEnabled(AValue: Boolean);
74
AcceptFilesDragnull75 function AcceptFilesDrag: Boolean;
76 procedure DropFiles(const FileNames: array of string);
77
HasCancelControlnull78 function HasCancelControl: Boolean;
HasDefaultControlnull79 function HasDefaultControl: Boolean;
80
81 property Enabled: Boolean read GetEnabled write SetEnabled;
82 end;
83
84 { TCocoaPanel }
85
86 TCocoaPanel = objcclass(NSPanel, NSWindowDelegateProtocol)
87 protected
windowShouldClosenull88 function windowShouldClose(sender : id): LongBool; message 'windowShouldClose:';
89 procedure windowWillClose(notification: NSNotification); message 'windowWillClose:';
90 procedure windowDidBecomeKey(notification: NSNotification); message 'windowDidBecomeKey:';
91 procedure windowDidResignKey(notification: NSNotification); message 'windowDidResignKey:';
92 procedure windowDidResize(notification: NSNotification); message 'windowDidResize:';
93 procedure windowDidMove(notification: NSNotification); message 'windowDidMove:';
94 public
95 callback: IWindowCallback;
acceptsFirstRespondernull96 function acceptsFirstResponder: LCLObjCBoolean; override;
canBecomeKeyWindownull97 function canBecomeKeyWindow: LCLObjCBoolean; override;
becomeFirstRespondernull98 function becomeFirstResponder: LCLObjCBoolean; override;
resignFirstRespondernull99 function resignFirstResponder: LCLObjCBoolean; override;
lclGetCallbacknull100 function lclGetCallback: ICommonCallback; override;
101 procedure lclClearCallback; override;
102 // mouse
103 procedure mouseDown(event: NSEvent); override;
104 procedure mouseUp(event: NSEvent); override;
105 procedure rightMouseDown(event: NSEvent); override;
106 procedure rightMouseUp(event: NSEvent); override;
107 procedure rightMouseDragged(event: NSEvent); override;
108 procedure otherMouseDown(event: NSEvent); override;
109 procedure otherMouseUp(event: NSEvent); override;
110 procedure otherMouseDragged(event: NSEvent); override;
111 procedure mouseDragged(event: NSEvent); override;
112 procedure mouseEntered(event: NSEvent); override;
113 procedure mouseExited(event: NSEvent); override;
114 procedure mouseMoved(event: NSEvent); override;
115 end;
116
117 { TCocoaWindow }
118
119 TCocoaWindowContent = objcclass;
120
121 TCocoaWindow = objcclass(NSWindow, NSWindowDelegateProtocol)
122 protected
123 fieldEditor: TCocoaFieldEditor;
124 firedMouseEvent: Boolean;
125 isInFullScreen: Boolean;
126 orderOutAfterFS : Boolean;
127 fsview: TCocoaWindowContent;
128
windowShouldClosenull129 function windowShouldClose(sender : id): LongBool; message 'windowShouldClose:';
130 procedure windowWillClose(notification: NSNotification); message 'windowWillClose:';
windowWillReturnFieldEditor_toObjectnull131 function windowWillReturnFieldEditor_toObject(sender: NSWindow; client: id): id; message 'windowWillReturnFieldEditor:toObject:';
132 procedure windowDidBecomeKey(notification: NSNotification); message 'windowDidBecomeKey:';
133 procedure windowDidResignKey(notification: NSNotification); message 'windowDidResignKey:';
134 procedure windowDidResize(notification: NSNotification); message 'windowDidResize:';
135 procedure windowDidMove(notification: NSNotification); message 'windowDidMove:';
136 procedure windowDidMiniaturize(notification: NSNotification); message 'windowDidMiniaturize:';
137 procedure windowDidDeminiaturize(notification: NSNotification); message 'windowDidDeminiaturize:';
138 // fullscreen notifications are only reported for 10.7 fullscreen
139 procedure windowWillEnterFullScreen(notification: NSNotification); message 'windowWillEnterFullScreen:';
140 procedure windowDidEnterFullScreen(notification: NSNotification); message 'windowDidEnterFullScreen:';
141 procedure windowDidExitFullScreen(notification: NSNotification); message 'windowDidExitFullScreen:';
142 public
143 _keyEvCallback: ICommonCallback;
144 callback: IWindowCallback;
145 keepWinLevel : NSInteger;
146 //LCLForm: TCustomForm;
147 procedure dealloc; override;
acceptsFirstRespondernull148 function acceptsFirstResponder: LCLObjCBoolean; override;
canBecomeKeyWindownull149 function canBecomeKeyWindow: LCLObjCBoolean; override;
becomeFirstRespondernull150 function becomeFirstResponder: LCLObjCBoolean; override;
resignFirstRespondernull151 function resignFirstResponder: LCLObjCBoolean; override;
lclGetCallbacknull152 function lclGetCallback: ICommonCallback; override;
153 procedure lclClearCallback; override;
154 // mouse
155 procedure mouseDown(event: NSEvent); override;
156 procedure mouseUp(event: NSEvent); override;
157 procedure rightMouseDown(event: NSEvent); override;
158 procedure rightMouseUp(event: NSEvent); override;
159 procedure rightMouseDragged(event: NSEvent); override;
160 procedure otherMouseDown(event: NSEvent); override;
161 procedure otherMouseUp(event: NSEvent); override;
162 procedure otherMouseDragged(event: NSEvent); override;
163 procedure mouseDragged(event: NSEvent); override;
164 procedure mouseEntered(event: NSEvent); override;
165 procedure mouseExited(event: NSEvent); override;
166 procedure mouseMoved(event: NSEvent); override;
167 procedure scrollWheel(event: NSEvent); override;
168 procedure sendEvent(event: NSEvent); override;
169 // key
170 procedure keyDown(event: NSEvent); override;
171 // menu support
172 procedure lclItemSelected(sender: id); message 'lclItemSelected:';
173
174 procedure lclSwitchFullScreen(AEnabled: Boolean); message 'lclSwitchFullScreen:';
lclIsFullScreennull175 function lclIsFullScreen: Boolean; message 'lclIsFullScreen';
176 end;
177
178 { TCocoaDesignOverlay }
179
180 TCocoaDesignOverlay = objcclass(NSView)
181 callback : ICommonCallback;
182 procedure drawRect(r: NSRect); override;
acceptsFirstRespondernull183 function acceptsFirstResponder: LCLObjCBoolean; override;
hitTestnull184 function hitTest(aPoint: NSPoint): NSView; override;
lclGetCallbacknull185 function lclGetCallback: ICommonCallback; override;
186 procedure lclClearCallback; override;
187 end;
188
189 { TCocoaWindowContentDocument }
190
191 TCocoaWindowContentDocument = objcclass(TCocoaCustomControl)
192 protected
193 procedure didBecomeKeyNotification(sender: NSNotification); message 'didBecomeKeyNotification:';
194 procedure didResignKeyNotification(sender: NSNotification); message 'didResignKeyNotification:';
195 public
196 overlay: NSView;
197 wincallback: IWindowCallback;
lclWindowStatenull198 function lclWindowState: Integer; override;
199 procedure didAddSubview(aview: NSView); override;
200 procedure setNeedsDisplay_(aflag: LCLObjCBoolean); override;
201 procedure setNeedsDisplayInRect(arect: NSRect); override;
202 // NSDraggingDestinationCategory
draggingEnterednull203 function draggingEntered(sender: NSDraggingInfoProtocol): NSDragOperation; override;
performDragOperationnull204 function performDragOperation(sender: NSDraggingInfoProtocol): LCLObjCBoolean; override;
205 end;
206
207 { TCocoaWindowContent }
208
209 TCocoaWindowContent = objcclass(TCocoaScrollView)
210 private
211 _stringValue: NSString;
212 public
213 wincallback: IWindowCallback;
214 isembedded: Boolean; // true - if the content is inside of another control, false - if the content is in its own window;
215 preventKeyOnShow: Boolean;
216 ownwin: NSWindow;
217 fswin: NSWindow; // window that was used as a content prior to switching to old-school fullscreen
218 popup_parent: HWND; // if not 0, indicates that we should set the popup parent
performKeyEquivalentnull219 function performKeyEquivalent(event: NSEvent): LCLObjCBoolean; override;
220 procedure resolvePopupParent(); message 'resolvePopupParent';
lclOwnWindownull221 function lclOwnWindow: NSWindow; message 'lclOwnWindow';
222 procedure lclSetFrame(const r: TRect); override;
lclFramenull223 function lclFrame: TRect; override;
224 procedure lclRelativePos(var Left, Top: Integer); override;
225 procedure viewDidMoveToSuperview; override;
226 procedure viewDidMoveToWindow; override;
227 procedure viewWillMoveToWindow(newWindow: CocoaAll.NSWindow); override;
228 procedure dealloc; override;
229 procedure setHidden(aisHidden: LCLObjCBoolean); override;
230
231 procedure setStringValue(avalue: NSString); message 'setStringValue:';
stringValuenull232 function stringValue: NSString; message 'stringValue';
233 end;
234
235 implementation
236
237 { TCocoaDesignOverlay }
238
239 procedure TCocoaDesignOverlay.drawRect(r: NSRect);
240 begin
241 if Assigned(callback) then
242 callback.DrawOverlay(NSGraphicsContext.currentContext, bounds, r);
243 inherited drawRect(r);
244 end;
245
TCocoaDesignOverlay.acceptsFirstRespondernull246 function TCocoaDesignOverlay.acceptsFirstResponder: LCLObjCBoolean;
247 begin
248 Result:=false; // no focus
249 end;
250
hitTestnull251 function TCocoaDesignOverlay.hitTest(aPoint: NSPoint): NSView;
252 begin
253 Result:=nil; // no mouse
254 end;
255
lclGetCallbacknull256 function TCocoaDesignOverlay.lclGetCallback: ICommonCallback;
257 begin
258 Result := callback;
259 end;
260
261 procedure TCocoaDesignOverlay.lclClearCallback;
262 begin
263 callback := nil;
264 end;
265
266 { TCocoaWindowContent }
267
lclWindowStatenull268 function TCocoaWindowContentDocument.lclWindowState: Integer;
269 begin
270 if window.lclGetCallback = wincallback then // not embedded
271 Result := window.lclWindowState
272 else
273 Result := inherited lclWindowState
274 end;
275
276 procedure TCocoaWindowContentDocument.didAddSubview(aview: NSView);
277 const
278 mustHaveSizing = (NSViewWidthSizable or NSViewHeightSizable);
279 begin
280 if Assigned(aview) and Assigned(overlay) and (overlay<>aview) then
281 begin
282 overlay.retain;
283 overlay.removeFromSuperview;
284 addSubview_positioned_relativeTo(overlay, NSWindowAbove, nil);
285 overlay.release;
286 overlay.setFrame(frame);
287 if (overlay.autoresizingMask and mustHaveSizing) <> mustHaveSizing then
288 overlay.setAutoresizingMask(overlay.autoresizingMask or mustHaveSizing);
289 end;
290 inherited didAddSubview(aview);
291 end;
292
293 procedure TCocoaWindowContentDocument.didBecomeKeyNotification(sender: NSNotification);
294 begin
295 if Assigned(callback) then
296 callback.DidBecomeKeyNotification;
297 end;
298
299 procedure TCocoaWindowContentDocument.didResignKeyNotification(sender: NSNotification);
300 begin
301 if Assigned(callback) then
302 callback.DidResignKeyNotification;
303 end;
304
305 { TCocoaWindowContent }
306
307 procedure NSResponderHotKeys(asender: NSResponder; event: NSEvent; var handled: LCLObjCBoolean; atarget: NSResponder);
308 var
309 undoManager: NSUndoManager;
310 ch : System.WideChar;
311 msk : LongWord; //NSEventModifierFlags;
312 chr : NSString;
313 const
314 NSModsMask = NSShiftKeyMask
315 or NSControlKeyMask
316 or NSAlternateKeyMask
317 or NSCommandKeyMask;
318 begin
319 // todo: system keys could be overriden. thus need to review the current
320 // keyboard configuration first. See "Key Bindings" at
321 // https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/EventOverview/TextDefaultsBindings/TextDefaultsBindings.html
322
323 handled := false;
324 if (event.type_ = NSKeyDown) then
325 begin
326 msk := (event.modifierFlags and NSModsMask);
327 if (msk = 0) then Exit;
328 // characters contain untranslated input characters, for layouts
329 // without latin characters (i.e. greek cyrillic, arabic).
330 // But for Latin based alphabet, "characters" are the same as
331 // "charactersWihtoutModifiers"....The Roman Empire legacy today!
332 //
333 // charatersWithoutModifiers contain translated characters for any layout.
334
335 // In order for the system shortkeys to work on any layout "characters"
336 // property must be used
337 chr := event.characters;
338 if (chr.length = 0)
339 then ch := #0
340 else ch := System.WideChar(chr.characterAtIndex(0));
341 case ch of
342 'a': if msk= NSCommandKeyMask then
343 handled := NSApplication(NSApp).sendAction_to_from(objcselector('selectAll:'), atarget, asender);
344 'c': if msk = NSCommandKeyMask then
345 handled := NSApplication(NSApp).sendAction_to_from(objcselector('copy:'), atarget, asender);
346 'v': if msk = NSCommandKeyMask then
347 handled := NSApplication(NSApp).sendAction_to_from(objcselector('paste:'), atarget, asender);
348 'x': if msk = NSCommandKeyMask then
349 handled := NSApplication(NSApp).sendAction_to_from(objcselector('cut:'), atarget, asender);
350 'z': if msk = NSCommandKeyMask then
351 begin
352 undoManager := atarget.undoManager;
353 if Assigned(undoManager) and undoManager.canUndo then
354 begin
355 handled := true;
356 undoManager.undo;
357 end;
358 end;
359 else if msk = (NSCommandKeyMask or NSShiftKeyMask) then
360 begin
361 undoManager := atarget.undoManager;
362 if Assigned(undoManager) and undoManager.canRedo then
363 begin
364 handled := true;
365 undoManager.redo;
366 end;
367 end;
368 end;
369 end;
370 end;
371
performKeyEquivalentnull372 function TCocoaWindowContent.performKeyEquivalent(event: NSEvent): LCLObjCBoolean;
373 var
374 resp : NSResponder;
375 wn : NSWindow;
376 ch : System.WideChar;
377 begin
378 Result := false;
379
380 // If the form has a default or cancel button, capture Return and Escape to
381 // prevent further processing. Actually clicking the buttons is handled in
382 // the LCL in response to the keyUp
383 if Assigned(wincallback) and (event.modifierFlags_ = 0) then
384 begin
385 ch := NSEventRawKeyChar(event);
386 if (((ch = System.WideChar(NSCarriageReturnCharacter)) and wincallback.HasDefaultControl)
387 or ((ch = #27{Escape}) and wincallback.HasCancelControl)) then
388 begin
389 Result := true;
390 Exit;
391 end;
392 end;
393
394 // Support Cut/Copy/Paste if the firstResponder is an NSTextView.
395 // This could be done in TCocoaFieldEditor and TCocoaTextView's
396 // performKeyEquivalent, but that wouldn't work for non-LCL edits.
397 // Xcode Cocoa apps rely on the commands existing in the main menu
398 wn := window;
399 if Assigned(wn) then
400 begin
401 resp := wn.firstResponder;
402 if Assigned(resp) and resp.isKindOfClass_(NSTextView) and
403 resp.lclIsEnabled then
404 begin
405 NSResponderHotKeys(self, event, Result, resp);
406 if Result then Exit;
407 end;
408 end;
409
410 Result := inherited performKeyEquivalent(event);
411 end;
412
413 procedure TCocoaWindowContent.resolvePopupParent();
414 var
415 lWindow: NSWindow;
416 isfront: Boolean;
417 begin
418 lWindow := nil;
419 if (popup_parent <> 0) then
420 begin
421 if (NSObject(popup_parent).isKindOfClass(TCocoaWindowContent)) then
422 begin
423 if (not TCocoaWindowContent(popup_parent).isembedded) then
424 lWindow := NSWindow(TCocoaWindowContent(popup_parent).window);
425 end
426 else
427 begin
428 lWindow := NSWindow(popup_parent);
429 end;
430 end;
431 if lWindow <> nil then
432 begin
433 isfront:=NSApplication(NSApp).mainWindow=self.window;
434
435 lWindow.addChildWindow_ordered(Self.window, NSWindowAbove);
436
437 // adding a window as a child, would bring the "child" form to the bottom
438 // of Zorder. need to restore the order.
439 if isfront then self.window.makeKeyAndOrderFront(nil);
440 end;
441 popup_parent := 0;
442 end;
443
lclOwnWindownull444 function TCocoaWindowContent.lclOwnWindow: NSWindow;
445 begin
446 if not isembedded then
447 Result := NSWindow(window)
448 else
449 Result := nil;
450 end;
451
452 procedure TCocoaWindowContent.lclSetFrame(const r: TRect);
453 begin
454 if isembedded then
455 inherited lclSetFrame(r)
456 else
457 window.lclSetFrame(r);
458 end;
459
TCocoaWindowContent.lclFramenull460 function TCocoaWindowContent.lclFrame: TRect;
461 var
462 wfrm : TRect;
463 begin
464 Result := inherited lclFrame;
465 if not isembedded then
466 begin
467 //Window bounds should return "client rect" in screen coordinates
468 if Assigned(window.screen) then
469 NSToLCLRect(window.frame, NSScreenZeroHeight, wfrm)
470 else
471 wfrm := NSRectToRect(frame);
472 OffsetRect(Result, -Result.Left+wfrm.Left, -Result.Top+wfrm.Top);
473 end;
474 end;
475
476 procedure TCocoaWindowContent.lclRelativePos(var Left, Top: Integer);
477 begin
478 if isembedded then
479 inherited lclRelativePos(Left, Top)
480 else
481 window.lclRelativePos(Left, Top);
482 end;
483
484 procedure TCocoaWindowContent.viewDidMoveToSuperview;
485 begin
486 inherited viewDidMoveToSuperview;
487 end;
488
489 procedure TCocoaWindowContent.viewDidMoveToWindow;
490 begin
491 isembedded := window.contentView <> self;
492 if isembedded then
493 begin
494 ownwin := nil;
495 end
496 else
497 begin
498 ownwin := NSWindow(window);
499 if Assigned(stringValue) then
500 ownwin.setTitle(stringValue);
501 end;
502 inherited viewDidMoveToWindow;
503 end;
504
505 procedure TCocoaWindowContent.viewWillMoveToWindow(newWindow: CocoaAll.NSWindow);
506 begin
507 if newWindow<>nil then
508 newWindow.setAcceptsMouseMovedEvents(True);
509 if not isembedded and (newWindow <> window) then
510 begin
511 if Assigned(window) then
512 setStringValue(window.title);
513 ownwin := nil;
514 isembedded := false;
515 end;
516 inherited viewWillMoveToWindow(newWindow);
517 end;
518
519 procedure TCocoaWindowContent.dealloc;
520 begin
521 inherited dealloc;
522 end;
523
524 procedure TCocoaWindowContent.setHidden(aisHidden: LCLObjCBoolean);
525 var
526 cw : TCocoaWindow;
527 begin
528 if isembedded then
529 begin
530 {$ifdef BOOLFIX}
531 inherited setHidden_(Ord(aisHidden));
532 {$else}
533 inherited setHidden(aisHidden);
534 {$endif}
535 end
536 else
537 begin
538 if aisHidden and window.isVisible then
539 begin
540 if (window.isKindOfClass(TCocoaWindow)) then
541 cw := TCocoaWindow(window)
542 else
543 cw := nil;
544 if cw.lclIsFullScreen then
545 begin
546 cw.orderOutAfterFS := true;
547 cw.lclSwitchFullScreen(false);
548 end else
549 window.orderOut(nil);
550 end
551 else
552 if not aisHidden and not window.isVisible then
553 begin
554 if preventKeyOnShow then // used for Hint-windows, so they would not steal the focus from the current window
555 window.orderFrontRegardless
556 else
557 window.makeKeyAndOrderFront(nil);
558 end;
559 end;
560 end;
561
562 procedure TCocoaWindowContent.setStringValue(avalue: NSString);
563 begin
564 if _stringValue = avalue then Exit;
565 if Assigned(_stringValue) then _stringValue.release;
566 _stringValue := AValue;
567 if Assigned(_stringValue) then _stringValue.retain;
568 end;
569
stringValuenull570 function TCocoaWindowContent.stringValue: NSString;
571 begin
572 Result := _stringValue;
573 end;
574
575 { TCocoaPanel }
576
windowShouldClosenull577 function TCocoaPanel.windowShouldClose(sender: id): LongBool;
578 var
579 canClose: Boolean;
580 begin
581 canClose := True;
582 if Assigned(callback) then
583 callback.CloseQuery(canClose);
584 Result := canClose;
585 end;
586
587 procedure TCocoaPanel.windowWillClose(notification: NSNotification);
588 begin
589 if Assigned(callback) then
590 callback.Close;
591 end;
592
593 procedure TCocoaPanel.windowDidBecomeKey(notification: NSNotification);
594 begin
595 if Assigned(callback) then
596 callback.Activate;
597 end;
598
599 procedure TCocoaPanel.windowDidResignKey(notification: NSNotification);
600 begin
601 if Assigned(callback) then
602 callback.Deactivate;
603 end;
604
605 procedure TCocoaPanel.windowDidResize(notification: NSNotification);
606 begin
607 if Assigned(callback) then
608 callback.Resize;
609 end;
610
611 procedure TCocoaPanel.windowDidMove(notification: NSNotification);
612 begin
613 if Assigned(callback) then
614 callback.Move;
615 end;
616
TCocoaPanel.acceptsFirstRespondernull617 function TCocoaPanel.acceptsFirstResponder: LCLObjCBoolean;
618 begin
619 Result := True;
620 end;
621
canBecomeKeyWindownull622 function TCocoaPanel.canBecomeKeyWindow: LCLObjCBoolean;
623 begin
624 Result := Assigned(callback) and callback.CanActivate;
625 end;
626
becomeFirstRespondernull627 function TCocoaPanel.becomeFirstResponder: LCLObjCBoolean;
628 begin
629 Result := inherited becomeFirstResponder;
630 // if Assigned(callback) then
631 // callback.BecomeFirstResponder;
632 end;
633
resignFirstRespondernull634 function TCocoaPanel.resignFirstResponder: LCLObjCBoolean;
635 begin
636 Result := inherited resignFirstResponder;
637 // if Assigned(callback) then
638 // callback.ResignFirstResponder;
639 end;
640
lclGetCallbacknull641 function TCocoaPanel.lclGetCallback: ICommonCallback;
642 begin
643 Result := callback;
644 end;
645
646 procedure TCocoaPanel.lclClearCallback;
647 begin
648 callback := nil;
649 contentView.lclClearCallback;
650 end;
651
652 procedure TCocoaPanel.mouseDown(event: NSEvent);
653 begin
654 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
655 inherited mouseDown(event);
656 end;
657
658 procedure TCocoaPanel.mouseUp(event: NSEvent);
659 begin
660 if Assigned(callback) then callback.MouseUpDownEvent(event);
661 inherited mouseUp(event);
662 end;
663
664 procedure TCocoaPanel.rightMouseDown(event: NSEvent);
665 begin
666 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
667 inherited rightMouseUp(event);
668 end;
669
670 procedure TCocoaPanel.rightMouseUp(event: NSEvent);
671 begin
672 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
673 inherited rightMouseDown(event);
674 end;
675
676 procedure TCocoaPanel.rightMouseDragged(event: NSEvent);
677 begin
678 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
679 inherited rightMouseDragged(event);
680 end;
681
682 procedure TCocoaPanel.otherMouseDown(event: NSEvent);
683 begin
684 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
685 inherited otherMouseDown(event);
686 end;
687
688 procedure TCocoaPanel.otherMouseUp(event: NSEvent);
689 begin
690 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
691 inherited otherMouseUp(event);
692 end;
693
694 procedure TCocoaPanel.otherMouseDragged(event: NSEvent);
695 begin
696 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
697 inherited otherMouseDown(event);
698 end;
699
700 procedure TCocoaPanel.mouseDragged(event: NSEvent);
701 begin
702 if not Assigned(callback) or not callback.MouseMove(event) then
703 inherited mouseDragged(event);
704 end;
705
706 procedure TCocoaPanel.mouseEntered(event: NSEvent);
707 begin
708 inherited mouseEntered(event);
709 end;
710
711 procedure TCocoaPanel.mouseExited(event: NSEvent);
712 begin
713 inherited mouseExited(event);
714 end;
715
716 procedure TCocoaPanel.mouseMoved(event: NSEvent);
717 begin
718 if not Assigned(callback) or not callback.MouseMove(event) then
719 inherited mouseMoved(event);
720 end;
721
722 { TCocoaWindow }
723
windowShouldClosenull724 function TCocoaWindow.windowShouldClose(sender: id): LongBool;
725 var
726 canClose: Boolean;
727 begin
728 canClose := True;
729 if Assigned(callback) then
730 callback.CloseQuery(canClose);
731 Result := canClose;
732 end;
733
TCocoaWindow.windowWillReturnFieldEditor_toObjectnull734 function TCocoaWindow.windowWillReturnFieldEditor_toObject(sender: NSWindow; client: id): id;
735 begin
736 //DebugLn('[TCocoaWindow.windowWillReturnFieldEditor_toObject]');
737 Result := nil;
738
739 if (NSObject(client).isKindOfClass(NSTextField)) and Assigned(NSObject(client).lclGetCallBack) then
740 begin
741 if (fieldEditor = nil) then
742 begin
743 fieldEditor := TCocoaFieldEditor.alloc.init;
744 fieldEditor.setFieldEditor(True);
745 end;
746 Result := fieldEditor;
747 end;
748 end;
749
750 procedure TCocoaWindow.windowWillClose(notification: NSNotification);
751 begin
752 if Assigned(callback) then
753 callback.Close;
754 end;
755
756 procedure TCocoaWindow.windowDidBecomeKey(notification: NSNotification);
757 begin
758 // forcing to keep the level as all other LCL windows
759 // Modal windows tend to "restore" their elevated level
760 // And that doesn't work for modal windows that are "Showing" other windows
761
762 // Another approach is to set elevated levels for windows, shown during modal session
763 // That requires to revoke the elevated level from windows on closing a window session
764 // This might be the way to go, if FormStyle (such as fsStayOnTop) would come
765 // in conflict with modality
766 if level <> keepWinLevel then begin
767 setLevel(keepWinLevel);
768 end;
769
770 if Assigned(callback) then
771 callback.Activate;
772
773 // LCL didn't change focus. TCocoaWindow should not keep the focus for itself
774 // and it should pass it to it's content view
775 if (firstResponder = self)
776 and Assigned(contentView)
777 and (contentView.isKindOfClass(TCocoaWindowContent)) then
778 self.makeFirstResponder( TCocoaWindowContent(contentView).documentView );
779 end;
780
781 procedure TCocoaWindow.windowDidResignKey(notification: NSNotification);
782 begin
783 if Assigned(callback) then
784 callback.Deactivate;
785 end;
786
787 procedure TCocoaWindow.windowDidResize(notification: NSNotification);
788 begin
789 if Assigned(callback) then
790 callback.Resize;
791 end;
792
793 procedure TCocoaWindow.windowDidMove(notification: NSNotification);
794 begin
795 if Assigned(callback) then
796 callback.Move;
797 end;
798
799 procedure TCocoaWindow.windowDidMiniaturize(notification: NSNotification);
800 begin
801 if Assigned(callback) then
802 callback.WindowStateChanged;
803 end;
804
805 procedure TCocoaWindow.windowDidDeminiaturize(notification: NSNotification);
806 begin
807 if Assigned(callback) then
808 callback.WindowStateChanged;
809 end;
810
811 procedure TCocoaWindow.windowWillEnterFullScreen(notification: NSNotification);
812 begin
813 if not isInFullScreen then isInFullScreen := true;
814 // setting fullscreen flag, prior to the "Fullscreen" has actually been enabled.
815 // MacOS does 10.7 fullscreen switch with an animation (that's about 1 second long)
816 // if during that animation there's another call toggleFullScreen() is made
817 // then macOS produces an output "not in fullscreen state" and ignores the call.
818 end;
819
820 procedure TCocoaWindow.windowDidEnterFullScreen(notification: NSNotification);
821 begin
822 if not isInFullScreen then isInFullScreen := true;
823 end;
824
825 procedure TCocoaWindow.windowDidExitFullScreen(notification: NSNotification);
826 begin
827 if isInFullScreen then isInFullScreen := false;
828 if orderOutAfterFS then begin
829 self.orderOut(nil);
830 orderOutAfterFS := false;
831 end;
832 end;
833
834 procedure TCocoaWindow.dealloc;
835 begin
836 if (fieldEditor <> nil) then
837 begin
838 fieldEditor.release;
839 fieldEditor := nil;
840 end;
841 inherited dealloc;
842 end;
843
acceptsFirstRespondernull844 function TCocoaWindow.acceptsFirstResponder: LCLObjCBoolean;
845 begin
846 Result := True;
847 end;
848
canBecomeKeyWindownull849 function TCocoaWindow.canBecomeKeyWindow: LCLObjCBoolean;
850 begin
851 Result := Assigned(callback) and callback.CanActivate;
852 end;
853
becomeFirstRespondernull854 function TCocoaWindow.becomeFirstResponder: LCLObjCBoolean;
855 begin
856 Result := inherited becomeFirstResponder;
857 // uncommenting the following lines starts an endless focus loop
858
859 // if Assigned(callback) then
860 // callback.BecomeFirstResponder;
861 end;
862
resignFirstRespondernull863 function TCocoaWindow.resignFirstResponder: LCLObjCBoolean;
864 begin
865 Result := inherited resignFirstResponder;
866 // if Assigned(callback) then
867 // callback.ResignFirstResponder;
868 end;
869
lclGetCallbacknull870 function TCocoaWindow.lclGetCallback: ICommonCallback;
871 begin
872 Result := callback;
873 end;
874
875 procedure TCocoaWindow.lclClearCallback;
876 begin
877 callback := nil;
878 contentView.lclClearCallback;
879 end;
880
881 procedure TCocoaWindow.mouseDown(event: NSEvent);
882 begin
883 //if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
884 inherited mouseDown(event);
885 end;
886
887 procedure TCocoaWindow.mouseUp(event: NSEvent);
888 begin
889 //firedMouseEvent:=true;
890 //if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
891 inherited mouseUp(event);
892 end;
893
894 procedure TCocoaWindow.rightMouseDown(event: NSEvent);
895 begin
896 //if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
897 inherited rightMouseUp(event);
898 end;
899
900 procedure TCocoaWindow.rightMouseUp(event: NSEvent);
901 begin
902 //if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
903 inherited rightMouseDown(event);
904 end;
905
906 procedure TCocoaWindow.rightMouseDragged(event: NSEvent);
907 begin
908 //if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
909 inherited rightMouseDragged(event);
910 end;
911
912 procedure TCocoaWindow.otherMouseDown(event: NSEvent);
913 begin
914 //if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
915 inherited otherMouseDown(event);
916 end;
917
918 procedure TCocoaWindow.otherMouseUp(event: NSEvent);
919 begin
920 //if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
921 inherited otherMouseUp(event);
922 end;
923
924 procedure TCocoaWindow.otherMouseDragged(event: NSEvent);
925 begin
926 //if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
927 inherited otherMouseDown(event);
928 end;
929
930 procedure TCocoaWindow.mouseDragged(event: NSEvent);
931 begin
932 //if not Assigned(callback) or not callback.MouseMove(event) then
933 inherited mouseDragged(event);
934 end;
935
936 procedure TCocoaWindow.mouseEntered(event: NSEvent);
937 begin
938 inherited mouseEntered(event);
939 end;
940
941 procedure TCocoaWindow.mouseExited(event: NSEvent);
942 begin
943 inherited mouseExited(event);
944 end;
945
946 procedure TCocoaWindow.mouseMoved(event: NSEvent);
947 begin
948 // no need to call for callback or anything, because WindowContent
949 // will take care of it anyway
950 inherited mouseMoved(event);
951 end;
952
953 procedure TCocoaWindow.scrollWheel(event: NSEvent);
954 begin
955 if not Assigned(callback) or not callback.scrollWheel(event) then
956 inherited scrollWheel(event);
957 end;
958
959 procedure TCocoaWindow.sendEvent(event: NSEvent);
960 var
961 Epos: NSPoint;
962 cr : NSRect;
963 fr : NSRect;
964 prc: Boolean;
965 begin
966 if event.type_ = NSLeftMouseUp then
967 // This code is introduced here for an odd cocoa feature.
968 // mouseUp is not fired, if pressed on Window's title.
969 // (even though mouseDown, mouseMove and mouseDragged are fired)
970 // (there are some information in the internet, that mouseDown is not firing as well)
971 // (however this is not true for macOS 10.12)
972 // The logic below is as following. If mouseUp event arrived
973 // and mouse position is on the title of the form.
974 // then try to process the event. If event was not processed, call mouseUp()
975 // specifically.
976 begin
977 Epos:=event.locationInWindow;
978 fr := frame;
979 fr.origin.x:=0;
980 fr.origin.y:=0;
981 cr := contentRectForFrameRect(fr);
982 if NSPointInRect(Epos, fr) and not NSPointInRect(Epos, cr) then
983 begin
984 firedMouseEvent := false;
985 inherited sendEvent(event);
986 if not firedMouseEvent then mouseUp(event);
987 end
988 else
989 inherited sendEvent(event);
990 end
991 else
992 inherited sendEvent(event);
993 end;
994
995 procedure TCocoaWindow.keyDown(event: NSEvent);
996 var
997 mn : NSMenu;
998 allowcocoa : Boolean;
999 begin
1000 if performKeyEquivalent(event) then
1001 Exit;
1002
1003 mn := NSApp.MainMenu;
1004 if Assigned(mn) and mn.performKeyEquivalent(event) then
1005 Exit;
1006
1007 if Assigned(_keyEvCallback) then
1008 begin
1009 allowcocoa := True;
1010 _keyEvCallback.KeyEvAfterDown(allowcocoa);
1011 if not allowcocoa then
1012 Exit;
1013 end;
1014
1015 inherited keyDown(event);
1016 end;
1017
draggingEnterednull1018 function TCocoaWindowContentDocument.draggingEntered(sender: NSDraggingInfoProtocol): NSDragOperation;
1019 begin
1020 Result := NSDragOperationNone;
1021 if (wincallback <> nil) and (wincallback.AcceptFilesDrag) then
1022 Result := sender.draggingSourceOperationMask();
1023 end;
1024
performDragOperationnull1025 function TCocoaWindowContentDocument.performDragOperation(sender: NSDraggingInfoProtocol): LCLObjCBoolean;
1026 var
1027 draggedURLs{, lClasses}: NSArray;
1028 lFiles: array of string;
1029 i: Integer;
1030 pboard: NSPasteboard;
1031 lNSStr: NSString;
1032 //lClass: pobjc_class;
1033 begin
1034 Result := False;
1035 pboard := sender.draggingPasteboard();
1036
1037 // Multiple strings
1038 draggedURLs := pboard.propertyListForType(NSFilenamesPboardType);
1039 SetLength(lFiles, draggedURLs.count);
1040 for i := 0 to draggedURLs.count-1 do
1041 begin
1042 lNSStr := NSString(draggedURLs.objectAtIndex(i));
1043 lFiles[i] := NSStringToString(lNSStr);
1044 end;
1045
1046 // Multiple URLs -> Results in strange URLs with file:// protocol
1047 {if pboard.types.containsObject(NSURLPboardType) then
1048 begin
1049 lClass := NSURL.classClass;
1050 lClasses := NSArray.arrayWithObjects_count(@lClass, 1);
1051 draggedURLs := pboard.readObjectsForClasses_options(lClasses, nil);
1052 SetLength(lFiles, draggedURLs.count);
1053 for i := 0 to draggedURLs.count-1 do
1054 begin
1055 lNSStr := NSURL(draggedURLs.objectAtIndex(i)).absoluteString;
1056 lFiles[i] := NSStringToString(lNSStr);
1057 end;
1058 end;}
1059
1060 if (Length(lFiles) > 0) and (wincallback <> nil) then
1061 wincallback.DropFiles(lFiles);
1062 Result := True;
1063 end;
1064
1065 procedure TCocoaWindowContentDocument.setNeedsDisplay_(aflag: LCLObjCBoolean);
1066 begin
1067 inherited setNeedsDisplay_(aflag);
1068 if Assigned(overlay) then overlay.setNeedsDisplay_(aflag);
1069 end;
1070
1071 procedure TCocoaWindowContentDocument.setNeedsDisplayInRect(arect: NSRect);
1072 begin
1073 inherited setNeedsDisplayInRect(arect);
1074 if Assigned(overlay) then overlay.setNeedsDisplayInRect(arect);
1075 end;
1076
1077 procedure TCocoaWindow.lclItemSelected(sender: id);
1078 begin
1079
1080 end;
1081
1082 procedure TCocoaWindow.lclSwitchFullScreen(AEnabled: Boolean);
1083 const
1084 fsmask = NSWindowCollectionBehaviorFullScreenPrimary
1085 or
1086 NSWindowCollectionBehaviorFullScreenAuxiliary;
1087 begin
1088 if isInFullScreen = AEnabled then Exit; // nothing to do
1089
1090 //todo: there are two flavours of full-screen
1091 // (soft) macOS 10.7+ toggleFullScreen()
1092 // (hard) macOS 10.5+ enterFullScreenMode_withOptions()
shouldnull1093 // the function should be smart enough to figure out the available mode
1094
1095 isInFullScreen := AEnabled;
1096 if NSAppKitVersionNumber >= NSAppKitVersionNumber10_7 then
1097 begin
1098 if Self.collectionBehavior and fsmask = 0 then
1099 Self.setCollectionBehavior(Self.collectionBehavior or NSWindowCollectionBehaviorFullScreenPrimary);
1100 Self.toggleFullScreen(nil);
1101 end
1102 else
1103 begin
1104 if AEnabled then
1105 begin
1106 fsview := TCocoaWindowContent(contentView);
1107 fsview.fswin := self;
1108 fsview.enterFullScreenMode_withOptions(self.screen, nil);
1109 end else begin
1110 fsview.exitFullScreenModeWithOptions(nil);
1111 self.setContentView(fsview);
1112 fsview := nil;
1113 end;
1114 end;
1115 end;
1116
TCocoaWindow.lclIsFullScreennull1117 function TCocoaWindow.lclIsFullScreen: Boolean;
1118 begin
1119 Result := isInFullScreen;
1120 end;
1121
1122 { LCLWindowExtension }
1123
LCLWindowExtension.lclIsVisiblenull1124 function LCLWindowExtension.lclIsVisible: Boolean;
1125 begin
1126 Result := isVisible;
1127 end;
1128
1129 procedure LCLWindowExtension.lclSetVisible(AVisible: Boolean);
1130 begin
1131 if AVisible then
1132 orderFrontRegardless
1133 else
1134 orderOut(nil);
1135 end;
1136
LCLWindowExtension.lclIsEnablednull1137 function LCLWindowExtension.lclIsEnabled: Boolean;
1138 begin
1139 Result := contentView.lclIsEnabled;
1140 end;
1141
1142 procedure LCLWindowExtension.lclSetEnabled(AEnabled: Boolean);
1143 begin
1144 contentView.lclSetEnabled(AEnabled);
1145 end;
1146
LCLWindowExtension.lclWindowStatenull1147 function LCLWindowExtension.lclWindowState: Integer;
1148 const
1149 NSFullScreenWindowMask = 1 shl 14;
1150 begin
1151 if isMiniaturized then
1152 Result := SIZE_MINIMIZED
1153 else
1154 if (styleMask and NSFullScreenWindowMask) <> 0 then
1155 Result := SIZE_FULLSCREEN
1156 else
1157 if isZoomed then
1158 Result := SIZE_MAXIMIZED
1159 else
1160 Result := SIZE_RESTORED;
1161 end;
1162
1163 procedure LCLWindowExtension.lclInvalidateRect(const r: TRect);
1164 begin
1165 contentView.lclInvalidateRect(r);
1166 end;
1167
1168 procedure LCLWindowExtension.lclInvalidate;
1169 begin
1170 contentView.lclInvalidate;
1171 end;
1172
1173 procedure LCLWindowExtension.lclUpdate;
1174 begin
1175 contentView.lclUpdate;
1176 end;
1177
1178 procedure LCLWindowExtension.lclRelativePos(var Left, Top: Integer);
1179 var
1180 f: NSRect;
1181 begin
1182 if Assigned(screen) then
1183 begin
1184 f:=frame;
1185 Left := Round(f.origin.x);
1186 Top := Round(NSScreenZeroHeight - f.size.height - f.origin.y);
1187 //debugln('Top:'+dbgs(Top));
1188 end;
1189 end;
1190
1191 procedure LCLWindowExtension.lclLocalToScreen(var X, Y:Integer);
1192 var
1193 f: NSRect;
1194 begin
1195 if Assigned(screen) then
1196 begin
1197 f := frame;
1198 inc(X, Round(f.origin.x));
1199 inc(Y, Round(NSScreenZeroHeight - f.size.height - f.origin.y));
1200 end;
1201 end;
1202
1203 procedure LCLWindowExtension.lclScreenToLocal(var X, Y: Integer);
1204 var
1205 f: NSRect;
1206 begin
1207 if Assigned(screen) then
1208 begin
1209 f := frame;
1210 dec(X, Round(f.origin.x));
1211 dec(Y, Round(screen.frame.size.height - f.size.height - f.origin.y));
1212 end;
1213 end;
1214
LCLWindowExtension.lclFramenull1215 function LCLWindowExtension.lclFrame: TRect;
1216 begin
1217 if Assigned(contentView) then
1218 Result:=contentView.lclFrame
1219 else
1220 begin
1221 if Assigned(screen) then
1222 NSToLCLRect(frame, NSScreenZeroHeight, Result)
1223 else
1224 Result := NSRectToRect(frame);
1225 end;
1226 end;
1227
LCLWindowExtension.lclGetTopBarHeightnull1228 function LCLWindowExtension.lclGetTopBarHeight:integer;
1229 var nw,nf: NSRect;
1230 begin
1231 nf:= NSMakeRect (0, 0, 100, 100);
1232 nw:=contentRectForFrameRect(nf);
1233 result:=round(nf.size.height-nw.size.height);
1234 end;
1235
1236 procedure LCLWindowExtension.lclOffsetMousePos(var Point: NSPoint);
1237 begin
1238 Point.y := contentView.bounds.size.height - Point.y;
1239 end;
1240
1241 procedure NSScreenGetRect(sc: NSScreen; mainScreenHeight: double; out r: TRect);
1242 var
1243 fr : NSRect;
1244 begin
1245 fr := sc.frame;
1246 r := Bounds(
1247 Round(fr.origin.x),
1248 Round(fr.origin.y - fr.size.height + mainScreenHeight),
1249 Round(fr.size.width), Round(fr.size.height)
1250 );
1251 end;
1252
1253 procedure NSScreenGetRect(sc: NSScreen; out r: TRect);
1254 begin
1255 NSScreenGetRect(sc, NSScreen.mainScreen.frame.size.height, r);
1256 end;
1257
1258 procedure LCLWindowExtension.lclSetFrame(const r: TRect);
1259 var
1260 ns : NSRect;
1261 h : integer;
1262 begin
1263 LCLToNSRect(r, NSScreenZeroHeight, ns);
1264
1265 // add topbar height
1266 h:=lclGetTopBarHeight;
1267 ns.size.height:=ns.size.height+h;
1268 ns.origin.y:=ns.origin.y-h;
1269 {$ifdef BOOLFIX}
1270 setFrame_display_(ns, Ord(isVisible));
1271 {$else}
1272 setFrame_display(ns, isVisible);
1273 {$endif}
1274 end;
1275
LCLWindowExtension.lclClientFramenull1276 function LCLWindowExtension.lclClientFrame: TRect;
1277 var
1278 wFrame, cFrame: NSRect;
1279 begin
1280 wFrame := frame;
1281 cFrame := contentRectForFrameRect(wFrame);
1282 Result.Left := Round(cFrame.origin.x - wFrame.origin.x);
1283 Result.Top := Round(wFrame.origin.y + wFrame.size.height - cFrame.origin.y - cFrame.size.height);
1284 Result.Right := Result.Left + Round(cFrame.size.width);
1285 Result.Bottom := Result.Top + Round(cFrame.size.height);
1286 end;
1287
1288 end.
1289
1290