From 36b9cd3dbc11dbb9936caa9bf2adc02a70dcfab5 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 14 Jan 2020 01:41:38 +0000 Subject: [PATCH] Merged revision(s) 62539-62541 #f588bfbd67-#f588bfbd67, 62545 #7c70ec7fc5 from trunk: cocoa: fixes for pattern brush drawing, by Yuriy Sydorov. bug #36565 ........ cocoa: manual scroller no longer reports any mouse events (to prevent double events). This matches the TCocoaScrollView implementation. Forcefully syncing scroll position with position of scrollers :) before reporting to LCL. bug #36048 ........ cocoa: adding support for small and large change parameters of a scrollbar ........ cocoa: checkbox and radiobuttons initial value are now set without change notification ........ git-svn-id: branches/fixes_2_0@62546 - --- lcl/interfaces/cocoa/cocoagdiobjects.pas | 94 +++++++++++++--- lcl/interfaces/cocoa/cocoascrollers.pas | 134 +++++------------------ lcl/interfaces/cocoa/cocoawsstdctrls.pas | 58 +++++++--- 3 files changed, 151 insertions(+), 135 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoagdiobjects.pas b/lcl/interfaces/cocoa/cocoagdiobjects.pas index 1a2ac7c27b..1c705939f2 100644 --- a/lcl/interfaces/cocoa/cocoagdiobjects.pas +++ b/lcl/interfaces/cocoa/cocoagdiobjects.pas @@ -119,16 +119,20 @@ type property ColorRef: TColorRef read GetColorRef; end; + TCocoaPatternColorMode = (cpmBitmap, cpmBrushColor, cpmContextColor); + { TCocoaBrush } TCocoaBrush = class(TCocoaColorObject) strict private FCGPattern: CGPatternRef; - FColored: Boolean; + FPatternColorMode: TCocoaPatternColorMode; FBitmap: TCocoaBitmap; FColor: NSColor; + FFgColor: TColorRef; private FImage: CGImageRef; + procedure DrawPattern(c: CGContextRef); strict protected procedure Clear; @@ -3090,11 +3094,8 @@ end; procedure DrawBitmapPattern(info: UnivPtr; c: CGContextRef); MWPascal; var ABrush: TCocoaBrush absolute info; - AImage: CGImageRef; begin - AImage := ABrush.FImage; - CGContextDrawImage(c, GetCGRect(0, 0, CGImageGetWidth(AImage), CGImageGetHeight(AImage)), - AImage); + ABrush.DrawPattern(c); end; procedure TCocoaBrush.SetHatchStyle(AHatch: PtrInt); @@ -3122,11 +3123,11 @@ begin CGDataProvider := CGDataProviderCreateWithData(nil, @HATCH_DATA[AHatch], 8, nil); FImage := CGImageMaskCreate(8, 8, 1, 1, 1, CGDataProvider, nil, 0); CGDataProviderRelease(CGDataProvider); - FColored := False; + FPatternColorMode := cpmBrushColor; if FCGPattern <> nil then CGPatternRelease(FCGPattern); FCGPattern := CGPatternCreate(Self, GetCGRect(0, 0, 8, 8), CGAffineTransformIdentity, 8.0, 8.0, kCGPatternTilingConstantSpacing, - Ord(FColored), ACallBacks); + 0, ACallBacks); end; end; @@ -3134,6 +3135,7 @@ procedure TCocoaBrush.SetBitmap(ABitmap: TCocoaBitmap); var AWidth, AHeight: Integer; ACallBacks: CGPatternCallbacks; + CGDataProvider: CGDataProviderRef; begin AWidth := ABitmap.Width; AHeight := ABitmap.Height; @@ -3142,12 +3144,25 @@ begin if (FBitmap <> nil) then FBitmap.Release; FBitmap := TCocoaBitmap.Create(ABitmap); if FImage <> nil then CGImageRelease(FImage); - FImage := CGImageCreateCopy(MacOSAll.CGImageRef( FBitmap.imageRep.CGImageForProposedRect_context_hints(nil, nil, nil))); - FColored := True; + if FBitmap.BitmapType = cbtMono then + begin + with FBitmap do + begin + CGDataProvider := CGDataProviderCreateWithData(nil, Data, DataSize, nil); + FImage := CGImageMaskCreate(Width, Height, BitsPerSample, BitsPerPixel, BytesPerRow, CGDataProvider, nil, 0); + CGDataProviderRelease(CGDataProvider); + end; + FPatternColorMode := cpmContextColor; + end + else + begin + FImage := CGImageCreateCopy(MacOSAll.CGImageRef( FBitmap.imageRep.CGImageForProposedRect_context_hints(nil, nil, nil))); + FPatternColorMode := cpmBitmap; + end; if FCGPattern <> nil then CGPatternRelease(FCGPattern); FCGPattern := CGPatternCreate(Self, GetCGRect(0, 0, AWidth, AHeight), CGAffineTransformIdentity, CGFloat(AWidth), CGFloat(AHeight), kCGPatternTilingConstantSpacing, - Ord(FColored), ACallBacks); + Ord(FPatternColorMode = cpmBitmap), ACallBacks); end; procedure TCocoaBrush.SetImage(AImage: NSImage); @@ -3159,14 +3174,14 @@ begin ACallBacks.drawPattern := @DrawBitmapPattern; if FImage <> nil then CGImageRelease(FImage); FImage := CGImageCreateCopy(MacOSAll.CGImageRef( AImage.CGImageForProposedRect_context_hints(nil, nil, nil))); - FColored := True; + FPatternColorMode := cpmBitmap; Rect.origin.x := 0; Rect.origin.y := 0; Rect.size := CGSize(AImage.size); if FCGPattern <> nil then CGPatternRelease(FCGPattern); FCGPattern := CGPatternCreate(Self, Rect, CGAffineTransformIdentity, Rect.size.width, Rect.size.height, kCGPatternTilingConstantSpacing, - Ord(FColored), ACallBacks); + 1, ACallBacks); end; procedure TCocoaBrush.SetColor(AColor: NSColor); @@ -3274,6 +3289,22 @@ begin end; end; +procedure TCocoaBrush.DrawPattern(c: CGContextRef); +var + R: CGRect; + sR, sG, sB: single; +begin + R:=CGRectMake(0, 0, CGImageGetWidth(FImage), CGImageGetHeight(FImage)); + if FPatternColorMode = cpmContextColor then + begin + CGContextSetRGBFillColor(c, Red/255, Green/255, Blue/255, 1); + CGContextFillRect(c, R); + ColorToRGBFloat(FFgColor, sR, sG, sB); + CGContextSetRGBFillColor(c, sR, sG, sB, 1); + end; + CGContextDrawImage(c, R, FImage); +end; + procedure TCocoaBrush.Clear; begin if FColor <> nil then @@ -3313,6 +3344,9 @@ var AROP2: Integer; APatternSpace: CGColorSpaceRef; BaseSpace: CGColorSpaceRef; + sR, sG, sB: single; + sz: CGSize; + offset: TPoint; begin if ADC = nil then Exit; @@ -3333,12 +3367,38 @@ begin if Assigned(FCGPattern) then begin - if not FColored then - BaseSpace := CGColorSpaceCreateDeviceRGB - else + // Set proper pattern alignment + offset:=ADC.GetLogicalOffset; + with CGPointApplyAffineTransform(CGPointMake(0,0), CGContextGetCTM(ADC.CGContext)) do begin - BaseSpace := nil; - RGBA[0] := 1.0; + sz.width:=x - offset.X; + sz.height:=y + offset.Y; + sz.width:=Round(sz.width) mod CGImageGetWidth(FImage); + sz.height:=Round(sz.height) mod CGImageGetHeight(FImage); + end; + CGContextSetPatternPhase(ADC.CGContext, sz); + + case FPatternColorMode of + cpmBitmap: + begin + BaseSpace := nil; + RGBA[0] := 1.0; + end; + cpmBrushColor: + begin + BaseSpace := CGColorSpaceCreateDeviceRGB; + end; + cpmContextColor: + begin + BaseSpace := CGColorSpaceCreateDeviceRGB; + SetColor(ADC.BkColor, True); + FFgColor:=ColorToRGB(ADC.TextColor); + ColorToRGBFloat(FFgColor, sR, sG, sB); + RGBA[0]:=sR; + RGBA[1]:=sG; + RGBA[2]:=sB; + RGBA[3]:=1.0; + end; end; APatternSpace := CGColorSpaceCreatePattern(BaseSpace); CGContextSetFillColorSpace(ADC.CGContext, APatternSpace); diff --git a/lcl/interfaces/cocoa/cocoascrollers.pas b/lcl/interfaces/cocoa/cocoascrollers.pas index 2027e3e244..8f90098e46 100644 --- a/lcl/interfaces/cocoa/cocoascrollers.pas +++ b/lcl/interfaces/cocoa/cocoascrollers.pas @@ -67,7 +67,6 @@ type fhscroll : NSScroller; fvscroll : NSScroller; public - isHosted: Boolean; callback: ICommonCallback; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; @@ -93,17 +92,6 @@ type function allocVerticalScroller(avisible: Boolean): NSScroller; message 'allocVerticalScroller:'; // mouse function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override; - procedure mouseDown(event: NSEvent); override; - procedure mouseUp(event: NSEvent); override; - procedure rightMouseDown(event: NSEvent); override; - procedure rightMouseUp(event: NSEvent); override; - procedure rightMouseDragged(event: NSEvent); override; - procedure otherMouseDown(event: NSEvent); override; - procedure otherMouseUp(event: NSEvent); override; - procedure otherMouseDragged(event: NSEvent); override; - procedure mouseDragged(event: NSEvent); override; - procedure mouseMoved(event: NSEvent); override; - procedure scrollWheel(event: NSEvent); override; end; { TCocoaScrollBar } @@ -117,6 +105,8 @@ type maxInt : Integer; pageInt : Integer; suppressLCLMouse: Boolean; + largeInc: Integer; + smallInc: Integer; procedure actionScrolling(sender: NSObject); message 'actionScrolling:'; function IsHorizontal: Boolean; message 'IsHorizontal'; @@ -139,15 +129,14 @@ type procedure mouseDragged(event: NSEvent); override; procedure mouseMoved(event: NSEvent); override; procedure scrollWheel(event: NSEvent); override; - end; { TCocoaManualScrollHost } TCocoaManualScrollHost = objcclass(TCocoaScrollView) - procedure setDocumentView(aview: NSView); override; function lclContentView: NSView; override; function lclClientFrame: TRect; override; + procedure scrollWheel(theEvent: NSEvent); override; end; function isMouseEventInScrollBar(host: TCocoaManualScrollView; event: NSEvent): Boolean; @@ -187,19 +176,26 @@ end; function AdjustScrollerPage(sc: TCocoaScrollBar; prt: NSScrollerPart): Boolean; var - adj : Integer; + adj : integer; sz : Integer; dlt : double; v : double; begin - Result := isIncDecPagePart(prt); - if not Result then Exit; + Result := false; + case prt of + NSScrollerDecrementPage: adj := -sc.largeInc; + NSScrollerIncrementPage: adj := sc.largeInc; + NSScrollerDecrementLine: adj := -sc.smallInc; + NSScrollerIncrementLine: adj := sc.smallInc; + else + adj := 0; + end; + if adj = 0 then Exit; + sz := sc.maxInt - sc.minInt - sc.pageInt; if sz = 0 then Exit; // do nothing! - if sc.pageInt = 0 then dlt := 1 / sz - else dlt := sc.pageInt / sz; - if prt = NSScrollerDecrementPage then dlt := -dlt; + dlt := adj / sz; v := sc.doubleValue; v := v + dlt; @@ -292,13 +288,6 @@ end; { TCocoaManualScrollHost } -procedure TCocoaManualScrollHost.setDocumentView(aview: NSView); -begin - inherited setDocumentView(aview); - if Assigned(aview) and (aview.isKindOfClass(TCocoaManualScrollView)) then - TCocoaManualScrollView(aview).isHosted := true; -end; - function TCocoaManualScrollHost.lclContentView: NSView; begin if Assigned(documentView) then @@ -316,6 +305,16 @@ begin else Result:=inherited lclClientFrame; end; +procedure TCocoaManualScrollHost.scrollWheel(theEvent: NSEvent); +var + nr : NSResponder; +begin + nr := nextResponder; + // do not call inherited scrollWheel, it suppresses the scroll event + if Assigned(nr) then nr.scrollWheel(theEvent) + else inherited scrollWheel(theEvent); +end; + { TCocoaManualScrollView } function TCocoaManualScrollView.lclGetCallback: ICommonCallback; @@ -623,83 +622,6 @@ begin end; end; -procedure TCocoaManualScrollView.mouseDown(event: NSEvent); -begin - if isMouseEventInScrollBar(self, event) or not Assigned(callback) or not callback.MouseUpDownEvent(event) then - begin - inherited mouseDown(event); - end; -end; - -procedure TCocoaManualScrollView.mouseUp(event: NSEvent); -begin - if isMouseEventInScrollBar(self, event) or not Assigned(callback) or not callback.MouseUpDownEvent(event) then - inherited mouseUp(event); -end; - -procedure TCocoaManualScrollView.rightMouseDown(event: NSEvent); -begin - if isMouseEventInScrollBar(self, event) or not Assigned(callback) or not callback.MouseUpDownEvent(event) then - inherited rightMouseDown(event); -end; - -procedure TCocoaManualScrollView.rightMouseUp(event: NSEvent); -begin - if isMouseEventInScrollBar(self, event) or not Assigned(callback) or not callback.MouseUpDownEvent(event) then - inherited rightMouseUp(event); -end; - -procedure TCocoaManualScrollView.rightMouseDragged(event: NSEvent); -begin - if not Assigned(callback) or not callback.MouseUpDownEvent(event) then - inherited rightMouseDragged(event); -end; - -procedure TCocoaManualScrollView.otherMouseDown(event: NSEvent); -begin - if isMouseEventInScrollBar(self, event) or not Assigned(callback) or not callback.MouseUpDownEvent(event) then - inherited otherMouseDown(event); -end; - -procedure TCocoaManualScrollView.otherMouseUp(event: NSEvent); -begin - if isMouseEventInScrollBar(self, event) or not Assigned(callback) or not callback.MouseUpDownEvent(event) then - inherited otherMouseUp(event); -end; - -procedure TCocoaManualScrollView.otherMouseDragged(event: NSEvent); -begin - if not Assigned(callback) or not callback.MouseMove(event) then - inherited otherMouseDragged(event); -end; - -procedure TCocoaManualScrollView.mouseDragged(event: NSEvent); -begin - if not Assigned(callback) or not callback.MouseMove(event) then - inherited mouseDragged(event); -end; - -procedure TCocoaManualScrollView.mouseMoved(event: NSEvent); -begin - if isMouseEventInScrollBar(self, event) then - begin - inherited mouseMoved(event) - end - else if not Assigned(callback) or not callback.MouseMove(event) then - inherited mouseMoved(event); -end; - -procedure TCocoaManualScrollView.scrollWheel(event: NSEvent); -begin - // when hosted, the processing of scrollWheel is duplciated - if not isHosted then - begin - if isMouseEventInScrollBar(self, event) or not Assigned(callback) or not callback.scrollWheel(event) then - inherited scrollWheel(event); - end; -end; - - { TCocoaScrollView } function TCocoaScrollView.lclClientFrame: TRect; @@ -736,6 +658,10 @@ begin if holdscroll>0 then Exit; inc(holdscroll); try + // update scrollers (this is required, if scrollWheel was called) + // so processing LM_xSCROLL will not cause any actually scrolling, + // as the current position will match! + self.reflectScrolledClipView(contentView); if (dx<>0) and assigned(callback) then callback.scroll(false, round(nw.origin.x)); diff --git a/lcl/interfaces/cocoa/cocoawsstdctrls.pas b/lcl/interfaces/cocoa/cocoawsstdctrls.pas index 26b3b386fc..3038ac9baa 100644 --- a/lcl/interfaces/cocoa/cocoawsstdctrls.pas +++ b/lcl/interfaces/cocoa/cocoawsstdctrls.pas @@ -329,6 +329,7 @@ procedure TextViewSetAllignment(txt: NSTextView; align: TAlignment); procedure TextFieldSetAllignment(txt: NSTextField; align: TAlignment); procedure TextFieldSetBorderStyle(txt: NSTextField; astyle: TBorderStyle); procedure RadioButtonSwitchSiblings(checkedRadio: NSButton); +procedure ButtonSetState(btn: NSButton; NewState: TCheckBoxState); procedure ScrollViewSetScrollStyles(AScroll: TCocoaScrollView; AStyles: TScrollStyle); @@ -447,6 +448,20 @@ begin end; end; +procedure ButtonSetState(btn: NSButton; NewState: TCheckBoxState); +const + buttonState: array [TcheckBoxState] of NSInteger = + (NSOffState, NSOnState, NSMixedState); +begin + if NewState = cbGrayed then + {$ifdef BOOLFIX} + btn.setAllowsMixedState_(Ord(true)); + {$else} + btn.setAllowsMixedState(true); + {$endif} + btn.setState(buttonState[NewState]); +end; + procedure ScrollViewSetScrollStyles(AScroll: TCocoaScrollView; AStyles: TScrollStyle); begin AScroll.setHasVerticalScroller(VerticalScrollerVisible[AStyles]); @@ -773,7 +788,8 @@ end; class function TCocoaWSCustomCheckBox.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; var - btn: NSButton; + btn: TCocoaButton; + cb: IButtonCallback; begin btn := AllocButton(AWinControl, TLCLCheckBoxCallBack, AParams, 0, NSSwitchButton); // changes in AllowGrayed are never sent to WS! @@ -784,6 +800,14 @@ begin {$else} NSButton(btn).setAllowsMixedState(true); {$endif} + ; + //todo: This place needs a cleanup! + // Assigning state, while having callback removed + // TCocoaButton.setState is causing OnChange event, if callback is not nil + cb := btn.callback; + btn.callback := nil; + ButtonSetState(btn, TCustomCheckBox(AWinControl).State); + btn.callback := cb; Result := TLCLIntfHandle(btn); end; @@ -820,16 +844,8 @@ class procedure TCocoaWSCustomCheckBox.SetState( const buttonState: array [TcheckBoxState] of NSInteger = (NSOffState, NSOnState, NSMixedState); begin - if ACustomCheckBox.HandleAllocated then - begin - if NewState = cbGrayed then - {$ifdef BOOLFIX} - NSButton(ACustomCheckBox.Handle).setAllowsMixedState_(Ord(true)); - {$else} - NSButton(ACustomCheckBox.Handle).setAllowsMixedState(true); - {$endif} - NSButton(ACustomCheckBox.Handle).setState(buttonState[NewState]); - end; + if not ACustomCheckBox.HandleAllocated then Exit; + ButtonSetState(NSButton(ACustomCheckBox.Handle), ACustomCheckBox.State); end; class procedure TCocoaWSCustomCheckBox.GetPreferredSize( @@ -872,18 +888,28 @@ end; class function TCocoaWSRadioButton.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; var - btn: NSButton; + btn: TCocoaButton; + cb: IButtonCallback; begin btn := AllocButton(AWinControl, TLCLRadioButtonCallback, AParams, 0, NSRadioButton); Result := TLCLIntfHandle(btn); + //todo: this needs to be improved! + cb := btn.callback; + btn.callback := nil; + ButtonSetState(btn, TCustomCheckBox(AWinControl).State); + btn.callback := cb; end; class procedure TCocoaWSRadioButton.SetState( const ACustomCheckBox: TCustomCheckBox; const NewState: TCheckBoxState); +var + btn : NSButton; begin + if not ACustomCheckBox.HandleAllocated then Exit; + btn := NSButton(ACustomCheckBox.Handle); if NewState = cbChecked then - RadioButtonSwitchSiblings(NSButton(ACustomCheckBox.Handle)); - TCocoaWSCustomCheckBox.SetState(ACustomCheckBox, NewState); + RadioButtonSwitchSiblings(btn); + ButtonSetState(btn, NewState); end; { TCocoaWSCustomStaticText } @@ -1939,6 +1965,10 @@ begin scr.minInt:=TCustomScrollBar(AWinControl).Min; scr.maxInt:=TCustomScrollBar(AWinControl).Max; scr.pageInt:=TCustomScrollBar(AWinControl).PageSize; + scr.largeInc:=abs(TCustomScrollBar(AWinControl).LargeChange); + scr.smallInc:=abs(TCustomScrollBar(AWinControl).SmallChange); + if scr.largeInc=0 then scr.largeInc:=1; + if scr.smallInc=0 then scr.smallInc:=1; Result:=TLCLIntfHandle(scr);