ウィジェット外をクリックした場合にフォーカスを維持する方法、ナビゲーション処理について

お世話になっております。

タイトルに記載しております通り、以下2点についてお伺いさせていただきたいです。

①.フォーカス可能なウィジェット外をクリックした際にフォーカスを維持する対処、実装方法

②.アプリケーションウィンドウが非アクティブの際、入力によるナビゲーションを完全に無効化する方法

まず①についてですが、実現したいこととして「アプリケーションウィンドウ内の非UI(フォーカス可能でない部分)をクリックした場合でもフォーカスを維持し続ける」という内容があります。

こちらについて、現在私の環境では以下の実装を行うことで対処しております。

`void UFocusRetainButton::NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{
if(bHasFocused) { return; }

Super::NativeOnMouseEnter(InGeometry, InMouseEvent);

if(!IsValid(Button)) { return; }

if (const UWorld* World = GetWorld())
{
if (ULocalPlayer* LocalPlayer = World->GetFirstLocalPlayerFromController())
{
// EFocusCause::Navigationとしてパラメータを渡す
LocalPlayer->GetSlateOperations().SetUserFocus(Button->TakeWidget(), EFocusCause::Navigation);
}
}
}`

`void UFocusRetainButton::NativeOnRemovedFromFocusPath(const FFocusEvent& InFocusEvent)
{
if(!bHasFocused) { return; }

Super::NativeOnRemovedFromFocusPath(InFocusEvent);

if(!IsValid(Button)) { return; }

const EFocusCause Cause = InFocusEvent.GetCause();
const bool bIsRetainFocus = (bRetainFocus) && (Cause == EFocusCause::Mouse);
if (bIsRetainFocus)
{
// ボタン外をクリックした場合はキーボードフォーカスが失われないようにする
if (const UWorld* World = GetWorld())
{
if (ULocalPlayer* LocalPlayer = World->GetFirstLocalPlayerFromController())
{
LocalPlayer->GetSlateOperations().SetUserFocus(Button->TakeWidget(), EFocusCause::Navigation);
}
}
}
}`上記の実装につきまして、問題などありますでしょうか?

また、上記実装以外により良い対処方法がありましたらご教示いただけますと幸いです。

次に②についてですが、実現したいこととして「アプリケーションウィンドウを非アクティブ(ウィンドウ外をクリック)にした際、入力によるナビゲーションを完全に無効化にする」という内容があります。

こちら詳細をご説明させていただきますと、アプリケーションウィンドウが非アクティブの際、キーボード、マウスによる入力は受け付けないものの、パッドによるナビゲーション入力が生きたままとなってしまっているため、これを無効化したいです。

上記問題に対する対処方法がありましたらご教示いただけますと幸いです。

参考までに、本問題を再現したプロジェクトを添付させていただきます。

以上です。宜しくお願いいたします。

再現手順
①に対する再現手順

1.レベルブループリントを開き、​bRetainWidgetFocusパラメータをTrueまたはFalseにする。(Trueでフォーカスを維持、FalseでUEでの従来の動作になります。)

2.PIEまたはスタンドアローンで実行する

3.画面左に表示されるボタン以外の箇所をクリックする

②に対する再現手順

1.PIEまたはスタンドアローンで実行する

2.アプリケーションウィンドウ外をクリックし、非アクティブにする

3.パッドでの入力を行う​

お世話になっております。

ご質問ありがとうございます。

ご質問の①につきましては回答までもう少々お時間をいただけますと幸いです。

ご質問の②につきましては、そもそもUEでは非アクティブ状態のウィンドウであってもゲームパッドからの入力を受け付けます。「ウィンドウが非アクティブ状態の場合、ゲームパッドからフォーカス操作のみ受け付けないようにする」というご要件かと思いますが、この場合、ゲーム全体として「非アクティブ状態のウィンドウでもキャラクターなどを動かすことができるが、メニューボタンを押してメニューを開いたとたん、UIの操作ができなくなる」という挙動になってしまう恐れがありますが、こちらは問題ございませんでしょうか?

