Working on a focusable check box for gamepad support

This commit is contained in:
Steve Streeting 2021-03-02 12:59:18 +00:00
parent b66805057a
commit 413491fd05
4 changed files with 376 additions and 0 deletions

View File

@ -0,0 +1,78 @@
#include "StevesUI/FocusableCheckBox.h"
#include "StevesUI/SFocusableCheckBox.h"
TSharedRef<SWidget> UFocusableCheckBox::RebuildWidget()
{
MyCheckbox = SNew(SFocusableCheckBox)
.OnCheckStateChanged( BIND_UOBJECT_DELEGATE(FOnCheckStateChanged, SlateOnCheckStateChangedCallback) )
.Style(&WidgetStyle)
.HAlign( HorizontalAlignment )
.ClickMethod(ClickMethod)
.TouchMethod(TouchMethod)
.PressMethod(PressMethod)
.IsFocusable(IsFocusable)
// Our new events
.OnHovered(BIND_UOBJECT_DELEGATE(FSimpleDelegate, SlateHandleHovered))
.OnUnhovered(BIND_UOBJECT_DELEGATE(FSimpleDelegate, SlateHandleUnhovered))
.OnFocusReceived(BIND_UOBJECT_DELEGATE(FSimpleDelegate, SlateHandleFocusReceived))
.OnFocusLost(BIND_UOBJECT_DELEGATE(FSimpleDelegate, SlateHandleFocusLost))
;
if ( GetChildrenCount() > 0 )
{
MyCheckbox->SetContent(GetContentSlot()->Content ? GetContentSlot()->Content->TakeWidget() : SNullWidget::NullWidget);
}
// Copy Widget style but make normal same as hovered
FocussedStyle = WidgetStyle;
FocussedStyle.UncheckedImage = FocussedStyle.UncheckedHoveredImage;
FocussedStyle.CheckedImage = FocussedStyle.CheckedHoveredImage;
FocussedStyle.UndeterminedImage = FocussedStyle.UndeterminedHoveredImage;
return MyCheckbox.ToSharedRef();
}
void UFocusableCheckBox::SlateHandleFocusReceived()
{
ApplyFocusStyle();
OnFocusReceived.Broadcast();
}
void UFocusableCheckBox::SlateHandleFocusLost()
{
UndoFocusStyle();
OnFocusLost.Broadcast();
}
void UFocusableCheckBox::ApplyFocusStyle()
{
if (MyCheckbox.IsValid() && bUseHoverStyleWhenFocussed)
{
MyCheckbox->SetStyle(&FocussedStyle);
}
}
void UFocusableCheckBox::UndoFocusStyle()
{
if (MyCheckbox.IsValid())
{
MyCheckbox->SetStyle(&WidgetStyle);
}
}
void UFocusableCheckBox::SlateHandleHovered()
{
if (bTakeFocusOnHover)
{
SetFocus();
}
OnHovered.Broadcast();
}
void UFocusableCheckBox::SlateHandleUnhovered()
{
OnUnhovered.Broadcast();
}

View File

