Class TUIState

Unit

Declaration

type TUIState = class(TCastleUserInterface)

Description

UI state, to manage the state of your game UI. See also https://castle-engine.io/manual_2d_user_interface.php#section_ui_state for an overview of using TUIState.

In simple cases, only one state is current at a given time, and it can be get or set using the TUIState.Current property. In more complex cases, you can use TUIState.Push and TUIState.Pop to build a stack of states, and in effect multiple states are active at the same time. All of the states on stack are started, but only the top-most is resumed.

Each state has Start and Stop methods that you can override to perform work when state becomes part of the current state stack, or stops being part of it. You can also override Resume and Pause methods, to perform work when the state becomes the top-most state or is no longer the top-most state. The distinction becomes important once you play around with pushing/popping states. The names are deliberaly similar to Android lifecycle callback names.

You can add/remove state-specific UI controls in various ways. You can add them in the constructor of this state (and then free in destructor), or add them in Start, free in Stop.

  1. It's simplest and best to add/keep children controls as real children of the current state, so add them using methods TCastleUserInterface.InsertFront and TCastleUserInterface.InsertBack.

  2. Eventually, for special tricks, you can add controls that are conceptually the state "children" directly to the StateContainer.Controls list. This allows to keep some children on the StateContainer.Controls list for a longer time (not only when this state is active), which may be useful for optimization, to not reinitialize GL resources too often. To do this, add controls using StateContainer.Controls.InsertFront(...), remove them by StateContainer.Controls.Remove(...), and make sure to override InsertAtPosition method such that state instance is inserted in StateContainer.Controls right behind your UI.

Current state is also placed on the list of container controls. This way state is notified about UI events, and can react to them. Since state-specific UI should always be at the front of us, or our children, so in case of events that can be "handled" (like TCastleUserInterface.Press, TCastleUserInterface.Release events) the state-specific UI controls will handle them before the state itself (if you override TCastleUserInterface.Press or such in state, be sure to call inherited first, to make sure it really happens).

This way state can

  • catch press/release and similar events, when no other state-specific control handled them,

  • catch update, GL context open/close and other useful events,

  • can have it's own render function, to directly draw UI.

See the TCastleUserInterface class for a lot of useful methods that you can override in your state descendants to capture various events.

Hierarchy

Overview

Fields

Public class var Log: boolean;

Methods

Protected function StateContainer: TUIContainer; virtual;
Protected function InsertAtPosition: Integer; virtual;
Protected function FreeAtStop: TComponent;
Public class procedure Push(const NewState: TUIState);
Public class procedure Pop;
Public class procedure Pop(const CurrentTopMostState: TUIState);
Public class function StateStackCount: Integer;
Public constructor Create(AOwner: TComponent); override;
Public destructor Destroy; override;
Public constructor CreateUntilStopped;
Public procedure Start; virtual;
Public procedure Stop; virtual;
Public procedure Resume; virtual;
Public procedure Pause; virtual;
Public procedure Finish; virtual; deprecated 'use Stop';
Public function Active: boolean;
Public function Press(const Event: TInputPressRelease): boolean; override;
Public function Release(const Event: TInputPressRelease): boolean; override;
Public function Motion(const Event: TInputMotion): boolean; override;
Public procedure Update(const SecondsPassed: Single; var HandleInput: boolean); override;
Public procedure InsertUserInterface(const DesignUrl: String; const FinalOwner: TComponent; out Ui: TCastleUserInterface; out UiOwner: TComponent);
Public procedure InsertUserInterface(const DesignUrl: String; const FinalOwner: TComponent; out UiOwner: TComponent);

Properties

Public class property Current: TUIState read GetCurrent write SetCurrent;
Public class property CurrentTop: TUIState read GetCurrentTop;
Public class property StateStack [constIndex:Integer]: TUIState read GetStateStack;
Public property InterceptInput: boolean read FInterceptInput write FInterceptInput default false;
Published property FullSize default true;

Description

Fields

Public class var Log: boolean;

When True, state operations will send a log to CastleLog.

Methods

Protected function StateContainer: TUIContainer; virtual;

Container on which state works. By default, this is Application.MainWindow if you use CastleWindow or TCastleControlBase.MainControl if you use CastleControl. When the state is current, then Container property (from ancestor, see TCastleUserInterface.Container) is equal to this.

Protected function InsertAtPosition: Integer; virtual;

Position on StateContainer.Controls where we insert this state. By default, state is inserted as the front-most control, so position is equal to StateContainer.Controls.Count.

Protected function FreeAtStop: TComponent;

Assign this component as owner for your controls, to make them freed during nearest Stop.

Public class procedure Push(const NewState: TUIState);

Pushing the state adds it at the top of the state stack.

The state known as Current is conceptually at the bottom of state stack, always. When it is nil, then pushing new state sets the Current state. Otherwise Current state is left as-it-is, new state is added on top.

Public class procedure Pop;

Pop the current top-most state, whatever it is.

Public class procedure Pop(const CurrentTopMostState: TUIState);

Pop the top-most state, checking it is as expected. Makes a warning, and does nothing, if the current top-most state is different than indicated. This is usually a safer (more chance to easily catch bugs) version of Pop than the parameter-less version.

Public class function StateStackCount: Integer;
 
Public constructor Create(AOwner: TComponent); override;

