// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Misc/Attribute.h" #include "Input/Reply.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Styling/SlateColor.h" #include "Layout/Margin.h" #include "Sound/SlateSound.h" #include "Styling/SlateTypes.h" #include "Styling/CoreStyle.h" #include "Framework/SlateDelegates.h" #include "Styling/SlateWidgetStyleAsset.h" #include "Widgets/Layout/SBorder.h" class FPaintArgs; class FSlateWindowElementList; enum class ETextFlowDirection : uint8; enum class ETextShapingMethod : uint8; /** * Slate's Buttons are clickable Widgets that can contain arbitrary widgets as its Content(). */ class SButton : public SBorder { SLATE_DECLARE_WIDGET_API(SButton, SBorder, SLATE_API) #if WITH_ACCESSIBILITY // Allow the accessible button to "click" this button friend class FSlateAccessibleButton; #endif public: SLATE_BEGIN_ARGS( SButton ) : _Content() , _ButtonStyle( &FCoreStyle::Get().GetWidgetStyle< FButtonStyle >( "Button" ) ) , _TextStyle( &FCoreStyle::Get().GetWidgetStyle< FTextBlockStyle >("ButtonText") ) , _HAlign( HAlign_Fill ) , _VAlign( VAlign_Fill ) , _ContentPadding(FMargin(4.0, 2.0)) , _Text() , _ClickMethod( EButtonClickMethod::DownAndUp ) , _TouchMethod( EButtonTouchMethod::DownAndUp ) , _PressMethod( EButtonPressMethod::DownAndUp ) , _DesiredSizeScale( FVector2D(1,1) ) , _ContentScale( FVector2D(1,1) ) , _ButtonColorAndOpacity(FLinearColor::White) , _ForegroundColor(FSlateColor::UseStyle()) , _IsFocusable( true ) { } /** Slot for this button's content (optional) */ SLATE_DEFAULT_SLOT( FArguments, Content ) /** The visual style of the button */ SLATE_STYLE_ARGUMENT( FButtonStyle, ButtonStyle ) /** The text style of the button */ SLATE_STYLE_ARGUMENT( FTextBlockStyle, TextStyle ) /** Horizontal alignment */ SLATE_ARGUMENT( EHorizontalAlignment, HAlign ) /** Vertical alignment */ SLATE_ARGUMENT( EVerticalAlignment, VAlign ) /** Spacing between button's border and the content. */ SLATE_ATTRIBUTE( FMargin, ContentPadding ) /** If set, overrides the button style's additional spacing between the button's border and the content when not pressed. */ SLATE_ATTRIBUTE( FMargin, NormalPaddingOverride ) /** If set, overrides the button style's additional spacing between the button's border and the content when pressed. */ SLATE_ATTRIBUTE( FMargin, PressedPaddingOverride ) /** The text to display in this button, if no custom content is specified */ SLATE_ATTRIBUTE( FText, Text ) /** Called when the button is clicked */ SLATE_EVENT( FOnClicked, OnClicked ) /** Called when the button is pressed */ SLATE_EVENT( FSimpleDelegate, OnPressed ) /** Called when the button is released */ SLATE_EVENT( FSimpleDelegate, OnReleased ) SLATE_EVENT( FSimpleDelegate, OnHovered ) SLATE_EVENT( FSimpleDelegate, OnUnhovered ) /** Sets the rules to use for determining whether the button was clicked. This is an advanced setting and generally should be left as the default. */ SLATE_ARGUMENT( EButtonClickMethod::Type, ClickMethod ) /** How should the button be clicked with touch events? */ SLATE_ARGUMENT( EButtonTouchMethod::Type, TouchMethod ) /** How should the button be clicked with keyboard/controller button events? */ SLATE_ARGUMENT( EButtonPressMethod::Type, PressMethod ) SLATE_ATTRIBUTE( FVector2D, DesiredSizeScale ) SLATE_ATTRIBUTE( FVector2D, ContentScale ) SLATE_ATTRIBUTE( FSlateColor, ButtonColorAndOpacity ) SLATE_ATTRIBUTE( FSlateColor, ForegroundColor ) /** Sometimes a button should only be mouse-clickable and never keyboard focusable. */ SLATE_ARGUMENT( bool, IsFocusable ) /** The sound to play when the button is pressed */ SLATE_ARGUMENT( TOptional, PressedSoundOverride ) /** The sound to play when the button is clicked */ SLATE_ARGUMENT( TOptional, ClickedSoundOverride ) /** The sound to play when the button is hovered */ SLATE_ARGUMENT( TOptional, HoveredSoundOverride ) /** Which text shaping method should we use? (unset to use the default returned by GetDefaultTextShapingMethod) */ SLATE_ARGUMENT( TOptional, TextShapingMethod ) /** Which text flow direction should we use? (unset to use the default returned by GetDefaultTextFlowDirection) */ SLATE_ARGUMENT( TOptional, TextFlowDirection ) SLATE_END_ARGS() SLATE_API virtual ~SButton(); protected: SLATE_API SButton(); public: /** @return the Foreground color that this widget sets; unset options if the widget does not set a foreground color */ virtual FSlateColor GetForegroundColor() const final { return Super::GetForegroundColor(); } /** @return the Foreground color that this widget sets when this widget or any of its ancestors are disabled; unset options if the widget does not set a foreground color */ SLATE_API virtual FSlateColor GetDisabledForegroundColor() const final; /** * Returns true if this button is currently pressed * * @return True if pressed, otherwise false * @note IsPressed used to be virtual. Use SetAppearPressed to assign an attribute if you need to override the default behavior. */ bool IsPressed() const { return bIsPressed || AppearPressedAttribute.Get(); } /** * Construct this widget * * @param InArgs The declaration data for this widget */ SLATE_API void Construct( const FArguments& InArgs ); /** See ContentPadding attribute */ SLATE_API void SetContentPadding(TAttribute InContentPadding); /** See HoveredSound attribute */ SLATE_API void SetHoveredSound(TOptional InHoveredSound); /** See PressedSound attribute */ SLATE_API void SetPressedSound(TOptional InPressedSound); /** See ClickedSound attribute */ SLATE_API void SetClickedSound(TOptional InClickedSound); /** See OnClicked event */ SLATE_API void SetOnClicked(FOnClicked InOnClicked); /** Set OnHovered event */ SLATE_API void SetOnHovered(FSimpleDelegate InOnHovered); /** Set OnUnhovered event */ SLATE_API void SetOnUnhovered(FSimpleDelegate InOnUnhovered); /** See ButtonStyle attribute */ SLATE_API void SetButtonStyle(const FButtonStyle* ButtonStyle); SLATE_API void SetClickMethod(EButtonClickMethod::Type InClickMethod); SLATE_API void SetTouchMethod(EButtonTouchMethod::Type InTouchMethod); SLATE_API void SetPressMethod(EButtonPressMethod::Type InPressMethod); #if !UE_BUILD_SHIPPING SLATE_API void SimulateClick(); #endif // !UE_BUILD_SHIPPING public: //~ SWidget overrides SLATE_API virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; SLATE_API virtual bool SupportsKeyboardFocus() const override; SLATE_API virtual void OnFocusLost( const FFocusEvent& InFocusEvent ) override; SLATE_API virtual FReply OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent ) override; SLATE_API virtual FReply OnKeyUp( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent ) override; SLATE_API virtual FReply OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override; SLATE_API virtual FReply OnMouseButtonDoubleClick(const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent) override; SLATE_API virtual FReply OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override; SLATE_API virtual FReply OnMouseMove( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override; SLATE_API virtual void OnMouseEnter( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override; SLATE_API virtual void OnMouseLeave( const FPointerEvent& MouseEvent ) override; SLATE_API virtual void OnMouseCaptureLost(const FCaptureLostEvent& CaptureLostEvent) override; SLATE_API virtual bool IsInteractable() const override; #if WITH_ACCESSIBILITY SLATE_API virtual TSharedRef CreateAccessibleWidget() override; #endif protected: SLATE_API virtual FVector2D ComputeDesiredSize(float) const override; //~ SWidget protected: /** Press the button */ SLATE_API virtual void Press(); /** Release the button */ SLATE_API virtual void Release(); /** Execute the "OnClicked" delegate, and get the reply */ SLATE_API FReply ExecuteOnClick(); /** @return combines the user-specified margin and the button's internal margin. */ SLATE_API FMargin GetCombinedPadding() const; /** @return True if the disabled effect should be shown. */ SLATE_API bool GetShowDisabledEffect() const; /** Utility function to translate other input click methods to regular ones. */ SLATE_API TEnumAsByte GetClickMethodFromInputType(const FPointerEvent& MouseEvent) const; /** Utility function to determine if the incoming mouse event is for a precise tap or click */ SLATE_API bool IsPreciseTapOrClick(const FPointerEvent& MouseEvent) const; /** Play the pressed sound */ SLATE_API void PlayPressedSound() const; /** Play the clicked sound */ SLATE_API void PlayClickedSound() const; /** Play the hovered sound */ SLATE_API void PlayHoverSound() const; /** Set if this button can be focused */ void SetIsFocusable(bool bInIsFocusable) { bIsFocusable = bInIsFocusable; } SLATE_API void ExecuteHoverStateChanged(bool bPlaySound); protected: /** @return the BorderForegroundColor attribute. */ TSlateAttributeRef GetBorderForegroundColorAttribute() const { return TSlateAttributeRef(SharedThis(this), BorderForegroundColorAttribute); } /** @return the ContentPadding attribute. */ TSlateAttributeRef GetContentPaddingAttribute() const { return TSlateAttributeRef(SharedThis(this), ContentPaddingAttribute); } /** Set the AppearPressed look. */ void SetAppearPressed(TAttribute InValue) { AppearPressedAttribute.Assign(*this, MoveTemp(InValue)); } /** @return the AppearPressed attribute. */ TSlateAttributeRef GetAppearPressedAttribute() const { return TSlateAttributeRef(SharedThis(this), AppearPressedAttribute); } private: SLATE_API void UpdatePressStateChanged(); SLATE_API void UpdatePadding(); SLATE_API void UpdateShowDisabledEffect(); SLATE_API void UpdateBorderImage(); SLATE_API void UpdateForegroundColor(); SLATE_API void UpdateDisabledForegroundColor(); private: /** The location in screenspace the button was pressed */ FVector2D PressedScreenSpacePosition; /** Style resource for the button */ const FButtonStyle* Style; /** The delegate to execute when the button is clicked */ FOnClicked OnClicked; /** The delegate to execute when the button is pressed */ FSimpleDelegate OnPressed; /** The delegate to execute when the button is released */ FSimpleDelegate OnReleased; /** The delegate to execute when the button is hovered */ FSimpleDelegate OnHovered; /** The delegate to execute when the button exit the hovered state */ FSimpleDelegate OnUnhovered; /** The Sound to play when the button is hovered */ FSlateSound HoveredSound; /** The Sound to play when the button is pressed */ FSlateSound PressedSound; /** The Sound to play when the button is clicked */ FSlateSound ClickedSound; /** Sets whether a click should be triggered on mouse down, mouse up, or that both a mouse down and up are required. */ TEnumAsByte ClickMethod; /** How should the button be clicked with touch events? */ TEnumAsByte TouchMethod; /** How should the button be clicked with keyboard/controller button events? */ TEnumAsByte PressMethod; /** Can this button be focused? */ uint8 bIsFocusable:1; /** True if this button is currently in a pressed state */ uint8 bIsPressed:1; /** True if NormalPaddingAttribute is overriding the button style's normal padding */ uint8 bIsStyleNormalPaddingOverridden:1; /** True if PressedPaddingAttribute is overriding the button style's pressed padding */ uint8 bIsStylePressedPaddingOverridden:1; private: /** Optional foreground color that will be inherited by all of this widget's contents */ TSlateAttribute BorderForegroundColorAttribute; /** Padding specified by the user; it will be combind with the button's internal padding. */ TSlateAttribute ContentPaddingAttribute; /** Normal padding override specified by the user, or the button style's normal padding if not being overridden. */ TSlateAttribute NormalPaddingAttribute; /** Pressed padding override specified by the user, or the button style's pressed padding if not being overridden. */ TSlateAttribute PressedPaddingAttribute; /** Optional foreground color that will be inherited by all of this widget's contents */ TSlateAttribute AppearPressedAttribute; };