How to design Toast dialog?

unit ToastLayerUI;

{this code is not run! Like this design}

interface

uses
SysUtils, Classes, CastleUIControls, CastleControls;

type
TToastLayerUI = class(TCustomControl)
private
FText: string;
FDurationMs: Integer;
FVisibleToast: Boolean;
FStartTick: Cardinal;
FLabel: TCastleLabel;
FBackground: TCastleRectangle;
procedure UpdateToast;
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure ShowToast(const Text: string; DurationMs: Integer = 3000);
procedure HideToast;
end;

var
ToastLayerUI: TToastLayerUI;

implementation

{ TToastLayerUI }

constructor TToastLayerUI.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Width := 400;
Height := 80;
Visible := False;

FBackground := TCastleRectangle.Create(Self);
FBackground.Parent := Self;
FBackground.Align := alClient;
FBackground.Fill.Color := $60000000; 
FBackground.Brush.Kind := bkSolid;

FLabel := TCastleLabel.Create(Self);
FLabel.Parent := Self;
FLabel.Align := alClient;
FLabel.Alignment := taCenter;
FLabel.Layout := tlCenter;
FLabel.WordWrap := True;
FLabel.Font.Color := clWhite;
end;

destructor TToastLayerUI.Destroy;
begin
FLabel.Free;
FBackground.Free;
inherited;

end;

procedure TToastLayerUI.Paint;
begin

end;

procedure TToastLayerUI.ShowToast(const Text: string; DurationMs: Integer = 3000);
begin
FText := Text;
FLabel.Caption := Text;
FDurationMs := DurationMs;
FStartTick := GetTickCount;
if not Visible then
begin
Visible := True;
FVisibleToast := True;
end;
end;

procedure TToastLayerUI.HideToast;
begin
if Visible then
begin
Visible := False;
FVisibleToast := False;
end;
end;

procedure TToastLayerUI.UpdateToast;
begin
if not FVisibleToast then Exit;
if GetTickCount - FStartTick >= Cardinal(FDurationMs) then
HideToast;
end;

end.

I had a look at your code, and the first issue is an architectural mismatch in how TToastLayerUI is implemented. You’re inheriting from TCustomControl, which is an operating‑system widget, and then placing TCastleLabel and TCastleRectangle inside it. These Castle components require a CGE rendering context - they only draw inside a TCastleControl, TCastleWindow, or a UI hierarchy managed by CGE. A plain TCustomControl sits completely outside the CGE rendering pipeline, so any Castle UI elements placed on it simply won’t render.

There’s also the fact that you override Paint with an empty method. That means the OS control itself draws nothing. Combined with the above, the result is a control that neither paints itself nor allows CGE to paint anything inside it.

If you want a toast implemented as a normal OS widget, you’d need to use standard LCL/VCL controls (e.g., TLabel) and draw the background yourself inside Paint, without involving Castle UI elements.

If your intention is to use Castle UI components like TCastleLabel and TCastleRectangle, then the control should be a TCastleUserInterface (or be placed inside a TCastleControl), so it participates in CGE’s rendering and update loop.

I agree with @DiggiDoggi and want to propose a possible solution:

if you are not specifically bound to the Toasts as conceptual feature, but need just some notification system, which notifies player and then message disappears, please take a look on Castle Game Engine: CastleNotifications: Class TCastleNotifications and its usage in castle-engine/examples/3d_games/explore_impressive_castle at master · castle-engine/castle-engine · GitHub

In addition to above answers:

  1. You can design your own notification UI visually.

    In the simplest case: just place the notification inside your view, and after you set everything up (rectangle, label) the way you like – make it initially not existing (set Exists to false, maybe also EditorSelectOnHover to false). This may be the simplest solution to what you need. Follow the manual about creating your views and manipulating them – when you want to show a message, just set Exists to true, change the label’s caption, and animate the color to make “fade out”.

    If you want just a single such notification to be visible at a time (which is also what Android’s “toast” term does, and what seems to be the intention) over a specific view (like a game view) then this is likely the simplest solution :slight_smile:

    For more complex usage, you can still design it visually, using the approach on Components to reuse a design in other designs | Manual | Castle Game Engine . No need to write everything using Pascal code (which led you to some mistakes, as pointed above – you probably didn’t want to use TCustomControl class at all, and you don’t want to deal with Paint method etc.). Once you have UI designed using above approach, you can instantiate it (it will be a descendant of TCastleUserInterface) and add such designed UI to your larger view (e.g. a game view). And after some timeout, remove it / fade out.

  2. A note about forum posts with long Pascal code:

    Instead of pasting long code into a forum post like this, better:

    1. Attach a working project (code and data) that reproduces your problem as a zip. In the post content, just quote particular lines.
    2. When you really want to paste long Pascal code in a forum post, use three backticks, like this:
```delphi
begin
  Writeln('hello');
end;
```

→ which will render like this:

begin
  Writeln('hello');
end;