Create an instance of the state. You willl typically create one instance of each state class (like TStateMain, TStatePlay) at the application initialization (e.g. in Application.OnInitialize callback), like

StateMain := TStateMain.Create(Application);
StatePlay := TStateMain.Create(Application);

Later you switch between states using Current or Push or Pop, like this:

TUIState.Current := StateMain;

Public destructor Destroy; override;
 
Public constructor CreateUntilStopped;

Create the instance TUIState that will be automatically freed when the state is stopped. This allows alternative usage of states (as opposed to the ones described in Create docs), where you create short-lived instances of state classes. Use it like this:

TUIState.Current := TStateMain.CreateUntilStopped;

The advantages:

  • You don't need to worry that some state field value will "survive" with an invalid value after Stop. So you don't need to clear everything in Stop or initialize everything in Start, instead you can depend that Start happens only once right after the constructor, so the instance fields are clear.

  • You avoid having global variables, keeping singletons of each state class. So the code is a little safer.

  • You can reintroduce your own constructor to require some parameters, instead of exposing state parameters as public fields/properties.

The disadvantage is that you cannot store in state fields anything that should "survive" the state Stop. You can instead use "class variables" in state class, or any global variable.

Public procedure Start; virtual;

State becomes active, it's now part of the state stack.

Started state is part of the StateStack, and will soon become running (top-most on the stack). When the state is set to be current, by TUIState.Current := MyState, this happens:

  1. MyStart is pushed as the top-most state on state stack.

  2. MyStart.Start is called.

  3. MyStart is added to the StateContainer.Controls list, so the state methods GLContextOpen and Resize are called (as for all normal TCastleUserInterface instances).

  4. MyStar.Resume is called.

Public procedure Stop; virtual;

State is no longer active, no longer part of state stack.

When the state stops becoming active, this happens:

  1. MyStart.Pause is called.

  2. MyStart is removed from the StateContainer.Controls list. So the state method GLContextClose is called (as for all normal TCastleUserInterface instances).

  3. MyStart.Stop is called.

  4. MyStart is removed from the on state stack.

This is always called to finalize the started state. When the state is destroyed, it's Pause and Stop are called too, so you can use this method to reliably finalize whatever you initialized in Start.

Public procedure Resume; virtual;

State is now the top-most state. See Start and Stop docs about state lifecycle methods. This is called after Start, it is also called when you pop another state, making this state the top-most.

Public procedure Pause; virtual;

State is no longer the top-most state. See Start and Stop docs about state lifecycle methods. This is called before Stop, it is also called when another state is pushed over this state, so this stops being the the top-most state.

Public procedure Finish; virtual; deprecated 'use Stop';

Warning: this symbol is deprecated: use Stop

 
Public function Active: boolean;

State is right now part of the state stack, which means it's between Start and Stop calls. The state is added to the stack before the Start call, and removed after the Stop call, so this returns True during all the methods — Start, Resume, Pause, Stop.

Public function Press(const Event: TInputPressRelease): boolean; override;
 
Public function Release(const Event: TInputPressRelease): boolean; override;
 
Public function Motion(const Event: TInputMotion): boolean; override;
 
Public procedure Update(const SecondsPassed: Single; var HandleInput: boolean); override;
 
Public procedure InsertUserInterface(const DesignUrl: String; const FinalOwner: TComponent; out Ui: TCastleUserInterface; out UiOwner: TComponent);

Load and show a user interface from a .castle-user-interface file, designed in Castle Game Engine Editor.

This is an utility method, loading a UI in a typical way into the TUIState. It is not the only way to load a .castle-user-interface file, a more general approach is to use UserInterfaceLoad from CastleComponentSerialize unit, and manually call InsertFront to add it to the state UI.

The loaded UI is returned under the Ui parameter, unless you use an overloaded version that omits this parameter.

It is owned by UiOwner, which is useful to find the components by name, like

ButtonQuit := UiOwner.FindRequiredComponent('ButtonQuit') as TCastleButton;

The UiOwner, in turn, is owned by the FinalOwner. You typically use this method inside overridden Start, and as FinalOwner you pass FreeAtStop – this way the user interface is freed when the state is stopped.

Public procedure InsertUserInterface(const DesignUrl: String; const FinalOwner: TComponent; out UiOwner: TComponent);
 

Properties

Public class property Current: TUIState read GetCurrent write SetCurrent;

Current state. In case multiple states are active (only possible if you used Push method), this is the bottom state (use CurrentTop to get top state). Setting this resets whole state stack.

Public class property CurrentTop: TUIState read GetCurrentTop;
 
Public class property StateStack [constIndex:Integer]: TUIState read GetStateStack;
 
Public property InterceptInput: boolean read FInterceptInput write FInterceptInput default false;

Prevents passing mouse/keyboard events to the UI states underneath.

More precisely, when this property is True, then the Press, Release and Motion events are marked as "handled" in this UI state. This means that they will not be processed further, by UI controls under this state, in particular by UI states that are underneath this state in state stack (created by Push method). They will also not be passed to final container (TCastleWindowBase, TCastleControlBase) callbacks like TCastleWindowBase.OnPress (as these callbacks are always used at the end, when nothing else handled the event).

Published property FullSize default true;

TUIState control makes most sense when it is FullSize, filling the whole window.

This way it always captures events on the whole container. And the child controls (anchored to this) behave like anchored to the whole container.


Generated by PasDoc 0.15.0.