Unreal Engine 5.x Plugin Development - Focusable Widget Check

Unreal Engine 5.x Plugin Development - Focusable Widget Check

Will we have a native method solution instead of Manual Type Casting and reflection for checking Widget focusability.

Date: October 6, 2024
Purpose: To determine how to check whether a widget is focusable in Unreal Engine 5.4 using C++.


1. Initial Requirement

The user is developing a plugin in Unreal Engine 5.4 and requested help in checking whether a widget is focusable using C++.


2. Initial Suggestion

I initially suggested using the IsFocusable() method, assuming it existed in the base UWidget class. Here’s the basic code snippet provided:

#include "Blueprint/UserWidget.h"
#include "Components/Widget.h"

bool IsWidgetFocusable(UWidget* Widget)
{
    if (Widget)
    {
        return Widget->IsFocusable();
    }
    return false;
}

However, this suggestion was incorrect because UWidget, the base class, does not have an IsFocusable() method. This method is only present in specific widget classes like UButton, UEditableText, etc.


3. Corrected Approach

The correct approach involves checking for focusability only in widgets that inherit the bIsFocusable property. Here’s how the corrected approach was structured:

Approach: Type Casting for Focusable Widgets

To handle this, I suggested manually casting to specific widget types like UButton, UEditableText, and UCheckBox, which are known to support focusable behavior.

Example code provided:

#include "Components/Widget.h"
#include "Components/Button.h"
#include "Components/EditableText.h"
#include "Components/CheckBox.h"

bool IsWidgetFocusable(UWidget* Widget)
{
    if (Widget)
    {
        if (UButton* Button = Cast<UButton>(Widget))
        {
            return Button->IsFocusable();
        }
        else if (UEditableText* EditableText = Cast<UEditableText>(Widget))
        {
            return EditableText->IsFocusable();
        }
        else if (UCheckBox* CheckBox = Cast<UCheckBox>(Widget))
        {
            return CheckBox->IsFocusable();
        }
    }

    return false;
}

This solution is more reliable but requires manual casting, making it less flexible for widgets that may be added later or for a generalized solution.


4. Generalized Solution Using Reflection

The user requested a more general solution, one that checks if a widget inherits focusability dynamically, without casting to specific widget types.

Approach: Reflection-Based Solution

In Unreal Engine, reflection can be used to find and access the bIsFocusable property dynamically using Unreal’s FindPropertyByName and FBoolProperty classes.

Here’s the reflection-based solution provided:

#include "Components/Widget.h"
#include "UObject/Class.h"

// General function to check if a widget is focusable
bool IsWidgetFocusable(UWidget* Widget)
{
    if (!Widget)
    {
        return false;
    }

    // Use reflection to check if the widget class has the "bIsFocusable" property
    if (FProperty* FocusableProperty = Widget->GetClass()->FindPropertyByName(FName("bIsFocusable")))
    {
        // Check if the property is a boolean type and get its value
        if (FBoolProperty* BoolProp = CastField<FBoolProperty>(FocusableProperty))
        {
            return BoolProp->GetPropertyValue_InContainer(Widget);
        }
    }

    return false;
}

This approach works for any widget class that has the bIsFocusable property, making it more flexible.


5. Performance Considerations

The user inquired whether reflection is performance-intensive. I explained that reflection can introduce performance overhead, especially when called frequently or on large numbers of objects.

Reflection Overhead Factors:
  • Frequency of Calls: Using reflection every frame or across a large number of widgets can cause performance issues.
  • Number of Widgets: The more widgets checked via reflection, the higher the overhead.
Performance Optimization: Caching Reflection Results

I recommended an optimization to cache the result of the FindPropertyByName lookup to avoid repeatedly performing reflection for the same widget class:

#include "Components/Widget.h"
#include "UObject/Class.h"

bool IsWidgetFocusable(UWidget* Widget)
{
    static FProperty* FocusableProperty = nullptr;

    if (!Widget)
    {
        return false;
    }

    // Cache the property if it hasn't been cached yet
    if (!FocusableProperty)
    {
        FocusableProperty = Widget->GetClass()->FindPropertyByName(FName("bIsFocusable"));
    }

    // If the property exists, check if it’s a boolean and get its value
    if (FocusableProperty)
    {
        if (FBoolProperty* BoolProp = CastField<FBoolProperty>(FocusableProperty))
        {
            return BoolProp->GetPropertyValue_InContainer(Widget);
        }
    }

    return false;
}

This caching technique reduces the reflection overhead by performing the lookup only once per widget class, making it more efficient.


6. Alternative Solution: Custom Base Class

To avoid reflection entirely, I suggested an alternative approach by creating a custom base widget class that explicitly supports the focusability check. This solution avoids the overhead of reflection and keeps the code clean:

class UMyBaseWidget : public UWidget
{
    GENERATED_BODY()

public:
    virtual bool IsFocusable() const override
    {
        return bIsFocusable;
    }
};

This approach allows direct access to the bIsFocusable property in derived classes, improving performance at the cost of needing custom classes for all widgets.


Summary

  • Initial Solution: Suggested using IsFocusable() on UWidget, which was incorrect.
  • Corrected Approach: Manually casting to known focusable widget types (e.g., UButton, UEditableText).
  • General Solution: Using Unreal Engine’s reflection system to dynamically check for the bIsFocusable property.
  • Performance: Reflection can be performance-intensive. Optimized using caching.
  • Alternative: Avoid reflection by using a custom widget base class that explicitly supports the focus check.

This report captures the complete discussion on determining widget focusability in Unreal Engine 5.4. Let me know if you need further clarifications or help!

1 Like

These approaches are a nice way to solve this problem do be mindful of how slow reflection can be, to make sure you don’t have unnecessary Lages.

Thank you for the Information that will really help a lot of people.