Class TCastleView

Unit

Declaration

type TCastleView = class(TCastleUserInterface)

Description

"View" represents the current state of your application user interface. See also https://castle-engine.io/views for an overview of using TCastleView.

In a given container (like a window), in the simple case only one TCastleView is current at a given time, and it can be get or set using the TCastleContainer.View property. In more complex cases, you can use TCastleContainer.PushView and TCastleContainer.PopView to build a stack of views, and in effect multiple views are active at the same time. All of the views on stack are started, but only the top-most is resumed.

Each TCastleView has Start and Stop methods that you can override to perform work when view becomes part of the current view stack, or stops being part of it.

You can also override Resume and Pause methods, to perform work when the view becomes the top-most view or is no longer the top-most view. The distinction becomes important once you play around with pushing/popping view.

To define view user interface:

  1. It is simplest to set DesignUrl to the design file you created using CGE editor. Such user interface controls will be created right before Start and destroyed right after Stop (so the view UI always "resets" when view starts).

  2. You can always create more UI controls and add them to the view at any point. The view is a TCastleUserInterface descendant and you can add UI to it just by using TCastleUserInterface.InsertFront.

    UI children can be added anytime you want, e.g. in Start or in overridden constructor.

    UI children can be removed or destroyed anytime you want as well. You can use FreeAtStop as an owner for anything you want to be automatically destroyed at Stop.

Current view is placed on the list of container controls. This way view is notified about UI events, and can react to them. Note that our children will handle events before the view itself is notified about them, following TCastleUserInterface events behavior. This way view can:

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

Hierarchy

Overview

Fields

Public class var Log: boolean;

Methods

Protected function ContainerWidth: Cardinal; override;
Protected function ContainerHeight: Cardinal; override;
Protected function ContainerRect: TRectangle; override;
Protected function ContainerSizeKnown: boolean; override;
Protected function StateContainer: TCastleContainer; deprecated 'use Container to get the container in which we are already started, use PredictedContainer if the state is not yet started';
Protected function Container: TCastleContainer;
Protected function FreeAtStop: TComponent;
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 Render; override;
Public function UIScale: Single; override;
Public procedure InsertUserInterface(const ADesignUrl: String; const FinalOwner: TComponent; out Ui: TCastleUserInterface; out UiOwner: TComponent); overload; deprecated 'instead of this, set DesignUrl in constructor';
Public procedure InsertUserInterface(const ADesignUrl: String; const FinalOwner: TComponent; out UiOwner: TComponent); overload; deprecated 'instead of this, set DesignUrl in constructor';
Public procedure WaitForRenderAndCall(const Event: TNotifyEvent);
Public function DesignedComponent(const ComponentName: String): TComponent;
Public class procedure Push(const NewState: TCastleView); deprecated 'use Container.PushView';
Public class procedure Pop; overload; deprecated 'use Container.PopView';
Public class procedure Pop(const CurrentTopMostState: TCastleView); overload; deprecated 'use Container.PopView';
Public class function StateStackCount: Integer; deprecated 'use Container.ViewStackCount';

Properties

Public property InterceptInput: boolean read FInterceptInput write FInterceptInput default false;
Public property DesignUrl: String read FDesignUrl write SetDesignUrl;
Public property DesignPreload: Boolean read FDesignPreload write SetDesignPreload default false;
Public class property Current: TCastleView read GetCurrent write SetCurrent; deprecated 'use Container.View';
Public class property CurrentTop: TCastleView read GetCurrentTop; deprecated 'use Container.FrontView';
Public class property StateStack [constIndex:Integer]: TCastleView read GetStateStack; deprecated 'use Container.ViewStack';
Published property FullSize default true;

Description

Fields

Public class var Log: boolean;

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

Methods

Protected function ContainerWidth: Cardinal; override;

The container size is always known: we assume that view will be part of PredictedContainer, if the view not yet started.

So the view knows container size, regardless if we are between Start / Stop, regardless if the TCastleView is already added to some TCastleContainer.Children. This makes all other routines, like ParentRect, EffectiveRect, EffectiveWidth, EffectiveHeight also work.

Protected function ContainerHeight: Cardinal; override;
 
Protected function ContainerRect: TRectangle; override;
 
Protected function ContainerSizeKnown: boolean; override;
 
Protected function StateContainer: TCastleContainer; deprecated 'use Container to get the container in which we are already started, use PredictedContainer if the state is not yet started';

