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 [const Index: 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;

This item has no description.

Protected function ContainerRect: TRectangle; override;

This item has no description.

Protected function ContainerSizeKnown: boolean; override;

This item has no description.

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

This item has no description.

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;

This item has no description.

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

This item has no description.

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;

This item has no description. Showing description inherited from TCastleUserInterface.Press.

Override this method to react to user pressing a key, mouse button or mouse wheel. Return True if the event was handled, which prevents from passing this event to other UI controls.

When implementing in descendants it is best to override it like this:

function TMyControl.Press(const Event: TInputPressRelease): boolean;
begin
  Result := inherited;
  if Result then Exit; // exit if ancestor already handled this event

  if Event.IsKey(keyEnter) then
  begin
    // do something in reaction to Enter key
    ...
    // let engine know that this input event was handled
    Exit(true);
  end;

  if Event.IsMouseButton(buttonLeft) then
  begin
    // do something in reaction to left mouse button press
    ...
    // let engine know that this input event was handled
    Exit(true);
  end;
end;

These events are generated for all UI controls, whether they are considered "interactive" or not. These events are generated for non-interactive controls like TCastleRectangleControl or TCastleLabel as well. For example, these events ignore the TCastleButton.Enabled state, they are generated always (see https://github.com/castle-engine/castle-engine/issues/413 ). Use instead TCastleButton.OnClick to detect clicks on a button in a way that honors the TCastleButton.Enabled state.

When a control returns True from Press, it means it starts to "capture" subsequent mouse events: subsequent mouse moves and release will be send to this control even if mouse will move outside of this control.

The events Press and Release are passed to the parent only after the children had a chance to process this event. Overriding them makes sense if you draw something that "looks clickable" in TCastleUserInterface.Render, which is the standard place you should draw stuff. For example our TCastleButton draws there.

In contrast, the events PreviewPress and PreviewRelease are passed first to the parent control, before children have a chance to process this event. In partcular, overriding them makes sense if you draw something that "looks clickable" in TCastleUserInterface.RenderOverChildren.

Public function Release(const Event: TInputPressRelease): boolean; override;

This item has no description. Showing description inherited from TCastleUserInterface.Release.

Override this method to react to user releasing a key, mouse button. Return True if the event was handled, which prevents from passing this event to other UI controls.

This is counterpart to Press method. See Press for more details.

Note: We'd like this method to also be called when user releases a mouse wheel. But currently releasing of the mouse wheel is not reported now by any backend. Only releasing of keys and mouse buttons is reported.

Public function Motion(const Event: TInputMotion): boolean; override;

This item has no description. Showing description inherited from TCastleUserInterface.Motion.

Motion of mouse or touch.

Public procedure Update(const SecondsPassed: Single; var HandleInput: boolean); override;

This item has no description. Showing description inherited from TCastleUserInterface.Update.

Control may do here anything that must be continuously repeated. E.g. camera handles here falling down due to gravity, rotating model in Examine mode, and many more.

This method may be used, among many other things, to continuously react to the fact that user pressed some key (or mouse button). For example, if holding some key should move some 3D object, you should do something like:

if HandleInput then
begin
  if Container.Pressed[keyArrowRight] then
    Transform.Position := Transform.Position + Vector3(SecondsPassed * 10, 0, 0);
  HandleInput := false;
end;

Instead of directly using a key code, consider also using TInputShortcut that makes the input key nicely configurable. See engine tutorial about handling inputs.

Multiplying movement by SecondsPassed makes your operation frame-rate independent. Object will move by 10 units in a second, regardless of how many FPS your game has.

The code related to HandleInput is important if you write a generally-useful control that should nicely cooperate with all other controls, even when placed on top of them or under them. The correct approach is to only look at pressed keys/mouse buttons if HandleInput is True. Moreover, if you did check that HandleInput is True, and you did actually handle some keys, then you have to set HandleInput := false. This will prevent the other controls (behind the current control) from handling the keys (they will get HandleInput = False). And this is important to avoid doubly-processing the same key press, e.g. if two controls react to the same key, only the one on top should process it.

Note that to handle a single press / release (like "switch light on when pressing a key") you should rather use Press and Release methods. Use this method only for continuous handling (like "holding this key makes the light brighter and brighter").

To understand why such HandleInput approach is needed, realize that the "Update" events are called differently than simple mouse and key events like "Press" and "Release". "Press" and "Release" events return whether the event was somehow "handled", and the container passes them only to the controls under the mouse (decided by TCastleUserInterface.CapturesEventsAtPosition). And as soon as some control says it "handled" the event, other controls (even if under the mouse) will not receive the event.

This approach is not suitable for Update events. Some controls need to do the Update job all the time, regardless of whether the control is under the mouse and regardless of what other controls already did. So all controls (well, all controls that exist, in case of TCastleUserInterface, see TCastleUserInterface.Exists) receive Update calls.

So the "handled" status is passed through HandleInput. If a control is not under the mouse, it will receive HandleInput = False. If a control is under the mouse, it will receive HandleInput = True as long as no other control on top of it didn't already change it to False.

Public procedure Render; override;

This item has no description. Showing description inherited from TCastleUserInterface.Render.

Render a control. Called only when Exists and render context is initialized.

Do not call this method. It will be automatically called by the engine when needed. It will be called when UI is part of TCastleContainer.Controls list or rendered (e.g. for off-screen rendering) by TCastleContainer.RenderControl.

You should only override this method.

See https://castle-engine.io/manual_2d_ui_custom_drawn.php for examples what you can put here.

You can depend on some OpenGL state being set before calling this method. You can depend on it being set, and you can carelessly change it. This state we set:

  • Viewport is set to include whole container.

  • Depth test is off.

  • For ancient fixed-function pipeline (see TGLFeatures.RequestCapabilities):

    • The 2D orthographic projection is always set at the beginning. Useful for 2D controls.

    • The modelview matrix is set to identity. The matrix mode is always modelview.

    • The raster position is set to (0,0). The (deprecated) WindowPos is also set to (0,0).

    • Texturing, lighting, fog is off.

Beware that GLSL RenderContext.CurrentProgram has undefined value when this is called. You should always set it, before making direct OpenGL drawing calls (all the engine drawing routines do it already, this is only a concern if you make direct OpenGL / OpenGLES calls).

Public function UIScale: Single; override;

This item has no description. Showing description inherited from TCastleUserInterface.UIScale.

UI scale of this control, derived from container (see TCastleContainer.UIScaling and EnableUIScaling).

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

This item has no description.

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

This item has no description.

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

Warning: this symbol is deprecated: use Container.PopView

This item has no description.

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

Warning: this symbol is deprecated: use Container.PopView

This item has no description.

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

Warning: this symbol is deprecated: use Container.ViewStackCount

This item has no description.

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

This item has no description.

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

Warning: this symbol is deprecated: use Container.ViewStack

This item has no description.

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-snapshot.