Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UI controller docs #32

Merged
merged 6 commits into from
Sep 24, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.