@ -0,0 +1,93 @@
#include "SFocusableCheckBox.h"
void SFocusableCheckBox::Construct(const FArguments& InArgs)
{
// Call superclass, have to re-construct the args because they're not compatible
SCheckBox::Construct(SCheckBox::FArguments()
.Padding(InArgs._Padding)
.Style(InArgs._Style)
.Type(InArgs._Type)
.CheckedImage(InArgs._CheckedImage)
.ClickMethod(InArgs._ClickMethod)
.ForegroundColor(InArgs._ForegroundColor)
.HAlign(InArgs._HAlign)
.IsChecked(InArgs._IsChecked)
.IsFocusable(InArgs._IsFocusable)
.PressMethod(InArgs._PressMethod)
.TouchMethod(InArgs._TouchMethod)
.UncheckedImage(InArgs._UncheckedImage)
.UndeterminedImage(InArgs._UndeterminedImage)
.CheckedImage(InArgs._CheckedImage)
.UncheckedHoveredImage(InArgs._UncheckedHoveredImage)
.UndeterminedHoveredImage(InArgs._UndeterminedHoveredImage)
.CheckedHoveredImage(InArgs._CheckedHoveredImage)
.UncheckedPressedImage(InArgs._UncheckedPressedImage)
.UndeterminedPressedImage(InArgs._UndeterminedPressedImage)
.CheckedPressedImage(InArgs._CheckedPressedImage)
.CheckedSoundOverride(InArgs._CheckedSoundOverride)
.UncheckedSoundOverride(InArgs._UncheckedSoundOverride)
.HoveredSoundOverride(InArgs._HoveredSoundOverride)
.BorderBackgroundColor(InArgs._BorderBackgroundColor)
.OnCheckStateChanged(InArgs._OnCheckStateChanged)
.IsEnabled(InArgs._IsEnabled)
.Clipping(InArgs._Clipping)
.Cursor(InArgs._Cursor)
.Tag(InArgs._Tag)
.Visibility(InArgs._Visibility)
.AccessibleParams(InArgs._AccessibleParams)
.AccessibleText(InArgs._AccessibleText)
.ForceVolatile(InArgs._ForceVolatile)
.IsEnabled(InArgs._IsEnabled)
.RenderOpacity(InArgs._RenderOpacity)
.RenderTransform(InArgs._RenderTransform)
.RenderTransformPivot(InArgs._RenderTransformPivot)
.ToolTip(InArgs._ToolTip)
.ToolTipText(InArgs._ToolTipText)
);
OnHoveredDelegate = InArgs._OnHovered;
OnUnhoveredDelegate = InArgs._OnUnhovered;
OnFocusReceivedDelegate = InArgs._OnFocusReceived;
OnFocusLostDelegate = InArgs._OnFocusLost;
}
void SFocusableCheckBox::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
SCheckBox::OnMouseEnter(MyGeometry, MouseEvent);
// SCheckbox doesn't have hovered / unhovered events so we need to add them
}
void SFocusableCheckBox::OnMouseLeave(const FPointerEvent& MouseEvent)
{
SCheckBox::OnMouseLeave(MouseEvent);
}
FReply SFocusableCheckBox::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& InFocusEvent)
{
auto Ret = SCheckBox::OnFocusReceived(MyGeometry, InFocusEvent);
OnFocusReceivedDelegate.ExecuteIfBound();
return Ret;
}
void SFocusableCheckBox::OnFocusLost(const FFocusEvent& InFocusEvent)
{
SCheckBox::OnFocusLost(InFocusEvent);
OnFocusLostDelegate.ExecuteIfBound();
}
void SFocusableCheckBox::SetOnFocusReceived(FSimpleDelegate InOnFocusReceived)
{
OnFocusReceivedDelegate = InOnFocusReceived;
}
void SFocusableCheckBox::SetOnFocusLost(FSimpleDelegate InOnFocusLost)
{
OnFocusLostDelegate = InOnFocusLost;
}

View File

