Skip to content

Commit

Permalink
Add saved BUI positions (#5650)
Browse files Browse the repository at this point in the history
  • Loading branch information
metalgearsloth authored Feb 3, 2025
1 parent 2719b9f commit fc55c8e
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 16 deletions.
4 changes: 2 additions & 2 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ END TEMPLATE-->

### Breaking changes

*None yet*
* RemoveChild is called after OnClose for BaseWindow.

### New features

*None yet*
* BUIs now have their positions saved when closed and re-used when opened when using the `CreateWindow<T>` helper or via manually registering it via RegisterControl.

### Bugfixes

Expand Down
61 changes: 61 additions & 0 deletions Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Robust.Client.UserInterface;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;

namespace Robust.Client.GameObjects;

public sealed class UserInterfaceSystem : SharedUserInterfaceSystem
{
private Dictionary<EntityUid, Dictionary<Enum, Vector2>> _savedPositions = new();
private Dictionary<BoundUserInterface, Control> _registeredControls = new();

public override void Initialize()
{
base.Initialize();
Expand All @@ -17,6 +25,59 @@ public override void Shutdown()
ProtoManager.PrototypesReloaded -= OnProtoReload;
}

protected override void OnUserInterfaceShutdown(Entity<UserInterfaceComponent> ent, ref ComponentShutdown args)
{
base.OnUserInterfaceShutdown(ent, ref args);
_savedPositions.Remove(ent.Owner);
}

/// <inheritdoc />
public override void OpenUi(Entity<UserInterfaceComponent?> entity, Enum key, bool predicted = false)
{
var player = Player.LocalEntity;

if (player == null)
return;

OpenUi(entity, key, player.Value, predicted);
}

protected override void SavePosition(BoundUserInterface bui)
{
if (!_registeredControls.Remove(bui, out var control))
return;

var keyed = _savedPositions[bui.Owner];
keyed[bui.UiKey] = control.Position;
}

/// <summary>
/// Registers a control so it will later have its position stored by <see cref="SavePosition"/> when the BUI is closed.
/// </summary>
public void RegisterControl(BoundUserInterface bui, Control control)
{
DebugTools.Assert(!_registeredControls.ContainsKey(bui));
_registeredControls[bui] = control;
_savedPositions.GetOrNew(bui.Owner);
}

public override bool TryGetPosition(Entity<UserInterfaceComponent?> entity, Enum key, out Vector2 position)
{
position = default;

if (!_savedPositions.TryGetValue(entity.Owner, out var keyed))
{
return false;
}

if (!keyed.TryGetValue(key, out position))
{
return false;
}

return true;
}

private void OnProtoReload(PrototypesReloadedEventArgs obj)
{
var player = Player.LocalEntity;
Expand Down
40 changes: 37 additions & 3 deletions Robust.Client/UserInterface/BoundUserInterfaceExt.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,53 @@
using System;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects;

namespace Robust.Client.UserInterface;

public static class BoundUserInterfaceExt
{
private static T GetWindow<T>(BoundUserInterface bui) where T : BaseWindow, new()
{
var window = bui.CreateDisposableControl<T>();
window.OnClose += bui.Close;
var system = bui.EntMan.System<UserInterfaceSystem>();
system.RegisterControl(bui, window);
return window;
}

/// <summary>
/// Helper method to create a window and also handle closing the BUI when it's closed.
/// </summary>
public static T CreateWindow<T>(this BoundUserInterface bui) where T : BaseWindow, new()
{
var window = bui.CreateDisposableControl<T>();
window.OpenCentered();
window.OnClose += bui.Close;
var window = GetWindow<T>(bui);

if (bui.EntMan.System<UserInterfaceSystem>().TryGetPosition(bui.Owner, bui.UiKey, out var position))
{
window.Open(position);
}
else
{
window.OpenCentered();
}

return window;
}

public static T CreateWindowCenteredLeft<T>(this BoundUserInterface bui) where T : BaseWindow, new()
{
var window = GetWindow<T>(bui);

if (bui.EntMan.System<UserInterfaceSystem>().TryGetPosition(bui.Owner, bui.UiKey, out var position))
{
window.Open(position);
}
else
{
window.OpenCenteredLeft();
}

return window;
}

Expand Down
13 changes: 12 additions & 1 deletion Robust.Client/UserInterface/CustomControls/BaseWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;

Expand Down Expand Up @@ -36,8 +37,8 @@ public virtual void Close()
return;
}

Parent.RemoveChild(this);
OnClose?.Invoke();
Parent.RemoveChild(this);
}

protected internal override void KeyBindDown(GUIBoundKeyEventArgs args)
Expand Down Expand Up @@ -229,6 +230,16 @@ public void Open()
OnOpen?.Invoke();
}

/// <summary>
/// Opens the window and places it at the specified position.
/// </summary>
public void Open(Vector2 position)
{
Measure(Vector2Helpers.Infinity);
Open();
LayoutContainer.SetPosition(this, position);
}

public void OpenCentered() => OpenCenteredAt(new Vector2(0.5f, 0.5f));

public void OpenToLeft() => OpenCenteredAt(new Vector2(0, 0.5f));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
public abstract class BoundUserInterface : IDisposable
{
[Dependency] protected readonly IEntityManager EntMan = default!;
[Dependency] protected internal readonly IEntityManager EntMan = default!;
[Dependency] protected readonly ISharedPlayerManager PlayerManager = default!;
protected readonly SharedUserInterfaceSystem UiSystem;

Expand Down
52 changes: 43 additions & 9 deletions Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
using Robust.Shared.Collections;
Expand Down Expand Up @@ -45,6 +46,8 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
/// </summary>
private ValueList<Enum> _keys = new();

private ValueList<EntityUid> _entList = new();

public override void Initialize()
{
base.Initialize();
Expand Down Expand Up @@ -86,6 +89,11 @@ public override void Initialize()
SubscribeLocalEvent<UserInterfaceUserComponent, ComponentShutdown>(OnActorShutdown);
}

private void AddQueued(BoundUserInterface bui, bool value)
{
_queuedBuis.Add((bui, value));
}

/// <summary>
/// Validates the received message, and then pass it onto systems/components
/// </summary>
Expand Down Expand Up @@ -232,7 +240,7 @@ private void CloseUiInternal(Entity<UserInterfaceComponent?> ent, Enum key, Enti

if (ent.Comp.ClientOpenInterfaces.TryGetValue(key, out var cBui))
{
_queuedBuis.Add((cBui, false));
AddQueued(cBui, false);
}

if (ent.Comp.Actors.Count == 0)
Expand Down Expand Up @@ -274,18 +282,17 @@ private void OnUserInterfaceStartup(Entity<UserInterfaceComponent> ent, ref Comp
// PlayerAttachedEvent will catch some of these.
foreach (var (key, bui) in ent.Comp.ClientOpenInterfaces)
{
_queuedBuis.Add((bui, true));
AddQueued(bui, true);
}
}

private void OnUserInterfaceShutdown(Entity<UserInterfaceComponent> ent, ref ComponentShutdown args)
protected virtual void OnUserInterfaceShutdown(Entity<UserInterfaceComponent> ent, ref ComponentShutdown args)
{
var actors = new List<EntityUid>();
foreach (var (key, acts) in ent.Comp.Actors)
{
actors.Clear();
actors.AddRange(acts);
foreach (var actor in actors)
_entList.Clear();
_entList.AddRange(acts);
foreach (var actor in _entList)
{
CloseUiInternal(ent!, key, actor);
DebugTools.Assert(!acts.Contains(actor));
Expand Down Expand Up @@ -456,7 +463,7 @@ private void OnUserInterfaceHandleState(Entity<UserInterfaceComponent> ent, ref
}

var bui = ent.Comp.ClientOpenInterfaces[key];
_queuedBuis.Add((bui, false));
AddQueued(bui, false);
}
}

Expand Down Expand Up @@ -537,7 +544,7 @@ private void EnsureClientBui(Entity<UserInterfaceComponent> entity, Enum key, In
if (!open)
return;

_queuedBuis.Add((boundUserInterface, true));
AddQueued(boundUserInterface, true);
}

/// <summary>
Expand Down Expand Up @@ -653,6 +660,14 @@ public bool TryOpenUi(Entity<UserInterfaceComponent?> entity, Enum key, EntityUi
return true;
}

/// <summary>
/// Opens the UI for the local client. Does nothing on server.
/// </summary>
public virtual void OpenUi(Entity<UserInterfaceComponent?> entity, Enum key, bool predicted = false)
{

}

public void OpenUi(Entity<UserInterfaceComponent?> entity, Enum key, EntityUid? actor, bool predicted = false)
{
if (actor == null || !UIQuery.Resolve(entity.Owner, ref entity.Comp, false))
Expand Down Expand Up @@ -690,6 +705,23 @@ public void OpenUi(Entity<UserInterfaceComponent?> entity, Enum key, ICommonSess
OpenUi(entity, key, actorEnt.Value, predicted);
}

/// <summary>
/// Tries to return the saved position of a user interface.
/// </summary>
public virtual bool TryGetPosition(Entity<UserInterfaceComponent?> entity, Enum key, out Vector2 position)
{
position = Vector2.Zero;
return false;
}

/// <summary>
/// Saves a position for the BUI.
/// </summary>
protected virtual void SavePosition(BoundUserInterface bui)
{

}

/// <summary>
/// Sets a BUI state and networks it to all clients.
/// </summary>
Expand Down Expand Up @@ -1056,13 +1088,15 @@ public override void Update(float frameTime)
}
#endif
}
// Close BUI
else
{
if (UIQuery.TryComp(bui.Owner, out var uiComp))
{
uiComp.ClientOpenInterfaces.Remove(bui.UiKey);
}

SavePosition(bui);
bui.Dispose();
}
}
Expand Down

0 comments on commit fc55c8e

Please sign in to comment.