Table of Contents

Class UITKCallbackDialog

Namespace
FishMMO.Client
Assembly
FishMMO.Client.dll

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
Object
Component
Behaviour
MonoBehaviour
UITKCallbackDialog
Derived
Inherited Members
MonoBehaviour.IsInvoking()
MonoBehaviour.CancelInvoke()
MonoBehaviour.StopCoroutine(Coroutine)
MonoBehaviour.StopAllCoroutines()
MonoBehaviour.destroyCancellationToken
MonoBehaviour.useGUILayout
MonoBehaviour.didStart
MonoBehaviour.didAwake
MonoBehaviour.runInEditMode
Behaviour.enabled
Behaviour.isActiveAndEnabled
Component.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.transform
Component.transformHandle
Component.gameObject
Component.tag
Object.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.name
Object.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

UITKPanelLayer

RequestArmed

True while a caller is waiting for an answer.

protected bool RequestArmed { get; }

Property Value

bool

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

root VisualElement

The 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

overrideIsAlwaysOpen bool

When 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

direction int

-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

answer Action

The 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.