mirror of
https://github.com/litruv/TobiiEyetracking.git
synced 2026-07-26 03:36:11 +10:00
Added Tobii from 4.23 + updated
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2017- Tobii Technology AB. All rights reserved.
|
||||
*
|
||||
* @author Temaran | Fredrik Lindh | fredrik.lindh@tobii.com | https://github.com/Temaran
|
||||
******************************************************************************/
|
||||
|
||||
#include "STobiiGazeFocusableWidget.h"
|
||||
#include "TobiiGTOMBlueprintLibrary.h"
|
||||
|
||||
#include "Engine/Canvas.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Components/WidgetComponent.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "Slate/SceneViewport.h"
|
||||
|
||||
|
||||
static TAutoConsoleVariable<int32> CVarEnableCleanUI(TEXT("tobii.EnableCleanUI"), 1, TEXT("0 - CleanUI is disabled. 1 - CleanUI is enabled."));
|
||||
static TAutoConsoleVariable<float> CVarCleanUIFadeInTimeSecs(TEXT("tobii.CleanUIFadeInTimeSecs"), 0.0f, TEXT("We want the fade in time to be fairly fast so that the information is instantly available to the user."));
|
||||
static TAutoConsoleVariable<float> CVarCleanUIFadeOutTimeSecs(TEXT("tobii.CleanUIFadeOutTimeSecs"), 1.8f, TEXT("The fade out time should be fairly slow however to make sure that it doesn't draw peripheral vision attention."));
|
||||
static TAutoConsoleVariable<float> CVarCleanUIMinAlpha(TEXT("tobii.CleanUIMinAlpha"), 0.3f, TEXT("The clean UI won't fade something out beyond this minimum alpha."));
|
||||
static TAutoConsoleVariable<float> CVarCleanUIMaxAlpha(TEXT("tobii.CleanUIMaxAlpha"), 1.0f, TEXT("Change this if you don't want the clean UI to increase alpha beyond this point"));
|
||||
|
||||
STobiiGazeFocusableWidget::STobiiGazeFocusableWidget()
|
||||
: CleanUIAlpha(1.0f)
|
||||
, CleanUIContainersToPollHitsFrom()
|
||||
{
|
||||
bCanSupportFocus = false;
|
||||
|
||||
SetCanTick(true);
|
||||
}
|
||||
|
||||
void STobiiGazeFocusableWidget::Construct(const FArguments& InArgs)
|
||||
{
|
||||
SBox::FArguments ParentArgs;
|
||||
ParentArgs._HAlign = InArgs._HAlign;
|
||||
ParentArgs._VAlign = InArgs._VAlign;
|
||||
ParentArgs._Padding = InArgs._Padding;
|
||||
ParentArgs._Content = InArgs._Content;
|
||||
SBox::Construct(ParentArgs);
|
||||
|
||||
OnHovered = InArgs._OnHovered;
|
||||
OnUnhovered = InArgs._OnUnhovered;
|
||||
}
|
||||
|
||||
bool STobiiGazeFocusableWidget::HitByGaze()
|
||||
{
|
||||
return UMGWidget.IsValid() && (UMGWidget->CleanUIMode == ETobiiCleanUIMode::FocusExclusive ?
|
||||
UMGWidget->HasFocus() : UMGWidget->IsInFocusCollection());
|
||||
}
|
||||
|
||||
void STobiiGazeFocusableWidget::AddExtraCleanUIContainerToPollHitsFrom(STobiiGazeFocusableWidget* CleanUIContainerToPoll, bool PollGazeHits /*= true*/, bool PollMouseHits /*= true*/)
|
||||
{
|
||||
if (CleanUIContainerToPoll != nullptr)
|
||||
{
|
||||
CleanUIPollingInfo PollingInfo;
|
||||
PollingInfo.CleanUIToPollFrom = TWeakPtr<SWidget>(CleanUIContainerToPoll->AsShared());
|
||||
PollingInfo.PollGaze = PollGazeHits;
|
||||
PollingInfo.PollPointer = PollMouseHits;
|
||||
CleanUIContainersToPollHitsFrom.Add(PollingInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void STobiiGazeFocusableWidget::RemoveExtraCleanUIContainerToPollHitsFrom(STobiiGazeFocusableWidget* CleanUIContainerToStopPolling)
|
||||
{
|
||||
if (CleanUIContainerToStopPolling != nullptr)
|
||||
{
|
||||
CleanUIContainersToPollHitsFrom.RemoveAll([=](CleanUIPollingInfo& CurCleanUIPollingInfo)
|
||||
{
|
||||
return !CurCleanUIPollingInfo.CleanUIToPollFrom.IsValid() || CurCleanUIPollingInfo.CleanUIToPollFrom.HasSameObject(CleanUIContainerToStopPolling);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void STobiiGazeFocusableWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||
{
|
||||
SWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
|
||||
|
||||
if (!UMGWidget.IsValid()
|
||||
|| UMGWidget->CleanUIMode == ETobiiCleanUIMode::Disabled
|
||||
|| GEngine == nullptr
|
||||
|| GEngine->GameViewport == nullptr
|
||||
|| GEngine->GameViewport->GetGameViewport() == nullptr
|
||||
|| GetChildren()->Num() == 0)
|
||||
{
|
||||
CleanUIAlpha = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
bool bTriggerCleanUIOnMouseOver = UMGWidget.IsValid() && UMGWidget->bTriggerCleanUIOnMouseOver;
|
||||
bool bHitByGaze = HitByGaze();
|
||||
bool bShouldFadeIn = bHitByGaze || (bTriggerCleanUIOnMouseOver && bIsHovered);
|
||||
|
||||
// Check if we have any hits on our CleanUIContainersToPollHitsFrom to see if we should still fade in even though we still think we should fade out
|
||||
if (!bShouldFadeIn)
|
||||
{
|
||||
for (CleanUIPollingInfo& PollingInfo : CleanUIContainersToPollHitsFrom)
|
||||
{
|
||||
if (PollingInfo.CleanUIToPollFrom.IsValid())
|
||||
{
|
||||
TSharedPtr<SWidget> CleanUIWidget = PollingInfo.CleanUIToPollFrom.Pin();
|
||||
STobiiGazeFocusableWidget* CleanUIToPollFrom = (STobiiGazeFocusableWidget*)CleanUIWidget.Get();
|
||||
|
||||
if ((PollingInfo.PollGaze && CleanUIToPollFrom->HitByGaze())
|
||||
|| PollingInfo.PollPointer && bTriggerCleanUIOnMouseOver && CleanUIToPollFrom->bIsHovered)
|
||||
{
|
||||
bShouldFadeIn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UMGWidget->TimeCleanUIIsSuppressedForSecs = FMath::Max(UMGWidget->TimeCleanUIIsSuppressedForSecs -= InDeltaTime, 0.0f);
|
||||
bool bCleanUISuppressed = UMGWidget->TimeCleanUIIsSuppressedForSecs > FLT_EPSILON;
|
||||
bShouldFadeIn = bShouldFadeIn || bCleanUISuppressed;
|
||||
|
||||
float MinAlpha = UMGWidget->CleanUIMinAlphaOverride >= 0.0f ? UMGWidget->CleanUIMinAlphaOverride : CVarCleanUIMinAlpha.GetValueOnAnyThread();
|
||||
float MaxAlpha = UMGWidget->CleanUIMaxAlphaOverride >= 0.0f ? UMGWidget->CleanUIMaxAlphaOverride : CVarCleanUIMaxAlpha.GetValueOnAnyThread();
|
||||
if (bShouldFadeIn)
|
||||
{
|
||||
float FadeInTime = UMGWidget->CleanUIFadeInTimeSecsOverride >= 0.0f ? UMGWidget->CleanUIFadeInTimeSecsOverride : CVarCleanUIFadeInTimeSecs.GetValueOnAnyThread();
|
||||
CleanUIAlpha = FadeInTime > 0.0f ? FMath::Clamp(CleanUIAlpha + InDeltaTime / FadeInTime, MinAlpha, MaxAlpha) : MaxAlpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
float FadeOutTime = UMGWidget->CleanUIFadeOutTimeSecsOverride >= 0.0f ? UMGWidget->CleanUIFadeOutTimeSecsOverride : CVarCleanUIFadeOutTimeSecs.GetValueOnAnyThread();
|
||||
CleanUIAlpha = FadeOutTime > 0.0f ? FMath::Clamp(CleanUIAlpha - InDeltaTime / FadeOutTime, MinAlpha, MaxAlpha) : MinAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
int32 STobiiGazeFocusableWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
||||
{
|
||||
const float Alpha = UMGWidget.IsValid() ? (UMGWidget->CleanUIMode == ETobiiCleanUIMode::Silent ? 1.0f : CleanUIAlpha) : 1.0f;
|
||||
FWidgetStyle CompoundedWidgetStyle = FWidgetStyle(InWidgetStyle).BlendColorAndOpacityTint(FLinearColor(1.0f, 1.0f, 1.0f, Alpha));
|
||||
return SBox::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, CompoundedWidgetStyle, bParentEnabled);
|
||||
}
|
||||
|
||||
void STobiiGazeFocusableWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||
{
|
||||
SWidget::OnMouseEnter(MyGeometry, MouseEvent);
|
||||
OnHovered.ExecuteIfBound();
|
||||
Invalidate(EInvalidateWidget::Layout);
|
||||
}
|
||||
|
||||
void STobiiGazeFocusableWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
|
||||
{
|
||||
// Call parent implementation
|
||||
SWidget::OnMouseLeave(MouseEvent);
|
||||
OnUnhovered.ExecuteIfBound();
|
||||
Invalidate(EInvalidateWidget::Layout);
|
||||
}
|
||||
|
||||
STobiiGazeFocusableWidget* STobiiGazeFocusableWidget::TryCastToTobiiGazeFocusable(SWidget* Widget)
|
||||
{
|
||||
if (Widget != nullptr && Widget->GetType() == FName("STobiiGazeFocusableWidget"))
|
||||
{
|
||||
return (STobiiGazeFocusableWidget*)Widget;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
318
Source/TobiiGTOM/Private/GTOMAwareUI/STobiiRadialMenuWidget.cpp
Normal file
318
Source/TobiiGTOM/Private/GTOMAwareUI/STobiiRadialMenuWidget.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2017- Tobii Technology AB. All rights reserved.
|
||||
*
|
||||
* @author Temaran | Fredrik Lindh | fredrik.lindh@tobii.com | https://github.com/Temaran
|
||||
******************************************************************************/
|
||||
|
||||
#include "STobiiRadialMenuWidget.h"
|
||||
#include "TobiiGTOMBlueprintLibrary.h"
|
||||
|
||||
#include "Engine/Canvas.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Components/WidgetComponent.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Slate/SceneViewport.h"
|
||||
|
||||
#define TWO_PI (6.28318530718)
|
||||
#define PI_OVER_TWO (1.57079632679)
|
||||
|
||||
STobiiRadialMenuWidget::STobiiRadialMenuWidget()
|
||||
: Children(this)
|
||||
{
|
||||
SetCanTick(false);
|
||||
bCanSupportFocus = false;
|
||||
}
|
||||
|
||||
void STobiiRadialMenuWidget::Construct(const STobiiRadialMenuWidget::FArguments& InArgs)
|
||||
{
|
||||
const int32 NumSlots = InArgs.Slots.Num();
|
||||
for (int32 SlotIndex = 0; SlotIndex < NumSlots; ++SlotIndex)
|
||||
{
|
||||
Children.Add(InArgs.Slots[SlotIndex]);
|
||||
}
|
||||
|
||||
bUseHardBorder = InArgs._UseHardBorder;
|
||||
BorderColor = InArgs._BorderColor;
|
||||
PanelColor = InArgs._PanelColor;
|
||||
BorderThicknessPx = InArgs._BorderThicknessPx;
|
||||
SegmentSeparationPx = InArgs._SegmentSeparationPx;
|
||||
AngularDisplacementDeg = InArgs._AngularDisplacementDeg;
|
||||
RadiusPx = InArgs._RadiusPx;
|
||||
VertexCount = InArgs._VertexCount;
|
||||
|
||||
BrushResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(*FCoreStyle::Get().GetDefaultBrush());
|
||||
}
|
||||
|
||||
void STobiiRadialMenuWidget::ClearChildren()
|
||||
{
|
||||
if (Children.Num())
|
||||
{
|
||||
Invalidate(EInvalidateWidget::Layout);
|
||||
Children.Empty();
|
||||
}
|
||||
}
|
||||
|
||||
int32 STobiiRadialMenuWidget::RemoveSlot(const TSharedRef<SWidget>& SlotWidget)
|
||||
{
|
||||
Invalidate(EInvalidateWidget::Layout);
|
||||
|
||||
for (int32 SlotIdx = 0; SlotIdx < Children.Num(); ++SlotIdx)
|
||||
{
|
||||
if (SlotWidget == Children[SlotIdx].GetWidget())
|
||||
{
|
||||
Children.RemoveAt(SlotIdx);
|
||||
return SlotIdx;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void STobiiRadialMenuWidget::GeneratePanelRenderData(const FGeometry& AllottedGeometry, int32 NrChildren, TArray<FTobiiRadialMenuPanelRenderData>& OutChildRenderData) const
|
||||
{
|
||||
OutChildRenderData.Empty(NrChildren);
|
||||
|
||||
const FVector2D WidgetCenter = AllottedGeometry.GetLocalSize() / 2.0f;
|
||||
const int32 VerticesPerSegment = FMath::Max(VertexCount / Children.Num(), 3);
|
||||
const float ActualRadius = FMath::Min(WidgetCenter.X, WidgetCenter.Y);
|
||||
const float AngleStep = TWO_PI / Children.Num();
|
||||
const float AngleHalfStep = AngleStep / 2.0f;
|
||||
const float AngleMinorStep = AngleStep / VerticesPerSegment;
|
||||
const float HalfSeparation = SegmentSeparationPx / 2.0f;
|
||||
const float BorderCenterOffsetDist = HalfSeparation / FMath::Sin(AngleHalfStep);
|
||||
const float BorderSeparationAngle = FMath::Asin(HalfSeparation / ActualRadius);
|
||||
|
||||
const float NoBorderDistOffset = HalfSeparation + BorderThicknessPx;
|
||||
const float NoBorderRadius = ActualRadius - BorderThicknessPx;
|
||||
const float NoBorderCenterOffsetDist = NoBorderDistOffset / FMath::Sin(AngleHalfStep);
|
||||
const float NoBorderSeparationAngle = FMath::Asin(NoBorderDistOffset / NoBorderRadius);
|
||||
|
||||
float CurrentChildAngle = PI_OVER_TWO + FMath::DegreesToRadians(AngularDisplacementDeg); //Start at the top
|
||||
|
||||
for (int32 ChildIndex = 0; ChildIndex < NrChildren; ++ChildIndex)
|
||||
{
|
||||
FTobiiRadialMenuPanelRenderData NewRenderData;
|
||||
|
||||
const STobiiRadialMenuWidget::FSlot& CurChild = Children[ChildIndex];
|
||||
const float ChildAlpha = CurChild.AlphaAttr.Get();
|
||||
|
||||
FColor AdjustedBorderColor = BorderColor;
|
||||
AdjustedBorderColor.A *= ChildAlpha;
|
||||
FColor AdjustedPanelColor = PanelColor;
|
||||
AdjustedPanelColor.A *= ChildAlpha;
|
||||
|
||||
//Panel polygon
|
||||
{
|
||||
float ChildSine, ChildCosine;
|
||||
FMath::SinCos(&ChildSine, &ChildCosine, -CurrentChildAngle);
|
||||
FVector2D WidgetCenterToChildCenterDir(ChildCosine, ChildSine);
|
||||
|
||||
//First generate the nexus point
|
||||
const FVector2D CenterVertexPosition = WidgetCenter + WidgetCenterToChildCenterDir * NoBorderCenterOffsetDist;
|
||||
FSlateVertex CenterVertex = FSlateVertex::Make<ESlateVertexRounding::Enabled>(AllottedGeometry.GetAccumulatedRenderTransform(), CenterVertexPosition, FVector2D::ZeroVector, AdjustedPanelColor);
|
||||
NewRenderData.PanelVertexData.Add(CenterVertex);
|
||||
|
||||
//Next we need to find our segment extreme points
|
||||
const float SegmentStartVertexAngle = CurrentChildAngle - AngleHalfStep + NoBorderSeparationAngle;
|
||||
const float SegmentEndVertexAngle = CurrentChildAngle + AngleHalfStep - NoBorderSeparationAngle;
|
||||
|
||||
//Generate all edge vertices
|
||||
for (int32 VertexIdx = 0; VertexIdx < VerticesPerSegment; VertexIdx++)
|
||||
{
|
||||
const float AngleAlpha = (float)VertexIdx / (VerticesPerSegment - 1);
|
||||
const float CurrentAngle = FMath::Lerp(SegmentStartVertexAngle, SegmentEndVertexAngle, AngleAlpha);
|
||||
float VertexSine, VertexCosine;
|
||||
FMath::SinCos(&VertexSine, &VertexCosine, -CurrentAngle);
|
||||
|
||||
const FVector2D SegmentVertexPosition = WidgetCenter + FVector2D(NoBorderRadius * VertexCosine, NoBorderRadius * VertexSine);
|
||||
FSlateVertex SegmentVertex = FSlateVertex::Make<ESlateVertexRounding::Enabled>(AllottedGeometry.GetAccumulatedRenderTransform(), SegmentVertexPosition, FVector2D::ZeroVector, AdjustedPanelColor);
|
||||
NewRenderData.PanelVertexData.Add(SegmentVertex);
|
||||
}
|
||||
|
||||
//Build indices
|
||||
for (int32 VertexIdx = 1; VertexIdx < VerticesPerSegment; VertexIdx++)
|
||||
{
|
||||
NewRenderData.PanelIndexData.Add(0);
|
||||
NewRenderData.PanelIndexData.Add(VertexIdx);
|
||||
NewRenderData.PanelIndexData.Add(VertexIdx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
//Border polygon
|
||||
{
|
||||
float ChildSine, ChildCosine;
|
||||
FMath::SinCos(&ChildSine, &ChildCosine, -CurrentChildAngle);
|
||||
FVector2D WidgetCenterToChildCenterDir(ChildCosine, ChildSine);
|
||||
|
||||
//First generate the nexus point
|
||||
const FVector2D CenterVertexPosition = WidgetCenter + WidgetCenterToChildCenterDir * BorderCenterOffsetDist;
|
||||
FSlateVertex CenterVertex = FSlateVertex::Make<ESlateVertexRounding::Enabled>(AllottedGeometry.GetAccumulatedRenderTransform(), CenterVertexPosition, FVector2D::ZeroVector, AdjustedBorderColor);
|
||||
NewRenderData.BorderVertexData.Add(CenterVertex);
|
||||
|
||||
//Next we need to find our segment extreme points
|
||||
const float SegmentStartVertexAngle = CurrentChildAngle - AngleHalfStep + BorderSeparationAngle;
|
||||
const float SegmentEndVertexAngle = CurrentChildAngle + AngleHalfStep - BorderSeparationAngle;
|
||||
|
||||
//Generate all edge vertices
|
||||
for (int32 VertexIdx = 0; VertexIdx < VerticesPerSegment; VertexIdx++)
|
||||
{
|
||||
const float AngleAlpha = (float)VertexIdx / (VerticesPerSegment - 1);
|
||||
const float CurrentAngle = FMath::Lerp(SegmentStartVertexAngle, SegmentEndVertexAngle, AngleAlpha);
|
||||
float VertexSine, VertexCosine;
|
||||
FMath::SinCos(&VertexSine, &VertexCosine, -CurrentAngle);
|
||||
|
||||
const FVector2D SegmentVertexPosition = WidgetCenter + FVector2D(ActualRadius * VertexCosine, ActualRadius * VertexSine);
|
||||
|
||||
FSlateVertex SegmentVertex = FSlateVertex::Make<ESlateVertexRounding::Enabled>(AllottedGeometry.GetAccumulatedRenderTransform(), SegmentVertexPosition, FVector2D::ZeroVector, AdjustedBorderColor);
|
||||
NewRenderData.BorderVertexData.Add(SegmentVertex);
|
||||
}
|
||||
|
||||
//Add the panel vertices at the end of the array
|
||||
const int32 FirstPanelVertexIdx = NewRenderData.BorderVertexData.Num();
|
||||
for (FSlateVertex SlateVertex : NewRenderData.PanelVertexData)
|
||||
{
|
||||
if (bUseHardBorder)
|
||||
{
|
||||
SlateVertex.Color = AdjustedBorderColor;
|
||||
}
|
||||
|
||||
NewRenderData.BorderVertexData.Add(SlateVertex);
|
||||
}
|
||||
|
||||
//Build indices, building quads from the inner panel outwards
|
||||
for (int32 VertexIdx = 0; VertexIdx < VerticesPerSegment; VertexIdx++)
|
||||
{
|
||||
//First tri
|
||||
NewRenderData.BorderIndexData.Add(FirstPanelVertexIdx + VertexIdx);
|
||||
NewRenderData.BorderIndexData.Add(VertexIdx);
|
||||
NewRenderData.BorderIndexData.Add(VertexIdx + 1);
|
||||
|
||||
//Second tri
|
||||
NewRenderData.BorderIndexData.Add(FirstPanelVertexIdx + VertexIdx);
|
||||
NewRenderData.BorderIndexData.Add(VertexIdx + 1);
|
||||
NewRenderData.BorderIndexData.Add(FirstPanelVertexIdx + VertexIdx + 1);
|
||||
}
|
||||
|
||||
//Final two tris
|
||||
NewRenderData.BorderIndexData.Add(NewRenderData.BorderVertexData.Num() - 1);
|
||||
NewRenderData.BorderIndexData.Add(FirstPanelVertexIdx - 1);
|
||||
NewRenderData.BorderIndexData.Add(0);
|
||||
NewRenderData.BorderIndexData.Add(NewRenderData.BorderVertexData.Num() - 1);
|
||||
NewRenderData.BorderIndexData.Add(0);
|
||||
NewRenderData.BorderIndexData.Add(FirstPanelVertexIdx);
|
||||
}
|
||||
|
||||
OutChildRenderData.Add(MoveTemp(NewRenderData));
|
||||
CurrentChildAngle += AngleStep;
|
||||
}
|
||||
}
|
||||
|
||||
void STobiiRadialMenuWidget::OnArrangeChildren(const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren) const
|
||||
{
|
||||
if (Children.Num() > 0)
|
||||
{
|
||||
const FVector2D WidgetCenter = AllottedGeometry.GetLocalSize() / 2.0f;
|
||||
const float ActualRadius = FMath::Min(WidgetCenter.X, WidgetCenter.Y);
|
||||
const float AngleStep = TWO_PI / Children.Num();
|
||||
|
||||
FVector2D LargestInscribedRect;
|
||||
float DistanceToRectCenter;
|
||||
UTobiiGTOMBlueprintLibrary::FindLargestInscribedAlignedRect(AngleStep, ActualRadius, LargestInscribedRect, DistanceToRectCenter);
|
||||
const float ChildElementSideLength = FMath::Min(LargestInscribedRect.X, LargestInscribedRect.Y);
|
||||
const float SlightlyScaledDownSize = ChildElementSideLength * 0.8f;
|
||||
|
||||
float CurrentAngle = PI_OVER_TWO + FMath::DegreesToRadians(AngularDisplacementDeg); //Start at the top
|
||||
for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex)
|
||||
{
|
||||
const STobiiRadialMenuWidget::FSlot& CurChild = Children[ChildIndex];
|
||||
const TSharedRef<SWidget>& CurWidget = CurChild.GetWidget();
|
||||
|
||||
const EVisibility ChildVisibility = CurWidget->GetVisibility();
|
||||
if (ArrangedChildren.Accepts(ChildVisibility))
|
||||
{
|
||||
const FVector2D Offset = CurChild.OffsetAttr.Get();
|
||||
float ChildSine, ChildCosine;
|
||||
FMath::SinCos(&ChildSine, &ChildCosine, -CurrentAngle);
|
||||
const FVector2D ChildCenter(DistanceToRectCenter * ChildCosine, DistanceToRectCenter * ChildSine);
|
||||
|
||||
const float ChildScale = CurChild.ScaleAttr.Get();
|
||||
const FVector2D ChildSize(SlightlyScaledDownSize * ChildScale, SlightlyScaledDownSize * ChildScale);
|
||||
const FVector2D BaseChildOffset = -ChildSize / 2.0f;
|
||||
const FVector2D LocalPosition = WidgetCenter + ChildCenter + BaseChildOffset + Offset;
|
||||
CurrentAngle += AngleStep;
|
||||
|
||||
ArrangedChildren.AddWidget(ChildVisibility, AllottedGeometry.MakeChild(CurWidget, LocalPosition, ChildSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 STobiiRadialMenuWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
||||
{
|
||||
SCOPED_NAMED_EVENT_TEXT("SConstraintCanvas", FColor::Orange);
|
||||
|
||||
if (Children.Num() < 2)
|
||||
{
|
||||
return LayerId;
|
||||
}
|
||||
|
||||
FArrangedChildren ArrangedChildren(EVisibility::Visible);
|
||||
OnArrangeChildren(AllottedGeometry, ArrangedChildren);
|
||||
|
||||
TArray<FTobiiRadialMenuPanelRenderData> ChildRenderData;
|
||||
GeneratePanelRenderData(AllottedGeometry, Children.Num(), ChildRenderData);
|
||||
|
||||
const bool bForwardedEnabled = ShouldBeEnabled(bParentEnabled);
|
||||
const float AngleStep = TWO_PI / Children.Num();
|
||||
|
||||
float CurrentChildAngle = PI_OVER_TWO + FMath::DegreesToRadians(AngularDisplacementDeg); //Start at the top
|
||||
|
||||
// Because we paint multiple children, we must track the maximum layer id that they produced in case one of our parents
|
||||
// wants to an overlay for all of its contents.
|
||||
int32 MaxLayerId = LayerId;
|
||||
const FPaintArgs NewArgs = Args.WithNewParent(this);
|
||||
for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex)
|
||||
{
|
||||
FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex];
|
||||
if (!IsChildWidgetCulled(MyCullingRect, CurWidget))
|
||||
{
|
||||
MaxLayerId++;
|
||||
|
||||
//Draw underlying panel if our render data is valid
|
||||
if (ChildIndex < ChildRenderData.Num())
|
||||
{
|
||||
const FTobiiRadialMenuPanelRenderData& CurrentRenderData = ChildRenderData[ChildIndex];
|
||||
|
||||
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId
|
||||
, BrushResourceHandle
|
||||
, CurrentRenderData.PanelVertexData
|
||||
, CurrentRenderData.PanelIndexData
|
||||
, nullptr, 0, 0, ESlateDrawEffect::NoPixelSnapping);
|
||||
|
||||
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId
|
||||
, BrushResourceHandle
|
||||
, CurrentRenderData.BorderVertexData
|
||||
, CurrentRenderData.BorderIndexData
|
||||
, nullptr, 0, 0, ESlateDrawEffect::NoPixelSnapping);
|
||||
}
|
||||
|
||||
const STobiiRadialMenuWidget::FSlot& CurChild = Children[ChildIndex];
|
||||
FWidgetStyle CompoundedWidgetStyle = FWidgetStyle(InWidgetStyle).BlendColorAndOpacityTint(FLinearColor(1.0f, 1.0f, 1.0f, CurChild.AlphaAttr.Get()));
|
||||
CurWidget.Widget->Paint(NewArgs, CurWidget.Geometry, MyCullingRect, OutDrawElements, MaxLayerId, CompoundedWidgetStyle, bForwardedEnabled);
|
||||
CurrentChildAngle += AngleStep;
|
||||
}
|
||||
}
|
||||
|
||||
return MaxLayerId;
|
||||
}
|
||||
|
||||
FVector2D STobiiRadialMenuWidget::ComputeDesiredSize(float LayoutScaleMultiplier) const
|
||||
{
|
||||
const float DiameterPx = RadiusPx * 2.0f;
|
||||
return FVector2D(DiameterPx, DiameterPx);
|
||||
}
|
||||
|
||||
FChildren* STobiiRadialMenuWidget::GetChildren()
|
||||
{
|
||||
return &Children;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2017- Tobii Technology AB. All rights reserved.
|
||||
*
|
||||
* @author Temaran | Fredrik Lindh | fredrik.lindh@tobii.com | https://github.com/Temaran
|
||||
******************************************************************************/
|
||||
|
||||
#include "TobiiGazeFocusableWidget.h"
|
||||
#include "TobiiGTOMBlueprintLibrary.h"
|
||||
|
||||
#include "Components/SizeBoxSlot.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "UMG"
|
||||
|
||||
static TMap<FTobiiFocusableUID, TWeakObjectPtr<UTobiiGazeFocusableWidget>> GRegisteredTobiiScreenSpaceFocusableWidgets;
|
||||
|
||||
UTobiiGazeFocusableWidget::UTobiiGazeFocusableWidget(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
|
||||
, OnHovered()
|
||||
, OnUnhovered()
|
||||
|
||||
, bHasGazeFocus(false)
|
||||
, bIsGazeFocusable(true)
|
||||
, GazeFocusPriority(0.0f)
|
||||
, MaxFocusDistance(-1.0f)
|
||||
, FocusLayer("")
|
||||
, ReceivedGazeFocus()
|
||||
, LostGazeFocus()
|
||||
|
||||
, CleanUIMode(ETobiiCleanUIMode::Disabled)
|
||||
, TimeCleanUIIsSuppressedForSecs(0.0f)
|
||||
, CleanUIFadeInTimeSecsOverride(-1.0f)
|
||||
, CleanUIFadeOutTimeSecsOverride(-1.0f)
|
||||
, CleanUIMinAlphaOverride(-1.0f)
|
||||
, CleanUIMaxAlphaOverride(-1.0f)
|
||||
, bTriggerCleanUIOnMouseOver(false)
|
||||
|
||||
, WorldSpaceHostWidgetComponent()
|
||||
, MySlateWidget()
|
||||
{
|
||||
Visibility = ESlateVisibility::Visible;
|
||||
bIsVariable = true;
|
||||
}
|
||||
|
||||
void UTobiiGazeFocusableWidget::ReleaseSlateResources(bool bReleaseChildren)
|
||||
{
|
||||
Super::ReleaseSlateResources(bReleaseChildren);
|
||||
|
||||
MySlateWidget.Reset();
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> UTobiiGazeFocusableWidget::RebuildWidget()
|
||||
{
|
||||
MySizeBox = MySlateWidget = SNew(STobiiGazeFocusableWidget)
|
||||
.OnHovered_UObject(this, &ThisClass::SlateHandleHovered)
|
||||
.OnUnhovered_UObject(this, &ThisClass::SlateHandleUnhovered);
|
||||
|
||||
MySlateWidget->UMGWidget = this;
|
||||
|
||||
if (GetChildrenCount() > 0)
|
||||
{
|
||||
Cast<USizeBoxSlot>(GetContentSlot())->BuildSlot(MySizeBox.ToSharedRef());
|
||||
}
|
||||
|
||||
return MySizeBox.ToSharedRef();
|
||||
}
|
||||
|
||||
bool UTobiiGazeFocusableWidget::IsHoveredByPointer()
|
||||
{
|
||||
if (MySlateWidget.IsValid())
|
||||
{
|
||||
return MySlateWidget->IsHoveredByPointer();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UTobiiGazeFocusableWidget::RegisterWidgetToGTOM(UWidgetComponent* HostWidget)
|
||||
{
|
||||
if (HostWidget != nullptr)
|
||||
{
|
||||
WorldSpaceHostWidgetComponent = HostWidget;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 Uid = GetUniqueID();
|
||||
if (GRegisteredTobiiScreenSpaceFocusableWidgets.Contains(Uid))
|
||||
{
|
||||
//Overwrite
|
||||
GRegisteredTobiiScreenSpaceFocusableWidgets[Uid] = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
GRegisteredTobiiScreenSpaceFocusableWidgets.Add(Uid, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UTobiiGazeFocusableWidget::IsWorldSpaceWidget()
|
||||
{
|
||||
return WorldSpaceHostWidgetComponent.IsValid();
|
||||
}
|
||||
|
||||
UWidgetComponent* UTobiiGazeFocusableWidget::GetWorldSpaceHostWidgetComponent()
|
||||
{
|
||||
return WorldSpaceHostWidgetComponent.IsValid() ? WorldSpaceHostWidgetComponent.Get() : nullptr;
|
||||
}
|
||||
|
||||
bool UTobiiGazeFocusableWidget::HasFocus()
|
||||
{
|
||||
FTobiiGazeFocusData FocusData;
|
||||
return UTobiiGTOMBlueprintLibrary::GetGazeFocusData(FocusData) && FocusData.FocusedWidget == this;
|
||||
}
|
||||
|
||||
bool UTobiiGazeFocusableWidget::IsInFocusCollection()
|
||||
{
|
||||
TArray<FTobiiGazeFocusData> AllFocusData;
|
||||
if (UTobiiGTOMBlueprintLibrary::GetAllGazeFocusData(AllFocusData))
|
||||
{
|
||||
for (const FTobiiGazeFocusData& FocusData : AllFocusData)
|
||||
{
|
||||
if (FocusData.FocusedWidget == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float UTobiiGazeFocusableWidget::GetCleanUIAlpha()
|
||||
{
|
||||
if (MySlateWidget.IsValid())
|
||||
{
|
||||
return MySlateWidget->GetCleanUIAlpha();
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
void UTobiiGazeFocusableWidget::AddGazeFocusableWidgetToPollHitsFrom(UTobiiGazeFocusableWidget* GazeFocusableWidgetToPoll, bool PollGazeHits /*= true*/, bool PollMouseHits /*= true*/)
|
||||
{
|
||||
if (GazeFocusableWidgetToPoll != nullptr)
|
||||
{
|
||||
STobiiGazeFocusableWidget* SlateCleanUIContainerToPoll = GazeFocusableWidgetToPoll->GetSlateGazeFocusableWidget();
|
||||
if (SlateCleanUIContainerToPoll != nullptr && MySlateWidget.IsValid())
|
||||
{
|
||||
MySlateWidget->AddExtraCleanUIContainerToPollHitsFrom(SlateCleanUIContainerToPoll, PollGazeHits, PollMouseHits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiGazeFocusableWidget::RemoveGazeFocusableWidgetToPollHitsFrom(UTobiiGazeFocusableWidget* GazeFocusableWidgetToStopPolling)
|
||||
{
|
||||
if (GazeFocusableWidgetToStopPolling != nullptr)
|
||||
{
|
||||
STobiiGazeFocusableWidget* SlateCleanUIContainerToStopPolling = GazeFocusableWidgetToStopPolling->GetSlateGazeFocusableWidget();
|
||||
if (SlateCleanUIContainerToStopPolling != nullptr && MySlateWidget.IsValid())
|
||||
{
|
||||
MySlateWidget->RemoveExtraCleanUIContainerToPollHitsFrom(SlateCleanUIContainerToStopPolling);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiGazeFocusableWidget::ReceiveFocus()
|
||||
{
|
||||
ReceivedGazeFocus.Broadcast(this);
|
||||
}
|
||||
|
||||
void UTobiiGazeFocusableWidget::LoseFocus()
|
||||
{
|
||||
LostGazeFocus.Broadcast(this);
|
||||
}
|
||||
|
||||
void UTobiiGazeFocusableWidget::SlateHandleHovered()
|
||||
{
|
||||
OnHovered.Broadcast(this);
|
||||
}
|
||||
|
||||
void UTobiiGazeFocusableWidget::SlateHandleUnhovered()
|
||||
{
|
||||
OnUnhovered.Broadcast(this);
|
||||
}
|
||||
|
||||
STobiiGazeFocusableWidget* UTobiiGazeFocusableWidget::GetSlateGazeFocusableWidget()
|
||||
{
|
||||
if (MySlateWidget.IsValid())
|
||||
{
|
||||
return MySlateWidget.Get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TMap<FTobiiFocusableUID, TWeakObjectPtr<UTobiiGazeFocusableWidget>>& UTobiiGazeFocusableWidget::GetTobiiScreenSpaceFocusableWidgets()
|
||||
{
|
||||
TArray<FTobiiFocusableUID> IdsToPrune;
|
||||
for (auto& WidgetPair : GRegisteredTobiiScreenSpaceFocusableWidgets)
|
||||
{
|
||||
if (!WidgetPair.Value.IsValid())
|
||||
{
|
||||
IdsToPrune.Add(WidgetPair.Key);
|
||||
}
|
||||
}
|
||||
for (FTobiiFocusableUID IdToPrune : IdsToPrune)
|
||||
{
|
||||
GRegisteredTobiiScreenSpaceFocusableWidgets.Remove(IdToPrune);
|
||||
}
|
||||
|
||||
return GRegisteredTobiiScreenSpaceFocusableWidgets;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
const FText UTobiiGazeFocusableWidget::GetPaletteCategory()
|
||||
{
|
||||
return LOCTEXT("Tobii", "Tobii");
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
334
Source/TobiiGTOM/Private/GTOMAwareUI/TobiiRadialMenuWidget.cpp
Normal file
334
Source/TobiiGTOM/Private/GTOMAwareUI/TobiiRadialMenuWidget.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2017- Tobii Technology AB. All rights reserved.
|
||||
*
|
||||
* @author Temaran | Fredrik Lindh | fredrik.lindh@tobii.com | https://github.com/Temaran
|
||||
******************************************************************************/
|
||||
|
||||
#include "TobiiRadialMenuWidget.h"
|
||||
#include "TobiiGazeFocusableWidget.h"
|
||||
#include "STobiiGazeFocusableWidget.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "UMG"
|
||||
|
||||
UTobiiRadialMenuSlot::UTobiiRadialMenuSlot(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, Offset(0.0f, 0.0f)
|
||||
, Scale(1.0f)
|
||||
, Alpha(1.0f)
|
||||
|
||||
, Slot(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuSlot::ReleaseSlateResources(bool bReleaseChildren)
|
||||
{
|
||||
Super::ReleaseSlateResources(bReleaseChildren);
|
||||
Slot = nullptr;
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuSlot::BuildSlot(TSharedRef<STobiiRadialMenuWidget> RadialMenu)
|
||||
{
|
||||
Slot = &RadialMenu->AddSlot()
|
||||
[
|
||||
Content == nullptr ? SNullWidget::NullWidget : Content->TakeWidget()
|
||||
];
|
||||
|
||||
SynchronizeProperties();
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
bool UTobiiRadialMenuSlot::NudgeByDesigner(const FVector2D& NudgeDirection, const TOptional<int32>& GridSnapSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UTobiiRadialMenuSlot::DragDropPreviewByDesigner(const FVector2D& LocalCursorPosition, const TOptional<int32>& XGridSnapSize, const TOptional<int32>& YGridSnapSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuSlot::SynchronizeFromTemplate(const UPanelSlot* const TemplateSlot)
|
||||
{
|
||||
}
|
||||
|
||||
#endif //WITH_EDITOR
|
||||
|
||||
void UTobiiRadialMenuSlot::SetOffset(FVector2D InOffset)
|
||||
{
|
||||
Offset = InOffset;
|
||||
if (Slot)
|
||||
{
|
||||
Slot->Offset(InOffset);
|
||||
}
|
||||
}
|
||||
FVector2D UTobiiRadialMenuSlot::GetOffset() const
|
||||
{
|
||||
if (Slot)
|
||||
{
|
||||
return Slot->OffsetAttr.Get();
|
||||
}
|
||||
|
||||
return FVector2D::ZeroVector;
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuSlot::SetScale(float InScale)
|
||||
{
|
||||
Scale = InScale;
|
||||
if (Slot)
|
||||
{
|
||||
Slot->Scale(InScale);
|
||||
}
|
||||
}
|
||||
float UTobiiRadialMenuSlot::GetScale() const
|
||||
{
|
||||
if (Slot)
|
||||
{
|
||||
return Slot->ScaleAttr.Get();
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuSlot::SetAlpha(float InAlpha)
|
||||
{
|
||||
Alpha = InAlpha;
|
||||
if (Slot)
|
||||
{
|
||||
Slot->Alpha(InAlpha);
|
||||
}
|
||||
}
|
||||
float UTobiiRadialMenuSlot::GetAlpha() const
|
||||
{
|
||||
if (Slot)
|
||||
{
|
||||
return Slot->AlphaAttr.Get();
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuSlot::SynchronizeProperties()
|
||||
{
|
||||
SetOffset(Offset);
|
||||
SetScale(Scale);
|
||||
SetAlpha(Alpha);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
void UTobiiRadialMenuSlot::PreEditChange(class FEditPropertyChain& PropertyAboutToChange)
|
||||
{
|
||||
Super::PreEditChange(PropertyAboutToChange);
|
||||
}
|
||||
void UTobiiRadialMenuSlot::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
|
||||
{
|
||||
SynchronizeProperties();
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UTobiiRadialMenuWidget::UTobiiRadialMenuWidget(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, bUseHardBorder(true)
|
||||
, BorderColor(FColor(230, 230, 230))
|
||||
, PanelColor(FColor(70, 70, 70))
|
||||
, BorderThicknessPx(5.0f)
|
||||
, SegmentSeparationPx(10.0f)
|
||||
, AngularDisplacementDeg(0.0f)
|
||||
, RadiusPx(500.0f)
|
||||
, VertexCount(360)
|
||||
{
|
||||
bIsVariable = false;
|
||||
|
||||
STobiiRadialMenuWidget::FArguments Defaults;
|
||||
Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get());
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::ReleaseSlateResources(bool bReleaseChildren)
|
||||
{
|
||||
Super::ReleaseSlateResources(bReleaseChildren);
|
||||
MyRadialMenu.Reset();
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> UTobiiRadialMenuWidget::RebuildWidget()
|
||||
{
|
||||
MyRadialMenu = SNew(STobiiRadialMenuWidget)
|
||||
.UseHardBorder(bUseHardBorder)
|
||||
.BorderColor(BorderColor)
|
||||
.PanelColor(PanelColor)
|
||||
.BorderThicknessPx(BorderThicknessPx)
|
||||
.SegmentSeparationPx(SegmentSeparationPx)
|
||||
.AngularDisplacementDeg(AngularDisplacementDeg)
|
||||
.RadiusPx(RadiusPx)
|
||||
.VertexCount(VertexCount);
|
||||
|
||||
for (UPanelSlot* PanelSlot : Slots)
|
||||
{
|
||||
if (UTobiiRadialMenuSlot* TypedSlot = Cast<UTobiiRadialMenuSlot>(PanelSlot))
|
||||
{
|
||||
TypedSlot->Parent = this;
|
||||
TypedSlot->BuildSlot(MyRadialMenu.ToSharedRef());
|
||||
|
||||
UTobiiGazeFocusableWidget* GazeFocusableWidget = Cast<UTobiiGazeFocusableWidget>(TypedSlot->Content);
|
||||
if (GazeFocusableWidget != nullptr)
|
||||
{
|
||||
STobiiGazeFocusableWidget* SlateWidget = GazeFocusableWidget->GetSlateGazeFocusableWidget();
|
||||
//SlateWidget->ParentRadialMenuSlot = TypedSlot->GetSlateSlot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MyRadialMenu.ToSharedRef();
|
||||
}
|
||||
|
||||
UClass* UTobiiRadialMenuWidget::GetSlotClass() const
|
||||
{
|
||||
return UTobiiRadialMenuSlot::StaticClass();
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::SetUseHardBorder(bool bNewUseHardBorder)
|
||||
{
|
||||
bUseHardBorder = bNewUseHardBorder;
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
MyRadialMenu->bUseHardBorder = bUseHardBorder;
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::SetBorderColor(FColor NewBorderColor)
|
||||
{
|
||||
BorderColor = NewBorderColor;
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
MyRadialMenu->BorderColor = BorderColor;
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::SetPanelColor(FColor NewPanelColor)
|
||||
{
|
||||
PanelColor = NewPanelColor;
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
MyRadialMenu->PanelColor = PanelColor;
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::SetBorderThicknessPx(float NewBorderThicknessPx)
|
||||
{
|
||||
BorderThicknessPx = NewBorderThicknessPx;
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
MyRadialMenu->BorderThicknessPx = BorderThicknessPx;
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::SetSegmentSeparationPx(float NewSegmentSeparationPx)
|
||||
{
|
||||
SegmentSeparationPx = NewSegmentSeparationPx;
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
MyRadialMenu->SegmentSeparationPx = SegmentSeparationPx;
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::SetAngularDisplacementDeg(float NewAngularDisplacementDeg)
|
||||
{
|
||||
AngularDisplacementDeg = NewAngularDisplacementDeg;
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
MyRadialMenu->AngularDisplacementDeg = AngularDisplacementDeg;
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::SetRadiusPx(float NewRadiusPx)
|
||||
{
|
||||
RadiusPx = NewRadiusPx;
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
MyRadialMenu->RadiusPx = RadiusPx;
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::SetVertexCount(int32 NewVertexCount)
|
||||
{
|
||||
VertexCount = NewVertexCount;
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
MyRadialMenu->VertexCount = VertexCount;
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::OnSlotAdded(UPanelSlot* InSlot)
|
||||
{
|
||||
// Add the child to the live canvas if it already exists
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
CastChecked<UTobiiRadialMenuSlot>(InSlot)->BuildSlot(MyRadialMenu.ToSharedRef());
|
||||
}
|
||||
}
|
||||
|
||||
void UTobiiRadialMenuWidget::OnSlotRemoved(UPanelSlot* InSlot)
|
||||
{
|
||||
// Remove the widget from the live slot if it exists.
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
TSharedPtr<SWidget> Widget = InSlot->Content->GetCachedWidget();
|
||||
if (Widget.IsValid())
|
||||
{
|
||||
MyRadialMenu->RemoveSlot(Widget.ToSharedRef());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UTobiiRadialMenuSlot* UTobiiRadialMenuWidget::AddRadialMenuChild(UWidget* Content)
|
||||
{
|
||||
return Cast<UTobiiRadialMenuSlot>(Super::AddChild(Content));
|
||||
}
|
||||
|
||||
bool UTobiiRadialMenuWidget::GetGeometryForSlot(int32 SlotIndex, FGeometry& ArrangedGeometry) const
|
||||
{
|
||||
UTobiiRadialMenuSlot* PanelSlot = CastChecked<UTobiiRadialMenuSlot>(Slots[SlotIndex]);
|
||||
return GetGeometryForSlot(PanelSlot, ArrangedGeometry);
|
||||
}
|
||||
|
||||
bool UTobiiRadialMenuWidget::GetGeometryForSlot(UTobiiRadialMenuSlot* InSlot, FGeometry& ArrangedGeometry) const
|
||||
{
|
||||
if (InSlot->Content == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (MyRadialMenu.IsValid())
|
||||
{
|
||||
FArrangedChildren ArrangedChildren(EVisibility::All);
|
||||
MyRadialMenu->ArrangeChildren(MyRadialMenu->GetCachedGeometry(), ArrangedChildren);
|
||||
|
||||
for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ChildIndex++)
|
||||
{
|
||||
if (ArrangedChildren[ChildIndex].Widget == InSlot->Content->GetCachedWidget())
|
||||
{
|
||||
ArrangedGeometry = ArrangedChildren[ChildIndex].Geometry;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
const FText UTobiiRadialMenuWidget::GetPaletteCategory()
|
||||
{
|
||||
return LOCTEXT("Tobii", "Tobii");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
Reference in New Issue
Block a user