{ ------------------------------------------- CarbonButtons.pp - Carbon buttons classes ------------------------------------------- ***************************************************************************** This file is part of the Lazarus Component Library (LCL) See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** } unit CarbonButtons; {$mode objfpc}{$H+} interface // defines {$I carbondefines.inc} uses // rtl+ftl Classes, SysUtils, // carbon bindings MacOSAll, // LCL Carbon CarbonPrivate, CarbonProc, CarbonDbgConsts, // LCL LCLMessageGlue, LCLType, LCLProc, Graphics; type TUpdateValueEvent = procedure (Sender: TObject; CurrentValue: Integer; var AValue: Integer) of object; { TCarbonCustomCheckBox } TCarbonCustomCheckBox = class(TCarbonControl) private fSupressNotify : Boolean; LastState: Integer; isSetState: Boolean; public UpdateValue: TUpdateValueEvent; class function GetValidEvents: TCarbonControlEvents; override; procedure Hit({%H-}AControlPart: ControlPartCode); override; procedure ValueChanged; override; function RetrieveState: Integer; virtual; procedure SetState(AState: Integer; NotifyChangeState: Boolean); virtual; end; { TCarbonCheckBox } TCarbonCheckBox = class(TCarbonCustomCheckBox) protected procedure CreateWidget(const AParams: TCreateParams); override; public procedure BoundsChanged; override; end; { TCarbonToggleBox } TCarbonToggleBox = class(TCarbonCustomCheckBox) protected procedure CreateWidget(const AParams: TCreateParams); override; end; { TCarbonRadioButton } TCarbonRadioButton = class(TCarbonCustomCheckBox) protected procedure CreateWidget(const AParams: TCreateParams); override; public procedure ValueChanged; override; procedure BoundsChanged; override; end; { TCarbonCustomButton } TCarbonCustomButton = class(TCarbonControl) public class function GetValidEvents: TCarbonControlEvents; override; procedure Hit({%H-}AControlPart: ControlPartCode); override; public procedure SetDefault(ADefault: Boolean); virtual; end; { TCarbonButton } TCarbonButton = class(TCarbonCustomButton) protected procedure CreateWidget(const AParams: TCreateParams); override; public procedure BoundsChanged; override; end; { TCarbonBitBtn } TCarbonBitBtn = class(TCarbonCustomButton) private fPlacement : ControlButtonTextPlacement; fAlignment : ControlButtonTextAlignment; CustomFont : Boolean; protected procedure CreateWidget(const AParams: TCreateParams); override; procedure UpdateButtonStyle; public function GetPreferredSize: TPoint; override; procedure SetFont(const AFont: TFont); override; procedure SetGlyph(Glyph: CGImageRef); virtual; procedure SetLayout(APlacement: ControlButtonTextPlacement; ATextAlign: ControlButtonTextAlignment); virtual; procedure SetDefault({%H-}ADefault: Boolean); override; function GetBounds(var ARect:TRect):Boolean;override; function SetBounds(const ARect: TRect): Boolean; override; end; implementation { TCarbonCustomCheckBox } {------------------------------------------------------------------------------ Method: TCarbonCustomCheckBox.GetValidEvents Returns: Set of events with installed handlers ------------------------------------------------------------------------------} class function TCarbonCustomCheckBox.GetValidEvents: TCarbonControlEvents; begin Result := [cceValueChanged, cceHit]; end; {------------------------------------------------------------------------------ Method: TCarbonCustomCheckBox.Hit Params: AControlPart - Hitted control part Hit event handler ------------------------------------------------------------------------------} procedure TCarbonCustomCheckBox.Hit(AControlPart: ControlPartCode); begin // do nothing, because value changed will be fired immediately end; {------------------------------------------------------------------------------ Method: TCarbonCustomCheckBox.ValueChanged Value changed event handler ------------------------------------------------------------------------------} procedure TCarbonCustomCheckBox.ValueChanged; var NewState: Integer; RS: Integer; begin if not isSetState and Assigned(UpdateValue) then begin RS:=RetrieveState; NewState:=RS; UpdateValue(Self, LastState, NewState); if NewState<>RS then SetValue(NewState); end; LastState:=RetrieveState; if not fSupressNotify then LCLSendChangedMsg(LCLObject) else fSupressNotify := False; end; {------------------------------------------------------------------------------ Method: TCarbonCustomCheckBox.RetrieveState Returns: State of Carbon custom check box ------------------------------------------------------------------------------} function TCarbonCustomCheckBox.RetrieveState: Integer; begin Result := GetControl32BitValue(ControlRef(Widget)); end; {------------------------------------------------------------------------------ Method: TCarbonCustomCheckBox.SetState Params: AState - New state Sets the new state of Carbon custom check box ------------------------------------------------------------------------------} procedure TCarbonCustomCheckBox.SetState(AState: Integer; NotifyChangeState: Boolean); begin if RetrieveState=AState then Exit; isSetState:=True; fSupressNotify := not NotifyChangeState; SetControl32BitValue(ControlRef(Widget), AState); LastState:=AState; isSetState:=False; end; { TCarbonCheckBox } {------------------------------------------------------------------------------ Method: TCarbonCheckBox.CreateWidget Params: AParams - Creation parameters Creates Carbon check box ------------------------------------------------------------------------------} procedure TCarbonCheckBox.CreateWidget(const AParams: TCreateParams); var Control: ControlRef; Value: UInt32; begin Value := 0; if OSError( CreateCheckBoxControl(GetTopParentWindow, ParamsToCarbonRect(AParams), nil, Value, True, Control{%H-}), Self, SCreateWidget, 'CreateCheckBoxControl') then RaiseCreateWidgetError(LCLObject); Widget := Control; inherited; SetText(AParams.Caption); end; const // values are used from Interface Builder StdCheckBoxNormalSize = 18; StdCheckBoxSmallSize = 12; StdCheckBoxTinySize = 0; // 10 procedure TCarbonCheckBox.BoundsChanged; begin inherited BoundsChanged; SetControlViewStyle(Widget, StdCheckBoxTinySize, StdCheckBoxSmallSize, StdCheckBoxNormalSize); end; { TCarbonToggleBox } {------------------------------------------------------------------------------ Method: TCarbonToggleBox.CreateWidget Params: AParams - Creation parameters Creates Carbon toggle box ------------------------------------------------------------------------------} procedure TCarbonToggleBox.CreateWidget(const AParams: TCreateParams); var Control: ControlRef; Value: UInt32; begin Value := 0; if OSError( CreateBevelButtonControl(GetTopParentWindow, ParamsToCarbonRect(AParams), nil, kControlBevelButtonNormalBevel, kControlBehaviorToggles, nil, 0, 0, 0, Control{%H-}), Self, SCreateWidget, SCreateBevelButton) then RaiseCreateWidgetError(LCLObject); Widget := Control; inherited; SetText(AParams.Caption); SetControl32BitValue(Control, Value); end; { TCarbonRadioButton } {------------------------------------------------------------------------------ Method: TCarbonRadioButton.CreateWidget Params: AParams - Creation parameters Creates Carbon radio button ------------------------------------------------------------------------------} procedure TCarbonRadioButton.CreateWidget(const AParams: TCreateParams); var Control: ControlRef; Value: UInt32; begin Value := 0; if OSError( CreateRadioButtonControl(GetTopParentWindow, ParamsToCarbonRect(AParams), nil, Value, True, Control{%H-}), Self, SCreateWidget, 'CreateRadioButtonControl') then RaiseCreateWidgetError(LCLObject); Widget := Control; inherited; SetText(AParams.Caption); end; {------------------------------------------------------------------------------ Method: TCarbonRadioButton.ValueChanged Value changed event handler ------------------------------------------------------------------------------} procedure TCarbonRadioButton.ValueChanged; var Parent : HIViewRef; v : HIViewRef; ctrl : TCarbonControl; begin Parent := HIViewGetSuperview(Widget); if not Assigned(Parent) then Exit; if RetrieveState<>kControlCheckBoxUncheckedValue then begin v := HIViewGetFirstSubview(Parent); while Assigned(v) do begin if (v <> Widget) then begin ctrl := GetCarbonControl(v); if ctrl is TCarbonRadioButton then TCarbonRadioButton(ctrl).SetState(kControlCheckBoxUncheckedValue, True); end; v := HIViewGetNextView(v); end; end; inherited; end; const // values are used from Interface Builder StdRadioButtonNormalSize = 16; StdRadioButtonSmallSize = 14; StdRadioButtonTinySize = 0; // 10 procedure TCarbonRadioButton.BoundsChanged; begin inherited BoundsChanged; SetControlViewStyle(Widget, StdRadioButtonTinySize, StdRadioButtonSmallSize, StdRadioButtonNormalSize); end; { TCarbonCustomButton } {------------------------------------------------------------------------------ Method: TCarbonCustomButton.GetValidEvents Returns: Set of events with installed handlers ------------------------------------------------------------------------------} class function TCarbonCustomButton.GetValidEvents: TCarbonControlEvents; begin Result := [cceHit]; end; {------------------------------------------------------------------------------ Method: TCarbonCustomButton.Hit Params: AControlPart - Hitted control part Hit event handler ------------------------------------------------------------------------------} procedure TCarbonCustomButton.Hit(AControlPart: ControlPartCode); begin LCLSendClickedMsg(LCLObject); end; {------------------------------------------------------------------------------ Method: TCarbonCustomButton.SetDefault Params: ADefault - Is default Sets the default indication ------------------------------------------------------------------------------} procedure TCarbonCustomButton.SetDefault(ADefault: Boolean); begin OSError( SetControlData(ControlRef(Widget), kControlEntireControl, kControlPushButtonDefaultTag, SizeOf(Boolean), @ADefault), Self, 'SetDefault', SSetData); end; { TCarbonButton } {------------------------------------------------------------------------------ Method: TCarbonButton.CreateWidget Params: AParams - Creation parameters Creates Carbon button ------------------------------------------------------------------------------} procedure TCarbonButton.CreateWidget(const AParams: TCreateParams); var Control: ControlRef; begin // create the button at bounds if OSError( CreatePushButtonControl(GetTopParentWindow, ParamsToCarbonRect(AParams), nil, Control{%H-}), Self, SCreateWidget, 'CreatePushButtonControl') then RaiseCreateWidgetError(LCLObject); Widget := Control; inherited; SetText(AParams.Caption); end; const // values are used from Interface Builder, should (can?) be evaluated from themes MaxPushButtonHeight = 22; StdPushButtonNormalHeight = 20; StdPushButtonSmallHeight = 17; StdPushButtonTinyHeight = 0; // 14 NormalPushBtnAddV = 2; SmallPushBtnAddV = 1; TinyPushBtnAddV = 0; function PushBtnAddV(PushBtnStyle: Integer=kThemePushButtonNormal): Integer; begin case PushBtnStyle of kThemePushButtonMini: Result:=TinyPushBtnAddV; kThemePushButtonSmall: Result:=SmallPushBtnAddV; kThemePushButtonNormal: Result:=NormalPushBtnAddV; else Result:=0; end; end; procedure GetBevelButtonStyle(const r: TRect; var ButtonStyle: ThemeButtonKind; var ThemeFont: ThemeFontID); var h : Integer; begin h := r.Bottom - r.Top; if h < StdPushButtonSmallHeight-1 then begin {tiny pushbutton} ButtonStyle := kThemePushButtonMini; ThemeFont := kThemeMiniSystemFont; end else if h < StdPushButtonNormalHeight-1 then begin {small pushbutton} ButtonStyle := kThemePushButtonSmall; ThemeFont := kThemeSmallSystemFont; end else if h <= MaxPushButtonHeight then begin {normal pushbutton} ButtonStyle := kThemePushButtonNormal; ThemeFont := kThemePushButtonFont; end else begin {bevelbutton} ButtonStyle := kThemeRoundedBevelButton; ThemeFont := kThemeSystemFont; end; end; procedure TCarbonButton.BoundsChanged; begin inherited BoundsChanged; SetControlViewStyle(Widget, StdPushButtonTinyHeight, StdPushButtonSmallHeight, StdPushButtonNormalHeight); end; { TCarbonBitBtn } {------------------------------------------------------------------------------ Method: TCarbonBitBtn.CreateWidget Params: AParams - Creation parameters Creates Carbon bitmap button ------------------------------------------------------------------------------} procedure TCarbonBitBtn.CreateWidget(const AParams: TCreateParams); var Control: ControlRef; begin if OSError( CreateBevelButtonControl(GetTopParentWindow, ParamsToCarbonRect(AParams), nil, kControlBevelButtonLargeBevel, kControlBehaviorPushbutton, nil, 0, 0, 0, Control{%H-}), Self, SCreateWidget, SCreateBevelButton) then RaiseCreateWidgetError(LCLObject); Widget := Control; inherited; SetText(AParams.Caption); UpdateButtonStyle; end; procedure TCarbonBitBtn.UpdateButtonStyle; var bnds : TRect; ButtonKind : ThemeButtonKind; FontStyle : ControlFontStyleRec; themeid : ThemeFontID; begin GetBounds(bnds{%H-}); GetBevelButtonStyle(bnds, ButtonKind{%H-}, themeid{%H-}); if not CustomFont then begin FillChar(FontStyle{%H-}, sizeof(FontStyle), 0); FontStyle.font := themeid; FontStyle.flags := kControlUseThemeFontIDMask; OSError(SetControlFontStyle(ControlRef(Widget), FontStyle), Self, 'UpdateButtonStyle', SSetFontStyle, 'kControlBevelButtonKindTag'); end; OSError(SetControlData(ControlRef(Widget), kControlEntireControl, kControlBevelButtonKindTag, SizeOf(ThemeButtonKind), @ButtonKind), Self, 'UpdateButtonStyle', SSetData, 'kControlBevelButtonKindTag'); end; function TCarbonBitBtn.GetPreferredSize: TPoint; begin Result:=inherited GetPreferredSize; Result.Y := 20 + PushBtnAddV; end; procedure TCarbonBitBtn.SetFont(const AFont: TFont); begin inherited; CustomFont:= not IsFontNameDefault(AFont.Name) and (AFont.Name<>''); UpdateButtonStyle; end; {------------------------------------------------------------------------------ Method: TCarbonBitBtn.SetGlyph Params: AGlyph - New glyph bitmap Sets the glyph bitmap ------------------------------------------------------------------------------} procedure TCarbonBitBtn.SetGlyph({const AGlyph: TBitmap} Glyph: CGImageRef); var ContentInfo: ControlButtonContentInfo; begin ContentInfo.imageRef := Glyph; if Assigned(Glyph) then ContentInfo.contentType := kControlContentCGImageRef else ContentInfo.contentType := kControlContentTextOnly; try OSError(SetBevelButtonContentInfo(ControlRef(Widget), @ContentInfo), Self, 'SetGlyph', 'SetBevelButtonContentInfo'); finally CGImageRelease(ContentInfo.imageRef); end; UpdateButtonStyle; //SetLayout((LCLObject as TCustomBitBtn).Layout); end; {------------------------------------------------------------------------------ Method: TCarbonBitBtn.SetLayout Params: ALayout - Bitmap and caption layout Sets the bitmap and caption layout ------------------------------------------------------------------------------} procedure TCarbonBitBtn.SetLayout(APlacement: ControlButtonTextPlacement; ATextAlign: ControlButtonTextAlignment); begin OSError(SetBevelButtonTextPlacement(ControlRef(Widget), APlacement), Self, 'SetLayout', 'SetBevelButtonTextPlacement'); OSError(SetBevelButtonTextAlignment(ControlRef(Widget), ATextAlign, 0), Self, 'SetLayout', 'SetBevelButtonTextAlignment'); fPlacement := APlacement; fAlignment := ATextAlign; UpdateButtonStyle; Invalidate; end; {------------------------------------------------------------------------------ Method: TCarbonBitBtn.SetDefault Params: ADefault - Is default Sets the default indication ------------------------------------------------------------------------------} procedure TCarbonBitBtn.SetDefault(ADefault: Boolean); begin // not supported end; function TCarbonBitBtn.GetBounds(var ARect:TRect):Boolean; begin Result:=inherited GetBounds(ARect); inc(ARect.Bottom, PushBtnAddV); end; function TCarbonBitBtn.SetBounds(const ARect: TRect): Boolean; var r : TRect; begin r:=ARect; dec(r.Bottom, PushBtnAddV); Result:=inherited SetBounds(r); UpdateButtonStyle; end; end.