フォーカスの問題のみピンポイントで対応するか、非アクティブ状態の際にキーボードやマウスと同様、ゲームパッドの入力自体を受け付けないようにするかで対応方法が変わってくるため、念のため確認させていただきたく存じます。

以上、よろしくお願いいたします。

お世話になっております。

ご返信いただきありがとうございます。

質問内容②に対して、ご質問いただいた内容にお答えさせていただきます。

>「非アクティブ状態のウィンドウでもキャラクターなどを動かすことができるが、メニューボタンを押してメニューを開いたとたん、UIの操作ができなくなる」という挙動になってしまう恐れがありますが

こちらに関しまして、現在私の環境では非アクティブ状態時にIMCを無効化にする対応を施しています。そのため、「非アクティブ状態のウィンドウでもキャラクターなどを動かすことができる」という内容に対しては既にキャラクターを動かせないように対応済みとなります。

しかし、ウィジェットに対してパッドの上下左右入力によるナビゲーションでの移動は無効化できておらず、上下入力の入力操作を行った場合にウィジェット間の移動が行えてしまうため、これを無効化したいというのが目的となります。

以上です。よろしくお願いいたします。

お世話になっております。

ご回答いただき、ありがとうございます。

長期に渡って返信が滞ってしまい大変申し訳ありません。

①に対するご回答、また、問題点に関しまして諸々承知いたしました。

頂いた問題点を考慮しつつ、実装を進めようと思います。

②に関しまして、具体的な実装内容についてご教示いただきありがとうございます。

頂いた内容を元に私の環境で実装を行ったところ、本来想定していた内容に沿った挙動になっていることが確認できました。

以上のことから、今回質問させていただいた内容は解決とさせていただければと存じます。

お忙しい中、ご対応いただきありがとうございました。

お世話になっております。

ご回答いただき、誠にありがとうございます。

非アクティブ環境でのIMC無効化対策については、すでに実施されているとのこと、承知いたしました。

エンジンコード側に大きく手を入れるのではなく、プロジェクト側で実装する方法としては、ご提示いただいた①の方法でほぼ問題ないかと存じます。あえて問題点を挙げるとすれば、「フォーカスを留めさせる」というより「フォーカスが外れた際に即座に再フォーカスを行う」アプローチであるため、SetUserFocus()が二回実行されることが挙げられます。これにより、OnFocusChangingやOnFocusLostといったコールバックも二度呼び出されてしまうため、これらのコールバックを利用するコードにとって、本命のフォーカス変更に伴うコールバックがどちらになるか識別しづらくなるという可能性があるかもしれません。ただし、現状実害がないのであれば、大きな問題にはならないかと存じます。

②につきましては、①を小改造することで実装できそうです。ゲームパッドを操作したときのFFocusEvent::GetCause()の戻り値はEFocusCause::Navigationとなるため、IMCを無効化したコードを流用し、非アクティブ時にCauseがEFocusCause::Navigationだった場合にも再フォーカスを実行すれば、ご要件を満たせるのではないかと思います。

具体的には、下記のようなご対応を行っていただく想定となります。

・現在①用に実行している再フォーカスのパラメータを、EFocusCause::NavigationからEFocusCause::SetDirectlyに変更

・【非アクティブ時】NativeOnAddedToFocusPath()で、InFocusEvent.GetCause()がEFocusCause::Navigationの場合、Super::NativeOnAddedToFocusPath()のコール前に早期return

・【非アクティブ時】NativeOnRemovedFromFocusPath()で、InFocusEvent.GetCause()がEFocusCause::Navigationの場合、bIsRetainFocusがtrueになるよう判定式を調整

上記の内容で問題がないか、一度ご確認いただけますと幸いです。

以上、よろしくお願いいたします。

回答のご確認をありがとうございました!

それでは本件は解決済みとしてCloseさせていただきます。

以上、よろしくお願いいたします。