mirror of
https://github.com/litruv/TobiiEyetracking.git
synced 2026-07-24 18:56:09 +10:00
Added Tobii from 4.23 + updated
This commit is contained in:
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;
|
||||
}
|
||||
Reference in New Issue
Block a user