using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; namespace Content.Client.UserInterface.Controls; /// /// A Button that requires a second click to actually invoke its OnPressed action.
/// When clicked once it will change rendering modes to be prefixed by /// and displays on the button instead of .
///
/// After the first click needs to elapse before it can be clicked again to confirm.
/// When the button doesn't get clicked a second time before passes it changes back to its normal state.
///
/// /// Colors for the different states need to be set in the stylesheet /// public sealed class ConfirmButton : Button { [Dependency] private readonly IGameTiming _gameTiming = default!; public const string ConfirmPrefix = "confirm-"; private TimeSpan? _nextReset; private TimeSpan? _nextCooldown; private string? _confirmationText; private string? _text; /// /// Fired when the button was pressed and confirmed /// public new event Action? OnPressed; /// /// /// Hides the buttons text property to be able to sanely replace the button text with /// when asking for confirmation /// public new string? Text { get => _text; set { _text = value; base.Text = IsConfirming ? _confirmationText : value; } } /// /// The text displayed on the button when waiting for a second click /// [ViewVariables(VVAccess.ReadWrite)] public string ConfirmationText { get => _confirmationText ?? Loc.GetString("generic-confirm"); set => _confirmationText = value; } /// /// The time until the button reverts to normal /// [ViewVariables(VVAccess.ReadWrite)] public TimeSpan ResetTime { get; set; } = TimeSpan.FromSeconds(2); /// /// The time until the button accepts a second click. This is to prevent accidentally confirming the button /// [ViewVariables(VVAccess.ReadWrite)] public TimeSpan CooldownTime { get; set; } = TimeSpan.FromSeconds(.5); [ViewVariables] public bool IsConfirming = false; public ConfirmButton() { IoCManager.InjectDependencies(this); base.OnPressed += HandleOnPressed; } protected override void FrameUpdate(FrameEventArgs args) { if (IsConfirming && _gameTiming.CurTime > _nextReset) { IsConfirming = false; base.Text = Text; DrawModeChanged(); } if (Disabled && _gameTiming.CurTime > _nextCooldown) Disabled = false; } protected override void DrawModeChanged() { if (IsConfirming) { switch (DrawMode) { case DrawModeEnum.Normal: SetOnlyStylePseudoClass(ConfirmPrefix + StylePseudoClassNormal); break; case DrawModeEnum.Pressed: SetOnlyStylePseudoClass(ConfirmPrefix + StylePseudoClassPressed); break; case DrawModeEnum.Hover: SetOnlyStylePseudoClass(ConfirmPrefix + StylePseudoClassHover); break; case DrawModeEnum.Disabled: SetOnlyStylePseudoClass(ConfirmPrefix + StylePseudoClassDisabled); break; default: throw new ArgumentOutOfRangeException(); } return; } base.DrawModeChanged(); } private void HandleOnPressed(ButtonEventArgs buttonEvent) { //Prevent accidental confirmations from double clicking if (IsConfirming && _nextCooldown > _gameTiming.CurTime) return; switch (IsConfirming) { case false: _nextCooldown = _gameTiming.CurTime + CooldownTime; _nextReset = _gameTiming.CurTime + ResetTime; Disabled = true; break; case true: OnPressed?.Invoke(buttonEvent); break; } base.Text = IsConfirming ? Text : ConfirmationText; IsConfirming = !IsConfirming; } }