@ -0,0 +1,146 @@
#pragma once
#include "CoreMinimal.h"
#include "Widgets/Input/SCheckBox.h"
class SFocusableCheckBox : public SCheckBox
{
public:
// Seems in slate we have to duplicate all the args from superclass
SLATE_BEGIN_ARGS( SFocusableCheckBox )
: _Content()
, _Style( &FCoreStyle::Get().GetWidgetStyle< FCheckBoxStyle >("Checkbox") )
, _Type()
, _OnCheckStateChanged()
, _IsChecked( ECheckBoxState::Unchecked )
, _HAlign( HAlign_Fill )
, _CheckBoxContentUsesAutoWidth(true)
, _Padding()
, _ClickMethod( EButtonClickMethod::DownAndUp )
, _TouchMethod(EButtonTouchMethod::DownAndUp)
, _PressMethod(EButtonPressMethod::DownAndUp)
, _ForegroundColor()
, _BorderBackgroundColor ()
, _IsFocusable( true )
, _UncheckedImage( nullptr )
, _UncheckedHoveredImage( nullptr )
, _UncheckedPressedImage( nullptr )
, _CheckedImage( nullptr )
, _CheckedHoveredImage( nullptr )
, _CheckedPressedImage( nullptr )
, _UndeterminedImage( nullptr )
, _UndeterminedHoveredImage( nullptr )
, _UndeterminedPressedImage( nullptr )
{
}
/** Content to be placed next to the check box, or for a toggle button, the content to be placed inside the button */
SLATE_DEFAULT_SLOT( FArguments, Content )
/** The style structure for this checkbox' visual style */
SLATE_STYLE_ARGUMENT( FCheckBoxStyle, Style )
/** Type of check box (set by the Style arg but the Style can be overridden with this) */
SLATE_ARGUMENT( TOptional<ESlateCheckBoxType::Type>, Type )
/** Called when the checked state has changed */
SLATE_EVENT( FOnCheckStateChanged, OnCheckStateChanged )
/** Whether the check box is currently in a checked state */
SLATE_ATTRIBUTE( ECheckBoxState, IsChecked )
/** How the content of the toggle button should align within the given space*/
SLATE_ARGUMENT( EHorizontalAlignment, HAlign )
/** Whether or not the content portion of the checkbox should layout using auto-width. When true the content will always be arranged at its desired size as opposed to resizing to the available space. */
SLATE_ARGUMENT(bool, CheckBoxContentUsesAutoWidth)
/** Spacing between the check box image and its content (set by the Style arg but the Style can be overridden with this) */
SLATE_ATTRIBUTE( FMargin, Padding )
/** 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)
/** Foreground color for the checkbox's content and parts (set by the Style arg but the Style can be overridden with this) */
SLATE_ATTRIBUTE( FSlateColor, ForegroundColor )
/** The color of the background border (set by the Style arg but the Style can be overridden with this) */
SLATE_ATTRIBUTE( FSlateColor, BorderBackgroundColor )
SLATE_ARGUMENT( bool, IsFocusable )
SLATE_EVENT( FOnGetContent, OnGetMenuContent )
/** The sound to play when the check box is checked */
SLATE_ARGUMENT( TOptional<FSlateSound>, CheckedSoundOverride )
/** The sound to play when the check box is unchecked */
SLATE_ARGUMENT( TOptional<FSlateSound>, UncheckedSoundOverride )
/** The sound to play when the check box is hovered */
SLATE_ARGUMENT( TOptional<FSlateSound>, HoveredSoundOverride )
/** The unchecked image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, UncheckedImage)
/** The unchecked hovered image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, UncheckedHoveredImage)
/** The unchecked pressed image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, UncheckedPressedImage)
/** The checked image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, CheckedImage)
/** The checked hovered image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, CheckedHoveredImage)
/** The checked pressed image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, CheckedPressedImage)
/** The undetermined image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, UndeterminedImage)
/** The undetermined hovered image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, UndeterminedHoveredImage)
/** The undetermined pressed image for the checkbox - overrides the style's */
SLATE_ARGUMENT(const FSlateBrush*, UndeterminedPressedImage)
// This is the bit we're adding
// SCheckBox is missing hovered/unhovered too
SLATE_EVENT( FSimpleDelegate, OnHovered )
SLATE_EVENT( FSimpleDelegate, OnUnhovered )
SLATE_EVENT( FSimpleDelegate, OnFocusReceived )
SLATE_EVENT( FSimpleDelegate, OnFocusLost )
SLATE_END_ARGS()
public:
void Construct( const FArguments& InArgs );
virtual FReply OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& InFocusEvent) override;
virtual void OnFocusLost(const FFocusEvent& InFocusEvent) override;
void SetOnFocusReceived(FSimpleDelegate InOnFocusReceived);
void SetOnFocusLost(FSimpleDelegate InOnFocusLost);
protected:
FSimpleDelegate OnHoveredDelegate;
FSimpleDelegate OnUnhoveredDelegate;
FSimpleDelegate OnFocusReceivedDelegate;
FSimpleDelegate OnFocusLostDelegate;
public:
virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;
};

View File

@ -0,0 +1,59 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/CheckBox.h"
#include "FocusableCheckBox.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCheckBoxHoveredEvent);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCheckBoxUnhoveredEvent);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCheckBoxFocusReceivedEvent);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCheckBoxFocusLostEvent);
/**
* This is a simple subclass of UCheckBox to provide some missing features
*
* * Focus events
* * Hover events
* * Focus style based on hover style
* * Assign focus to self on hover to prevent double-highlighting
*/
UCLASS()
class STEVESUEHELPERS_API UFocusableCheckBox : public UCheckBox
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere)
bool bUseHoverStyleWhenFocussed = true;
UPROPERTY( BlueprintAssignable, Category = "CheckBox|Event" )
FOnCheckBoxHoveredEvent OnHovered;
UPROPERTY( BlueprintAssignable, Category = "CheckBox|Event" )
FOnCheckBoxUnhoveredEvent OnUnhovered;
UPROPERTY(BlueprintAssignable, Category="CheckBox|Event")
FOnCheckBoxFocusReceivedEvent OnFocusReceived;
UPROPERTY(BlueprintAssignable, Category="CheckBox|Event")
FOnCheckBoxFocusLostEvent OnFocusLost;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
bool bTakeFocusOnHover = true;
protected:
FCheckBoxStyle FocussedStyle;
void SlateHandleFocusReceived();
void SlateHandleFocusLost();
void ApplyFocusStyle();
void UndoFocusStyle();
void SlateHandleHovered();
void SlateHandleUnhovered();
virtual TSharedRef<SWidget> RebuildWidget() override;
};