More work on focus, and good docs
This commit is contained in:
@@ -7,10 +7,11 @@
|
||||
#include "LuprexViewportClient.h"
|
||||
#include "Common.h"
|
||||
#include "PlayerControllerBase.h"
|
||||
#include "RootCanvas.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Layout/WidgetPath.h"
|
||||
#include "Widgets/SViewport.h"
|
||||
#include "Slate/SObjectWidget.h"
|
||||
|
||||
UlxViewportClient::UlxViewportClient(const FObjectInitializer &ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
@@ -18,44 +19,44 @@ UlxViewportClient::UlxViewportClient(const FObjectInitializer &ObjectInitializer
|
||||
UE_LOG(LogLuprexIntegration, Display, TEXT("UlxViewportClient constructed"));
|
||||
}
|
||||
|
||||
bool UlxViewportClient::TryBringToFront(const FWidgetPath &Path)
|
||||
{
|
||||
UGameInstance *GI = GetGameInstance();
|
||||
if (!GI) return false;
|
||||
AlxPlayerControllerBase *PC = Cast<AlxPlayerControllerBase>(
|
||||
GI->GetFirstLocalPlayerController(GetWorld()));
|
||||
if (!PC) return false;
|
||||
|
||||
for (int32 Idx = 0; Idx < Path.Widgets.Num(); ++Idx)
|
||||
{
|
||||
SWidget &SW = Path.Widgets[Idx].Widget.Get();
|
||||
if (SW.GetType() != FName(TEXT("SObjectWidget"))) continue;
|
||||
UUserWidget *Widget = static_cast<SObjectWidget&>(SW).GetWidgetObject();
|
||||
if (Widget && Widget->GetParent() == PC->RootCanvas)
|
||||
{
|
||||
UlxRootCanvasPanel::BringToFront(Widget);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UlxViewportClient::InputKey(const FInputKeyEventArgs &EventArgs)
|
||||
{
|
||||
UE_LOG(LogLuprexIntegration, Display, TEXT("UlxViewportClient::InputKey key=%s event=%d"),
|
||||
*EventArgs.Key.ToString(), (int32)EventArgs.Event);
|
||||
|
||||
// Only act on left mouse button presses that bubbled up to the
|
||||
// viewport unhandled. Walk the widget path under the cursor and
|
||||
// find the nearest focusable ancestor of whatever was hit. If it
|
||||
// isn't the game viewport itself, hand it to the player controller
|
||||
// to apply on its next UpdateInputMode pass; that's the point in
|
||||
// the frame where we can override SViewport's own click-focus
|
||||
// behaviour without fighting it.
|
||||
// viewport unhandled. If the click landed on a descendant of a
|
||||
// top-level widget in the root canvas, bring that top-level widget
|
||||
// to the front.
|
||||
if ((EventArgs.Event == IE_Pressed) && (EventArgs.Key == EKeys::LeftMouseButton))
|
||||
{
|
||||
FSlateApplication &Slate = FSlateApplication::Get();
|
||||
FVector2D MousePos = Slate.GetCursorPos();
|
||||
FWidgetPath Path = Slate.LocateWindowUnderMouse(
|
||||
MousePos, Slate.GetInteractiveTopLevelWindows());
|
||||
|
||||
if (Path.IsValid())
|
||||
{
|
||||
TSharedPtr<SViewport> ViewportWidget = GetGameViewportWidget();
|
||||
for (int32 Idx = Path.Widgets.Num() - 1; Idx >= 0; --Idx)
|
||||
{
|
||||
TSharedRef<SWidget> Widget = Path.Widgets[Idx].Widget;
|
||||
if (!Widget->SupportsKeyboardFocus()) continue;
|
||||
if (ViewportWidget.IsValid() && Widget == ViewportWidget) break;
|
||||
if (UGameInstance *GI = GetGameInstance())
|
||||
{
|
||||
if (AlxPlayerControllerBase *PC = Cast<AlxPlayerControllerBase>(
|
||||
GI->GetFirstLocalPlayerController(GetWorld())))
|
||||
{
|
||||
PC->ClickToFocus(Widget);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Path.IsValid() && TryBringToFront(Path)) return true;
|
||||
}
|
||||
|
||||
return Super::InputKey(EventArgs);
|
||||
|
||||
@@ -25,4 +25,7 @@ public:
|
||||
UlxViewportClient(const FObjectInitializer &ObjectInitializer);
|
||||
|
||||
virtual bool InputKey(const FInputKeyEventArgs &EventArgs) override;
|
||||
|
||||
private:
|
||||
bool TryBringToFront(const FWidgetPath &Path);
|
||||
};
|
||||
|
||||
@@ -135,7 +135,7 @@ void AlxPlayerControllerBase::RestoreFocusToFrontWidget(const UObject *Context)
|
||||
{
|
||||
// This will trigger UpdateInputMode to shift focus back to
|
||||
// the front window, if the front window wants focus.
|
||||
FromContext(Context)->LastWidgetGrantedFocus = nullptr;
|
||||
FromContext(Context)->RootCanvas->LastWidgetGrantedFocus = nullptr;
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::AddWidgetToRoot(UUserWidget *Widget)
|
||||
@@ -319,32 +319,22 @@ void AlxPlayerControllerBase::UpdateInputMode()
|
||||
// viewport client notifies us of that fact. We then focus the
|
||||
// widget if possible.
|
||||
//
|
||||
if ((!ShowPointer) || (LastWidgetGrantedFocus != Focus))
|
||||
if ((!ShowPointer) || (RootCanvas->LastWidgetGrantedFocus != Focus))
|
||||
{
|
||||
if (Focus)
|
||||
{
|
||||
if (TSharedPtr<SWidget> SlateFocus = Focus->GetCachedWidget())
|
||||
{
|
||||
SlateOperations.SetUserFocus(SlateFocus.ToSharedRef());
|
||||
LastWidgetGrantedFocus = Focus;
|
||||
RootCanvas->LastWidgetGrantedFocus = Focus;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SlateOperations.SetUserFocus(ViewportWidgetRef);
|
||||
LastWidgetGrantedFocus = nullptr;
|
||||
RootCanvas->LastWidgetGrantedFocus = nullptr;
|
||||
}
|
||||
}
|
||||
else if (TSharedPtr<SWidget> ClickedWidget = ClickToFocusTarget.Pin())
|
||||
{
|
||||
SlateOperations.SetUserFocus(ClickedWidget.ToSharedRef());
|
||||
}
|
||||
ClickToFocusTarget.Reset();
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::ClickToFocus(TSharedRef<SWidget> Widget)
|
||||
{
|
||||
ClickToFocusTarget = Widget.ToWeakPtr();
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::UpdateLookAt()
|
||||
|
||||
@@ -80,9 +80,6 @@ public:
|
||||
UPROPERTY()
|
||||
FHitResult CurrentLookAt;
|
||||
|
||||
// The last widget whose focus request was granted.
|
||||
TObjectKey<UWidget> LastWidgetGrantedFocus;
|
||||
|
||||
// The single top-level UUserWidget added to the viewport. All
|
||||
// top-level UI widgets are children of RootCanvas inside it.
|
||||
UPROPERTY()
|
||||
@@ -94,14 +91,5 @@ public:
|
||||
UPROPERTY()
|
||||
UlxRootCanvasPanel *RootCanvas = nullptr;
|
||||
|
||||
// The viewport client uses this to notify us that the user
|
||||
// clicked on a focusable widget.
|
||||
void ClickToFocus(TSharedRef<SWidget> Widget);
|
||||
|
||||
private:
|
||||
TWeakPtr<SWidget> ClickToFocusTarget;
|
||||
|
||||
public:
|
||||
|
||||
bool MustCallLookAtChanged = false;
|
||||
};
|
||||
|
||||
@@ -101,6 +101,8 @@ void UlxRootCanvasPanel::BringToFront(UUserWidget *Widget)
|
||||
UlxRootCanvasPanel *Panel = Cast<UlxRootCanvasPanel>(Slot->Parent);
|
||||
if (!Panel) return;
|
||||
Slot->BringToFrontCount = ++Panel->BringToFrontCounter;
|
||||
// This refocuses the widget, even if it was already in front.
|
||||
Panel->LastWidgetGrantedFocus = Panel;
|
||||
}
|
||||
|
||||
void UlxRootCanvasPanel::SetWidgetWindowManagement(class UUserWidget *Widget,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Common.h"
|
||||
#include "UObject/ObjectKey.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Components/CanvasPanel.h"
|
||||
#include "Components/CanvasPanelSlot.h"
|
||||
@@ -128,6 +129,9 @@ public:
|
||||
meta = (DefaultToSelf = "Widget", ExpandEnumAsExecs = "Result"))
|
||||
static UlxRootCanvasSlot *GetRootCanvasSlot(class UUserWidget *Widget, ElxSuccessOrWrongType &Result);
|
||||
|
||||
// The last widget whose focus request was granted.
|
||||
TObjectKey<UWidget> LastWidgetGrantedFocus;
|
||||
|
||||
protected:
|
||||
|
||||
// We inherit most of our code from CanvasPanel. This causes the
|
||||
|
||||
Reference in New Issue
Block a user