You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3149443 on 2016/10/03 by Max.Preussner
MediaAssets: Better parameter names for MediaPlayer BP functions
Change 3149756 on 2016/10/03 by Max.Chen
Sequence Recorder: Set some settings to be clamped at 0 (sequence length, recording delay, audio gain, audio input buffer size, nearby actor recording proximity)
#jira UE-35233
Change 3149795 on 2016/10/03 by Max.Chen
Curve Editor: Set tangent to user when flattening or straightening tangents only when the tangent mode is auto and the interp mode is cubic.
#jira UE-36734
Change 3150378 on 2016/10/04 by Max.Preussner
PS4Media: Made video buffer sizes for file and HLS sources configurable (UE-36807)
#jira UE-36807
Change 3151414 on 2016/10/05 by Max.Chen
Sequencer: Fix case where restoring the last view target was getting skipped. It should always restore if the camera object and the unlock if camera actor object is null.
#jira UE-35285
Change 3152038 on 2016/10/05 by Max.Preussner
UdpMessaging: Code & documentation modernization pass
Change 3152471 on 2016/10/05 by Max.Chen
Cine Camera: Don't enable/disable actor ticking based soley on actor tracking since actor ticking is needed for other purposes. Instead, always enable actor ticking and only update actor tracking on tick if necessary. This fixes a
bug where the cine camera actor won't tick if you hook in event tick.
#jira UE-36625
Change 3152692 on 2016/10/05 by Max.Preussner
Messaging: API code & documentation modernization pass
Mostly removed shared pointer/ref typedefs as they prevent forward declarations and increase include complexity.
Change 3153824 on 2016/10/06 by Max.Preussner
Messaging: Renamed IConnectionBasedMessagingModule to ITcpMessagingModule and moved it into TcpMessaging
I recommend that we refactor this API. The dependency should be reversed, i.e. instead of AndroidDeviceDiscovery depending on the TcpMessaging plug-in module, the Engine should provide a central registry that device discovery
modules can notify, and that message transport plug-ins can register with and listen to OnConnectionAdded/Removed events etc. That way it supports an arbitrary number of transport plug-ins, and the Engine is not coupled to any of them. This
functionality is not necessarily related to messaging, and the Messaging API is transport agnostic anyway. I'll think about this some more.
Change 3153826 on 2016/10/06 by Max.Preussner
Messaging: Removed remaining typedefs in IMessageTracer to enable forward declaration and reduce include dependencies
Change 3153857 on 2016/10/06 by Max.Chen
Sequencer: Set snap time to dragged key on by default.
Change 3153980 on 2016/10/06 by Max.Preussner
SessionServices: Removed typedefs; code and documentation modernization pass
Change 3154313 on 2016/10/06 by Max.Chen
Sequencer: Set the paste keys time to the current time, rather than the mouse time.
Change 3154332 on 2016/10/06 by Max.Chen
Sequencer: Remove click to rename shot functionality in the shot thumbnail. Added rename shot to the shot context menu.
Change 3154377 on 2016/10/07 by Max.Chen
Sequencer: Add ability to step to beginning and ends of sections/shots using the hotkeys: , and .
Change 3154788 on 2016/10/07 by Max.Chen
Sequencer: Fix offsets that created when moving multiple sections. The offsets were being created because section bounds were being generated for all sections except for the current section. Instead, they should be computed for all
sections except for any that aren't being moved.
#jira UE-29152
Change 3159274 on 2016/10/11 by Max.Preussner
Core: Documentation fixes
Change 3159275 on 2016/10/11 by Max.Preussner
UdpMessaging: Added missing header
Change 3160746 on 2016/10/12 by Max.Preussner
MediaAssets: Added BP functions to query width, height, and aspect ratio of UMediaTexture instances
#jira UE-37241
Change 3160975 on 2016/10/12 by Max.Preussner
PS4Media: Better logging for SetRate failures
Change 3160995 on 2016/10/12 by Max.Preussner
MediaPlayerEditor: Fixed Media player selection is ignored if media specifies player overrides (UE-37248)
#jira UE-37248
Change 3161066 on 2016/10/12 by Max.Preussner
PS4Media: Enforcing minimum 8 byte alignment for media allocations
Change 3161069 on 2016/10/12 by Max.Preussner
PS4Media: Fixed log spam when setting play rate to current rate
Change 3162567 on 2016/10/13 by Max.Preussner
PS4Media: Made track switching code more readable
Change 3163447 on 2016/10/14 by Max.Preussner
PS4Media: Fixed array out of bounds assertions
Change 3163772 on 2016/10/14 by Max.Preussner
MfMedia: Fixed a number of timing related issues
Change 3163980 on 2016/10/15 by Max.Chen
Sequencer: Remove folder name numeric padding so that the naming convention is similar to creating objects in the level.
Change 3164581 on 2016/10/17 by Andrew.Rodham
Sequencer: Ensure global pre-animated state is restored in reverse order
Change 3164582 on 2016/10/17 by Andrew.Rodham
Sequencer: Ensure pre animated state is restored for all actor components before saving default state
Change 3164583 on 2016/10/17 by Andrew.Rodham
Sequencer: Re-enabled support for pre and post roll
Change 3165464 on 2016/10/17 by Max.Chen
Sequencer: Default number frame handles to 0 so that there's no change in behavior when rendering out a master sequence of shots. Handle frames need to enabled explicitly by the user.
Copy from Release-4.14
#jira UE-37416
Change 3165483 on 2016/10/17 by Max.Chen
Sequencer: Enable restore state for attach section completion
Change 3165771 on 2016/10/18 by Andrew.Rodham
Sequencer: Force evaluate when rendering thumbnails
#jira UE-37321
Change 3166057 on 2016/10/18 by Andrew.Rodham
Sequencer: Only set defaults for tracks that have no keys, and where the requested default has changed
#jira UE-37285
Change 3166218 on 2016/10/18 by Max.Preussner
MediaPlayerEditor: Failure opening media, even though it opened successfully (UE-37470)
#jira UE-37470
Change 3166247 on 2016/10/18 by Max.Preussner
WmfMedia: Showing progress bar while media is being resolved
Change 3166289 on 2016/10/18 by Max.Preussner
MfMedia: Showing progress bar while media is being resolved
Change 3166993 on 2016/10/18 by Max.Preussner
MfMedia: Fixed info string not reset on media close.
Change 3166999 on 2016/10/19 by Max.Preussner
Media: Fixed NV12 and NV21 support
Change 3167008 on 2016/10/19 by Max.Preussner
Media: Removed vertical NV12 alignment
Change 3167029 on 2016/10/19 by Max.Preussner
WmfMedia: Temp fix for RGB32 encoded AVIs rendering upside-down and too bright (UE-37505)
#jira UE-37505
Change 3168593 on 2016/10/19 by Max.Chen
Sequencer: Change paste at time to local time, so that the paste happens in the local time of the sequence rather than the global time if pasting in a shot level sequence.
Change 3168626 on 2016/10/19 by Max.Chen
Sequencer: Clamp to view bounds should snap to frame if frame snapping is on.
Change 3168627 on 2016/10/19 by Max.Chen
Sequencer: Initialize working and view range to be 10% larger than playback range.
Change 3168760 on 2016/10/20 by Max.Preussner
Media: Revamped media texture buffer management to support padded frames
Added support for Windows bitmap buffers.
Fixed a number of format, conversion and/or looping issues in WmfMedia and MfMedia.
Not all shaders have been updated yet.
Change 3169640 on 2016/10/20 by Max.Chen
Sequencer: Add current camera to FLevelSequencePlayerSnapshot. Adjust DefaultBurnIn to include a few more parameters like focal length and focus distance.
#jira UE-37407
Change 3170677 on 2016/10/21 by Max.Chen
Movie Scene Capture: Add toggle to override engine scalability settings to cinematic scalability.
#jira UE-36560
Change 3170710 on 2016/10/21 by Max.Preussner
Media: Optimized handling of RGB input
Change 3170712 on 2016/10/21 by Max.Preussner
Media: Fixed NV21 conversion shader scaling
Change 3170923 on 2016/10/21 by Max.Preussner
UBT: Copied XboxOne project generator fix from Fortnite CL# 3170868
Change 3171494 on 2016/10/23 by Max.Chen
Sequencer: Fix fbx export from master sequence not finding bound objects.
#jira UE-35752
Change 3171506 on 2016/10/23 by Max.Chen
Sequencer: Draw where in and out points of the shot section are, just like subsequences do. Change to only draw the green starting line if StartOffset is negative.
#jira UE-35473
Change 3171743 on 2016/10/24 by Andrew.Rodham
Editor: Added support for detail customizations on root structs
- Also added the ability to add external struct data onto a detail category builder, and property type customization.
Change 3171752 on 2016/10/24 by Andrew.Rodham
Sequencer: Fixed spawnable ownership
- Spawnables are no longer destroyed when the cursor leaves the master playback range.
- Spawnable ownership now operates as it previously did before the evaluation rework.
- bIgnoreOwnershipInEditor has been removed since its existence was a work around for when we didn't evaluate sub sequences from the master sequence.
- FMovieSceneSequenceID is now a struct so that it can be used in array properties
- Meta data now exists for each segment of an evaluation field. Currently this only includes the sub sequence IDs that exist at that time, but it may be expanded to include all evaluation entities (tracks + sections) in future so
we don't have to calculate that at runtime.
Change 3171756 on 2016/10/24 by Andrew.Rodham
Sequencer: Added ability to trigger events with parameters
- It's now possible to supply an event payload on event track keys which are to be passed to a given event. The structure must match the signature of the event, or a warning will be emitted.
- Added a templated TGenericKeyArea, TKeyFrameManipulator and TCurveInterface that allow to generic manipulation of keyframe section data. In time we will port the other key areas over to this representation.
- This new architecture affords the common manipulation of time-based keyframes in a value-agnostic manner.
Change 3172935 on 2016/10/24 by Max.Preussner
MediaPlayerEditor: Fixed MediaPlayer asset not being dirtied when creating media sound wave or texture for it
Change 3173947 on 2016/10/25 by Max.Preussner
SlateRemote: Disabled plug-in, but enabled server by default
Change 3174510 on 2016/10/26 by Max.Chen
Sequencer: Fix slomo track crash
#jira UE-37802
Change 3174698 on 2016/10/26 by Andrew.Rodham
UMG: Fixed objects bound to a panel slot animating their slot's content instead of the slot itself
#jira UE-37775
Change 3174780 on 2016/10/26 by Max.Preussner
MediaAssets: Accepting decoder defined buffer dimensions for RGB buffers
Change 3174789 on 2016/10/26 by Max.Preussner
MediaPlayerEditor: Showing desired player name instead of current player name if no media loaded
Change 3174817 on 2016/10/26 by Max.Preussner
WmfMedia: Added support for Motion JPEG (MJPG)
Change 3174825 on 2016/10/26 by Max.Preussner
WmfMedia: Added support for non-RGB32 uncompressed formats
Change 3174834 on 2016/10/26 by Max.Preussner
MediaPlayerAssets: Allow pausing while buffering media
Change 3174886 on 2016/10/26 by Andrew.Rodham
Core: Fixed range test that was testing incorrect behavior
Change 3174889 on 2016/10/26 by Andrew.Rodham
Sequencer: Fixed AssignActor behavior
- Also ensure that cached object state is invalidated when playback context changes
#jira UE-37798
Change 3174905 on 2016/10/26 by Andrew.Rodham
Sequencer: Changed assert when failing to create an audio component to a log message
- Audio no longer plays when GEngine->UseSound() is false
#jira UE-37772
Change 3174980 on 2016/10/26 by Andrew.Rodham
Sequencer: Remove warning when event endpoint could not be found for a given context
#jira UE-37824
Change 3175001 on 2016/10/26 by Andrew.Rodham
Sequencer: Evaluate sequence with EMovieScenePlaybackStatus::Jumping on Pause.
- Also protect Pause() against reentrancy when being called from an event
Change 3175012 on 2016/10/26 by Max.Chen
Sequence Recorder: Fixes an empty working and view range after recording.
On StopRecording() update playback range after nullifying the current sequence so that the playback range isn't empty.
Added SetViewRange and SetWorkingRange.
#jira UE-34191
Change 3177760 on 2016/10/28 by Max.Chen
Sequence Recorder: Don't update the current sequence name if it's already set. This fixes a bug where if you pass in a sequence name to record to, it gets reset to the name in the sequence recorder settings.
#jira UE-37808
Change 3178529 on 2016/10/28 by Max.Chen
Matinee to Level Sequence: Added interface to extend the matinee to level sequence converter
#jira UE-37328
#2864
[CL 3178562 by Max Chen in Main branch]
1437 lines
36 KiB
C++
1437 lines
36 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "UMGPrivatePCH.h"
|
|
|
|
#include "UMGSequencePlayer.h"
|
|
#include "SceneViewport.h"
|
|
#include "WidgetAnimation.h"
|
|
|
|
#include "WidgetBlueprintLibrary.h"
|
|
#include "WidgetLayoutLibrary.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "UMG"
|
|
|
|
|
|
static FGeometry NullGeometry;
|
|
static FSlateRect NullRect;
|
|
static FSlateWindowElementList NullElementList;
|
|
static FWidgetStyle NullStyle;
|
|
|
|
FPaintContext::FPaintContext()
|
|
: AllottedGeometry(NullGeometry)
|
|
, MyClippingRect(NullRect)
|
|
, OutDrawElements(NullElementList)
|
|
, LayerId(0)
|
|
, WidgetStyle(NullStyle)
|
|
, bParentEnabled(true)
|
|
, MaxLayer(0)
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
// UUserWidget
|
|
|
|
UUserWidget::UUserWidget(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
, bCanEverTick(true)
|
|
, bCanEverPaint(true)
|
|
{
|
|
ViewportAnchors = FAnchors(0, 0, 1, 1);
|
|
Visiblity_DEPRECATED = Visibility = ESlateVisibility::SelfHitTestInvisible;
|
|
|
|
bInitialized = false;
|
|
bSupportsKeyboardFocus_DEPRECATED = true;
|
|
bIsFocusable = false;
|
|
ColorAndOpacity = FLinearColor::White;
|
|
ForegroundColor = FSlateColor::UseForeground();
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
bUseDesignTimeSize_DEPRECATED = false;
|
|
bUseDesiredSizeAtDesignTime_DEPRECATED = false;
|
|
DesignTimeSize = FVector2D(100, 100);
|
|
PaletteCategory = LOCTEXT("UserCreated", "User Created");
|
|
DesignSizeMode = EDesignPreviewSizeMode::FillScreen;
|
|
#endif
|
|
}
|
|
|
|
bool UUserWidget::Initialize()
|
|
{
|
|
// If it's not initialized initialize it, as long as it's not the CDO, we never initialize the CDO.
|
|
if ( !bInitialized && !HasAnyFlags(RF_ClassDefaultObject) )
|
|
{
|
|
bInitialized = true;
|
|
|
|
// Only do this if this widget is of a blueprint class
|
|
UWidgetBlueprintGeneratedClass* BGClass = Cast<UWidgetBlueprintGeneratedClass>(GetClass());
|
|
if (BGClass != nullptr)
|
|
{
|
|
//TODO NickD: This is a hack, and should be undone later
|
|
UWidgetBlueprintGeneratedClass* SuperBGClass = Cast<UWidgetBlueprintGeneratedClass>(BGClass->GetSuperClass());
|
|
const bool bNoRootWidget = (nullptr == BGClass->WidgetTree) || (nullptr == BGClass->WidgetTree->RootWidget);
|
|
if (bNoRootWidget && SuperBGClass)
|
|
{
|
|
SuperBGClass->InitializeWidget(this);
|
|
}
|
|
else
|
|
{
|
|
BGClass->InitializeWidget(this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InitializeNativeClassData();
|
|
}
|
|
|
|
if ( WidgetTree == nullptr )
|
|
{
|
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"));
|
|
}
|
|
|
|
// Map the named slot bindings to the available slots.
|
|
WidgetTree->ForEachWidget([&] (UWidget* Widget) {
|
|
if ( UNamedSlot* NamedWidget = Cast<UNamedSlot>(Widget) )
|
|
{
|
|
for ( FNamedSlotBinding& Binding : NamedSlotBindings )
|
|
{
|
|
if ( Binding.Content && Binding.Name == NamedWidget->GetFName() )
|
|
{
|
|
NamedWidget->ClearChildren();
|
|
NamedWidget->AddChild(Binding.Content);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UUserWidget::BeginDestroy()
|
|
{
|
|
Super::BeginDestroy();
|
|
|
|
// If anyone ever calls BeginDestroy explicitly on a widget we need to immediately remove it from
|
|
// the the parent as it may be owned currently by a slate widget. As long as it's the viewport we're
|
|
// fine.
|
|
RemoveFromParent();
|
|
|
|
// If it's not owned by the viewport we need to take more extensive measures. If the GC widget still
|
|
// exists after this point we should just reset the widget, which will forcefully cause the SObjectWidget
|
|
// to lose access to this UObject.
|
|
TSharedPtr<SObjectWidget> SafeGCWidget = MyGCWidget.Pin();
|
|
if ( SafeGCWidget.IsValid() )
|
|
{
|
|
SafeGCWidget->ResetWidget();
|
|
}
|
|
}
|
|
|
|
void UUserWidget::PostEditImport()
|
|
{
|
|
Super::PostEditImport();
|
|
|
|
Initialize();
|
|
}
|
|
|
|
void UUserWidget::PostDuplicate(bool bDuplicateForPIE)
|
|
{
|
|
Super::PostDuplicate(bDuplicateForPIE);
|
|
|
|
Initialize();
|
|
}
|
|
|
|
void UUserWidget::ReleaseSlateResources(bool bReleaseChildren)
|
|
{
|
|
Super::ReleaseSlateResources(bReleaseChildren);
|
|
|
|
UWidget* RootWidget = GetRootWidget();
|
|
if ( RootWidget )
|
|
{
|
|
RootWidget->ReleaseSlateResources(bReleaseChildren);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::SynchronizeProperties()
|
|
{
|
|
Super::SynchronizeProperties();
|
|
|
|
// We get the GCWidget directly because MyWidget could be the fullscreen host widget if we've been added
|
|
// to the viewport.
|
|
TSharedPtr<SObjectWidget> SafeGCWidget = MyGCWidget.Pin();
|
|
if ( SafeGCWidget.IsValid() )
|
|
{
|
|
TAttribute<FLinearColor> ColorBinding = OPTIONAL_BINDING(FLinearColor, ColorAndOpacity);
|
|
TAttribute<FSlateColor> ForegroundColorBinding = OPTIONAL_BINDING(FSlateColor, ForegroundColor);
|
|
|
|
SafeGCWidget->SetColorAndOpacity(ColorBinding);
|
|
SafeGCWidget->SetForegroundColor(ForegroundColorBinding);
|
|
SafeGCWidget->SetPadding(Padding);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::SetColorAndOpacity(FLinearColor InColorAndOpacity)
|
|
{
|
|
ColorAndOpacity = InColorAndOpacity;
|
|
|
|
TSharedPtr<SObjectWidget> SafeGCWidget = MyGCWidget.Pin();
|
|
if ( SafeGCWidget.IsValid() )
|
|
{
|
|
SafeGCWidget->SetColorAndOpacity(ColorAndOpacity);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::SetForegroundColor(FSlateColor InForegroundColor)
|
|
{
|
|
ForegroundColor = InForegroundColor;
|
|
|
|
TSharedPtr<SObjectWidget> SafeGCWidget = MyGCWidget.Pin();
|
|
if ( SafeGCWidget.IsValid() )
|
|
{
|
|
SafeGCWidget->SetForegroundColor(ForegroundColor);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::SetPadding(FMargin InPadding)
|
|
{
|
|
Padding = InPadding;
|
|
|
|
TSharedPtr<SObjectWidget> SafeGCWidget = MyGCWidget.Pin();
|
|
if ( SafeGCWidget.IsValid() )
|
|
{
|
|
SafeGCWidget->SetPadding(Padding);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::PostInitProperties()
|
|
{
|
|
Super::PostInitProperties();
|
|
}
|
|
|
|
UWorld* UUserWidget::GetWorld() const
|
|
{
|
|
if ( UWorld* LastWorld = CachedWorld.Get() )
|
|
{
|
|
return LastWorld;
|
|
}
|
|
|
|
if ( HasAllFlags(RF_ClassDefaultObject) )
|
|
{
|
|
// If we are a CDO, we must return nullptr instead of calling Outer->GetWorld() to fool UObject::ImplementsGetWorld.
|
|
return nullptr;
|
|
}
|
|
|
|
// Use the Player Context's world, if a specific player context is given, otherwise fall back to
|
|
// following the outer chain.
|
|
if ( PlayerContext.IsValid() )
|
|
{
|
|
if ( UWorld* World = PlayerContext.GetWorld() )
|
|
{
|
|
CachedWorld = World;
|
|
return World;
|
|
}
|
|
}
|
|
|
|
// Could be a GameInstance, could be World, could also be a WidgetTree, so we're just going to follow
|
|
// the outer chain to find the world we're in.
|
|
UObject* Outer = GetOuter();
|
|
|
|
while ( Outer )
|
|
{
|
|
UWorld* World = Outer->GetWorld();
|
|
if ( World )
|
|
{
|
|
CachedWorld = World;
|
|
return World;
|
|
}
|
|
|
|
Outer = Outer->GetOuter();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UUMGSequencePlayer* UUserWidget::GetOrAddPlayer(UWidgetAnimation* InAnimation)
|
|
{
|
|
if (InAnimation)
|
|
{
|
|
// @todo UMG sequencer - Restart animations which have had Play called on them?
|
|
UUMGSequencePlayer** FoundPlayer = ActiveSequencePlayers.FindByPredicate(
|
|
[&](const UUMGSequencePlayer* Player)
|
|
{
|
|
return Player->GetAnimation() == InAnimation;
|
|
});
|
|
|
|
if (!FoundPlayer)
|
|
{
|
|
UUMGSequencePlayer* NewPlayer = NewObject<UUMGSequencePlayer>(this);
|
|
ActiveSequencePlayers.Add(NewPlayer);
|
|
|
|
NewPlayer->OnSequenceFinishedPlaying().AddUObject(this, &UUserWidget::OnAnimationFinishedPlaying);
|
|
|
|
NewPlayer->InitSequencePlayer(*InAnimation, *this);
|
|
|
|
return NewPlayer;
|
|
}
|
|
else
|
|
{
|
|
return *FoundPlayer;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void UUserWidget::Invalidate()
|
|
{
|
|
TSharedPtr<SWidget> CachedWidget = GetCachedWidget();
|
|
if (CachedWidget.IsValid())
|
|
{
|
|
CachedWidget->Invalidate(EInvalidateWidget::LayoutAndVolatility);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::PlayAnimation( UWidgetAnimation* InAnimation, float StartAtTime, int32 NumberOfLoops, EUMGSequencePlayMode::Type PlayMode, float PlaybackSpeed)
|
|
{
|
|
FScopedNamedEvent NamedEvent(FColor::Emerald, "Widget::PlayAnimation");
|
|
|
|
UUMGSequencePlayer* Player = GetOrAddPlayer(InAnimation);
|
|
if (Player)
|
|
{
|
|
Player->Play(StartAtTime, NumberOfLoops, PlayMode, PlaybackSpeed);
|
|
|
|
Invalidate();
|
|
|
|
OnAnimationStarted(InAnimation);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::PlayAnimationTo(UWidgetAnimation* InAnimation, float StartAtTime, float EndAtTime, int32 NumberOfLoops, EUMGSequencePlayMode::Type PlayMode, float PlaybackSpeed)
|
|
{
|
|
FScopedNamedEvent NamedEvent(FColor::Emerald, "Widget::PlayAnimationTo");
|
|
|
|
UUMGSequencePlayer* Player = GetOrAddPlayer(InAnimation);
|
|
if (Player)
|
|
{
|
|
Player->PlayTo(StartAtTime, EndAtTime, NumberOfLoops, PlayMode, PlaybackSpeed);
|
|
|
|
Invalidate();
|
|
|
|
OnAnimationStarted(InAnimation);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::StopAnimation(const UWidgetAnimation* InAnimation)
|
|
{
|
|
if(InAnimation)
|
|
{
|
|
// @todo UMG sequencer - Restart animations which have had Play called on them?
|
|
UUMGSequencePlayer** FoundPlayer = ActiveSequencePlayers.FindByPredicate([&](const UUMGSequencePlayer* Player) { return Player->GetAnimation() == InAnimation; } );
|
|
|
|
if(FoundPlayer)
|
|
{
|
|
(*FoundPlayer)->Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
float UUserWidget::PauseAnimation(const UWidgetAnimation* InAnimation)
|
|
{
|
|
if ( InAnimation )
|
|
{
|
|
// @todo UMG sequencer - Restart animations which have had Play called on them?
|
|
UUMGSequencePlayer** FoundPlayer = ActiveSequencePlayers.FindByPredicate([&] (const UUMGSequencePlayer* Player) { return Player->GetAnimation() == InAnimation; });
|
|
|
|
if ( FoundPlayer )
|
|
{
|
|
( *FoundPlayer )->Pause();
|
|
return (float)( *FoundPlayer )->GetTimeCursorPosition();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
float UUserWidget::GetAnimationCurrentTime(const UWidgetAnimation* InAnimation) const
|
|
{
|
|
if (InAnimation)
|
|
{
|
|
const UUMGSequencePlayer*const* FoundPlayer = ActiveSequencePlayers.FindByPredicate([&](const UUMGSequencePlayer* Player) { return Player->GetAnimation() == InAnimation; });
|
|
if (FoundPlayer)
|
|
{
|
|
return (float)(*FoundPlayer)->GetTimeCursorPosition();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool UUserWidget::IsAnimationPlaying(const UWidgetAnimation* InAnimation) const
|
|
{
|
|
if (InAnimation)
|
|
{
|
|
UUMGSequencePlayer* const* FoundPlayer = ActiveSequencePlayers.FindByPredicate(
|
|
[ &](const UUMGSequencePlayer* Player)
|
|
{
|
|
return Player->GetAnimation() == InAnimation;
|
|
});
|
|
|
|
if (FoundPlayer)
|
|
{
|
|
return (*FoundPlayer)->GetPlaybackStatus() == EMovieScenePlayerStatus::Playing;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool UUserWidget::IsAnyAnimationPlaying() const
|
|
{
|
|
return ActiveSequencePlayers.Num() > 0;
|
|
}
|
|
|
|
void UUserWidget::SetNumLoopsToPlay(const UWidgetAnimation* InAnimation, int32 InNumLoopsToPlay)
|
|
{
|
|
if (InAnimation)
|
|
{
|
|
UUMGSequencePlayer** FoundPlayer = ActiveSequencePlayers.FindByPredicate([&](const UUMGSequencePlayer* Player) { return Player->GetAnimation() == InAnimation; });
|
|
|
|
if (FoundPlayer)
|
|
{
|
|
(*FoundPlayer)->SetNumLoopsToPlay(InNumLoopsToPlay);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UUserWidget::SetPlaybackSpeed(const UWidgetAnimation* InAnimation, float PlaybackSpeed)
|
|
{
|
|
if (InAnimation)
|
|
{
|
|
UUMGSequencePlayer** FoundPlayer = ActiveSequencePlayers.FindByPredicate([&](const UUMGSequencePlayer* Player) { return Player->GetAnimation() == InAnimation; });
|
|
|
|
if (FoundPlayer)
|
|
{
|
|
(*FoundPlayer)->SetPlaybackSpeed(PlaybackSpeed);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UUserWidget::ReverseAnimation(const UWidgetAnimation* InAnimation)
|
|
{
|
|
if (InAnimation)
|
|
{
|
|
UUMGSequencePlayer** FoundPlayer = ActiveSequencePlayers.FindByPredicate([&](const UUMGSequencePlayer* Player) { return Player->GetAnimation() == InAnimation; });
|
|
|
|
if (FoundPlayer)
|
|
{
|
|
(*FoundPlayer)->Reverse();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UUserWidget::OnAnimationFinishedPlaying(UUMGSequencePlayer& Player)
|
|
{
|
|
OnAnimationFinished( Player.GetAnimation() );
|
|
|
|
if ( Player.GetPlaybackStatus() == EMovieScenePlayerStatus::Stopped )
|
|
{
|
|
StoppedSequencePlayers.Add(&Player);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::PlaySound(USoundBase* SoundToPlay)
|
|
{
|
|
if (SoundToPlay)
|
|
{
|
|
FSlateSound NewSound;
|
|
NewSound.SetResourceObject(SoundToPlay);
|
|
FSlateApplication::Get().PlaySound(NewSound);
|
|
}
|
|
}
|
|
|
|
UWidget* UUserWidget::GetWidgetHandle(TSharedRef<SWidget> InWidget)
|
|
{
|
|
return WidgetTree->FindWidget(InWidget);
|
|
}
|
|
|
|
TSharedRef<SWidget> UUserWidget::RebuildWidget()
|
|
{
|
|
// In the event this widget is replaced in memory by the blueprint compiler update
|
|
// the widget won't be properly initialized, so we ensure it's initialized and initialize
|
|
// it if it hasn't been.
|
|
if ( !bInitialized )
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
// Setup the player context on sub user widgets, if we have a valid context
|
|
if (PlayerContext.IsValid())
|
|
{
|
|
WidgetTree->ForEachWidget([&] (UWidget* Widget) {
|
|
if ( UUserWidget* UserWidget = Cast<UUserWidget>(Widget) )
|
|
{
|
|
UserWidget->SetPlayerContext(PlayerContext);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Add the first component to the root of the widget surface.
|
|
TSharedRef<SWidget> UserRootWidget = WidgetTree->RootWidget ? WidgetTree->RootWidget->TakeWidget() : TSharedRef<SWidget>(SNew(SSpacer));
|
|
|
|
return UserRootWidget;
|
|
}
|
|
|
|
void UUserWidget::OnWidgetRebuilt()
|
|
{
|
|
// When a user widget is rebuilt we can safely initialize the navigation now since all the slate
|
|
// widgets should be held onto by a smart pointer at this point.
|
|
WidgetTree->ForEachWidget([&] (UWidget* Widget) {
|
|
Widget->BuildNavigation();
|
|
});
|
|
|
|
if (!IsDesignTime())
|
|
{
|
|
// Notify the widget that it has been constructed.
|
|
NativeConstruct();
|
|
}
|
|
}
|
|
|
|
TSharedPtr<SWidget> UUserWidget::GetSlateWidgetFromName(const FName& Name) const
|
|
{
|
|
UWidget* WidgetObject = WidgetTree->FindWidget(Name);
|
|
if ( WidgetObject )
|
|
{
|
|
return WidgetObject->GetCachedWidget();
|
|
}
|
|
|
|
return TSharedPtr<SWidget>();
|
|
}
|
|
|
|
UWidget* UUserWidget::GetWidgetFromName(const FName& Name) const
|
|
{
|
|
return WidgetTree->FindWidget(Name);
|
|
}
|
|
|
|
void UUserWidget::GetSlotNames(TArray<FName>& SlotNames) const
|
|
{
|
|
// Only do this if this widget is of a blueprint class
|
|
if ( UWidgetBlueprintGeneratedClass* BGClass = Cast<UWidgetBlueprintGeneratedClass>(GetClass()) )
|
|
{
|
|
SlotNames.Append(BGClass->NamedSlots);
|
|
}
|
|
else // For non-blueprint widget blueprints we have to go through the widget tree to locate the named slots dynamically.
|
|
{
|
|
TArray<FName> NamedSlots;
|
|
WidgetTree->ForEachWidget([&] (UWidget* Widget) {
|
|
if ( Widget && Widget->IsA<UNamedSlot>() )
|
|
{
|
|
NamedSlots.Add(Widget->GetFName());
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
UWidget* UUserWidget::GetContentForSlot(FName SlotName) const
|
|
{
|
|
for ( const FNamedSlotBinding& Binding : NamedSlotBindings )
|
|
{
|
|
if ( Binding.Name == SlotName )
|
|
{
|
|
return Binding.Content;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void UUserWidget::SetContentForSlot(FName SlotName, UWidget* Content)
|
|
{
|
|
bool bFoundExistingSlot = false;
|
|
|
|
// Find the binding in the existing set and replace the content for that binding.
|
|
for ( int32 BindingIndex = 0; BindingIndex < NamedSlotBindings.Num(); BindingIndex++ )
|
|
{
|
|
FNamedSlotBinding& Binding = NamedSlotBindings[BindingIndex];
|
|
|
|
if ( Binding.Name == SlotName )
|
|
{
|
|
bFoundExistingSlot = true;
|
|
|
|
if ( Content )
|
|
{
|
|
Binding.Content = Content;
|
|
}
|
|
else
|
|
{
|
|
NamedSlotBindings.RemoveAt(BindingIndex);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !bFoundExistingSlot && Content )
|
|
{
|
|
// Add the new binding to the list of bindings.
|
|
FNamedSlotBinding NewBinding;
|
|
NewBinding.Name = SlotName;
|
|
NewBinding.Content = Content;
|
|
|
|
NamedSlotBindings.Add(NewBinding);
|
|
}
|
|
|
|
// Dynamically insert the new widget into the hierarchy if it exists.
|
|
if ( WidgetTree )
|
|
{
|
|
UNamedSlot* NamedSlot = Cast<UNamedSlot>(WidgetTree->FindWidget(SlotName));
|
|
if ( NamedSlot )
|
|
{
|
|
NamedSlot->ClearChildren();
|
|
|
|
if ( Content )
|
|
{
|
|
NamedSlot->AddChild(Content);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UWidget* UUserWidget::GetRootWidget() const
|
|
{
|
|
if ( WidgetTree )
|
|
{
|
|
return WidgetTree->RootWidget;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void UUserWidget::AddToViewport(int32 ZOrder)
|
|
{
|
|
AddToScreen(nullptr, ZOrder);
|
|
}
|
|
|
|
bool UUserWidget::AddToPlayerScreen(int32 ZOrder)
|
|
{
|
|
if ( ULocalPlayer* LocalPlayer = GetOwningLocalPlayer() )
|
|
{
|
|
AddToScreen(LocalPlayer, ZOrder);
|
|
return true;
|
|
}
|
|
|
|
FMessageLog("PIE").Error(LOCTEXT("AddToPlayerScreen_NoPlayer", "AddToPlayerScreen Failed. No Owning Player!"));
|
|
return false;
|
|
}
|
|
|
|
void UUserWidget::AddToScreen(ULocalPlayer* Player, int32 ZOrder)
|
|
{
|
|
if ( !FullScreenWidget.IsValid() )
|
|
{
|
|
if ( UPanelWidget* ParentPanel = GetParent() )
|
|
{
|
|
FMessageLog("PIE").Error(FText::Format(LOCTEXT("WidgetAlreadyHasParent", "The widget '{0}' already has a parent widget. It can't also be added to the viewport!"),
|
|
FText::FromString(GetClass()->GetName())));
|
|
return;
|
|
}
|
|
|
|
// First create and initialize the variable so that users calling this function twice don't
|
|
// attempt to add the widget to the viewport again.
|
|
TSharedRef<SConstraintCanvas> FullScreenCanvas = SNew(SConstraintCanvas);
|
|
FullScreenWidget = FullScreenCanvas;
|
|
|
|
TSharedRef<SWidget> UserSlateWidget = TakeWidget();
|
|
|
|
FullScreenCanvas->AddSlot()
|
|
.Offset(BIND_UOBJECT_ATTRIBUTE(FMargin, GetFullScreenOffset))
|
|
.Anchors(BIND_UOBJECT_ATTRIBUTE(FAnchors, GetViewportAnchors))
|
|
.Alignment(BIND_UOBJECT_ATTRIBUTE(FVector2D, GetFullScreenAlignment))
|
|
[
|
|
UserSlateWidget
|
|
];
|
|
|
|
// If this is a game world add the widget to the current worlds viewport.
|
|
UWorld* World = GetWorld();
|
|
if ( World && World->IsGameWorld() )
|
|
{
|
|
if ( UGameViewportClient* ViewportClient = World->GetGameViewport() )
|
|
{
|
|
if ( Player )
|
|
{
|
|
ViewportClient->AddViewportWidgetForPlayer(Player, FullScreenCanvas, ZOrder);
|
|
}
|
|
else
|
|
{
|
|
// We add 10 to the zorder when adding to the viewport to avoid
|
|
// displaying below any built-in controls, like the virtual joysticks on mobile builds.
|
|
ViewportClient->AddViewportWidgetContent(FullScreenCanvas, ZOrder + 10);
|
|
}
|
|
|
|
// Just in case we already hooked this delegate, remove the handler.
|
|
FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this);
|
|
|
|
// Widgets added to the viewport are automatically removed if the persistent level is unloaded.
|
|
FWorldDelegates::LevelRemovedFromWorld.AddUObject(this, &UUserWidget::OnLevelRemovedFromWorld);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FMessageLog("PIE").Warning(FText::Format(LOCTEXT("WidgetAlreadyOnScreen", "The widget '{0}' was already added to the screen."),
|
|
FText::FromString(GetClass()->GetName())));
|
|
}
|
|
}
|
|
|
|
void UUserWidget::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)
|
|
{
|
|
// If the InLevel is null, it's a signal that the entire world is about to disappear, so
|
|
// go ahead and remove this widget from the viewport, it could be holding onto too many
|
|
// dangerous actor references that won't carry over into the next world.
|
|
if ( InLevel == nullptr && InWorld == GetWorld() )
|
|
{
|
|
RemoveFromParent();
|
|
MarkPendingKill();
|
|
}
|
|
}
|
|
|
|
void UUserWidget::RemoveFromViewport()
|
|
{
|
|
RemoveFromParent();
|
|
}
|
|
|
|
void UUserWidget::RemoveFromParent()
|
|
{
|
|
if ( FullScreenWidget.IsValid() )
|
|
{
|
|
TSharedPtr<SWidget> WidgetHost = FullScreenWidget.Pin();
|
|
|
|
// If this is a game world add the widget to the current worlds viewport.
|
|
UWorld* World = GetWorld();
|
|
if ( World && World->IsGameWorld() )
|
|
{
|
|
if ( UGameViewportClient* ViewportClient = World->GetGameViewport() )
|
|
{
|
|
TSharedRef<SWidget> WidgetHostRef = WidgetHost.ToSharedRef();
|
|
|
|
ViewportClient->RemoveViewportWidgetContent(WidgetHostRef);
|
|
|
|
if ( ULocalPlayer* LocalPlayer = GetOwningLocalPlayer() )
|
|
{
|
|
ViewportClient->RemoveViewportWidgetForPlayer(LocalPlayer, WidgetHostRef);
|
|
}
|
|
|
|
FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Super::RemoveFromParent();
|
|
}
|
|
}
|
|
|
|
bool UUserWidget::GetIsVisible() const
|
|
{
|
|
return FullScreenWidget.IsValid();
|
|
}
|
|
|
|
bool UUserWidget::IsInViewport() const
|
|
{
|
|
return FullScreenWidget.IsValid();
|
|
}
|
|
|
|
void UUserWidget::SetPlayerContext(const FLocalPlayerContext& InPlayerContext)
|
|
{
|
|
PlayerContext = InPlayerContext;
|
|
}
|
|
|
|
const FLocalPlayerContext& UUserWidget::GetPlayerContext() const
|
|
{
|
|
return PlayerContext;
|
|
}
|
|
|
|
ULocalPlayer* UUserWidget::GetOwningLocalPlayer() const
|
|
{
|
|
if (PlayerContext.IsValid())
|
|
{
|
|
return PlayerContext.GetLocalPlayer();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void UUserWidget::SetOwningLocalPlayer(ULocalPlayer* LocalPlayer)
|
|
{
|
|
if ( LocalPlayer )
|
|
{
|
|
PlayerContext = FLocalPlayerContext(LocalPlayer, GetWorld());
|
|
}
|
|
}
|
|
|
|
APlayerController* UUserWidget::GetOwningPlayer() const
|
|
{
|
|
return PlayerContext.IsValid() ? PlayerContext.GetPlayerController() : nullptr;
|
|
}
|
|
|
|
class APawn* UUserWidget::GetOwningPlayerPawn() const
|
|
{
|
|
if ( APlayerController* PC = GetOwningPlayer() )
|
|
{
|
|
return PC->GetPawn();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void UUserWidget::SetPositionInViewport(FVector2D Position, bool bRemoveDPIScale )
|
|
{
|
|
if ( bRemoveDPIScale )
|
|
{
|
|
float Scale = UWidgetLayoutLibrary::GetViewportScale(this);
|
|
|
|
ViewportOffsets.Left = Position.X / Scale;
|
|
ViewportOffsets.Top = Position.Y / Scale;
|
|
}
|
|
else
|
|
{
|
|
ViewportOffsets.Left = Position.X;
|
|
ViewportOffsets.Top = Position.Y;
|
|
}
|
|
|
|
ViewportAnchors = FAnchors(0, 0);
|
|
}
|
|
|
|
void UUserWidget::SetDesiredSizeInViewport(FVector2D DesiredSize)
|
|
{
|
|
ViewportOffsets.Right = DesiredSize.X;
|
|
ViewportOffsets.Bottom = DesiredSize.Y;
|
|
|
|
ViewportAnchors = FAnchors(0, 0);
|
|
}
|
|
|
|
void UUserWidget::SetAnchorsInViewport(FAnchors Anchors)
|
|
{
|
|
ViewportAnchors = Anchors;
|
|
}
|
|
|
|
void UUserWidget::SetAlignmentInViewport(FVector2D Alignment)
|
|
{
|
|
ViewportAlignment = Alignment;
|
|
}
|
|
|
|
FMargin UUserWidget::GetFullScreenOffset() const
|
|
{
|
|
// If the size is zero, and we're not stretched, then use the desired size.
|
|
FVector2D FinalSize = FVector2D(ViewportOffsets.Right, ViewportOffsets.Bottom);
|
|
if ( FinalSize.IsZero() && !ViewportAnchors.IsStretchedVertical() && !ViewportAnchors.IsStretchedHorizontal() )
|
|
{
|
|
TSharedPtr<SWidget> CachedWidget = GetCachedWidget();
|
|
if ( CachedWidget.IsValid() )
|
|
{
|
|
FinalSize = CachedWidget->GetDesiredSize();
|
|
}
|
|
}
|
|
|
|
return FMargin(ViewportOffsets.Left, ViewportOffsets.Top, FinalSize.X, FinalSize.Y);
|
|
}
|
|
|
|
FAnchors UUserWidget::GetViewportAnchors() const
|
|
{
|
|
return ViewportAnchors;
|
|
}
|
|
|
|
FVector2D UUserWidget::GetFullScreenAlignment() const
|
|
{
|
|
return ViewportAlignment;
|
|
}
|
|
|
|
void UUserWidget::RemoveObsoleteBindings(const TArray<FName>& NamedSlots)
|
|
{
|
|
for (int32 BindingIndex = 0; BindingIndex < NamedSlotBindings.Num(); BindingIndex++)
|
|
{
|
|
const FNamedSlotBinding& Binding = NamedSlotBindings[BindingIndex];
|
|
|
|
if (!NamedSlots.Contains(Binding.Name))
|
|
{
|
|
NamedSlotBindings.RemoveAt(BindingIndex);
|
|
BindingIndex--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UUserWidget::PreSave(const class ITargetPlatform* TargetPlatform)
|
|
{
|
|
Super::PreSave(TargetPlatform);
|
|
|
|
// Remove bindings that are no longer contained in the class.
|
|
if ( UWidgetBlueprintGeneratedClass* BGClass = Cast<UWidgetBlueprintGeneratedClass>(GetClass()) )
|
|
{
|
|
RemoveObsoleteBindings(BGClass->NamedSlots);
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
const FText UUserWidget::GetPaletteCategory()
|
|
{
|
|
return PaletteCategory;
|
|
}
|
|
|
|
void UUserWidget::SetDesignerFlags(EWidgetDesignFlags::Type NewFlags)
|
|
{
|
|
Super::SetDesignerFlags(NewFlags);
|
|
|
|
WidgetTree->ForEachWidget([&] (UWidget* Widget) {
|
|
Widget->SetDesignerFlags(NewFlags);
|
|
});
|
|
}
|
|
|
|
#endif
|
|
|
|
void UUserWidget::OnAnimationStarted_Implementation(const UWidgetAnimation* Animation)
|
|
{
|
|
|
|
}
|
|
|
|
void UUserWidget::OnAnimationFinished_Implementation(const UWidgetAnimation* Animation)
|
|
{
|
|
|
|
}
|
|
|
|
// Native handling for SObjectWidget
|
|
|
|
void UUserWidget::NativeConstruct()
|
|
{
|
|
Construct();
|
|
}
|
|
|
|
void UUserWidget::NativeDestruct()
|
|
{
|
|
StopListeningForAllInputActions();
|
|
Destruct();
|
|
}
|
|
|
|
void UUserWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
|
{
|
|
GInitRunaway();
|
|
|
|
TickActionsAndAnimation(MyGeometry, InDeltaTime);
|
|
|
|
if ( bCanEverTick )
|
|
{
|
|
Tick(MyGeometry, InDeltaTime);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::TickActionsAndAnimation(const FGeometry& MyGeometry, float InDeltaTime)
|
|
{
|
|
if ( IsDesignTime() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Update active movie scenes, none will be removed here, but new
|
|
// ones can be added during the tick, if a player ends and triggers
|
|
// starting another animation
|
|
for ( int32 Index = 0; Index < ActiveSequencePlayers.Num(); Index++ )
|
|
{
|
|
UUMGSequencePlayer* Player = ActiveSequencePlayers[Index];
|
|
Player->Tick( InDeltaTime );
|
|
}
|
|
|
|
const bool bWasPlayingAnimation = IsPlayingAnimation();
|
|
|
|
// The process of ticking the players above can stop them so we remove them after all players have ticked
|
|
for ( UUMGSequencePlayer* StoppedPlayer : StoppedSequencePlayers )
|
|
{
|
|
ActiveSequencePlayers.RemoveSwap(StoppedPlayer);
|
|
}
|
|
|
|
StoppedSequencePlayers.Empty();
|
|
|
|
// If we're no longer playing animations invalidate layout so that we recache the volatility of the widget.
|
|
if ( bWasPlayingAnimation && IsPlayingAnimation() == false )
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
UWorld* World = GetWorld();
|
|
if ( World )
|
|
{
|
|
// Update any latent actions we have for this actor
|
|
FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
|
|
LatentActionManager.ProcessLatentActions(this, InDeltaTime);
|
|
}
|
|
}
|
|
|
|
void UUserWidget::ListenForInputAction( FName ActionName, TEnumAsByte< EInputEvent > EventType, bool bConsume, FOnInputAction Callback )
|
|
{
|
|
if ( !InputComponent )
|
|
{
|
|
InitializeInputComponent();
|
|
}
|
|
|
|
if ( InputComponent )
|
|
{
|
|
FInputActionBinding NewBinding( ActionName, EventType.GetValue() );
|
|
NewBinding.bConsumeInput = bConsume;
|
|
NewBinding.ActionDelegate.GetDelegateForManualSet().BindUObject( this, &ThisClass::OnInputAction, Callback );
|
|
|
|
InputComponent->AddActionBinding( NewBinding );
|
|
}
|
|
}
|
|
|
|
void UUserWidget::StopListeningForInputAction( FName ActionName, TEnumAsByte< EInputEvent > EventType )
|
|
{
|
|
if ( InputComponent )
|
|
{
|
|
for ( int32 ExistingIndex = InputComponent->GetNumActionBindings() - 1; ExistingIndex >= 0; --ExistingIndex )
|
|
{
|
|
const FInputActionBinding& ExistingBind = InputComponent->GetActionBinding( ExistingIndex );
|
|
if ( ExistingBind.ActionName == ActionName && ExistingBind.KeyEvent == EventType )
|
|
{
|
|
InputComponent->RemoveActionBinding( ExistingIndex );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UUserWidget::StopListeningForAllInputActions()
|
|
{
|
|
if ( InputComponent )
|
|
{
|
|
for ( int32 ExistingIndex = InputComponent->GetNumActionBindings() - 1; ExistingIndex >= 0; --ExistingIndex )
|
|
{
|
|
InputComponent->RemoveActionBinding( ExistingIndex );
|
|
}
|
|
|
|
UnregisterInputComponent();
|
|
|
|
InputComponent->ClearActionBindings();
|
|
InputComponent->MarkPendingKill();
|
|
InputComponent = nullptr;
|
|
}
|
|
}
|
|
|
|
bool UUserWidget::IsListeningForInputAction( FName ActionName ) const
|
|
{
|
|
bool bResult = false;
|
|
if ( InputComponent )
|
|
{
|
|
for ( int32 ExistingIndex = InputComponent->GetNumActionBindings() - 1; ExistingIndex >= 0; --ExistingIndex )
|
|
{
|
|
const FInputActionBinding& ExistingBind = InputComponent->GetActionBinding( ExistingIndex );
|
|
if ( ExistingBind.ActionName == ActionName )
|
|
{
|
|
bResult = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
void UUserWidget::RegisterInputComponent()
|
|
{
|
|
if ( InputComponent )
|
|
{
|
|
if ( APlayerController* Controller = GetOwningPlayer() )
|
|
{
|
|
Controller->PushInputComponent(InputComponent);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UUserWidget::UnregisterInputComponent()
|
|
{
|
|
if ( InputComponent )
|
|
{
|
|
if ( APlayerController* Controller = GetOwningPlayer() )
|
|
{
|
|
Controller->PopInputComponent(InputComponent);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UUserWidget::SetInputActionPriority( int32 NewPriority )
|
|
{
|
|
if ( InputComponent )
|
|
{
|
|
Priority = NewPriority;
|
|
InputComponent->Priority = Priority;
|
|
}
|
|
}
|
|
|
|
void UUserWidget::SetInputActionBlocking( bool bShouldBlock )
|
|
{
|
|
if ( InputComponent )
|
|
{
|
|
bStopAction = bShouldBlock;
|
|
InputComponent->bBlockInput = bStopAction;
|
|
}
|
|
}
|
|
|
|
void UUserWidget::OnInputAction( FOnInputAction Callback )
|
|
{
|
|
if ( GetIsEnabled() )
|
|
{
|
|
Callback.ExecuteIfBound();
|
|
}
|
|
}
|
|
|
|
void UUserWidget::InitializeInputComponent()
|
|
{
|
|
if ( APlayerController* Controller = GetOwningPlayer() )
|
|
{
|
|
InputComponent = NewObject< UInputComponent >( this, NAME_None, RF_Transient );
|
|
InputComponent->bBlockInput = bStopAction;
|
|
InputComponent->Priority = Priority;
|
|
Controller->PushInputComponent( InputComponent );
|
|
}
|
|
else
|
|
{
|
|
FMessageLog("PIE").Info(FText::Format(LOCTEXT("NoInputListeningWithoutPlayerController", "Unable to listen to input actions without a player controller in {0}."), FText::FromName(GetClass()->GetFName())));
|
|
}
|
|
}
|
|
|
|
void UUserWidget::NativePaint( FPaintContext& InContext ) const
|
|
{
|
|
if ( bCanEverPaint )
|
|
{
|
|
OnPaint( InContext );
|
|
}
|
|
}
|
|
|
|
bool UUserWidget::NativeIsInteractable() const
|
|
{
|
|
return IsInteractable();
|
|
}
|
|
|
|
bool UUserWidget::NativeSupportsKeyboardFocus() const
|
|
{
|
|
return bIsFocusable;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnFocusReceived( const FGeometry& InGeometry, const FFocusEvent& InFocusEvent )
|
|
{
|
|
return OnFocusReceived( InGeometry, InFocusEvent ).NativeReply;
|
|
}
|
|
|
|
void UUserWidget::NativeOnFocusLost( const FFocusEvent& InFocusEvent )
|
|
{
|
|
OnFocusLost( InFocusEvent );
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnKeyChar( const FGeometry& InGeometry, const FCharacterEvent& InCharEvent )
|
|
{
|
|
return OnKeyChar( InGeometry, InCharEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnPreviewKeyDown( const FGeometry& InGeometry, const FKeyEvent& InKeyEvent )
|
|
{
|
|
return OnPreviewKeyDown( InGeometry, InKeyEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnKeyDown( const FGeometry& InGeometry, const FKeyEvent& InKeyEvent )
|
|
{
|
|
return OnKeyDown( InGeometry, InKeyEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnKeyUp( const FGeometry& InGeometry, const FKeyEvent& InKeyEvent )
|
|
{
|
|
return OnKeyUp( InGeometry, InKeyEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnAnalogValueChanged( const FGeometry& InGeometry, const FAnalogInputEvent& InAnalogEvent )
|
|
{
|
|
return OnAnalogValueChanged( InGeometry, InAnalogEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnMouseButtonDown( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
return OnMouseButtonDown( InGeometry, InMouseEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnPreviewMouseButtonDown( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
return OnPreviewMouseButtonDown( InGeometry, InMouseEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnMouseButtonUp( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
return OnMouseButtonUp(InGeometry, InMouseEvent).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnMouseMove( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
return OnMouseMove( InGeometry, InMouseEvent ).NativeReply;
|
|
}
|
|
|
|
void UUserWidget::NativeOnMouseEnter( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
OnMouseEnter( InGeometry, InMouseEvent );
|
|
}
|
|
|
|
void UUserWidget::NativeOnMouseLeave( const FPointerEvent& InMouseEvent )
|
|
{
|
|
OnMouseLeave( InMouseEvent );
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnMouseWheel( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
return OnMouseWheel( InGeometry, InMouseEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnMouseButtonDoubleClick( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent )
|
|
{
|
|
return OnMouseButtonDoubleClick( InGeometry, InMouseEvent ).NativeReply;
|
|
}
|
|
|
|
void UUserWidget::NativeOnDragDetected( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, UDragDropOperation*& InOperation )
|
|
{
|
|
OnDragDetected( InGeometry, InMouseEvent, InOperation );
|
|
}
|
|
|
|
void UUserWidget::NativeOnDragEnter( const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation )
|
|
{
|
|
OnDragEnter( InGeometry, InDragDropEvent, InOperation );
|
|
}
|
|
|
|
void UUserWidget::NativeOnDragLeave( const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation )
|
|
{
|
|
OnDragLeave( InDragDropEvent, InOperation );
|
|
}
|
|
|
|
bool UUserWidget::NativeOnDragOver( const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation )
|
|
{
|
|
return OnDragOver( InGeometry, InDragDropEvent, InOperation );
|
|
}
|
|
|
|
bool UUserWidget::NativeOnDrop( const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation )
|
|
{
|
|
return OnDrop( InGeometry, InDragDropEvent, InOperation );
|
|
}
|
|
|
|
void UUserWidget::NativeOnDragCancelled( const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation )
|
|
{
|
|
OnDragCancelled( InDragDropEvent, InOperation );
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnTouchGesture( const FGeometry& InGeometry, const FPointerEvent& InGestureEvent )
|
|
{
|
|
return OnTouchGesture( InGeometry, InGestureEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnTouchStarted( const FGeometry& InGeometry, const FPointerEvent& InGestureEvent )
|
|
{
|
|
return OnTouchStarted( InGeometry, InGestureEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnTouchMoved( const FGeometry& InGeometry, const FPointerEvent& InGestureEvent )
|
|
{
|
|
return OnTouchMoved( InGeometry, InGestureEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnTouchEnded( const FGeometry& InGeometry, const FPointerEvent& InGestureEvent )
|
|
{
|
|
return OnTouchEnded( InGeometry, InGestureEvent ).NativeReply;
|
|
}
|
|
|
|
FReply UUserWidget::NativeOnMotionDetected( const FGeometry& InGeometry, const FMotionEvent& InMotionEvent )
|
|
{
|
|
return OnMotionDetected( InGeometry, InMotionEvent ).NativeReply;
|
|
}
|
|
|
|
FCursorReply UUserWidget::NativeOnCursorQuery( const FGeometry& InGeometry, const FPointerEvent& InCursorEvent )
|
|
{
|
|
return FCursorReply::Unhandled();
|
|
}
|
|
|
|
void UUserWidget::PostLoad()
|
|
{
|
|
Super::PostLoad();
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
|
|
if ( GetLinkerUE4Version() < VER_UE4_DEPRECATE_USER_WIDGET_DESIGN_SIZE )
|
|
{
|
|
if ( bUseDesignTimeSize_DEPRECATED )
|
|
{
|
|
DesignSizeMode = EDesignPreviewSizeMode::Custom;
|
|
}
|
|
else if ( bUseDesiredSizeAtDesignTime_DEPRECATED )
|
|
{
|
|
DesignSizeMode = EDesignPreviewSizeMode::Desired;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void UUserWidget::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
|
|
if ( Ar.IsLoading() )
|
|
{
|
|
if ( Ar.UE4Ver() < VER_UE4_USERWIDGET_DEFAULT_FOCUSABLE_FALSE )
|
|
{
|
|
bIsFocusable = bSupportsKeyboardFocus_DEPRECATED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
//
|
|
// DEPRECATED
|
|
//
|
|
/////////////////////////////////////////////////////
|
|
|
|
void UUserWidget::Construct_Implementation()
|
|
{
|
|
|
|
}
|
|
|
|
void UUserWidget::Tick_Implementation(FGeometry MyGeometry, float InDeltaTime)
|
|
{
|
|
|
|
}
|
|
|
|
void UUserWidget::OnPaint_Implementation(FPaintContext& Context) const
|
|
{
|
|
|
|
}
|
|
|
|
FEventReply UUserWidget::OnFocusReceived_Implementation(FGeometry MyGeometry, FFocusEvent InFocusEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
void UUserWidget::OnFocusLost_Implementation(FFocusEvent InFocusEvent)
|
|
{
|
|
|
|
}
|
|
|
|
FEventReply UUserWidget::OnKeyChar_Implementation(FGeometry MyGeometry, FCharacterEvent InCharacterEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnPreviewKeyDown_Implementation(FGeometry MyGeometry, FKeyEvent InKeyEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnKeyDown_Implementation(FGeometry MyGeometry, FKeyEvent InKeyEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnKeyUp_Implementation(FGeometry MyGeometry, FKeyEvent InKeyEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnAnalogValueChanged_Implementation(FGeometry MyGeometry, FAnalogInputEvent InAnalogInputEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnMouseButtonDown_Implementation(FGeometry MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnPreviewMouseButtonDown_Implementation(FGeometry MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnMouseButtonUp_Implementation(FGeometry MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnMouseMove_Implementation(FGeometry MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
void UUserWidget::OnMouseEnter_Implementation(FGeometry MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
|
|
}
|
|
|
|
void UUserWidget::OnMouseLeave_Implementation(const FPointerEvent& MouseEvent)
|
|
{
|
|
|
|
}
|
|
|
|
FEventReply UUserWidget::OnMouseWheel_Implementation(FGeometry MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnMouseButtonDoubleClick_Implementation(FGeometry InMyGeometry, const FPointerEvent& InMouseEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
void UUserWidget::OnDragDetected_Implementation(FGeometry MyGeometry, const FPointerEvent& PointerEvent, UDragDropOperation*& Operation)
|
|
{
|
|
|
|
}
|
|
|
|
void UUserWidget::OnDragCancelled_Implementation(const FPointerEvent& PointerEvent, UDragDropOperation* Operation)
|
|
{
|
|
|
|
}
|
|
|
|
void UUserWidget::OnDragEnter_Implementation(FGeometry MyGeometry, FPointerEvent PointerEvent, UDragDropOperation* Operation)
|
|
{
|
|
|
|
}
|
|
|
|
void UUserWidget::OnDragLeave_Implementation(FPointerEvent PointerEvent, UDragDropOperation* Operation)
|
|
{
|
|
|
|
}
|
|
|
|
bool UUserWidget::OnDragOver_Implementation(FGeometry MyGeometry, FPointerEvent PointerEvent, UDragDropOperation* Operation)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool UUserWidget::OnDrop_Implementation(FGeometry MyGeometry, FPointerEvent PointerEvent, UDragDropOperation* Operation)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FEventReply UUserWidget::OnTouchGesture_Implementation(FGeometry MyGeometry, const FPointerEvent& GestureEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnTouchStarted_Implementation(FGeometry MyGeometry, const FPointerEvent& InTouchEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnTouchMoved_Implementation(FGeometry MyGeometry, const FPointerEvent& InTouchEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnTouchEnded_Implementation(FGeometry MyGeometry, const FPointerEvent& InTouchEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
FEventReply UUserWidget::OnMotionDetected_Implementation(FGeometry MyGeometry, FMotionEvent InMotionEvent)
|
|
{
|
|
return UWidgetBlueprintLibrary::Unhandled();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
|
|
#undef LOCTEXT_NAMESPACE
|