Skip to content

Commit

Permalink
Add UI controller docs (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
DrSmugleaf committed Sep 24, 2023
1 parent 5cbea79 commit c1b99f1
Showing 1 changed file with 112 additions and 0 deletions.
112 changes: 112 additions & 0 deletions src/en/robust-toolbox/user-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,115 @@ The XAML code is automatically compiled to IL so it can be efficiently construct
</Control>
```

## UI Controllers
UI controllers are responsible for creating, updating and removing controls. Entity systems must not do this themselves.
Any data that they use must be obtained by binding their methods to system events (see example below) or by calling methods on IoC services, such as IPlayerManager.
To create a new UI controller, make a new class that inherits `UIController`. This type will then be instantiated automatically as a singleton by the `UserInterfaceManager`.
Widgets are retrieved within UI controllers by calling `UIManager.GetActiveUIWidgetOrNull<T>()`, where T is a widget such as `ActionsBar`.
The lifecycle of an UI controller is longer than that of entity systems; an UI controller may exist before entity systems are loaded, and stay alive after they are unloaded.

### Dependencies
An UI controller may have dependencies to other IoC services and controllers using the \[Dependency\] syntax.
For systems, \[UISystemDependency\] must be used instead.
Once systems are loaded, UI controller methods may be bound to events declared on them.

```cs
public sealed class ActionUIController : UIController, IOnSystemChanged<ActionsSystem>, IOnStateEntered<GameplayState>
{
// Dependency is used for IoC services and other controllers
[Dependency] private readonly GameplayStateLoadController _gameplayStateLoad = default!;

// For entity systems, UISystemDependency is used instead
[UISystemDependency] private readonly ActionsSystem? _actions = default!;

private ActionsWindow? _window;

public override void Initialize()
{
base.Initialize();

// We can bind methods to event fields on other UI controllers during initialize
_gameplayStateLoad.OnScreenLoad += LoadGui;
_gameplayStateLoad.OnScreenUnload += UnloadGui;

// UI controllers can also subscribe to local and network entity events
// Local events are events raised on the client using RaiseLocalEvent
// SubscribeLocalEvent<PlayerAttachedEvent>(ev => {});
// Network events are events raised by the server and sent to the client
// SubscribeNetworkEvent<PlayerAttachedEvent>(ev => {});
}

private void LoadGui()
{
DebugTools.Assert(_window == null);
_window = UIManager.CreateWindow<ActionsWindow>();
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
}

private void UnloadGui()
{
if (_window != null)
{
_window.Dispose();
_window = null;
}
}

private void ToggleWindow()
{
if (_window == null)
return;

if (_window.IsOpen)
{
_window.Close();
return;
}

_window.Open();
}

public void OnSystemLoaded(ActionsSystem system)
{
// We can bind to event fields on entity systems when that entity system is loaded
system.LinkActions += OnComponentLinked;
}

public void OnSystemUnloaded(ActionsSystem system)
{
// And unbind when the system is unloaded
system.LinkActions -= OnComponentLinked;
}

// This will be called when ActionsSystem raises an event on its LinkActions event field
private void OnComponentLinked(ActionsComponent component)
{
}

public void OnStateEntered(GameplayState state)
{
// Bind hotkeys once we enter the gameplay state (start a round and join it as the client)
CommandBinds.Builder
.Bind(ContentKeyFunctions.OpenActionsMenu, InputCmdHandler.FromDelegate(_ => ToggleWindow()))
.Register<ActionUIController>();
}

public void OnStateExited(GameplayState state)
{
// Unbind hotkeys after we exit the round (we enter the lobby or disconnect)
CommandBinds.Unregister<ActionUIController>();
}
}
```

UI controllers may also implement the following interfaces:
### IOnStateChanged<T>
Implements two methods: `OnStateEntered(T state)` and `OnStateExited(T state)`.
These methods are automatically called when the respective state is entered and exited, for example GameplayState.
If only the entering or exiting logic is needed, `IOnStateEntered<T>` and `IOnStateExited<T>` may be implemented instead.

### IOnSystemChanged<T>
Implements two methods: `OnSystemLoaded(T system)` and `OnSystemUnloaded(T system)`.
These methods are automatically called when the respective system is loaded and unloaded, for example ActionsSystem.
If only the loaded or unloaded logic is needed, `IOnSystemLoaded<T>` and `IOnStateUnloaded<T>` may be implemented instead.

0 comments on commit c1b99f1

Please sign in to comment.