Develop

Widget Regions for Gaze Targeting

Updated: Sep 8, 2026

What Widget Regions Are

This page is for developers building their own custom targeting system or custom UMG hit-testing logic. For creating a standard UMG UI that responds to gaze interactions, see Create a Gaze-Targetable UI.
A widget region is a world-space rectangle that represents one clickable sub-widget inside a UWidgetComponent. UIsdkWidgetRegionProvider binds to a single widget component, walks the UMG tree of its hosted UUserWidget, and exposes each clickable element as a region keyed by a stable int64 id. Ids come from UWidget::GetUniqueID() widened to int64, so they are always non-negative and stable for the lifetime of the underlying UWidget.
Two widget panels as region maps: one subdivided into clickable sub-widget rects, one resolving to a whole-panel region.
A nested icon wins the tiebreak on smaller pixel area, a clipped row keeps only its visible portion, and a fully clipped row is omitted from GetRegionIds.
Region rectangles are clipped to ancestor clipping volumes such as a scroll view, so a region reflects only the visible portion of its widget. An element clipped entirely out of view is omitted from GetRegionIds.
The provider reserves one sentinel id:
static constexpr int64 SelfRegionId = INDEX_NONE;
SelfRegionId represents the whole-panel region when the widget has no clickable sub-widgets. It is negative, so it cannot collide with a real id.
The gaze targeting component drives the provider through RegionRefreshIntervalSeconds on UIsdkGazeTargetingComponent (default 0.2 seconds). It consults the provider only in ConeCast mode; in Line mode the targeting component emits a whole-panel candidate and lets UMG resolve the sub-widget downstream.
Chain from the gaze targeting component through TargetingMethod to the widget region provider and the hosted UMG tree.
ConeCast consults the provider and walks the UMG tree into one region per clickable sub-widget; Line bypasses the provider and emits a whole-panel candidate.

Binding and Refresh

Two functions manage the provider’s lifecycle. Bind once, then refresh on every frame you plan to query.
FunctionExposureDescription
SetWidgetComponent(UWidgetComponent* InWidget)
BlueprintCallable
Binds the provider to a widget component. Passing nullptr clears the binding. The next RefreshRegions call performs a fresh rebuild.
RefreshRegions(float MinRebuildIntervalSeconds = 0.f)
BlueprintCallable
Recomputes the region map. A structural change (a clickable widget added, removed, enabled, or hidden) rebuilds immediately. A layout-only change (scroll, relayout, animated Render Transform) rebuilds at most once per MinRebuildIntervalSeconds. A non-positive interval rebuilds on every change. Returns true only when a rebuild occurred on this call.
GetRebuildCount() const
BlueprintPure
Returns the monotonic count of rebuilds since construction. Advances by one per genuine rebuild. Useful for churn detection and tests.
Decision flow for RefreshRegions across a structural change, a throttled layout-only change, and no change at all.
A structural change always rebuilds; a layout-only change waits for MinRebuildIntervalSeconds. Every rebuild advances GetRebuildCount and returns true.
The gaze targeting component passes its RegionRefreshIntervalSeconds straight through to RefreshRegions, so a custom driver that wants gaze-equivalent throttling passes 0.2f.

Spatial Queries

The remaining query surface projects world points into the panel’s pixel space and answers containment, snap-to-edge, tiebreak, and geometry questions.
FunctionExposureDescription
GetRegionIds(TArray<int64>& OutRegionIds) const
BlueprintCallable
Fills OutRegionIds with one id per cached clickable region. Emits a single SelfRegionId when the panel has no clickable sub-widgets. Regions clipped entirely out of view are omitted.
IsInside(const FVector& WorldPoint, int64 RegionId) const
BlueprintPure
Projects WorldPoint into panel pixel space and returns true when the point lies inside the region rect. SelfRegionId always returns true. Meaningful only for points on the panel plane.
FindNearestBoundaryPoint(const FVector& WorldPoint, int64 RegionId, FVector& OutWorldBoundaryPoint, bool& bOutInside) const
BlueprintCallable
Projects WorldPoint into panel pixel space and returns the nearest boundary point on the region rect: an outside point clamps onto the rect, an inside point snaps to the nearest edge. bOutInside reports which branch fired. Returns false for SelfRegionId or an unknown region.
TiebreakRegions(int64 RegionIdA, int64 RegionIdB) const
BlueprintPure
Returns -1 to favor A, 1 to favor B, 0 for a tie. Smaller pixel area wins, so the more specific nested element is preferred. An id that is not in the current region cache loses to any id that is.
GetRegionRectGeometry(int64 RegionId, FTransform& OutWorldPose, FVector2D& OutWorldSize) const
BlueprintCallable
Returns the world-space center pose and (width, height) of a region rect. The pose shares the component’s rotation, so local X is the panel normal. SelfRegionId resolves to the whole render-target rect.
GetRegionWidget(int64 RegionId) const
BlueprintPure
Returns the UWidget backing a region, or nullptr for SelfRegionId, an unknown region, or a widget that has been destroyed.
Region rect in panel pixel space with an outside point clamped onto the rect and an inside point snapped to the nearest edge.
FindNearestBoundaryPoint reports bOutInside as false for the clamped outside point and true for the snapped inside point.
Region rect in world space with OutWorldPose at its centre, OutWorldSize width and height, and local X as the panel normal.
GetRegionRectGeometry shares the component’s rotation, so local X points out of the panel. SelfRegionId resolves to the whole render-target rect.
The verified call pattern from the production cone-cast path binds once, refreshes before each query, enumerates region ids, and clamps outside points onto the nearest edge before scoring:
// Bind once, or whenever the panel changes.
Provider->SetWidgetComponent(WidgetComponent);

// Refresh the cache before any query. The interval throttles layout-only rebuilds.
Provider->RefreshRegions(0.2f);

TArray<int64> RegionIds;
Provider->GetRegionIds(RegionIds);
for (const int64 RegionId : RegionIds)
{
    FVector HitPoint = WorldHit;
    if (RegionId != UIsdkWidgetRegionProvider::SelfRegionId &&
        !Provider->IsInside(WorldHit, RegionId))
    {
        bool bInside = false;
        FVector ClampedPoint;
        if (!Provider->FindNearestBoundaryPoint(WorldHit, RegionId, ClampedPoint, bInside))
        {
            continue;
        }
        HitPoint = ClampedPoint;
    }
    // Score HitPoint against your own cone or ray test, then use TiebreakRegions
    // to break ties between nested regions when two survive.
}
Flow chart of the cone-cast query pattern from SetWidgetComponent through RefreshRegions, GetRegionIds, and TiebreakRegions.
A point outside a region is clamped by FindNearestBoundaryPoint, whose false return skips the region. Among survivors, the smaller pixel area wins.

Debug Component

UIsdkWidgetRegionDebugComponent is a BlueprintSpawnableComponent that draws a wireframe box per region. Visualization is compiled out of Shipping builds through #if !UE_BUILD_SHIPPING, so it costs nothing at release.
Play-in-Editor panel with red wireframe boxes drawn around each clickable region of the hosted UMG widget.
PropertyTypeDefaultDescription
bAutoWireWidget
bool
true
Binds to the owning actor’s first UWidgetComponent at BeginPlay when WidgetComponent is unset.
WidgetComponent
TObjectPtr<UWidgetComponent>
none
The widget component to visualize. Overrides auto-wire when set in the editor.

Learn more

Now that you know how to query widget regions with Interaction SDK, continue on to the following guides: