{ ----------------------------------------- CocoaThemes.pas - Cocoa Theme support ----------------------------------------- See Themes.pas for licencing and other further information. } unit CocoaThemes; {$mode objfpc}{$H+} {$modeswitch objectivec1} interface uses // rtl Types, Classes, SysUtils, // carbon bindings MacOSAll, // For HiTheme // cocoa bindings CocoaAll, // lcl LCLType, LCLProc, LCLIntf, Graphics, Themes, TmSchema, customdrawndrawers, // widgetset CocoaUtils, CocoaGDIObjects, Cocoa_Extra; type { TCocoaThemeServices } TCocoaThemeServices = class(TThemeServices) private protected callback : NSObject; BtnCell : NSButtonCell; hdrCell : NSTableHeaderCell; function SetButtonCellType(btn: NSButtonCell; Details: TThemedElementDetails): Boolean; procedure SetButtonCellToDetails(btn: NSButtonCell; Details: TThemedElementDetails); procedure CellDrawStart(dst: TCocoaContext; const r: Trect; out cur: NSGraphicsContext; out dstRect: NSRect); procedure CellDrawFrame(cell: NSCell; const dst: NSRect); procedure CellDrawEnd(dst: TCocoaContext; cur: NSGraphicsContext); function InitThemes: Boolean; override; function UseThemes: Boolean; override; function ThemedControlsEnabled: Boolean; override; (* procedure InternalDrawParentBackground({%H-}Window: HWND; {%H-}Target: HDC; {%H-}Bounds: PRect); override; *) function GetDrawState(Details: TThemedElementDetails): ThemeDrawState; function DrawButtonElement(DC: TCocoaContext; Details: TThemedElementDetails; R: TRect; {%H-}ClipRect: PRect): TRect; (* function DrawComboBoxElement(DC: TCarbonDeviceContext; Details: TThemedElementDetails; R: TRect; {%H-}ClipRect: PRect): TRect; *) function DrawHeaderElement(DC: TCocoaContext; Details: TThemedElementDetails; R: TRect; {%H-}ClipRect: PRect): TRect; (* function DrawRebarElement(DC: TCarbonDeviceContext; Details: TThemedElementDetails; R: TRect; {%H-}ClipRect: PRect): TRect;*) function DrawToolBarElement(DC: TCocoaContext; Details: TThemedElementDetails; R: TRect; {%H-}ClipRect: PRect): TRect; function DrawTreeviewElement(DC: TCocoaContext; Details: TThemedElementDetails; R: TRect; {%H-}ClipRect: PRect): TRect; (* function DrawWindowElement(DC: TCarbonDeviceContext; Details: TThemedElementDetails; R: TRect; {%H-}ClipRect: PRect): TRect; *) function GetCellForDetails(Details: TThemedElementDetails): NSCell; public BezelToolBar : NSBezelStyle; BezelButton : NSBezelStyle; constructor Create; destructor Destroy; override; procedure DrawElement(DC: HDC; Details: TThemedElementDetails; const R: TRect; ClipRect: PRect); override; (* procedure DrawEdge({%H-}DC: HDC; {%H-}Details: TThemedElementDetails; const {%H-}R: TRect; {%H-}Edge, {%H-}Flags: Cardinal; {%H-}AContentRect: PRect); override; procedure DrawIcon({%H-}DC: HDC; {%H-}Details: TThemedElementDetails; const {%H-}R: TRect; {%H-}himl: HIMAGELIST; {%H-}Index: Integer); override; procedure DrawText({%H-}DC: HDC; {%H-}Details: TThemedElementDetails; const {%H-}S: String; {%H-}R: TRect; {%H-}Flags, {%H-}Flags2: Cardinal); override; function ContentRect({%H-}DC: HDC; Details: TThemedElementDetails; BoundingRect: TRect): TRect; override; function HasTransparentParts({%H-}Details: TThemedElementDetails): Boolean; override; *) function GetDetailSize(Details: TThemedElementDetails): TSize; override; function GetOption(AOption: TThemeOption): Integer; override; end; implementation type TCocoaThemeCallback = objcclass(NSObject) procedure notifySysColorsChanged(notification: NSNotification); message 'notifySysColorsChanged:'; end; type TCocoaContextAccess = class(TCocoaContext); const IntBool : array [Boolean] of NSInteger = (0,1); { TCocoaThemeServices } {------------------------------------------------------------------------------ Method: TCocoaThemeServices.GetDrawState Params: Details - Details for themed element Returns: Draw state of the themed element passed ------------------------------------------------------------------------------} function TCocoaThemeServices.GetDrawState(Details: TThemedElementDetails): ThemeDrawState; { kThemeStateInactive = 0; kThemeStateActive = 1; kThemeStatePressed = 2; kThemeStateRollover = 6; kThemeStateUnavailable = 7; kThemeStateUnavailableInactive = 8; kThemeStatePressedUp = 2; draw with up pressed (increment/decrement buttons) kThemeStatePressedDown = 3; draw with down pressed (increment/decrement buttons) } begin if IsDisabled(Details) then Result := kThemeStateInactive else if IsPushed(Details) then Result := kThemeStatePressed else if IsHot(Details) then Result := kThemeStateRollover else Result := kThemeStateActive; end; (* {------------------------------------------------------------------------------ Method: TCarbonThemeServices.DrawComboBoxElement Params: DC - Carbon device context Details - Details for themed element R - Bounding rectangle ClipRect - Clipping rectangle Returns: ClientRect Draws a ComboBox element with native Carbon look ------------------------------------------------------------------------------} function TCarbonThemeServices.DrawComboBoxElement(DC: TCarbonDeviceContext; Details: TThemedElementDetails; R: TRect; ClipRect: PRect): TRect; var ButtonDrawInfo: HIThemeButtonDrawInfo; BoundsRect: HIRect; NewHeight: Integer; BtnWidth: Integer; begin ButtonDrawInfo.version := 0; ButtonDrawInfo.State := GetDrawState(Details); ButtonDrawInfo.value := kThemeButtonOn; ButtonDrawInfo.adornment := kThemeAdornmentNone; BoundsRect := RectToCGRect(R); NewHeight := GetCarbonThemeMetric(kThemeMetricPopupButtonHeight); BtnWidth := GetCarbonThemeMetric(kThemeMetricComboBoxLargeDisclosureWidth); ButtonDrawInfo.kind := kThemeComboBox; if BoundsRect.size.height < NewHeight then begin NewHeight := GetCarbonThemeMetric(kThemeMetricSmallPopupButtonHeight); BtnWidth := GetCarbonThemeMetric(kThemeMetricComboBoxSmallDisclosureWidth); ButtonDrawInfo.kind := kThemeComboBoxSmall; end; if BoundsRect.size.height < NewHeight then begin NewHeight := GetCarbonThemeMetric(kThemeMetricMiniPopupButtonHeight); BtnWidth := GetCarbonThemeMetric(kThemeMetricComboBoxMiniDisclosureWidth); ButtonDrawInfo.kind := kThemeComboBoxMini; end; OSError( HIThemeDrawButton(BoundsRect, ButtonDrawInfo, DC.CGContext, kHIThemeOrientationNormal, nil), Self, 'DrawComboBoxElement', 'HIThemeDrawButton'); BoundsRect.size.height := NewHeight + 1; BoundsRect.size.width := BoundsRect.size.width - BtnWidth; Result := CGRectToRect(BoundsRect); end;*) {------------------------------------------------------------------------------ Method: TCocoaThemeServices.DrawButtonElement Params: DC - Carbon device context Details - Details for themed element R - Bounding rectangle ClipRect - Clipping rectangle Returns: ClientRect Draws a button element with native look Button kinds: BP_PUSHBUTTON kThemeRoundedBevelButton, BP_RADIOBUTTON kThemeRadioButton, BP_CHECKBOX kThemeCheckBox, BP_GROUPBOX kHIThemeGroupBoxKindPrimary, BP_USERBUTTON kThemeRoundedBevelButton ------------------------------------------------------------------------------} function TCocoaThemeServices.DrawButtonElement(DC: TCocoaContext; Details: TThemedElementDetails; R: TRect; ClipRect: PRect): TRect; var lCanvas: TCanvas; lSize: TSize; lCDButton: TCDButtonStateEx; lState: TCDControlState = []; lDrawer: TCDDrawer; lPt: TPoint; cur : NSGraphicsContext; nsr : NSRect; b : NSButtonCell; begin b := NSButtonCell.alloc.initTextCell(NSString.string_); try // ideally, all uttons are drawn via Cocoa if SetButtonCellType(b, Details) then begin SetButtonCellToDetails(b, Details); CellDrawStart(DC, R, cur, nsr); CellDrawFrame(b, nsr); CellDrawEnd(DC, cur); Exit; end; finally b.release; end; lCDButton := TCDButtonStateEx.Create; lCanvas := TCanvas.Create; try lSize.CX := R.Right - R.Left; lSize.CY := R.Bottom - R.Top; if IsHot(Details) then lState += [csfMouseOver]; if IsPushed(Details) then lState += [csfSunken]; if IsChecked(Details) then lState += [csfSunken]; if not IsDisabled(Details) then lState += [csfEnabled]; lDrawer := GetDrawer(dsMacOSX); lCanvas.Handle := HDC(DC); lPt := Types.Point(R.Left, R.Top); if Details.Part = BP_GROUPBOX then lDrawer.DrawGroupBox(lCanvas, lPt, lSize, lState, lCDButton) else lDrawer.DrawButton(lCanvas, lPt, lSize, lState, lCDButton); Result := R; finally lCDButton.Free; lCanvas.Handle := 0; lCanvas.Free; end; end; {------------------------------------------------------------------------------ Method: TCocoaThemeServices.DrawHeaderElement Params: DC - Carbon device context Details - Details for themed element R - Bounding rectangle ClipRect - Clipping rectangle Returns: ClientRect Draws a header (THeaderControl same as ListView header) element with native macOS look ------------------------------------------------------------------------------} function TCocoaThemeServices.DrawHeaderElement(DC: TCocoaContext; Details: TThemedElementDetails; R: TRect; ClipRect: PRect): TRect; var cur : NSGraphicsContext; nsr : NSRect; begin if (HdrCell=nil) then begin hdrCell := NSTableHeaderCell.alloc.initTextCell(NSSTR('')); end; CellDrawStart(DC, R, cur, nsr); // this draws a header background hdrCell.setDrawsBackground(true); hdrCell.drawInteriorWithFrame_inView(nsr,nil); hdrCell.highlight_withFrame_inView(true, nsr, nil); // this draws a nice cell separator hdrCell.setDrawsBackground(false); CellDrawFrame(hdrCell, nsr); CellDrawEnd(DC, cur); Result := R; end; {------------------------------------------------------------------------------ Method: TCarbonThemeServices.DrawRebarElement Params: DC - Carbon device context Details - Details for themed element R - Bounding rectangle ClipRect - Clipping rectangle Returns: ClientRect Draws a rebar element (splitter) with native Carbon look ------------------------------------------------------------------------------} (* function TCarbonThemeServices.DrawRebarElement(DC: TCarbonDeviceContext; Details: TThemedElementDetails; R: TRect; ClipRect: PRect): TRect; var SplitterInfo: HIThemeSplitterDrawInfo; PlacardInfo: HIThemePlacardDrawInfo; ARect: HIRect; const SName = 'DrawRebarElement'; begin ARect := RectToCGRect(R); if Details.Part in [RP_GRIPPER, RP_GRIPPERVERT] then begin SplitterInfo.version := 0; SplitterInfo.State := kThemeStateActive; SplitterInfo.adornment := kHiThemeSplitterAdornmentNone; OSError( HIThemeDrawPaneSplitter(ARect, SplitterInfo, DC.CGContext, kHIThemeOrientationNormal), Self, SName, 'HIThemeDrawPaneSplitter'); end else if Details.Part = RP_BAND then begin PlacardInfo.version := 0; PlacardInfo.State := GetDrawState(Details); OSError( HIThemeDrawPlacard(ARect, PlacardInfo, DC.CGContext, kHIThemeOrientationNormal), Self, SName, 'HIThemeDrawPlacard'); end; Result := CGRectToRect(ARect); end;*) {------------------------------------------------------------------------------ Method: TCarbonThemeServices.DrawToolBarElement Params: DC - Carbon device context Details - Details for themed element R - Bounding rectangle ClipRect - Clipping rectangle Returns: ClientRect Draws a tool bar element with native Carbon look ------------------------------------------------------------------------------} function TCocoaThemeServices.DrawToolBarElement(DC: TCocoaContext; Details: TThemedElementDetails; R: TRect; ClipRect: PRect): TRect; var lCanvas: TCanvas; lSize: TSize; lCDToolbarItem: TCDToolBarItem; lCDToolbar: TCDToolBarStateEx; lDrawer: TCDDrawer; cur : NSGraphicsContext; nsr : NSRect; begin if Details.Part = TP_BUTTON then begin //BtnCell.setBezeled(true); SetButtonCellType(BtnCell, Details); SetButtonCellToDetails(BtnCell, Details); CellDrawStart(DC, R, cur, nsr); CellDrawFrame(btnCell, nsr); CellDrawEnd(DC, cur); Result := R; end else begin lCDToolbarItem := TCDToolBarItem.Create; lCDToolbar := TCDToolBarStateEx.Create; lCanvas := TCanvas.Create; try lSize.CX := R.Right - R.Left; lSize.CY := R.Bottom - R.Top; case Details.Part of TP_BUTTON, TP_DROPDOWNBUTTON, TP_SPLITBUTTON: begin lCDToolbarItem.Kind := tikButton; lCDToolbarItem.Width := lSize.CX; end; TP_SPLITBUTTONDROPDOWN: begin lCDToolbarItem.Kind := tikDropDownButton; lCDToolbarItem.SubpartKind := tiskArrow; lCDToolbarItem.Width := -1; end //TP_SEPARATOR, TP_SEPARATORVERT, TP_DROPDOWNBUTTONGLYPH: // tikSeparator, tikDivider else Exit; end; lCDToolbarItem.Down := IsChecked(Details); lCDToolbarItem.State := []; if IsHot(Details) then lCDToolbarItem.State := lCDToolbarItem.State + [csfMouseOver]; if IsPushed(Details) then lCDToolbarItem.State := lCDToolbarItem.State + [csfSunken]; if IsChecked(Details) then lCDToolbarItem.State := lCDToolbarItem.State + [csfSunken]; if not IsDisabled(Details) then lCDToolbarItem.State := lCDToolbarItem.State + [csfEnabled]; lCDToolbar.ToolBarHeight := lSize.CY; lDrawer := GetDrawer(dsMacOSX); lCanvas.Handle := HDC(DC); lDrawer.DrawToolBarItem(lCanvas, lSize, lCDToolbarItem, R.Left, R.Top, lCDToolbarItem.State, lCDToolbar); Result := R; finally lCDToolbarItem.Free; lCDToolbar.Free; lCanvas.Handle := 0; lCanvas.Free; end; end; end; function TCocoaThemeServices.DrawTreeviewElement(DC: TCocoaContext; Details: TThemedElementDetails; R: TRect; ClipRect: PRect): TRect; var ButtonDrawInfo: HIThemeButtonDrawInfo; LabelRect: HIRect; lBrush: TCocoaBrush; lOldBrush: HBRUSH; lPen: TCocoaPen; lOldPen: HGDIOBJ; lColor: NSColor; lPoints: array of TPoint; begin case Details.Part of TVP_TREEITEM: begin case Details.State of TREIS_NORMAL: lColor := ColorToNSColor(ColorToRGB(clWindow)); TREIS_HOT: lColor := ColorToNSColor(ColorToRGB(clHotLight)); TREIS_SELECTED: lColor := ColorToNSColor(ColorToRGB(clHighlight)); TREIS_DISABLED: lColor := ColorToNSColor(ColorToRGB(clWindow)); TREIS_SELECTEDNOTFOCUS: lColor := NSColor.secondarySelectedControlColor; TREIS_HOTSELECTED: lColor := ColorToNSColor(ColorToRGB(clHighlight)); else lColor := NSColor.blackColor; end; lBrush := TCocoaBrush.Create(lColor, False); DC.Rectangle(R.Left, R.Top, R.Right, R.Bottom, True, lBrush); lBrush.Free; Result := R; end; TVP_GLYPH, TVP_HOTGLYPH: begin // HIThemeDrawButton exists only in 32-bits and there is no Cocoa alternative =( {.$define CocoaUseHITheme} {$ifdef CocoaUseHITheme} {$ifdef CPU386} ButtonDrawInfo.version := 0; ButtonDrawInfo.State := GetDrawState(Details); ButtonDrawInfo.kind := kThemeDisclosureTriangle; if Details.State = GLPS_CLOSED then ButtonDrawInfo.value := kThemeDisclosureRight else ButtonDrawInfo.value := kThemeDisclosureDown; ButtonDrawInfo.adornment := kThemeAdornmentNone; LabelRect := RectToCGRect(R); // Felipe: Manual calibration to make TTreeView look right, strange that this is needed =/ LabelRect.origin.x := LabelRect.origin.x - 2; LabelRect.origin.y := LabelRect.origin.y - 1; HIThemeDrawButton(LabelRect, ButtonDrawInfo, DC.CGContext(), kHIThemeOrientationNormal, @LabelRect); Result := CGRectToRect(LabelRect); {$endif} {$else} SetLength(lPoints, 3); // face right if Details.State = GLPS_CLOSED then begin lPoints[0] := Types.Point(R.Left+1, R.Top); lPoints[1] := Types.Point(R.Left+1, R.Bottom-2); lPoints[2] := Types.Point(R.Right-1, (R.Top + R.Bottom-2) div 2); end // face down else begin lPoints[0] := Types.Point(R.Left, R.Top); lPoints[1] := Types.Point(R.Right-2, R.Top); lPoints[2] := Types.Point((R.Left + R.Right-2) div 2, R.Bottom-2); end; // select the appropriate brush & pen lColor := ColorToNSColor(Graphics.RGBToColor(121, 121, 121)); lBrush := TCocoaBrush.Create(lColor, False); lOldBrush := LCLIntf.SelectObject(HDC(DC), HGDIOBJ(lBrush)); lPen := TCocoaPen.Create(Graphics.RGBToColor(121, 121, 121), False); lOldPen := LCLIntf.SelectObject(HDC(DC), HGDIOBJ(lPen)); // Draw the triangle DC.Polygon(lPoints, 3, True); // restore the old brush and pen LCLIntf.SelectObject(HDC(DC), lOldBrush); LCLIntf.SelectObject(HDC(DC), lOldPen); lBrush.Free; lPen.Free; Result := R; {$endif} end; else Result := Bounds(0,0,0,0); end; end; function TCocoaThemeServices.GetCellForDetails(Details: TThemedElementDetails): NSCell; var btn : NSButtonCell; cocoaBtn : Integer; begin //todo: instead of recreating btn all the time, should it be cached instead? // typically as soon as a themed drawing stared, it would be ongoing // (i.e. customly drawn/themed controls) Result := nil; if Details.Element <> teButton then Exit; case Details.Part of BP_CHECKBOX: cocoaBtn := NSSwitchButton; BP_RADIOBUTTON: cocoaBtn := NSRadioButton; BP_PUSHBUTTON: cocoaBtn := NSMomentaryLightButton; else cocoaBtn := -1; end; if cocoaBtn < 0 then Exit; // unsupported button type btn := NSButtonCell(NSButtonCell.alloc).initTextCell(NSSTR('')); btn.setButtonType(NSButtonType(cocoaBtn)); SetButtonCellToDetails(btn, Details); btn.autorelease; Result := btn; end; constructor TCocoaThemeServices.Create; begin inherited Create; BtnCell := NSButtonCell.alloc.initTextCell(NSSTR('')); BezelToolBar := NSSmallSquareBezelStyle; // can be resized at any size BezelButton := NSSmallSquareBezelStyle; callback := TCocoaThemeCallback.alloc.init; NSNotificationCenter(NSNotificationCenter.defaultCenter).addObserver_selector_name_object( callback, ObjCSelector('notifySysColorsChanged:'), NSSystemColorsDidChangeNotification, nil ); end; destructor TCocoaThemeServices.Destroy; begin NSNotificationCenter(NSNotificationCenter.defaultCenter).removeObserver(callback); callback.release; if (HdrCell<>nil) then hdrCell.Release; inherited Destroy; end; (*function TCarbonThemeServices.DrawWindowElement(DC: TCarbonDeviceContext; Details: TThemedElementDetails; R: TRect; ClipRect: PRect): TRect; var WindowDrawInfo: HIThemeWindowDrawInfo; WindowWidgetDrawInfo: HIThemeWindowWidgetDrawInfo; BtnRect: HIRect; WindowShape: HIShapeRef; WindowRegion: WindowRegionCode; Offset: TPoint; begin WindowWidgetDrawInfo.version := 0; WindowWidgetDrawInfo.windowState := kThemeStateActive; WindowWidgetDrawInfo.windowType := kThemeDocumentWindow; WindowWidgetDrawInfo.widgetState := GetDrawState(Details); WindowWidgetDrawInfo.titleHeight := 0; WindowWidgetDrawInfo.titleWidth := 0; WindowWidgetDrawInfo.attributes := kThemeWindowHasFullZoom or kThemeWindowHasCloseBox or kThemeWindowHasCollapseBox; case Details.Part of WP_MINBUTTON, WP_MDIMINBUTTON: begin WindowWidgetDrawInfo.widgetType := kThemeWidgetCollapseBox; WindowRegion := kWindowCollapseBoxRgn; end; WP_MAXBUTTON: begin WindowWidgetDrawInfo.widgetType := kThemeWidgetZoomBox; WindowRegion := kWindowZoomBoxRgn; end; WP_CLOSEBUTTON, WP_SMALLCLOSEBUTTON, WP_MDICLOSEBUTTON: begin WindowWidgetDrawInfo.widgetType := kThemeWidgetCloseBox; WindowRegion := kWindowCloseBoxRgn; end; WP_RESTOREBUTTON, WP_MDIRESTOREBUTTON: begin WindowWidgetDrawInfo.widgetType := kThemeWidgetZoomBox; WindowRegion := kWindowZoomBoxRgn; end; else Exit; end; // We have a button rectanle but carbon expects from us a titlebar rectangle, // so we need to translate one coordinate to another BtnRect := RectToCGRect(Types.Rect(0, 0, 100, 100)); WindowDrawInfo.version := 0; WindowDrawInfo.windowType := WindowWidgetDrawInfo.windowType; WindowDrawInfo.attributes := WindowWidgetDrawInfo.attributes; WindowDrawInfo.state := WindowWidgetDrawInfo.windowState; WindowDrawInfo.titleHeight := WindowWidgetDrawInfo.titleHeight; WindowDrawInfo.titleWidth := WindowWidgetDrawInfo.titleWidth; WindowShape:=nil; HIThemeGetWindowShape(BtnRect, WindowDrawInfo, WindowRegion, WindowShape); HIShapeGetBounds(WindowShape, BtnRect); Offset := CGRectToRect(BtnRect).TopLeft; OffsetRect(R, -Offset.X, -Offset.Y); BtnRect := RectToCGRect(R); OSError( HIThemeDrawTitleBarWidget(BtnRect, WindowWidgetDrawInfo, DC.CGContext, kHIThemeOrientationNormal), Self, 'DrawTreeviewElement', 'HIThemeDrawButton'); Result := CGRectToRect(BtnRect); OffsetRect(Result, Offset.X, Offset.Y); end; *) {------------------------------------------------------------------------------ Method: TCocoaThemeServices.InitThemes Returns: If the themes are initialized ------------------------------------------------------------------------------} function TCocoaThemeServices.InitThemes: Boolean; begin Result := True; end; {------------------------------------------------------------------------------ Method: TCocoaThemeServices.InitThemes Returns: If the themes have to be used ------------------------------------------------------------------------------} function TCocoaThemeServices.UseThemes: Boolean; begin Result := True; end; {------------------------------------------------------------------------------ Method: TCocoaThemeServices.ThemedControlsEnabled Returns: If the themed controls are enabled ------------------------------------------------------------------------------} function TCocoaThemeServices.ThemedControlsEnabled: Boolean; begin Result := True; end; (* {------------------------------------------------------------------------------ Method: TCarbonThemeServices.ContentRect Params: DC - Carbon device context Details - Details for themed element BoundingRect - Bounding rectangle Returns: Content rectangle of the passed themed element ------------------------------------------------------------------------------} function TCarbonThemeServices.ContentRect(DC: HDC; Details: TThemedElementDetails; BoundingRect: TRect): TRect; begin case Details.Element of teComboBox: Result := DrawComboBoxElement(DefaultContext, Details, BoundingRect, nil); teHeader: Result := DrawHeaderElement(DefaultContext, Details, BoundingRect, nil); teButton: Result := DrawButtonElement(DefaultContext, Details, BoundingRect, nil); teRebar: Result := DrawRebarElement(DefaultContext, Details, BoundingRect, nil); teToolBar: Result := DrawToolBarElement(DefaultContext, Details, BoundingRect, nil); teWindow: Result := DrawWindowElement(DefaultContext, Details, BoundingRect, nil); end; end; {------------------------------------------------------------------------------ Method: TCarbonThemeServices.DrawEdge Params: DC - Carbon device context Details - Details for themed element R - Bounding rectangle Edge - Type of edge Flags - Type of border Draws an edge with native Carbon look ------------------------------------------------------------------------------} procedure TCarbonThemeServices.DrawEdge(DC: HDC; Details: TThemedElementDetails; const R: TRect; Edge, Flags: Cardinal; AContentRect: PRect); begin end; *) {------------------------------------------------------------------------------ Method: TCocoaThemeServices.DrawElement Params: DC - Cocoa device context Details - Details for themed element R - Bounding rectangle ClipRect - Clipping rectangle Draws an element with native Aqua look ------------------------------------------------------------------------------} procedure TCocoaThemeServices.DrawElement(DC: HDC; Details: TThemedElementDetails; const R: TRect; ClipRect: PRect); var Context: TCocoaContext absolute DC; begin if CheckDC(DC, 'TCocoaThemeServices.DrawElement') then begin case Details.Element of //teComboBox: DrawComboBoxElement(Context, Details, R, ClipRect); teButton: DrawButtonElement(Context, Details, R, ClipRect); teHeader: DrawHeaderElement(Context, Details, R, ClipRect); {teRebar: DrawRebarElement(Context, Details, R, ClipRect);} teToolBar: DrawToolBarElement(Context, Details, R, ClipRect); teTreeview: DrawTreeviewElement(Context, Details, R, ClipRect); // teWindow: DrawWindowElement(Context, Details, R, ClipRect); else //inherited DrawElement(DC, Details, R, ClipRect); this generates an endless loop end; end; end; function TCocoaThemeServices.GetDetailSize(Details: TThemedElementDetails ): TSize; var cl : NSCell; sz : NSSize; begin cl := GetCellForDetails(Details); if Assigned(cl) then begin sz:=cl.cellSize; Result.cx:=Round(sz.width); Result.cy:=Round(sz.height); end else if (Details.Element=teHeader) and (Details.Part=HP_HEADERSORTARROW) then begin Result.cx:=-1; // not supported yet Result.cy:=-1; end else Result:=inherited GetDetailSize(Details); end; (* {------------------------------------------------------------------------------ Method: TCarbonThemeServices.DrawIcon Params: DC - Carbon device context Details - Details for themed element R - Bounding rectangle himl - Image list Index - Icon index Draws an icon with native Carbon look ------------------------------------------------------------------------------} procedure TCarbonThemeServices.DrawIcon(DC: HDC; Details: TThemedElementDetails; const R: TRect; himl: HIMAGELIST; Index: Integer); begin end; {------------------------------------------------------------------------------ Method: TCarbonThemeServices.HasTransparentParts Params: Details - Details for themed element Returns: If the themed element has transparent parts ------------------------------------------------------------------------------} function TCarbonThemeServices.HasTransparentParts(Details: TThemedElementDetails): Boolean; begin Result := True; end; function TCarbonThemeServices.GetDetailSize(Details: TThemedElementDetails): TSize; const DefaultPushButtonWidth = 70; var BtnRect: CGRect; WindowDrawInfo: HIThemeWindowDrawInfo; WindowShape: HIShapeRef; begin case Details.Element of teTreeView: if (Details.Part in [TVP_GLYPH, TVP_HOTGLYPH]) then begin Result := Types.Size( GetCarbonThemeMetric(kThemeMetricDisclosureTriangleWidth), GetCarbonThemeMetric(kThemeMetricDisclosureTriangleHeight) ); end else Result := inherited GetDetailSize(Details); teButton: if Details.Part = BP_PUSHBUTTON then begin Result := Types.Size( DefaultPushButtonWidth, GetCarbonThemeMetric(kThemeMetricPushButtonHeight) ); end else Result := inherited GetDetailSize(Details); teWindow: if (Details.Part in [WP_MINBUTTON, WP_MDIMINBUTTON, WP_MAXBUTTON, WP_CLOSEBUTTON, WP_SMALLCLOSEBUTTON, WP_MDICLOSEBUTTON, WP_RESTOREBUTTON, WP_MDIRESTOREBUTTON]) then begin BtnRect := RectToCGRect(Types.Rect(0, 0, 100, 100)); WindowDrawInfo.version := 0; WindowDrawInfo.windowType := kThemeDocumentWindow; WindowDrawInfo.attributes := kThemeWindowHasFullZoom or kThemeWindowHasCloseBox or kThemeWindowHasCollapseBox; WindowDrawInfo.state := kThemeStateActive; WindowDrawInfo.titleHeight := 0; WindowDrawInfo.titleWidth := 0; WindowShape:=nil; HIThemeGetWindowShape(BtnRect, WindowDrawInfo, kWindowCloseBoxRgn, WindowShape); HIShapeGetBounds(WindowShape, BtnRect); with BtnRect.size do begin Result.cx := Round(width); Result.cy := Round(height); end; end else Result := inherited GetDetailSize(Details); else Result := inherited GetDetailSize(Details); end; end; *) function TCocoaThemeServices.GetOption(AOption: TThemeOption): Integer; begin case AOption of toShowButtonImages: Result := 0; toShowMenuImages: Result := 0; else Result := inherited GetOption(AOption); end; end; (* {------------------------------------------------------------------------------ Method: TCarbonThemeServices.InternalDrawParentBackground Params: Window - Handle to window Target - Carbon device context Bounds - Bounding rectangle Draws the parent background with native Carbon look ------------------------------------------------------------------------------} procedure TCarbonThemeServices.InternalDrawParentBackground(Window: HWND; Target: HDC; Bounds: PRect); begin // ? end; {------------------------------------------------------------------------------ Method: TCarbonThemeServices.DrawText Params: DC - Carbon device context Details - Details for themed element S - Text string to darw R - Bounding rectangle Flags - Draw flags Flags2 - Extra draw flags Draws the passed text with native Carbon look ------------------------------------------------------------------------------} procedure TCarbonThemeServices.DrawText(DC: HDC; Details: TThemedElementDetails; const S: String; R: TRect; Flags, Flags2: Cardinal); begin // end; *) function TCocoaThemeServices.SetButtonCellType(btn: NSButtonCell; Details: TThemedElementDetails): Boolean; var BtnType : NSButtonType; BezelStyle : NSBezelStyle; useBezel : Boolean; begin Result := true; BtnType := NSMomentaryPushButton; BezelStyle := NSRoundedBezelStyle; useBezel := false; if Details.Element = teToolBar then begin BtnType := NSToggleButton; // it usesd to be NSOnOffButton, but NSOnOffButton is not transparent. BezelStyle := BezelToolBar; useBezel := true; end else if Details.Element = teButton then begin useBezel := false; case Details.Part of BP_CHECKBOX: BtnType := NSSwitchButton; BP_RADIOBUTTON: BtnType := NSRadioButton; BP_PUSHBUTTON: begin BtnType := NSPushOnPushOffButton; BezelStyle := BezelButton; useBezel := true; end; else Result := false; end; end else Result := false; if Result then begin btn.setButtonType(BtnType); btn.setBezelStyle(BezelStyle); btn.setBezeled(useBezel); end; end; procedure TCocoaThemeServices.SetButtonCellToDetails(btn: NSButtonCell; Details: TThemedElementDetails); begin btn.setCellAttribute_to(NSCellAllowsMixedState, 1); btn.setCellAttribute_to(NSCellDisabled, IntBool[IsDisabled(Details)]); //btn.setHighlighted( IsHot(Details) ); btn.setHighlighted( IsPushed(Details)); if ((Details.Element = teToolBar) and (not IsPushed(Details)) and (not IsChecked(Details))) or ((Details.Element = teButton) and (Details.Part = BP_CHECKBOX)) then // this is "flat" mode. So unpushed state should draw no borders btn.setBordered(false) else btn.setBordered(true); if IsMixed(Details) then begin btn.setState(NSMixedState); end else if IsChecked(Details) then begin btn.setIntValue(1) end else btn.setIntValue(0); end; procedure TCocoaThemeServices.CellDrawStart(dst: TCocoaContext; const r: Trect; out cur: NSGraphicsContext; out dstRect: NSRect); var acc : TCocoaContextAccess absolute dst; begin cur := NSGraphicsContext.currentContext; NSGraphicsContext.setCurrentContext( acc.ctx ); acc.SetCGFillping(acc.CGContext, 0, -acc.Size.cy); LCLToNSRect( R, acc.size.cy, dstRect); end; procedure TCocoaThemeServices.CellDrawFrame(cell: NSCell; const dst: NSRect); begin cell.drawWithFrame_inView(dst, nil); end; procedure TCocoaThemeServices.CellDrawEnd(dst: TCocoaContext; cur: NSGraphicsContext); var acc : TCocoaContextAccess absolute dst; begin acc.SetCGFillping(acc.CGContext, 0, -acc.Size.cy); NSGraphicsContext.setCurrentContext( cur ); end; { TCocoaThemeCallback } procedure TCocoaThemeCallback.notifySysColorsChanged(notification: NSNotification); begin ThemeServices.UpdateThemes; Graphics.UpdateHandleObjects; ThemeServices.IntfDoOnThemeChange; end; end.