InputPopupController is a UIViewComponent that renders a single-line text prompt — rename dialogs, save names, seed entry, chat handles. It is the text-input sibling of the Confirmation Popup and drives the same two ways:
InputPopupArgs. The push is awaitable: PushAsync<string> resolves with the confirmed text.InputPopupController on the same GameObject as its caller, driven through the Value / Validate properties and OnConfirm + Fade.FadeIn().One prefab in a global navigation graph serves every text prompt in the game. Register the popup prefab on a NavNode (typically dismissMode: Pop with a Dim backdrop, hidden until pushed), then push it with args:
async void OnRenameClicked()
{
var result = await LunaNavigation.PushAsync<string>(_inputDest, new InputPopupArgs
{
Header = "Rename Hero",
InitialValue = _hero.Name,
Placeholder = "Enter a name",
Validate = v => !string.IsNullOrWhiteSpace(v) && v.Trim().Length >= 3,
});
if (!result.IsDismissed)
{
_hero.Name = result.Value.Trim();
}
}InputPopupArgs fields are optional — a null Header, Body, or Placeholder keeps the value serialized on the prefab, so a generic prompt works with new InputPopupArgs { InitialValue = current }. The text field itself is reset on every open: InitialValue when provided, empty otherwise — a previous caller's text never leaks into the next open.Validate gates Confirm. While the predicate returns false, the Confirm button is disabled (SetEnabled, style it via :disabled) and Enter does nothing. null means "require non-blank input"; pass v => true to allow anything, including empty.Return/KeypadEnter press in the focused field is equivalent to clicking Confirm (and respects Validate).result.IsDismissed is true and no text is delivered.The Showcase sample's Rename Player modal is this controller under a skinned UXML — ProfileScreen pushes it with InitialValue + a 3-character Validate and writes the awaited result back into the profile header.
An InputPopupController without a nav node is driven manually through its properties. On load it forces itself start-hidden (like the confirmation popup), and confirming just fades it back out instead of popping:
[SerializeField] InputPopupController _popup;
void OnRenameClicked()
{
_popup.Value = _hero.Name;
_popup.Validate = v => v.Trim().Length >= 3;
_popup.OnConfirm += OnRenameConfirmed;
_popup.UIView.Fade.FadeIn();
}
void OnRenameConfirmed(string text)
{
_hero.Name = text.Trim();
_popup.OnConfirm -= OnRenameConfirmed;
}Prefer the nav-destination mode for new work — the embedded mode duplicates a popup per host view and can't be awaited.
The popup queries these named elements in the UXML hosted by the view's PanelRenderer:
| Element | Name | Required |
|---|---|---|
Label | Header | optional |
Label | Body | optional |
TextField | Field | yes |
Button | Confirm | yes |
Button | Cancel | optional — wired to dismiss |
Button | Close | optional — wired to dismiss |
VisualElement | Backdrop | optional — direct clicks dismiss |
The Essentials sample ships a themed starting point at UXML/Components/InputPopup.uxml (+ .uss) — a clay panel matching the confirmation popup, with a slate text field and lime/slate buttons.
Add InputPopupController to the GameObject. In the inspector:
_textHeader / _textBody — default labels (used when a push passes no override)._placeholder — ghost text shown while the field is empty.For the nav-destination mode, also register the prefab on a NavNode in your graph and reference that destination key from callers.
namespace CupkekGames.Luna;
public class InputPopupController : UIViewComponent| Property | Type | Description |
|---|---|---|
Value | string | Read/write the field text (write skips change notification, then re-evaluates validation). |
Validate | Func<string, bool> | The active validation predicate. Assigning re-evaluates the Confirm button. |
TextHeader | string | Read/write the header label. |
TextBody | string | Read/write the body label. |
public event Action<string> OnConfirm;Fires with the confirmed text (in both modes, before the popup resolves). After a confirm the buttons are disabled, then the popup pops (nav mode) or fades out (embedded mode).
| Virtual | Called | Override to |
|---|---|---|
ApplyArgs(InputPopupArgs) | On fade-in, when the push carried args | Apply extra fields from an InputPopupArgs subclass. |
IsInputValid(string) | On every field change, Enter, and Confirm click | Replace the validation policy entirely. |
ResolveInput(string) | After a confirm | Delay or decorate the resolution (default: Pop(value) with a node, FadeOut() without). |
public class InputPopupArgs
{
public string Header;
public string Body;
public string InitialValue;
public string Placeholder;
public Func<string, bool> Validate;
}Passed as the push args; read on fade-in via GetArgs<InputPopupArgs>(). Subclass it to carry extra data for an InputPopupController subclass.
PushAsync sees IsDismissed); a UIViewActionEscape(Fade.FadeOut) covers the embedded mode.PushAsync<T> / Pop<T>, dismiss modesSettings
Theme
Light
Contrast
Material
Dark
Dim
Material Dark
System
Sidebar(Light & Contrast only)
Font Family
DM Sans
Wix
Inclusive Sans
AR One Sans
Direction