Set Visibility: Hidden, Collapsed, and hit testing¶
At a glance
Lives in: Blueprint API / Widget - Target: a Widget object
reference - Input: one ESlateVisibility value - Returns: no value;
execution continues - Can fail by: targeting None, changing a child
whose parent still hides it, or choosing a state with the wrong layout or
hit-test behavior - Official docs: Set Visibility
and ESlateVisibility
The one-minute version¶
Set Visibilitychanges one widget'sVisibilityproperty. It does not create, remove, disable, focus, animate, or destroy the widget.Visiblepaints the widget and lets it and its visible children participate in pointer hit testing.Hiddendoes not paint or receive pointer hits, but keeps its layout space.Collapseddoes not paint or receive pointer hits and takes no layout space, so surrounding layout can move.Hit Test Invisiblepaints the widget but prevents pointer hits on both it and its descendants.Self Hit Test Invisiblepaints the widget and prevents hits on that widget itself while still allowing eligible descendants to receive hits.- A parent's state gates its subtree. Setting a child to
Visiblecannot overcome a hidden or collapsed parent. - Choose one owner for
Visibility. A property binding and direct setter calls competing over the same property produce fragile UI.
What Set Visibility actually does¶
Every UMG Widget exposes a Visibility property using the
ESlateVisibility enum. Set Visibility writes one enum value to that
property and synchronizes the change to the underlying Slate widget.
The Blueprint node has execution input/output pins, a Widget target, and an
In Visibility enum input. It has no Boolean or object return value.
The C++ call shape is void UWidget::SetVisibility(ESlateVisibility): the
Blueprint target is a UWidget*, the enum keeps the same
ESlateVisibility name, and void means there is no data return.
pseudocode of the Blueprint contract - not engine source
function ShowInventoryPanel(InventoryPanel):
if InventoryPanel is None:
report an invalid reference
return
InventoryPanel.SetVisibility(Visible)
execution continues
The widget object remains the same object. Its variables, children, dispatcher bindings, and parent relationship still exist. Visibility changes whether Slate includes it in painting, layout, and hit testing according to the chosen enum value.
The five states¶
| Visibility | Painted? | Keeps layout space? | Self receives pointer hits? | Children can receive pointer hits? |
|---|---|---|---|---|
Visible |
yes | yes | yes | yes, if their own states allow it |
Hidden |
no | yes | no | no |
Collapsed |
no | no | no | no |
Hit Test Invisible |
yes | yes | no | no |
Self Hit Test Invisible |
yes | yes | no | yes, if their own states allow it |
The editor may display the last two as Not Hit-Testable (Self & All
Children) and Not Hit-Testable (Self Only). Those labels describe the
same distinction as HitTestInvisible and SelfHitTestInvisible in the enum.
Visible¶
Use Visible when the widget should draw and be eligible for normal pointer
interaction. It does not guarantee that an ancestor is visible, that the
widget is enabled, that it supports keyboard focus, or that another widget is
not covering it.
Hidden¶
Use Hidden when the widget should disappear but its slot should still reserve
the same space. This is useful when nearby controls must not shift:
Horizontal Box
-> Ammo Icon (Hidden when weapon has no ammo)
-> Ammo Count
The icon does not draw, but the count stays aligned.
Hidden is not "transparent but clickable." It is not painted or hit-testable.
Collapsed¶
Use Collapsed when the widget and its layout contribution should disappear.
A Vertical Box, Horizontal Box, Wrap Box, or other layout panel can rearrange
its remaining children as if the collapsed child took no space.
That is ideal for an optional settings row. It is a poor fit when hiding the row should not make the menu jump.
Hit Test Invisible¶
Use Hit Test Invisible for visible decoration that must not intercept pointer
input, including all widgets beneath it in that subtree. A full-screen overlay
with this state can display an effect without blocking controls elsewhere, but
buttons nested inside that overlay cannot receive pointer hits either.
Self Hit Test Invisible¶
Use Self Hit Test Invisible for a visible container or decoration whose
children still need pointer input. A Border can ignore hits on itself while a
Button nested inside remains eligible.
This state does not force every child to be interactive. A child that is Hidden, Collapsed, disabled, or hit-test-invisible still follows its own state.
Parent and child visibility compose¶
A child does not escape its ancestors. Think of the effective result as every state along the path from the root to that child:
PauseMenuRoot = Collapsed
└── ResumeButton = Visible
effective result: ResumeButton is not drawn, laid out, or hit-testable
Changing ResumeButton to Visible stores that child state, but the root still
gates the subtree. When the root becomes Visible, the button can appear using
the state it already had.
Hit testing composes the same way:
Overlay = Hit Test Invisible
└── ResumeButton = Visible
effective result: button is drawn but cannot receive pointer hits
Change the overlay to Self Hit Test Invisible when the overlay itself should
ignore hits but its eligible children should remain interactive.
When debugging, walk upward in the UMG hierarchy. Printing only the button's
own Get Visibility value misses an ancestor that suppresses it.
Visibility, enabled state, and focus are different¶
Set Is Enabled(false) leaves a widget visible and in layout while making it
unavailable for normal interaction. Controls commonly draw a disabled style,
which is useful for "crafting requires more materials" or an unavailable menu
choice. Visibility does not communicate that same state to the player.
| Intent | Typical choice |
|---|---|
| Show and allow interaction | Visible and enabled |
| Show an unavailable control | Visible and disabled |
| Show decoration that should not catch the pointer | Hit Test Invisible or Self Hit Test Invisible |
| Hide but reserve its slot | Hidden |
| Hide and reflow layout | Collapsed |
Keyboard/gamepad focus is another system. Hiding or collapsing the currently focused control does not define which control should receive focus next, and changing visibility does not change the PlayerController's input mode.
For a menu transition, make the handoff explicit:
OpenConfirmPanel
-> MainPanel Set Visibility Collapsed
-> ConfirmPanel Set Visibility Visible
-> ConfirmButton Set User Focus (Owning Player)
CloseMenu
-> MenuRoot Set Visibility Collapsed
-> Set Input Mode Game Only
-> Set Show Mouse Cursor false
Do not rely on the focus system to guess a useful replacement after its old target disappears.
Visibility and animations¶
Widget animation and visibility are separate properties. An animation that
drives render opacity to zero can leave the widget Visible and hit-testable.
Conversely, setting a widget to Hidden or Collapsed prevents its visuals
from being shown even if an animation is changing opacity or transforms.
A predictable fade pattern is:
Fade in:
Set Visibility Visible
Play Animation Forward
Fade out:
Play Animation Reverse
On Animation Finished -> Set Visibility Collapsed
The completion event, interruption rules, and desired final state belong to
your widget. Set Visibility itself does not start, stop, reverse, or wait for
an animation.
Avoid using only Set Render Opacity(0) to close an interactive panel. Unless
you also change its input state, an invisible-looking control can still sit in
the hit-test path.
Direct setters versus property bindings¶
A Designer binding on Visibility makes the binding function a value owner.
A direct Set Visibility call makes the event path a value owner. Mixing both
means one path can replace or override what the other just requested as the
property synchronizes.
Prefer one of these:
event-driven owner:
On InventoryOpened -> InventoryPanel Set Visibility Visible
On InventoryClosed -> InventoryPanel Set Visibility Collapsed
bound owner:
Visibility binding returns Visible or Collapsed from one authoritative state
The event-driven version is usually easier to trace and avoids repeatedly evaluating a binding. If you inherit a widget with a binding, remove that binding before making direct calls the new contract. Do not add delays or repeat setters to "win" against another owner.
Repeated layout changes¶
Collapsed changes whether the widget participates in layout. Toggling many
collapsed descendants can invalidate and recalculate surrounding layout, and
it can make controls visibly jump. That is correct behavior, but it should be
intentional.
- Use
Hiddenwhen space should remain stable. - Use
Collapsedwhen the layout should close the gap. - Use a
Widget Switcherwhen one of several same-area panels should be active. - Update visibility when the underlying state changes, not from an unrelated Tick or a binding on every child.
- Toggle a common parent when an entire group shares one state instead of issuing redundant calls to every descendant.
Visibility calls with the same value should still not be your synchronization
strategy. A named RefreshState function that derives the small number of
panel states from current data is easier to test.
Remove From Parent is a lifetime/layout choice¶
Visibility keeps the widget attached to its current parent. Remove from
Parent detaches it.
Set Visibility Collapsed
-> same widget remains in the hierarchy
-> same variables and parent slot remain
-> Set Visibility Visible can show it again
Remove from Parent
-> widget is detached
-> stored reference may still be valid
-> it must be added again before it can show
For panels inside a persistent HUD, visibility is usually simpler. For a temporary top-level popup, removing and later re-adding or recreating it may be the cleaner lifecycle. Neither choice restores input mode or focus by itself.
Local multiplayer and networking¶
Screen UI is local presentation. Each local player should change visibility on that player's widget instance, using that widget's owning player for focus and input operations.
In a network game, replicate the gameplay fact, not the enum state of a screen widget:
replicated ObjectiveActive changes
-> each owning client's HUD receives/reads the new fact
-> that local HUD sets ObjectivePanel visibility
The server normally has no useful viewport widget to manipulate, and one
client's Set Visibility call does not update another client's UI. Split-screen
also has several local widget instances in one process; avoid assuming player
index 0 owns every menu.
When it fails (and what failure does)¶
The node has no success output. Diagnose the visible behavior and the target reference:
- Target is
None. Blueprint can reportAccessed None, and the intended widget does not change. Guard optional references withIs Valid. - The wrong instance changed. A duplicate menu is visible while the stored reference points to another copy.
- An ancestor still suppresses the widget. The child says
Visible, but a parent is Hidden, Collapsed, or blocks descendant hit testing. - A binding or refresh path changes it back. Two systems own the same property.
- The panel is invisible but still blocks input. Opacity was changed, not visibility, or another transparent widget covers the controls.
- The panel appears but its buttons do nothing. An ancestor uses
Hit Test Invisible, the controls are disabled, focus/input mode is wrong, or another widget is on top. - The menu jumps when a row hides.
Collapsedremoved layout space whereHiddenwas intended.
There is no built-in retry and no Boolean to branch on. Use the Widget Reflector, runtime prints of the exact instance and visibility chain, and a breakpoint on every setter when another path appears to reverse the change.
What Set Visibility does not do¶
- It does not add a widget to a parent, viewport, or player screen.
- It does not remove or destroy the widget.
- It does not clear variables, stop dispatchers, or reset widget state.
- It does not enable/disable a control or choose keyboard/gamepad focus.
- It does not change input mode, pause, cursor state, or mapping contexts.
- It does not play a widget animation or make opacity zero.
- It does not hide an Actor or Scene Component in the 3D world.
- It does not replicate to another client.
Lookalikes - which one do I want?¶
| Node / property | Use when | Watch out |
|---|---|---|
| Widget Set Visibility | You want to control UMG painting, layout participation, and hit testing. | Parent state and the selected enum affect descendants. |
| Set Is Enabled | A visible control should be unavailable for interaction. | It stays in layout and usually draws a disabled style. |
| Set Render Opacity | You are fading or visually dimming a widget. | Zero opacity is not a visibility or input policy. |
| Remove from Parent | The widget should detach from its panel or viewport. | Stored references and other state can remain alive. |
| Widget Switcher | One child panel should occupy a shared region at a time. | It selects an active child; it is not actor visibility. |
| Set Actor Hidden In Game | You need to hide an Actor's rendered components in the world. | It does not target UMG widget layout. |
| Scene Component Set Visibility | You need to change a rendered component's world visibility. | Its propagation options and collision/lifetime are separate. |
Rule of thumb: choose visibility from the desired layout and input behavior, then handle focus, input mode, animation, and lifetime as separate policies.
Going deeper¶
- Remove from Parent and widget lifetime - detaching, reuse, Construct/Destruct, and cleanup.
- Create Widget + Add to Viewport - why a visibility change cannot display a widget that was never attached.
- Updating a Widget: Bindings vs Events - choosing one observable owner for presentation state.
- Set Input Mode & Show Mouse Cursor - the separate menu-input contract.
- Set Actor Hidden In Game / Set Visibility - the world-rendering nodes that share this name.
- Official docs: Set Visibility, ESlateVisibility, UWidget, and EVisibility.
- Engine source (requires engine access - we do not reproduce it here):
UWidget::SetVisibilityis implemented inEngine/Source/Runtime/UMG/Private/Components/Widget.cpp;ESlateVisibilityis declared inEngine/Source/Runtime/UMG/Public/Components/SlateWrapperTypes.h; Slate's correspondingEVisibilitybehavior is declared inEngine/Source/Runtime/SlateCore/Public/Layout/Visibility.h.