From 863c0b712522f27a2f9a6be8ebbf25ce403b4460 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 26 Nov 2013 05:14:46 +0000 Subject: [PATCH] cocoa: revise window creation approach. the handle returned from CreateHandle method is now window content, rather than window itself. to be done: create either window or panel based on the border style git-svn-id: trunk@43481 - --- lcl/interfaces/cocoa/cocoaprivate.pp | 262 ++++++++++++++++++++++++++- lcl/interfaces/cocoa/cocoawsforms.pp | 89 ++++++--- 2 files changed, 325 insertions(+), 26 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoaprivate.pp b/lcl/interfaces/cocoa/cocoaprivate.pp index d54f2ce4c4..9b0d36afa8 100644 --- a/lcl/interfaces/cocoa/cocoaprivate.pp +++ b/lcl/interfaces/cocoa/cocoaprivate.pp @@ -25,7 +25,7 @@ uses Types, Classes, SysUtils, CGGeometry, // Libs - MacOSAll, CocoaAll, CocoaUtils, CocoaGDIObjects, + CocoaAll, CocoaUtils, CocoaGDIObjects, // LCL LCLType, Controls; @@ -272,6 +272,37 @@ type procedure sendEvent(event: NSEvent); override; end; + + TCocoaWindow = objcclass(NSWindow, NSWindowDelegateProtocol) + protected + function windowShouldClose(sender : id): LongBool; message 'windowShouldClose:'; + procedure windowWillClose(notification: NSNotification); message 'windowWillClose:'; + procedure windowDidBecomeKey(notification: NSNotification); message 'windowDidBecomeKey:'; + procedure windowDidResignKey(notification: NSNotification); message 'windowDidResignKey:'; + procedure windowDidResize(notification: NSNotification); message 'windowDidResize:'; + procedure windowDidMove(notification: NSNotification); message 'windowDidMove:'; + public + callback: IWindowCallback; + function acceptsFirstResponder: Boolean; override; + function canBecomeKeyWindow: Boolean; override; + function becomeFirstResponder: Boolean; override; + function resignFirstResponder: Boolean; override; + function lclGetCallback: ICommonCallback; override; + procedure lclClearCallback; override; + // mouse + procedure mouseDown(event: NSEvent); override; + procedure mouseUp(event: NSEvent); override; + procedure rightMouseDown(event: NSEvent); override; + procedure rightMouseUp(event: NSEvent); override; + procedure otherMouseDown(event: NSEvent); override; + procedure otherMouseUp(event: NSEvent); override; + procedure mouseDragged(event: NSEvent); override; + procedure mouseEntered(event: NSEvent); override; + procedure mouseExited(event: NSEvent); override; + procedure mouseMoved(event: NSEvent); override; + procedure sendEvent(event: NSEvent); override; + end; + { TCocoaCustomControl } TCocoaCustomControl = objcclass(NSControl) @@ -302,6 +333,20 @@ type procedure resetCursorRects; override; end; + { TCocoaWindowContent } + + TCocoaWindowContent = objcclass(TCocoaCustomControl) + public + isembedded: Boolean; // true - if the content is inside of another control, false - if the content is in its own window; + ownwin: NSWindow; + function lclOwnWindow: NSWindow; message 'lclOwnWindow'; + procedure lclSetFrame(const r: TRect); override; + procedure viewDidMoveToSuperview; override; + procedure viewDidMoveToWindow; override; + procedure viewWillMoveToWindow(newWindow: NSWindow); override; + procedure dealloc; override; + end; + { TCocoaScrollView } TCocoaScrollView = objcclass(NSScrollView) @@ -433,6 +478,53 @@ begin Result := NSThread.currentThread.isMainThread; end; +{ TCocoaWindowContent } + +function TCocoaWindowContent.lclOwnWindow: NSWindow; +begin + if not isembedded then Result:=window + else Result:=nil; +end; + +procedure TCocoaWindowContent.lclSetFrame(const r: TRect); +begin + if isembedded then inherited lclSetFrame(r) + else window.lclSetFrame(r); +end; + +procedure TCocoaWindowContent.viewDidMoveToSuperview; +begin + + inherited viewDidMoveToSuperview; +end; + +procedure TCocoaWindowContent.viewDidMoveToWindow; +begin + isembedded:=window.contentView<>self; + if isembedded then begin + if Assigned(ownwin) then ownwin.close; + ownwin:=nil; + end else begin + ownwin:=window; + end; + inherited viewDidMoveToWindow; +end; + +procedure TCocoaWindowContent.viewWillMoveToWindow(newWindow: NSWindow); +begin + if not isembedded then begin + window.close; + ownwin:=nil; + isembedded:=false; + end; + inherited viewWillMoveToWindow(newWindow); +end; + +procedure TCocoaWindowContent.dealloc; +begin + inherited dealloc; +end; + { TCocoaPanel } function TCocoaPanel.windowShouldClose(sender: id): LongBool; @@ -601,6 +693,174 @@ begin inherited sendEvent(event); end; +{ TCocoaWindow } + +function TCocoaWindow.windowShouldClose(sender: id): LongBool; +var + canClose: Boolean; +begin + canClose := True; + if Assigned(callback) then + callback.CloseQuery(canClose); + Result := canClose; +end; + +procedure TCocoaWindow.windowWillClose(notification: NSNotification); +begin + if Assigned(callback) then + callback.Close; +end; + +procedure TCocoaWindow.windowDidBecomeKey(notification: NSNotification); +begin + if Assigned(callback) then + callback.Activate; +end; + +procedure TCocoaWindow.windowDidResignKey(notification: NSNotification); +begin + if Assigned(callback) then + callback.Deactivate; +end; + +procedure TCocoaWindow.windowDidResize(notification: NSNotification); +begin + if Assigned(callback) then + callback.Resize; +end; + +procedure TCocoaWindow.windowDidMove(notification: NSNotification); +begin + if Assigned(callback) then + callback.Move; +end; + +function TCocoaWindow.acceptsFirstResponder: Boolean; +begin + Result := True; +end; + +function TCocoaWindow.canBecomeKeyWindow: Boolean; +begin + Result := Assigned(callback) and callback.CanActivate; +end; + +function TCocoaWindow.becomeFirstResponder: Boolean; +begin + Result := inherited becomeFirstResponder; + if Assigned(callback) then + callback.BecomeFirstResponder; +end; + +function TCocoaWindow.resignFirstResponder: Boolean; +begin + Result := inherited resignFirstResponder; + if Assigned(callback) then + callback.ResignFirstResponder; +end; + +function TCocoaWindow.lclGetCallback: ICommonCallback; +begin + Result := callback; +end; + +procedure TCocoaWindow.lclClearCallback; +begin + callback := nil; + contentView.lclClearCallback; +end; + +procedure TCocoaWindow.mouseDown(event: NSEvent); +begin + if not Assigned(callback) or not callback.MouseUpDownEvent(event) then + inherited mouseDown(event); +end; + +procedure TCocoaWindow.mouseUp(event: NSEvent); +begin + if not Assigned(callback) or not callback.MouseUpDownEvent(event) then + inherited mouseUp(event); +end; + +procedure TCocoaWindow.rightMouseDown(event: NSEvent); +begin + if not Assigned(callback) or not callback.MouseUpDownEvent(event) then + inherited rightMouseUp(event); +end; + +procedure TCocoaWindow.rightMouseUp(event: NSEvent); +begin + if not Assigned(callback) or not callback.MouseUpDownEvent(event) then + inherited rightMouseDown(event); +end; + +procedure TCocoaWindow.otherMouseDown(event: NSEvent); +begin + if not Assigned(callback) or not callback.MouseUpDownEvent(event) then + inherited otherMouseDown(event); +end; + +procedure TCocoaWindow.otherMouseUp(event: NSEvent); +begin + if not Assigned(callback) or not callback.MouseUpDownEvent(event) then + inherited otherMouseUp(event); +end; + +procedure TCocoaWindow.mouseDragged(event: NSEvent); +begin + if not Assigned(callback) or not callback.MouseMove(event) then + inherited mouseDragged(event); +end; + +procedure TCocoaWindow.mouseEntered(event: NSEvent); +begin + inherited mouseEntered(event); +end; + +procedure TCocoaWindow.mouseExited(event: NSEvent); +begin + inherited mouseExited(event); +end; + +procedure TCocoaWindow.mouseMoved(event: NSEvent); +begin + if not Assigned(callback) or not callback.MouseMove(event) then + inherited mouseMoved(event); +end; + +procedure TCocoaWindow.sendEvent(event: NSEvent); +var + Message: NSMutableDictionary; + Handle: HWND; + Msg: Cardinal; + WP: WParam; + LP: LParam; + Result: NSNumber; + Obj: NSObject; +begin + if event.type_ = NSApplicationDefined then + begin + // event which we get through PostMessage or SendMessage + if event.subtype = LCLEventSubTypeMessage then + begin + // extract message data + Message := NSMutableDictionary(event.data1); + Handle := NSNumber(Message.objectForKey(NSMessageWnd)).unsignedIntegerValue; + Msg := NSNumber(Message.objectForKey(NSMessageMsg)).unsignedLongValue; + WP := NSNumber(Message.objectForKey(NSMessageWParam)).integerValue; + LP := NSNumber(Message.objectForKey(NSMessageLParam)).integerValue; + // deliver message and set result + Obj := NSObject(Handle); + // todo: check that Obj is still a valid NSView/NSWindow + Result := NSNumber.numberWithInteger(Obj.lclDeliverMessage(Msg, WP, LP)); + Message.setObject_forKey(Result, NSMessageResult); + Result.release; + end; + end + else + inherited sendEvent(event); +end; + { TCocoaScrollView } function TCocoaScrollView.acceptsFirstResponder: Boolean; diff --git a/lcl/interfaces/cocoa/cocoawsforms.pp b/lcl/interfaces/cocoa/cocoawsforms.pp index 34215defdf..25ee448a73 100644 --- a/lcl/interfaces/cocoa/cocoawsforms.pp +++ b/lcl/interfaces/cocoa/cocoawsforms.pp @@ -313,12 +313,15 @@ class function TCocoaWSCustomForm.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; var Form: TCustomForm absolute AWinControl; - win: TCocoaPanel; - cnt: TCocoaCustomControl; + win: TCocoaWindow; + cnt: TCocoaWindowContent; ns: NSString; R: NSRect; begin - win := TCocoaPanel(TCocoaPanel.alloc); + //todo: create TCocoaWindow or TCocoaPanel depending on the border style + // if parent is specified neither Window nor Panel needs to be created + // the only thing that needs to be created is Content + win := TCocoaWindow(TCocoaWindow.alloc); if not Assigned(win) then begin @@ -327,12 +330,11 @@ begin end; R := CreateParamsToNSRect(AParams); - win := TCocoaPanel(win.initWithContentRect_styleMask_backing_defer(R, GetStyleMaskFor(GetDesigningBorderStyle(Form), Form.BorderIcons), NSBackingStoreBuffered, False)); - win.setHidesOnDeactivate(False); + win := TCocoaWindow(win.initWithContentRect_styleMask_backing_defer(R, GetStyleMaskFor(GetDesigningBorderStyle(Form), Form.BorderIcons), NSBackingStoreBuffered, False)); UpdateWindowIcons(win, GetDesigningBorderStyle(Form), Form.BorderIcons); win.setLevel(FormStyleToWindowLevel[Form.FormStyle]); win.enableCursorRects; - TCocoaPanel(win).callback := TLCLWindowCallback.Create(win, AWinControl); + TCocoaWindow(win).callback := TLCLWindowCallback.Create(win, AWinControl); win.setDelegate(win); ns := NSStringUtf8(AWinControl.Caption); win.setTitle(ns); @@ -342,7 +344,7 @@ begin R.origin.x := 0; R.origin.y := 0; - cnt := TCocoaCustomControl.alloc.initWithFrame(R); + cnt := TCocoaWindowContent.alloc.initWithFrame(R); cnt.callback := TCocoaPanel(win).callback; win.setContentView(cnt); @@ -356,31 +358,47 @@ begin // TODO: docked forms end; - Result := TLCLIntfHandle(win); + Result := TLCLIntfHandle(cnt); end; class function TCocoaWSCustomForm.GetText(const AWinControl: TWinControl; var AText: String): Boolean; +var + win : NSWindow; begin Result := AWinControl.HandleAllocated; - if Result then - AText := NSStringToString(NSWindow(AWinControl.Handle).title); + if Result then begin + win := TCocoaWindowContent(AWinControl.Handle).lclOwnWindow; + if not Assigned(win) then AText:='' + else AText := NSStringToString(win.title); + end; end; class function TCocoaWSCustomForm.GetTextLen(const AWinControl: TWinControl; var ALength: Integer): Boolean; +var + win : NSWindow; begin Result := AWinControl.HandleAllocated; - if Result then - ALength := NSWindow(AWinControl.Handle).title.length; + if Result then begin + win := TCocoaWindowContent(AWinControl.Handle).lclOwnWindow; + if Assigned(win) then + ALength := NSWindow(AWinControl.Handle).title.length + else + ALength := 0; + end; end; class procedure TCocoaWSCustomForm.SetText(const AWinControl: TWinControl; const AText: String); var ns: NSString; + win : NSWindow; begin if not AWinControl.HandleAllocated then Exit; - ns := NSStringUtf8(AText); - NSwindow(AWinControl.Handle).setTitle(ns); - ns.release; + win:=TCocoaWindowContent(AWinControl.Handle).lclOwnWindow; + if Assigned(win) then begin + ns := NSStringUtf8(AText); + NSwindow(win).setTitle(ns); + ns.release; + end; end; class procedure TCocoaWSCustomForm.CloseModal(const ACustomForm: TCustomForm); @@ -396,33 +414,53 @@ begin end; class procedure TCocoaWSCustomForm.SetAlphaBlend(const ACustomForm: TCustomForm; const AlphaBlend: Boolean; const Alpha: Byte); +var + win : NSWindow; begin - if ACustomForm.HandleAllocated then + if ACustomForm.HandleAllocated then begin + win:=TCocoaWindowContent(ACustomForm.Handle).lclOwnWindow; + if not Assigned(win) then Exit; if AlphaBlend then - NSWindow(ACustomForm.Handle).setAlphaValue(Alpha / 255) + win.setAlphaValue(Alpha / 255) else - NSWindow(ACustomForm.Handle).setAlphaValue(1); + win.setAlphaValue(1); + end; end; class procedure TCocoaWSCustomForm.SetBorderIcons(const AForm: TCustomForm; const ABorderIcons: TBorderIcons); +var + win : NSWindow; begin - if AForm.HandleAllocated then - UpdateWindowMask(NSWindow(AForm.Handle), GetDesigningBorderStyle(AForm), ABorderIcons); + if AForm.HandleAllocated then begin + win:=TCocoaWindowContent(AForm.Handle).lclOwnWindow; + if Assigned(win) then + UpdateWindowMask(win, GetDesigningBorderStyle(AForm), ABorderIcons); + end; end; class procedure TCocoaWSCustomForm.SetFormBorderStyle(const AForm: TCustomForm; const AFormBorderStyle: TFormBorderStyle); +var + win : NSWindow; begin - if AForm.HandleAllocated then - UpdateWindowMask(NSWindow(AForm.Handle), AFormBorderStyle, AForm.BorderIcons); + if AForm.HandleAllocated then begin + win:=TCocoaWindowContent(AForm.Handle).lclOwnWindow; + if Assigned(win) then + UpdateWindowMask(win, AFormBorderStyle, AForm.BorderIcons); + end; end; class procedure TCocoaWSCustomForm.SetFormStyle(const AForm: TCustomform; const AFormStyle, AOldFormStyle: TFormStyle); +var + win : NSWindow; begin - if AForm.HandleAllocated then - NSWindow(AForm.Handle).setLevel(FormStyleToWindowLevel[AFormStyle]); + if AForm.HandleAllocated then begin + win:=TCocoaWindowContent(AForm.Handle).lclOwnWindow; + if Assigned(win) then + win.setLevel(FormStyleToWindowLevel[AFormStyle]); + end; end; class procedure TCocoaWSCustomForm.SetPopupParent( @@ -470,8 +508,9 @@ end; class procedure TCocoaWSCustomForm.SetBounds(const AWinControl: TWinControl; const ALeft, ATop, AWidth, AHeight: Integer); begin - if AWinControl.HandleAllocated then + if AWinControl.HandleAllocated then begin NSObject(AWinControl.Handle).lclSetFrame(Bounds(ALeft, ATop, AWidth, AHeight)); + end; end; end.