ECSatize AlertsSystem (#5559)
This commit is contained in:
@@ -1,226 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Alerts.UI;
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.Alerts
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedAlertsComponent))]
|
||||
public sealed class ClientAlertsComponent : SharedAlertsComponent
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
private AlertsUI? _ui;
|
||||
private AlertOrderPrototype? _alertOrder;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<AlertKey, AlertControl> _alertControls
|
||||
= new();
|
||||
|
||||
/// <summary>
|
||||
/// Allows calculating if we need to act due to this component being controlled by the current mob
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private bool CurrentlyControlled => _playerManager.LocalPlayer != null && _playerManager.LocalPlayer.ControlledEntity == Owner;
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
PlayerDetached();
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not AlertsComponentState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateAlertsControls();
|
||||
}
|
||||
|
||||
public void PlayerAttached()
|
||||
{
|
||||
if (!CurrentlyControlled || _ui != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_alertOrder = IoCManager.Resolve<IPrototypeManager>().EnumeratePrototypes<AlertOrderPrototype>().FirstOrDefault();
|
||||
if (_alertOrder == null)
|
||||
{
|
||||
Logger.ErrorS("alert", "no alertOrder prototype found, alerts will be in random order");
|
||||
}
|
||||
|
||||
_ui = new AlertsUI();
|
||||
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.AddChild(_ui);
|
||||
|
||||
UpdateAlertsControls();
|
||||
}
|
||||
|
||||
public void PlayerDetached()
|
||||
{
|
||||
foreach (var alertControl in _alertControls.Values)
|
||||
{
|
||||
alertControl.OnPressed -= AlertControlOnPressed;
|
||||
}
|
||||
|
||||
if (_ui != null)
|
||||
{
|
||||
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.RemoveChild(_ui);
|
||||
_ui = null;
|
||||
}
|
||||
_alertControls.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the displayed alerts based on current state of Alerts, performing
|
||||
/// a diff to ensure we only change what's changed (this avoids active tooltips disappearing any
|
||||
/// time state changes)
|
||||
/// </summary>
|
||||
private void UpdateAlertsControls()
|
||||
{
|
||||
if (!CurrentlyControlled || _ui == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// remove any controls with keys no longer present
|
||||
var toRemove = new List<AlertKey>();
|
||||
foreach (var existingKey in _alertControls.Keys)
|
||||
{
|
||||
if (!IsShowingAlert(existingKey))
|
||||
{
|
||||
toRemove.Add(existingKey);
|
||||
}
|
||||
}
|
||||
foreach (var alertKeyToRemove in toRemove)
|
||||
{
|
||||
_alertControls.Remove(alertKeyToRemove, out var control);
|
||||
if (control == null) return;
|
||||
_ui.AlertContainer.Children.Remove(control);
|
||||
}
|
||||
|
||||
// now we know that alertControls contains alerts that should still exist but
|
||||
// may need to updated,
|
||||
// also there may be some new alerts we need to show.
|
||||
// further, we need to ensure they are ordered w.r.t their configured order
|
||||
foreach (var (alertKey, alertState) in EnumerateAlertStates())
|
||||
{
|
||||
if (!alertKey.AlertType.HasValue)
|
||||
{
|
||||
Logger.WarningS("alert", "found alertkey without alerttype," +
|
||||
" alert keys should never be stored without an alerttype set: {0}", alertKey);
|
||||
continue;
|
||||
}
|
||||
var alertType = alertKey.AlertType.Value;
|
||||
if (!AlertManager.TryGet(alertType, out var newAlert))
|
||||
{
|
||||
Logger.ErrorS("alert", "Unrecognized alertType {0}", alertType);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_alertControls.TryGetValue(newAlert.AlertKey, out var existingAlertControl) &&
|
||||
existingAlertControl.Alert.AlertType == newAlert.AlertType)
|
||||
{
|
||||
// key is the same, simply update the existing control severity / cooldown
|
||||
existingAlertControl.SetSeverity(alertState.Severity);
|
||||
existingAlertControl.Cooldown = alertState.Cooldown;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (existingAlertControl != null)
|
||||
{
|
||||
_ui.AlertContainer.Children.Remove(existingAlertControl);
|
||||
}
|
||||
|
||||
// this is a new alert + alert key or just a different alert with the same
|
||||
// key, create the control and add it in the appropriate order
|
||||
var newAlertControl = CreateAlertControl(newAlert, alertState);
|
||||
if (_alertOrder != null)
|
||||
{
|
||||
var added = false;
|
||||
foreach (var alertControl in _ui.AlertContainer.Children)
|
||||
{
|
||||
if (_alertOrder.Compare(newAlert, ((AlertControl) alertControl).Alert) < 0)
|
||||
{
|
||||
var idx = alertControl.GetPositionInParent();
|
||||
_ui.AlertContainer.Children.Add(newAlertControl);
|
||||
newAlertControl.SetPositionInParent(idx);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!added)
|
||||
{
|
||||
_ui.AlertContainer.Children.Add(newAlertControl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_ui.AlertContainer.Children.Add(newAlertControl);
|
||||
}
|
||||
|
||||
_alertControls[newAlert.AlertKey] = newAlertControl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AlertControl CreateAlertControl(AlertPrototype alert, AlertState alertState)
|
||||
{
|
||||
var alertControl = new AlertControl(alert, alertState.Severity)
|
||||
{
|
||||
Cooldown = alertState.Cooldown
|
||||
};
|
||||
alertControl.OnPressed += AlertControlOnPressed;
|
||||
return alertControl;
|
||||
}
|
||||
|
||||
private void AlertControlOnPressed(ButtonEventArgs args)
|
||||
{
|
||||
if (args.Button is not AlertControl control)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AlertPressed(args, control);
|
||||
}
|
||||
|
||||
private void AlertPressed(ButtonEventArgs args, AlertControl alert)
|
||||
{
|
||||
if (args.Event.Function != EngineKeyFunctions.UIClick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma warning disable 618
|
||||
SendNetworkMessage(new ClickAlertMessage(alert.Alert.AlertType));
|
||||
#pragma warning restore 618
|
||||
}
|
||||
|
||||
protected override void AfterShowAlert()
|
||||
{
|
||||
UpdateAlertsControls();
|
||||
}
|
||||
|
||||
protected override void AfterClearAlert()
|
||||
{
|
||||
UpdateAlertsControls();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Alert;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Alerts
|
||||
namespace Content.Client.Alerts;
|
||||
|
||||
[UsedImplicitly]
|
||||
internal class ClientAlertsSystem : AlertsSystem
|
||||
{
|
||||
internal class ClientAlertsSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
public AlertOrderPrototype? AlertOrder { get; set; }
|
||||
|
||||
SubscribeLocalEvent<ClientAlertsComponent, PlayerAttachedEvent>((_, component, _) => component.PlayerAttached());
|
||||
SubscribeLocalEvent<ClientAlertsComponent, PlayerDetachedEvent>((_, component, _) => component.PlayerDetached());
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public event EventHandler? ClearAlerts;
|
||||
public event EventHandler<IReadOnlyDictionary<AlertKey, AlertState>>? SyncAlerts;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, PlayerAttachedEvent>((_, component, _) => PlayerAttached(component));
|
||||
SubscribeLocalEvent<AlertsComponent, PlayerDetachedEvent>((_, _, _) => PlayerDetached());
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, ComponentHandleState>(ClientAlertsHandleState);
|
||||
}
|
||||
|
||||
protected override void LoadPrototypes()
|
||||
{
|
||||
base.LoadPrototypes();
|
||||
|
||||
AlertOrder = _prototypeManager.EnumeratePrototypes<AlertOrderPrototype>().FirstOrDefault();
|
||||
if (AlertOrder == null)
|
||||
Logger.ErrorS("alert", "no alertOrder prototype found, alerts will be in random order");
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<AlertKey, AlertState>? ActiveAlerts
|
||||
{
|
||||
get
|
||||
{
|
||||
var ent = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
return ent is not null
|
||||
? GetActiveAlerts(ent.Value)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AfterShowAlert(AlertsComponent alertsComponent)
|
||||
{
|
||||
if (!CurControlled(alertsComponent.Owner, _playerManager))
|
||||
return;
|
||||
|
||||
SyncAlerts?.Invoke(this, alertsComponent.Alerts);
|
||||
}
|
||||
|
||||
protected override void AfterClearAlert(AlertsComponent alertsComponent)
|
||||
{
|
||||
if (!CurControlled(alertsComponent.Owner, _playerManager))
|
||||
return;
|
||||
|
||||
SyncAlerts?.Invoke(this, alertsComponent.Alerts);
|
||||
}
|
||||
|
||||
private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
var componentAlerts = (args.Current as AlertsComponentState)?.Alerts;
|
||||
if (componentAlerts == null) return;
|
||||
|
||||
//TODO: Do we really want to send alerts for non-attached entity?
|
||||
component.Alerts = componentAlerts;
|
||||
if (!CurControlled(component.Owner, _playerManager)) return;
|
||||
|
||||
SyncAlerts?.Invoke(this, componentAlerts);
|
||||
}
|
||||
|
||||
private void PlayerAttached(AlertsComponent clientAlertsComponent)
|
||||
{
|
||||
if (!CurControlled(clientAlertsComponent.Owner, _playerManager)) return;
|
||||
SyncAlerts?.Invoke(this, clientAlertsComponent.Alerts);
|
||||
}
|
||||
|
||||
protected override void HandleComponentShutdown(EntityUid uid)
|
||||
{
|
||||
base.HandleComponentShutdown(uid);
|
||||
|
||||
PlayerDetached();
|
||||
}
|
||||
|
||||
private void PlayerDetached()
|
||||
{
|
||||
ClearAlerts?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void AlertClicked(AlertType alertType)
|
||||
{
|
||||
RaiseNetworkEvent(new ClickAlertEvent(alertType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows calculating if we need to act due to this component being controlled by the current mob
|
||||
/// </summary>
|
||||
private static bool CurControlled(EntityUid entity, IPlayerManager playerManager)
|
||||
{
|
||||
return playerManager.LocalPlayer != null && playerManager.LocalPlayer.ControlledEntity == entity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,344 @@
|
||||
using Content.Client.Chat.Managers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Chat.Managers;
|
||||
using Content.Client.Chat.UI;
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Client.Alerts.UI
|
||||
namespace Content.Client.Alerts.UI;
|
||||
|
||||
public class AlertsFramePresenter : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The status effects display on the right side of the screen.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AlertsUI : Control
|
||||
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
|
||||
private IAlertsFrameView _alertsFrame;
|
||||
private ClientAlertsSystem? _alertsSystem;
|
||||
|
||||
public AlertsFramePresenter()
|
||||
{
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
// This is a lot easier than a factory
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
public const float ChatSeparation = 38f;
|
||||
_alertsFrame = new AlertsUI(_chatManager);
|
||||
_userInterfaceManager.StateRoot.AddChild((AlertsUI) _alertsFrame);
|
||||
|
||||
public AlertsUI()
|
||||
// This is required so that if we load after the system is initialized, we can bind to it immediately
|
||||
if (_systemManager.TryGetEntitySystem<ClientAlertsSystem>(out var alertsSystem))
|
||||
SystemBindingChanged(alertsSystem);
|
||||
|
||||
_systemManager.SystemLoaded += OnSystemLoaded;
|
||||
_systemManager.SystemUnloaded += OnSystemUnloaded;
|
||||
|
||||
_alertsFrame.AlertPressed += OnAlertPressed;
|
||||
|
||||
// initially populate the frame if system is available
|
||||
var alerts = alertsSystem?.ActiveAlerts;
|
||||
if (alerts != null)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
|
||||
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.End);
|
||||
LayoutContainer.SetAnchorTop(this, 0f);
|
||||
LayoutContainer.SetAnchorRight(this, 1f);
|
||||
LayoutContainer.SetAnchorBottom(this, 1f);
|
||||
LayoutContainer.SetMarginBottom(this, -180);
|
||||
LayoutContainer.SetMarginTop(this, 250);
|
||||
LayoutContainer.SetMarginRight(this, -10);
|
||||
SystemOnSyncAlerts(alertsSystem, alerts);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void EnteredTree()
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_userInterfaceManager.StateRoot.RemoveChild((AlertsUI) _alertsFrame);
|
||||
_alertsFrame.Dispose();
|
||||
_alertsFrame = null!;
|
||||
|
||||
SystemBindingChanged(null);
|
||||
_systemManager.SystemLoaded -= OnSystemLoaded;
|
||||
_systemManager.SystemUnloaded -= OnSystemUnloaded;
|
||||
}
|
||||
|
||||
private void OnAlertPressed(object? sender, AlertType e)
|
||||
{
|
||||
_alertsSystem?.AlertClicked(e);
|
||||
}
|
||||
|
||||
private void SystemOnClearAlerts(object? sender, EventArgs e)
|
||||
{
|
||||
_alertsFrame.ClearAllControls();
|
||||
}
|
||||
|
||||
private void SystemOnSyncAlerts(object? sender, IReadOnlyDictionary<AlertKey, AlertState> e)
|
||||
{
|
||||
if (sender is ClientAlertsSystem system)
|
||||
_alertsFrame.SyncControls(system, system.AlertOrder, e);
|
||||
}
|
||||
|
||||
//TODO: This system binding boilerplate seems to be duplicated between every presenter
|
||||
// prob want to pull it out into a generic object with callbacks for Onbind/OnUnbind
|
||||
#region System Binding
|
||||
|
||||
private void OnSystemLoaded(object? sender, SystemChangedArgs args)
|
||||
{
|
||||
if (args.System is ClientAlertsSystem system) SystemBindingChanged(system);
|
||||
}
|
||||
|
||||
private void OnSystemUnloaded(object? sender, SystemChangedArgs args)
|
||||
{
|
||||
if (args.System is ClientAlertsSystem) SystemBindingChanged(null);
|
||||
}
|
||||
|
||||
private void SystemBindingChanged(ClientAlertsSystem? newSystem)
|
||||
{
|
||||
if (newSystem is null)
|
||||
{
|
||||
base.EnteredTree();
|
||||
_chatManager.OnChatBoxResized += OnChatResized;
|
||||
OnChatResized(new ChatResizedEventArgs(HudChatBox.InitialChatBottom));
|
||||
if (_alertsSystem is null)
|
||||
return;
|
||||
|
||||
UnbindFromSystem();
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
else
|
||||
{
|
||||
base.ExitedTree();
|
||||
_chatManager.OnChatBoxResized -= OnChatResized;
|
||||
}
|
||||
|
||||
private void OnChatResized(ChatResizedEventArgs chatResizedEventArgs)
|
||||
{
|
||||
// resize us to fit just below the chatbox
|
||||
if (_chatManager.CurrentChatBox != null)
|
||||
if (_alertsSystem is null)
|
||||
{
|
||||
LayoutContainer.SetMarginTop(this, chatResizedEventArgs.NewBottom + ChatSeparation);
|
||||
BindToSystem(newSystem);
|
||||
return;
|
||||
}
|
||||
|
||||
UnbindFromSystem();
|
||||
BindToSystem(newSystem);
|
||||
}
|
||||
}
|
||||
|
||||
private void BindToSystem(ClientAlertsSystem system)
|
||||
{
|
||||
_alertsSystem = system;
|
||||
system.SyncAlerts += SystemOnSyncAlerts;
|
||||
system.ClearAlerts += SystemOnClearAlerts;
|
||||
}
|
||||
|
||||
private void UnbindFromSystem()
|
||||
{
|
||||
var system = _alertsSystem;
|
||||
|
||||
if (system is null)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
system.SyncAlerts -= SystemOnSyncAlerts;
|
||||
system.ClearAlerts -= SystemOnClearAlerts;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the frame of vertical set of alerts that show up on the HUD.
|
||||
/// </summary>
|
||||
public interface IAlertsFrameView : IDisposable
|
||||
{
|
||||
event EventHandler<AlertType>? AlertPressed;
|
||||
|
||||
void SyncControls(AlertsSystem alertsSystem, AlertOrderPrototype? alertOrderPrototype,
|
||||
IReadOnlyDictionary<AlertKey, AlertState> alertStates);
|
||||
void ClearAllControls();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The status effects display on the right side of the screen.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AlertsUI : Control, IAlertsFrameView
|
||||
{
|
||||
// also known as Control.Children?
|
||||
private readonly Dictionary<AlertKey, AlertControl> _alertControls = new();
|
||||
|
||||
public AlertsUI(IChatManager chatManager)
|
||||
{
|
||||
_chatManager = chatManager;
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
|
||||
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.End);
|
||||
LayoutContainer.SetAnchorTop(this, 0f);
|
||||
LayoutContainer.SetAnchorRight(this, 1f);
|
||||
LayoutContainer.SetAnchorBottom(this, 1f);
|
||||
LayoutContainer.SetMarginBottom(this, -180);
|
||||
LayoutContainer.SetMarginTop(this, 250);
|
||||
LayoutContainer.SetMarginRight(this, -10);
|
||||
}
|
||||
|
||||
public void SyncControls(AlertsSystem alertsSystem, AlertOrderPrototype? alertOrderPrototype,
|
||||
IReadOnlyDictionary<AlertKey, AlertState> alertStates)
|
||||
{
|
||||
// remove any controls with keys no longer present
|
||||
if (SyncRemoveControls(alertStates)) return;
|
||||
|
||||
// now we know that alertControls contains alerts that should still exist but
|
||||
// may need to updated,
|
||||
// also there may be some new alerts we need to show.
|
||||
// further, we need to ensure they are ordered w.r.t their configured order
|
||||
SyncUpdateControls(alertsSystem, alertOrderPrototype, alertStates);
|
||||
}
|
||||
|
||||
public void ClearAllControls()
|
||||
{
|
||||
foreach (var alertControl in _alertControls.Values)
|
||||
{
|
||||
alertControl.OnPressed -= AlertControlPressed;
|
||||
alertControl.Dispose();
|
||||
}
|
||||
|
||||
_alertControls.Clear();
|
||||
}
|
||||
|
||||
public event EventHandler<AlertType>? AlertPressed;
|
||||
|
||||
//TODO: This control caring about it's layout relative to other controls in the tree is terrible
|
||||
// the presenters or gamescreen should be dealing with this
|
||||
// probably want to tackle this after chatbox gets MVP'd
|
||||
#region Spaghetti
|
||||
|
||||
public const float ChatSeparation = 38f;
|
||||
private readonly IChatManager _chatManager;
|
||||
|
||||
protected override void EnteredTree()
|
||||
{
|
||||
base.EnteredTree();
|
||||
_chatManager.OnChatBoxResized += OnChatResized;
|
||||
OnChatResized(new ChatResizedEventArgs(HudChatBox.InitialChatBottom));
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
_chatManager.OnChatBoxResized -= OnChatResized;
|
||||
}
|
||||
|
||||
private void OnChatResized(ChatResizedEventArgs chatResizedEventArgs)
|
||||
{
|
||||
// resize us to fit just below the chat box
|
||||
if (_chatManager.CurrentChatBox != null)
|
||||
LayoutContainer.SetMarginTop(this, chatResizedEventArgs.NewBottom + ChatSeparation);
|
||||
else
|
||||
LayoutContainer.SetMarginTop(this, 250);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// This makes no sense but I'm leaving it in place in case I break anything by removing it.
|
||||
protected override void Resized()
|
||||
{
|
||||
// TODO: Can rework this once https://github.com/space-wizards/RobustToolbox/issues/1392 is done,
|
||||
// this is here because there isn't currently a good way to allow the grid to adjust its height based
|
||||
// on constraints, otherwise we would use anchors to lay it out
|
||||
base.Resized();
|
||||
AlertContainer.MaxGridHeight = Height;
|
||||
}
|
||||
|
||||
protected override void UIScaleChanged()
|
||||
{
|
||||
AlertContainer.MaxGridHeight = Height;
|
||||
base.UIScaleChanged();
|
||||
}
|
||||
|
||||
private bool SyncRemoveControls(IReadOnlyDictionary<AlertKey, AlertState> alertStates)
|
||||
{
|
||||
var toRemove = new List<AlertKey>();
|
||||
foreach (var existingKey in _alertControls.Keys)
|
||||
{
|
||||
if (!alertStates.ContainsKey(existingKey)) toRemove.Add(existingKey);
|
||||
}
|
||||
|
||||
foreach (var alertKeyToRemove in toRemove)
|
||||
{
|
||||
_alertControls.Remove(alertKeyToRemove, out var control);
|
||||
if (control == null) return true;
|
||||
AlertContainer.Children.Remove(control);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SyncUpdateControls(AlertsSystem alertsSystem, AlertOrderPrototype? alertOrderPrototype,
|
||||
IReadOnlyDictionary<AlertKey, AlertState> alertStates)
|
||||
{
|
||||
foreach (var (alertKey, alertState) in alertStates)
|
||||
{
|
||||
if (!alertKey.AlertType.HasValue)
|
||||
{
|
||||
Logger.WarningS("alert", "found alertkey without alerttype," +
|
||||
" alert keys should never be stored without an alerttype set: {0}", alertKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
var alertType = alertKey.AlertType.Value;
|
||||
if (!alertsSystem.TryGet(alertType, out var newAlert))
|
||||
{
|
||||
Logger.ErrorS("alert", "Unrecognized alertType {0}", alertType);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_alertControls.TryGetValue(newAlert.AlertKey, out var existingAlertControl) &&
|
||||
existingAlertControl.Alert.AlertType == newAlert.AlertType)
|
||||
{
|
||||
// key is the same, simply update the existing control severity / cooldown
|
||||
existingAlertControl.SetSeverity(alertState.Severity);
|
||||
existingAlertControl.Cooldown = alertState.Cooldown;
|
||||
}
|
||||
else
|
||||
{
|
||||
LayoutContainer.SetMarginTop(this, 250);
|
||||
if (existingAlertControl != null) AlertContainer.Children.Remove(existingAlertControl);
|
||||
|
||||
// this is a new alert + alert key or just a different alert with the same
|
||||
// key, create the control and add it in the appropriate order
|
||||
var newAlertControl = CreateAlertControl(newAlert, alertState);
|
||||
|
||||
//TODO: Can the presenter sort the states before giving it to us?
|
||||
if (alertOrderPrototype != null)
|
||||
{
|
||||
var added = false;
|
||||
foreach (var alertControl in AlertContainer.Children)
|
||||
{
|
||||
if (alertOrderPrototype.Compare(newAlert, ((AlertControl) alertControl).Alert) >= 0)
|
||||
continue;
|
||||
|
||||
var idx = alertControl.GetPositionInParent();
|
||||
AlertContainer.Children.Add(newAlertControl);
|
||||
newAlertControl.SetPositionInParent(idx);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!added) AlertContainer.Children.Add(newAlertControl);
|
||||
}
|
||||
else
|
||||
AlertContainer.Children.Add(newAlertControl);
|
||||
|
||||
_alertControls[newAlert.AlertKey] = newAlertControl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This makes no sense but I'm leaving it in place in case I break anything by removing it.
|
||||
|
||||
protected override void Resized()
|
||||
private AlertControl CreateAlertControl(AlertPrototype alert, AlertState alertState)
|
||||
{
|
||||
var alertControl = new AlertControl(alert, alertState.Severity)
|
||||
{
|
||||
// TODO: Can rework this once https://github.com/space-wizards/RobustToolbox/issues/1392 is done,
|
||||
// this is here because there isn't currently a good way to allow the grid to adjust its height based
|
||||
// on constraints, otherwise we would use anchors to lay it out
|
||||
base.Resized();
|
||||
AlertContainer.MaxGridHeight = Height;
|
||||
}
|
||||
Cooldown = alertState.Cooldown
|
||||
};
|
||||
alertControl.OnPressed += AlertControlPressed;
|
||||
return alertControl;
|
||||
}
|
||||
|
||||
protected override void UIScaleChanged()
|
||||
{
|
||||
AlertContainer.MaxGridHeight = Height;
|
||||
base.UIScaleChanged();
|
||||
}
|
||||
private void AlertControlPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
if (args.Button is not AlertControl control)
|
||||
return;
|
||||
|
||||
if (args.Event.Function != EngineKeyFunctions.UIClick)
|
||||
return;
|
||||
|
||||
AlertPressed?.Invoke(this, control.Alert.AlertType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,6 @@ namespace Content.Client.Entry
|
||||
IoCManager.Resolve<IClientPreferencesManager>().Initialize();
|
||||
IoCManager.Resolve<IStationEventManager>().Initialize();
|
||||
IoCManager.Resolve<EuiManager>().Initialize();
|
||||
IoCManager.Resolve<AlertManager>().Initialize();
|
||||
IoCManager.Resolve<ActionManager>().Initialize();
|
||||
IoCManager.Resolve<IVoteManager>().Initialize();
|
||||
IoCManager.Resolve<IGamePrototypeLoadManager>().Initialize();
|
||||
|
||||
@@ -19,7 +19,6 @@ using Content.Client.Viewport;
|
||||
using Content.Client.Voting;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Module;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
@@ -41,7 +40,6 @@ namespace Content.Client.IoC
|
||||
IoCManager.Register<IScreenshotHook, ScreenshotHook>();
|
||||
IoCManager.Register<IClickMapManager, ClickMapManager>();
|
||||
IoCManager.Register<IStationEventManager, StationEventManager>();
|
||||
IoCManager.Register<AlertManager, AlertManager>();
|
||||
IoCManager.Register<ActionManager, ActionManager>();
|
||||
IoCManager.Register<IClientAdminManager, ClientAdminManager>();
|
||||
IoCManager.Register<EuiManager, EuiManager>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Alerts.UI;
|
||||
using Content.Client.Chat;
|
||||
using Content.Client.Chat.Managers;
|
||||
using Content.Client.Chat.UI;
|
||||
@@ -38,6 +38,7 @@ namespace Content.Client.Viewport
|
||||
|
||||
[ViewVariables] private ChatBox? _gameChat;
|
||||
private ConstructionMenuPresenter? _constructionMenu;
|
||||
private AlertsFramePresenter? _alertsFramePresenter;
|
||||
|
||||
private FpsCounter _fpsCounter = default!;
|
||||
|
||||
@@ -107,6 +108,10 @@ namespace Content.Client.Viewport
|
||||
/// </summary>
|
||||
private void SetupPresenters()
|
||||
{
|
||||
// HUD
|
||||
_alertsFramePresenter = new AlertsFramePresenter();
|
||||
|
||||
// Windows
|
||||
_constructionMenu = new ConstructionMenuPresenter(_gameHud);
|
||||
}
|
||||
|
||||
@@ -115,7 +120,11 @@ namespace Content.Client.Viewport
|
||||
/// </summary>
|
||||
private void DisposePresenters()
|
||||
{
|
||||
// Windows
|
||||
_constructionMenu?.Dispose();
|
||||
|
||||
// HUD
|
||||
_alertsFramePresenter?.Dispose();
|
||||
}
|
||||
|
||||
internal static void FocusChat(ChatBox chat)
|
||||
|
||||
Reference in New Issue
Block a user