Created
October 10, 2022 15:13
-
-
Save rtm223/a43ca4d9003d7c0470d3f886d30a4d7d to your computer and use it in GitHub Desktop.
Cursor Receiver Widget
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Richard Meredith AB. All Rights Reserved | |
#include "UI/UMG/RTMCursorReceiverWidget.h" | |
#include "UI/Slate/SRTMCursorReceiver.h" | |
#define LOCTEXT_NAMESPACE "RTM" | |
TSharedRef<SWidget> URTMCursorReceiverWidget::RebuildWidget() | |
{ | |
Receiver = SNew(SRTMCursorReceiver) | |
.bDetectDragEvents(bDetectDragEvents) | |
.OnMoved(BIND_UOBJECT_DELEGATE(FRTMCursorReceiverMoveDelegate, SlateHandleMoved)) | |
.OnClicked(BIND_UOBJECT_DELEGATE(FRTMCursorReceiverClickDelegate, SlateHandleClicked)) | |
.OnPressed(BIND_UOBJECT_DELEGATE(FRTMCursorReceiverDelegate, SlateHandlePressed)) | |
.OnReleased(BIND_UOBJECT_DELEGATE(FRTMCursorReceiverDelegate, SlateHandleReleased)) | |
.OnDragMoved(BIND_UOBJECT_DELEGATE(FRTMCursorReceiverMoveDelegate, SlateHandleDragMoved)) | |
.OnWheelChanged(BIND_UOBJECT_DELEGATE(FRTMCursorReceiverWheelDelegate, SlateHandleWheelChanged)) | |
.OnHovered_UObject(this, &ThisClass::SlateHandleHovered) | |
.OnUnhovered_UObject(this, &ThisClass::SlateHandleUnhovered) | |
.OnDragStarted_UObject(this, &ThisClass::SlateHandleDragStarted) | |
.OnDragEnded_UObject(this, &ThisClass::SlateHandleDragEnded); | |
return Receiver.ToSharedRef(); | |
} | |
void URTMCursorReceiverWidget::ReleaseSlateResources(bool bReleaseChildren) | |
{ | |
Super::ReleaseSlateResources(bReleaseChildren); | |
Receiver.Reset(); | |
} | |
#if WITH_EDITOR | |
TSharedRef<SWidget> URTMCursorReceiverWidget::RebuildDesignWidget(TSharedRef<SWidget> Content) | |
{ | |
return Content; | |
} | |
const FText URTMCursorReceiverWidget::GetPaletteCategory() | |
{ | |
return LOCTEXT("WIDGETS_CATEGORY", "RTM"); | |
} | |
#endif | |
void URTMCursorReceiverWidget::SlateHandleHovered() { OnHovered.Broadcast(); } | |
void URTMCursorReceiverWidget::SlateHandleUnhovered() { OnUnhovered.Broadcast(); } | |
void URTMCursorReceiverWidget::SlateHandleMoved(const FVector2D& delta) {OnCursorMoved.Broadcast(delta); } | |
void URTMCursorReceiverWidget::SlateHandlePressed(const FKey& key) { OnPressed.Broadcast(key); } | |
void URTMCursorReceiverWidget::SlateHandleReleased(const FKey& key) { OnReleased.Broadcast(key); } | |
void URTMCursorReceiverWidget::SlateHandleDragStarted(const FKey& key) { OnDragStarted.Broadcast(key); } | |
void URTMCursorReceiverWidget::SlateHandleDragEnded(const FKey& key) { OnDragEnded.Broadcast(key); } | |
void URTMCursorReceiverWidget::SlateHandleDragMoved(const FVector2D& positionDelta) { OnDragMoved.Broadcast(positionDelta); } | |
void URTMCursorReceiverWidget::SlateHandleWheelChanged(float wheelDelta) { OnWheelChanged.Broadcast(wheelDelta); } | |
FReply URTMCursorReceiverWidget::SlateHandleClicked(const FKey& key) | |
{ | |
OnClicked.Broadcast(key); | |
return FReply::Handled(); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Richard Meredith AB. All Rights Reserved | |
#pragma once | |
#include "CoreMinimal.h" | |
#include "Components/Widget.h" | |
#include "RTMCursorReceiverWidget.generated.h" | |
class SRTMCursorReceiver; | |
UCLASS() | |
class RTMCOMMON_API URTMCursorReceiverWidget : public UWidget | |
{ | |
GENERATED_BODY() | |
public: | |
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEvent); | |
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FButtonEvent, const FKey&, key); | |
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMoveEvent, const FVector2D&, positionDelta); | |
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FWheelEvent, float, wheelDelta); | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FEvent OnHovered; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FEvent OnUnhovered; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FMoveEvent OnCursorMoved; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FButtonEvent OnClicked; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FButtonEvent OnPressed; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FButtonEvent OnReleased; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FButtonEvent OnDragStarted; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FButtonEvent OnDragEnded; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FMoveEvent OnDragMoved; | |
UPROPERTY(BlueprintAssignable, Category="Cursor|Events") | |
FWheelEvent OnWheelChanged; | |
protected: | |
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Cursor") | |
bool bDetectDragEvents = false; | |
virtual TSharedRef<SWidget> RebuildWidget() override; | |
virtual void ReleaseSlateResources(bool bReleaseChildren) override; | |
#if WITH_EDITOR | |
virtual TSharedRef<SWidget> RebuildDesignWidget(TSharedRef<SWidget> Content) override; | |
virtual const FText GetPaletteCategory() override; | |
#endif | |
//#if WITH_ACCESSIBILITY | |
// virtual TSharedPtr<SWidget> GetAccessibleWidget() const override; | |
//#endif | |
private: | |
TSharedPtr<SRTMCursorReceiver> Receiver; | |
void SlateHandleHovered(); | |
void SlateHandleUnhovered(); | |
void SlateHandleMoved(const FVector2D& delta); | |
void SlateHandlePressed(const FKey& key); | |
void SlateHandleReleased(const FKey& key); | |
FReply SlateHandleClicked(const FKey& key); | |
void SlateHandleDragStarted(const FKey& key); | |
void SlateHandleDragEnded(const FKey& key); | |
void SlateHandleDragMoved(const FVector2D& positionDelta); | |
void SlateHandleWheelChanged(float wheelDelta); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Richard Meredith AB. All Rights Reserved | |
#include "UI/Slate/SRTMCursorReceiver.h" | |
#include "SlateOptMacros.h" | |
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION | |
void SRTMCursorReceiver::Construct(const FArguments& InArgs) | |
{ | |
SetCanTick(false); | |
OnHovered = InArgs._OnHovered; | |
OnUnhovered = InArgs._OnUnhovered; | |
OnMoved = InArgs._OnMoved; | |
OnPressed = InArgs._OnPressed; | |
OnReleased = InArgs._OnReleased; | |
OnClicked = InArgs._OnClicked; | |
OnDragStarted = InArgs._OnDragStarted; | |
OnDragEnded = InArgs._OnDragEnded; | |
OnDragMoved = InArgs._OnDragMoved; | |
OnWheelChanged = InArgs._OnWheelChanged; | |
bDetectDragEvents = InArgs._bDetectDragEvents; | |
ActiveKey = EKeys::Invalid; | |
PressedCursorPosition = {0.0f, 0.0f}; | |
} | |
bool SRTMCursorReceiver::ComputeVolatility() const | |
{ | |
#if ENGINE_MAJOR_VERSION >= 5 | |
return GetContentScaleAttribute().IsBound(); | |
#else | |
return ContentScale.IsBound(); | |
#endif | |
} | |
void SRTMCursorReceiver::OnMouseEnter(const FGeometry& myGeometry, const FPointerEvent& mouseEvent) | |
{ | |
Super::OnMouseEnter(myGeometry, mouseEvent); | |
OnHovered.ExecuteIfBound(); | |
Invalidate(EInvalidateWidgetReason::Layout); | |
} | |
void SRTMCursorReceiver::OnMouseLeave(const FPointerEvent& MouseEvent) | |
{ | |
const bool wasHovered = IsHovered(); | |
Super::OnMouseLeave(MouseEvent); | |
MaybeRelease(EKeys::AnyKey); | |
if(wasHovered) | |
OnUnhovered.ExecuteIfBound(); | |
Invalidate(EInvalidateWidgetReason::Layout); | |
} | |
FReply SRTMCursorReceiver::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& mouseEvent) | |
{ | |
FReply reply = FReply::Unhandled(); | |
if(IsEnabled() && IsInterestingCursorButton(mouseEvent)) | |
{ | |
MaybePress(mouseEvent.GetEffectingButton()); | |
PressedCursorPosition = mouseEvent.GetScreenSpacePosition(); | |
reply = FReply::Handled().CaptureMouse(AsShared()); | |
if(bDetectDragEvents) | |
reply.DetectDrag(this->AsShared(), mouseEvent.GetEffectingButton()); | |
} | |
Invalidate(EInvalidateWidgetReason::Layout); | |
return reply; | |
} | |
FReply SRTMCursorReceiver::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& mouseEvent) | |
{ | |
FReply reply = FReply::Unhandled(); | |
if(IsEnabled() && IsInterestingCursorButton(mouseEvent)) | |
{ | |
reply = FReply::Handled(); | |
const FKey effectingButton = GetEffectiveCursorButton(mouseEvent); | |
if(ActiveKey == effectingButton) | |
reply = ExecuteOnClick(effectingButton); | |
else | |
reply = FReply::Handled(); | |
if(DragKey == effectingButton) | |
{ | |
OnDragEnded.ExecuteIfBound(DragKey); | |
DragKey = EKeys::Invalid; | |
} | |
MaybeRelease(effectingButton); | |
} | |
if(reply.GetMouseCaptor().IsValid() == false && HasMouseCapture()) | |
reply.ReleaseMouseCapture(); | |
Invalidate(EInvalidateWidgetReason::Layout); | |
return reply; | |
} | |
FReply SRTMCursorReceiver::OnMouseButtonDoubleClick(const FGeometry& myGeometry, const FPointerEvent& mouseEvent) | |
{ | |
return OnMouseButtonDown(myGeometry, mouseEvent); | |
} | |
FReply SRTMCursorReceiver::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& mouseEvent) | |
{ | |
if(bDetectDragEvents && FSlateApplication::Get().HasTraveledFarEnoughToTriggerDrag(mouseEvent, PressedCursorPosition)) | |
MaybeRelease(EKeys::AnyKey); | |
auto delta = mouseEvent.GetCursorDelta(); | |
if(!delta.IsNearlyZero()) | |
(IsDragging() ? OnDragMoved : OnMoved).ExecuteIfBound(delta); | |
return FReply::Unhandled(); | |
} | |
void SRTMCursorReceiver::OnMouseCaptureLost(const FCaptureLostEvent& captureLostEvent) | |
{ | |
if(HasMouseCapture()) | |
MaybeRelease(EKeys::AnyKey); | |
} | |
FReply SRTMCursorReceiver::OnMouseWheel(const FGeometry& myGeometry, const FPointerEvent& mouseEvent) | |
{ | |
OnWheelChanged.ExecuteIfBound(mouseEvent.GetWheelDelta()); | |
return FReply::Handled(); | |
} | |
FReply SRTMCursorReceiver::OnDragDetected(const FGeometry& myGeometry, const FPointerEvent& mouseEvent) | |
{ | |
Super::OnDragDetected(myGeometry, mouseEvent); | |
DragKey = ActiveKey; | |
MaybeRelease(ActiveKey); | |
OnDragStarted.ExecuteIfBound(DragKey); | |
return FReply::Handled(); | |
} | |
FReply SRTMCursorReceiver::ExecuteOnClick(const FKey& key) | |
{ | |
if(OnClicked.IsBound()) | |
{ | |
FReply reply = OnClicked.Execute(key); | |
//#if WITH_ACCESSIBILITY | |
// FSlateApplicationBase::Get().GetAccessibleMessageHandler()->OnWidgetEventRaised(AsShared(), EAccessibleEvent::Activate); | |
//#endif | |
return reply; | |
} | |
return FReply::Handled(); | |
} | |
void SRTMCursorReceiver::MaybePress(FKey key) | |
{ | |
if(ActiveKey == EKeys::Invalid) | |
{ | |
ActiveKey = key; | |
OnPressed.ExecuteIfBound(key); | |
} | |
} | |
void SRTMCursorReceiver::MaybeRelease(FKey key) | |
{ | |
if(ActiveKey == key) | |
{ | |
ActiveKey = EKeys::Invalid; | |
OnReleased.ExecuteIfBound(key); | |
} | |
} | |
FKey SRTMCursorReceiver::GetEffectiveCursorButton(const FPointerEvent& mouseEvent) | |
{ | |
if(mouseEvent.IsTouchEvent()) | |
return mouseEvent.GetPointerIndex() == 0 ? EKeys::LeftMouseButton : EKeys::Invalid; | |
return mouseEvent.GetEffectingButton(); | |
} | |
bool SRTMCursorReceiver::IsInterestingCursorButton(const FPointerEvent& mouseEvent) | |
{ | |
const FKey effectingButton = GetEffectiveCursorButton(mouseEvent); | |
return effectingButton == EKeys::LeftMouseButton || effectingButton == EKeys::RightMouseButton; | |
} | |
END_SLATE_FUNCTION_BUILD_OPTIMIZATION |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Richard Meredith AB. All Rights Reserved | |
#pragma once | |
#include "CoreMinimal.h" | |
#include "Widgets/SCompoundWidget.h" | |
DECLARE_DELEGATE_OneParam(FRTMCursorReceiverDelegate, const FKey&) | |
DECLARE_DELEGATE_OneParam(FRTMCursorReceiverMoveDelegate, const FVector2D&) | |
DECLARE_DELEGATE_OneParam(FRTMCursorReceiverWheelDelegate, float) | |
DECLARE_DELEGATE_RetVal_OneParam(FReply, FRTMCursorReceiverClickDelegate, const FKey&) | |
class RTMCOMMON_API SRTMCursorReceiver : public SCompoundWidget | |
{ | |
private: | |
using Super = SCompoundWidget; | |
public: | |
SLATE_BEGIN_ARGS(SRTMCursorReceiver) {} | |
SLATE_ARGUMENT(bool, bDetectDragEvents) | |
SLATE_EVENT(FSimpleDelegate, OnHovered) | |
SLATE_EVENT(FSimpleDelegate, OnUnhovered) | |
SLATE_EVENT(FRTMCursorReceiverMoveDelegate, OnMoved) | |
SLATE_EVENT(FRTMCursorReceiverDelegate, OnPressed) | |
SLATE_EVENT(FRTMCursorReceiverDelegate, OnReleased) | |
SLATE_EVENT(FRTMCursorReceiverClickDelegate, OnClicked) | |
SLATE_EVENT(FRTMCursorReceiverDelegate, OnDragStarted) | |
SLATE_EVENT(FRTMCursorReceiverDelegate, OnDragEnded) | |
SLATE_EVENT(FRTMCursorReceiverMoveDelegate, OnDragMoved) | |
SLATE_EVENT(FRTMCursorReceiverWheelDelegate, OnWheelChanged) | |
SLATE_DEFAULT_SLOT(FArguments, Content) | |
SLATE_END_ARGS() | |
void Construct(const FArguments& InArgs); | |
FORCEINLINE bool IsButtonPressed() const { return ActiveKey != EKeys::Invalid; } | |
FORCEINLINE bool IsDragging() const { return DragKey != EKeys::Invalid; } | |
protected: | |
virtual bool SupportsKeyboardFocus() const override { return false; } | |
virtual bool IsInteractable() const override { return IsEnabled(); } | |
virtual bool ComputeVolatility() const override; | |
virtual void OnMouseEnter(const FGeometry& myGeometry, const FPointerEvent& mouseEvent) override; | |
virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override; | |
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& mouseEvent) override; | |
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& mouseEvent) override; | |
virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& mouseEvent) override; | |
virtual FReply OnMouseButtonDoubleClick(const FGeometry& myGeometry, const FPointerEvent& mouseEvent) override; | |
virtual void OnMouseCaptureLost(const FCaptureLostEvent& captureLostEvent) override; | |
virtual FReply OnMouseWheel(const FGeometry& myGeometry, const FPointerEvent& mouseEvent) override; | |
virtual FReply OnDragDetected(const FGeometry& myGeometry, const FPointerEvent& mouseEvent) override; | |
private: | |
bool bDetectDragEvents = true; | |
FVector2D PressedCursorPosition = FVector2D::ZeroVector; | |
FKey ActiveKey = EKeys::Invalid; | |
FKey DragKey = EKeys::Invalid; | |
FReply ExecuteOnClick(const FKey& key); | |
void MaybePress(FKey key); | |
void MaybeRelease(FKey key); | |
FSimpleDelegate OnHovered; | |
FSimpleDelegate OnUnhovered; | |
FRTMCursorReceiverMoveDelegate OnMoved; | |
FRTMCursorReceiverDelegate OnPressed; | |
FRTMCursorReceiverDelegate OnReleased; | |
FRTMCursorReceiverClickDelegate OnClicked; | |
FRTMCursorReceiverDelegate OnDragStarted; | |
FRTMCursorReceiverDelegate OnDragEnded; | |
FRTMCursorReceiverMoveDelegate OnDragMoved; | |
FRTMCursorReceiverWheelDelegate OnWheelChanged; | |
static FKey GetEffectiveCursorButton(const FPointerEvent& mouseEvent); | |
bool IsInterestingCursorButton(const FPointerEvent& mouseEvent); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment