Class UITKCallbackDialog
Base class for the shared, singleton, answer-with-a-callback panels: the confirmation dialog, the text-input dialog and the list selector.
public abstract class UITKCallbackDialog : UITKControl
- Inheritance
-
ObjectComponentBehaviourMonoBehaviourUITKCallbackDialog
- Derived
- Inherited Members
-
MonoBehaviour.IsInvoking()MonoBehaviour.CancelInvoke()MonoBehaviour.StopCoroutine(Coroutine)MonoBehaviour.StopAllCoroutines()MonoBehaviour.destroyCancellationTokenMonoBehaviour.useGUILayoutMonoBehaviour.didStartMonoBehaviour.didAwakeMonoBehaviour.runInEditModeBehaviour.enabledBehaviour.isActiveAndEnabledComponent.GetComponent<T>()Component.TryGetComponent<T>(out T)Component.GetComponentInChildren<T>()Component.GetComponentsInChildren<T>()Component.GetComponentInParent<T>()Component.GetComponentsInParent<T>()Component.GetComponents<T>()Component.GetComponentIndex()Component.CompareTag(TagHandle)Component.transformComponent.transformHandleComponent.gameObjectComponent.tagObject.GetEntityId()Object.GetInstanceID()Object.GetHashCode()Object.InstantiateAsync<T>(T)Object.InstantiateAsync<T>(T, Transform)Object.InstantiateAsync<T>(T, Vector3, Quaternion)Object.InstantiateAsync<T>(T, Transform, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion, Transform)Object.Instantiate(Object)Object.Instantiate(Object, Scene)Object.Instantiate<T>(T, InstantiateParameters)Object.Instantiate<T>(T, Vector3, Quaternion, InstantiateParameters)Object.Instantiate(Object, Transform)Object.Instantiate<T>(T)Object.Instantiate<T>(T, Vector3, Quaternion)Object.Instantiate<T>(T, Vector3, Quaternion, Transform)Object.Instantiate<T>(T, Transform)Object.Destroy(Object)Object.DestroyImmediate(Object)Object.DontDestroyOnLoad(Object)Object.DestroyObject(Object)Object.FindObjectsOfType<T>()Object.FindObjectsByType<T>(FindObjectsSortMode)Object.FindObjectsByType<T>(FindObjectsInactive, FindObjectsSortMode)Object.FindObjectOfType<T>()Object.FindFirstObjectByType<T>()Object.FindAnyObjectByType<T>()Object.FindFirstObjectByType<T>(FindObjectsInactive)Object.FindAnyObjectByType<T>(FindObjectsInactive)Object.FindObjectsByType<T>()Object.FindObjectsByType<T>(FindObjectsInactive)Object.ToString()Object.nameObject.hideFlags
Remarks
The three of them are one panel each for the whole client, so every caller in the game writes its callbacks into the same fields. That makes the lifetime of those callbacks the whole problem, and each of the three used to get a different part of it wrong. The rules enforced here are:
1. Refuse, never hijack. An Open that arrives while a request is already on
screen is declined; it does not overwrite the message and the callbacks of the question
the player is currently reading. Without this a guild or party invite arriving on its own
timer replaces the body of an open confirmation while leaving the buttons where they are,
so "Yes" answers a question the player never saw.
2. Exactly one callback per request, on every exit path. Accept, cancel, the close
button, Escape, a programmatic Hide(), quit-to-login and a
refused Open all resolve the request exactly once. Callers lock themselves while a
dialog is up — UITKLogin hides its own panel and locks sign-in before opening the
verification prompt — so a path that answers with neither callback leaves the client with
no way back.
3. Callbacks are cleared before they are invoked, never after. Clearing afterwards loses the race against a callback that re-opens the dialog, and leaves the previous caller armed on a shared panel in the meantime.
Per-open content is applied from ApplyRequest(), which runs from both
OnAfterShow() and OnAfterStarting().
Writing it before Show() does not work — the document re-clones the UXML on enable
— and writing it only in OnAfterShow misses the very first open, where the visual
tree does not exist yet and ReinitializeIfTreeReplaced bails out.
Properties
Layer
Draw order tier for this panel. See UITKPanelLayer.
protected override UITKPanelLayer Layer { get; }
Property Value
RequestArmed
True while a caller is waiting for an answer.
protected bool RequestArmed { get; }
Property Value
Remarks
Tracked separately from Visible: a request is armed from the
instant TryClaim() succeeds, which is before Show() has run, and it
has to stay armed across the frame where the document is enabling.
Methods
ApplyRequest()
Writes this request's content into the live visual tree.
protected abstract void ApplyRequest()
Remarks
Runs on every show and after every tree rebuild, so it must be idempotent and must tolerate elements that are still null.
AttachDialogKeys(VisualElement)
Wires the shared Enter/Escape handling onto the panel root.
protected void AttachDialogKeys(VisualElement root)
Parameters
rootVisualElementThe panel root the keys are captured on.
Remarks
Registered in the trickle-down phase so the keys are read before a focused
TextField or Button consumes them: Return inside a text field would
otherwise be swallowed by the field, which is precisely where the player is typing
when they want to accept.
Unregister-then-register because this runs again on every tree rebuild, and the handler is a method group rather than a lambda so the unregister actually matches.
CancelRequest()
Answers the current request down its cancel path.
protected abstract void CancelRequest()
Remarks
This is what Escape, the close button, quit-to-login and any other dismissal that is not an explicit accept go through. Implementations capture their cancel callback and hand it to Resolve(Action).
ClearRequest()
Drops every callback and every piece of per-open state the subclass is holding.
protected abstract void ClearRequest()
Remarks
Called from Resolve(Action) before the answer is invoked. Implementations must not invoke anything from here.
DismissWithoutAnswer()
Takes the dialog down because the thing it was asking about has resolved itself, without answering it on the player's behalf.
public void DismissWithoutAnswer()
Remarks
Hide() means "the player dismissed this", and an armed request turns
that into a cancel — which is correct for Escape or a click away, and wrong for a
caller whose wait simply ended. The world-scene queue hit this: being successfully
routed calls HideQueueDialog, the armed request answered down its cancel path, and
the cancel callback was QuitToLogin. Arriving at the front of the queue was
therefore indistinguishable from choosing to leave it — and quitting to login revokes
the auth token, so the scene connection already in flight arrived with nothing to
authenticate with and the player was returned to the login screen.
FocusDefault()
Moves keyboard focus somewhere useful when the dialog opens.
protected virtual void FocusDefault()
Remarks
Something in the panel has to hold focus or there is no element for a key press to be dispatched to, and the Enter/Escape handling below never sees anything.
The default is the panel root rather than a button. A focused UI Toolkit
Button is activated by the space bar, so focusing Accept would let a player
who happens to be holding a movement key answer a confirmation they have not read —
and these dialogs arrive unprompted, on someone else's timing. The root is focusable
but activates nothing, so Enter and Escape go through the explicit handling here and
nothing else does anything at all. Tab still reaches the buttons for a player
deliberately navigating them.
Hide(bool)
Hides the panel, answering any outstanding request on the way out.
public override void Hide(bool overrideIsAlwaysOpen)
Parameters
overrideIsAlwaysOpenboolWhen true, the call is a no-op.
Remarks
This is the catch-all that makes rule 2 hold. Escape goes through
UIManager.CloseNext -> Hide(), quit-to-login goes
through Hide(false), and panels call Hide() on these dialogs directly.
None of those knew anything about the callbacks, so all three used to close the box
and leave whoever opened it waiting for an answer that could never arrive.
OnAfterShow()
Applies the pending request to the tree the player will actually see.
protected override void OnAfterShow()
OnAfterStarting()
Re-applies the pending request after the visual tree was rebuilt.
protected override void OnAfterStarting()
OnDestroying()
Answers any outstanding request before the panel goes away.
public override void OnDestroying()
OnNavigateKey(int)
Called when the player presses an arrow key.
protected virtual bool OnNavigateKey(int direction)
Parameters
directionint-1 for up, 1 for down.
Returns
- bool
True when the key was used and should not travel any further.
OnSubmitKey()
Called when the player presses Enter. Defaults to accepting the dialog.
protected virtual void OnSubmitKey()
Resolve(Action)
Answers the current request exactly once, then closes the panel.
protected void Resolve(Action answer)
Parameters
answerActionThe single callback for this exit path, already captured by the caller. May be null when the request was opened without one — the request is still consumed.
Remarks
ClearRequest() runs before answer so the panel is
already unarmed when the callback runs: these callbacks quit to login, disconnect and
open further dialogs, and any of that re-entering a still-armed panel would answer the
same request twice.
TryClaim()
Claims the panel for a new request, or refuses when one is already outstanding.
protected bool TryClaim()
Returns
- bool
True when the caller may proceed to fill in and show the dialog.