Warning: this symbol is deprecated: use Container to get the container in which we are already started, use PredictedContainer if the state is not yet started

 
Protected function Container: TCastleContainer;

The container in which the view works.

This is just like Container property (from ancestor, see TCastleUserInterface.Container) but it is already assigned even during Start and Stop.

This method deliberately hides the ancestor TCastleUserInterface.Container method. This way it will work always, in particular also during Start method. This allows you to do code like this in overridden Start method:

Container.OverrideCursor := mcNone;

Protected function FreeAtStop: TComponent;

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

This component is created on-demand (whenever you access it) and always destroyed right after running view Stop method.

The idea is that you can assign it as owner (1st parameter to the constructor of TComponent class or any classes descending from TComponent, this includes all CGE components) of something, like this:

MyInstance := TSomeComponent.Create(FreeAtStop);

And in effect, you know that MyInstance will be freed after the view stops. So you don't need to worry about it anymore, in particular you don't need to do FreeAndNil(MyInstance) in the state Stop.

Public constructor Create(AOwner: TComponent); override;

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

ViewMainMenu := TViewMainMenu.Create(Application);
ViewPlay := TViewPlay.Create(Application);

Later you switch between views using TCastleContainer.View or TCastleContainer.PushView or TCastleContainer.PopView, like this:

Container.View := ViewMain;

See https://castle-engine.io/views and numerous engine examples.

Public destructor Destroy; override;
 
Public constructor CreateUntilStopped;

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

Container.View := TViewMain.CreateUntilStopped;

The advantages:

  • You don't need to worry that some view 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 view class. So the code is a little safer.

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

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

Public procedure Start; virtual;

Executed when view becomes active, it's now part of the view stack.

Started view is part of the ViewStack, and will soon become running (top-most on the stack). When the view is set to be current, by Container.View := MyView, this happens:

  1. MyView is pushed as the top-most view on view stack.

  2. MyView.Start is called.

  3. MyView is added to the TCastleContainer.Controls list (using the container where you set TCastleContainer.PushView, TCastleContainer.View). This also means that the view methods GLContextOpen and Resize are called (as for all normal TCastleUserInterface instances).

  4. MyStar.Resume is called.

Public procedure Stop; virtual;

Executed when view is no longer active, no longer part of view stack.

When the view stops becoming active, this happens:

  1. MyView.Pause is called.

  2. MyView is removed from the TCastleContainer.Controls list (using the container to which we were added). So the view method GLContextClose is called (as for all normal TCastleUserInterface instances).

  3. MyView.Stop is called.

  4. MyView is removed from the on view stack.

This is always called to finalize the started view. When the view 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;

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

Public procedure Pause; virtual;

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

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

Warning: this symbol is deprecated: use Stop

 
Public function Active: boolean;

View is right now part of the view stack, which means it's between Start and Stop calls. The view 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 Render; override;
 
Public function UIScale: Single; override;
 
Public procedure InsertUserInterface(const ADesignUrl: String; const FinalOwner: TComponent; out Ui: TCastleUserInterface; out UiOwner: TComponent); overload; deprecated 'instead of this, set DesignUrl in constructor';

Warning: this symbol is deprecated: instead of this, set DesignUrl in constructor

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 TCastleView. 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 view 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 view is stopped.

Public procedure InsertUserInterface(const ADesignUrl: String; const FinalOwner: TComponent; out UiOwner: TComponent); overload; deprecated 'instead of this, set DesignUrl in constructor';

Warning: this symbol is deprecated: instead of this, set DesignUrl in constructor

 
Public procedure WaitForRenderAndCall(const Event: TNotifyEvent);

Wait until the render event happens (to redraw current view), and then call Event.

The scheduled Event will be called at the Update time.

If the view stopped before the scheduled events fired, then the remaining events are ignored. That is, the scheduled events are cleared every time you start the view.

One use-case of this is to show a loading view, where you load things, but also update the loading progress from time to time. Be careful in this case to not call this too often, as then your loading time will be tied to rendering time. For example, when the monitor refresh rate is 60, and the "vertical sync" is "on", then the render events happen at most 60 times per second. So if you call WaitForRenderAndCall too often during loading, you may spend more time waiting for render event (each WaitForRenderAndCall taking 1/60 of second) than doing actual loading.

Public function DesignedComponent(const ComponentName: String): TComponent;

When the DesignUrl is set, and the view is started, you can use this method to find loaded components. Like this:

MyButton := DesignedComponent('MyButton') as TCastleButton;

This method is seldom useful now. The published fields of the view are automatically initialized when loading / unloading design using DesignUrl. There's no need to use DesignedComponent for them.

See also
DesignUrl
Load a designed user interface (from .castle-user-interface file) when this view is started.
Public class procedure Push(const NewState: TCastleView); deprecated 'use Container.PushView';

Warning: this symbol is deprecated: use Container.PushView

 
Public class procedure Pop; overload; deprecated 'use Container.PopView';

Warning: this symbol is deprecated: use Container.PopView

 
Public class procedure Pop(const CurrentTopMostState: TCastleView); overload; deprecated 'use Container.PopView';

Warning: this symbol is deprecated: use Container.PopView

 
Public class function StateStackCount: Integer; deprecated 'use Container.ViewStackCount';

Warning: this symbol is deprecated: use Container.ViewStackCount

 

Properties

Public property InterceptInput: boolean read FInterceptInput write FInterceptInput default false;

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

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

Note that setting this to True means that calling inherited from your Press overridden implementation will always return True, as if the ancestor handled all the items. For this reason, in such case you should not immediately Exit when inherited is True. You should just ignore the ancestor result, like this:

function TMyView.Press(const Event: TInputPressRelease): Boolean;
begin
  Result := inherited;
  // ignore the ancestor result, as we use InterceptInput, so ancestor always returns true
  // if Result the Exit;

  if Event.IsMouseButton(buttonLeft) then
  begin
    ...
    Exit(true);
  end;
end;

Public property DesignUrl: String read FDesignUrl write SetDesignUrl;

Load a designed user interface (from .castle-user-interface file) when this view is started. You typically set this property in overridden constructor, and in effect the given design file will be loaded right before Start and it will be freed right after Stop.

Typical use-case looks like this:

constructor TViewPlay.Create(AOwner: TComponent);
begin
  inherited;
  DesignUrl := 'castle-data:/gameviewplay.castle-user-interface';
  // DesignPreload := true; // to make future "Container.View := ViewPlay" faster
end;

The published fields of this class, if they have equal name to any component in the design, are automatically initialized to the instance of this component. (And they will be set to Nil when the design is destroyed, typically at Stop.)

You can also modify this property when the view has already started (after Start and before Stop) in which case the previous design will be freed and new one will be loaded immediately.

Set this to empty string (default) to mean "no design should be loaded".

Note that this is not the only way to load a .castle-user-interface file. A more general approach is to use UserInterfaceLoad, and call InsertFront to add it to the view UI. Using this property just adds some comfortable automatic behavior (design is freed at stop, published fields of view are set, you can use comfortable DesignPreload).

See also
DesignPreload
Preload the design file (specified in DesignUrl) as soon as possible, making starting the view much faster.
Public property DesignPreload: Boolean read FDesignPreload write SetDesignPreload default false;

Preload the design file (specified in DesignUrl) as soon as possible, making starting the view much faster. Using this property means that we "preload" the design file, to cache the referenced images / TCastleScene instances etc. as soon as possible, to make the future loading of this design (when view starts) very quick.

Typically you set this property in overridden TCastleView constructor, right after (or before, it doesn't matter) setting DesignUrl. It will mean that constructor takes a longer time (which typically means that Application.OnInitialize takes a longer time) but in exchange future starting of the view (when you do e.g. Container.View := ViewXxx or Container.PushView(ViewXxx) will be much faster.

No functional difference should be visible, regardless of the DesignPreload value. Internally the designed component is added/removed from view at the same time. So think of this property as pure optimization – you decide whether to slow down the view constructor, or view start.

See also
DesignUrl
Load a designed user interface (from .castle-user-interface file) when this view is started.
Public class property Current: TCastleView read GetCurrent write SetCurrent; deprecated 'use Container.View';

Warning: this symbol is deprecated: use Container.View

generic function DesignedComponent<T: TComponent>(const ComponentName: String): T; overload;

Public class property CurrentTop: TCastleView read GetCurrentTop; deprecated 'use Container.FrontView';

Warning: this symbol is deprecated: use Container.FrontView

 
Public class property StateStack [constIndex:Integer]: TCastleView read GetStateStack; deprecated 'use Container.ViewStack';

Warning: this symbol is deprecated: use Container.ViewStack

 
Published property FullSize default true;

TCastleView 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.16.0.