TScrollBox without scrollbars - delphi

I want to dynamically create some buttons in a TScrollBox (that has VertScrollBar.Vissible= False).
I want to programmatically bring some of those buttons in view, so I would like to use something like:
ScrollBox.VertScrollBar.Position:= i; //Does not work
However, the box won't scroll to the indicated position unless the VertScrollBar.Vissible= True.
Note: ScrollBy() works, but I don't want to use that.
How to circumvent this behavior?
(A "solution" would be to let the scrollbars visible and hide them outside the screen (place the scrollbox in a panel))
Code:
unit UnitVert;
interface
uses
System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm3 = class(TForm)
ScrollBox1: TScrollBox;
Button1: TButton;
Button2: TButton;
procedure Button2Click(Sender: TObject);
end;
var
Form3: TForm3;
implementation {$R *.dfm}
procedure TForm3.Button2Click(Sender: TObject);
begin
ScrollBox1.VertScrollBar.Position:= -20;
//ScrollBox1.ScrollBy(0, -20); //Works
end;
end.
object Form3: TForm3
Left = 0
Top = 0
Caption = 'Form3'
ClientHeight = 336
ClientWidth = 635
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object ScrollBox1: TScrollBox
Left = 176
Top = 75
Width = 283
Height = 203
HorzScrollBar.Visible = False
VertScrollBar.Visible = False
TabOrder = 0
object Button1: TButton
Left = 188
Top = 132
Width = 123
Height = 99
Caption = 'Dummy'
TabOrder = 0
end
end
object Button2: TButton
Left = 26
Top = 50
Width = 111
Height = 51
Caption = 'Test'
TabOrder = 1
OnClick = Button2Click
end
end

Works as expected
Cannot reproduce your issue with D7 on Win7:
Scrollbox1.HorzScrollBar.Visible:= FALSE;
Scrollbox1.VertScrollBar.Visible:= FALSE;
Scrollbox1.ScrollBy( -30, -45 );
...moves the viewport 30 px to the left and 45 px to the top. Also note that the first parameter is X (horizontal) and the second parameter is Y (vertical) - actually anything I've seen in life was always in the X,Y order.
Why invisible scrollbars won't work
The method TWinControl.ScrollBy() includes this code:
IsVisible := (FHandle <> 0) and IsWindowVisible(FHandle);
if IsVisible then ScrollWindow(FHandle, DeltaX, DeltaY, nil, nil);
...which means: it is essentially using the WinAPI's ScrollWindow() function. Changing the position of one of the scrollbars executes TControlScrollBar.SetPosition(), which in turn calls .ScrollBy() again for just one dimension:
OldPos := FPosition;
if Kind = sbHorizontal then
FControl.ScrollBy(OldPos - Value, 0) else
FControl.ScrollBy(0, OldPos - Value);
...and since this time the parent control is the scrollbar (not the scrollbox) its invisibility prevents the WinAPI function from being called. Content wise there's no gain in using scrollbars - they just conveniently remember what you already scrolled.

Related

Delphi-docking form hides panel

I have a problem that docking form hides some other components inside of panel.
This is simple example of my problem: I have Panel1 with DockSite set to True. Inside Panel1 is Panel2. Panel2 alignts to client (Align=alClient). Inside Panel2 is memo field that is also aligned to client. It overlaps the entire Panel1.
I have another form (Form2) that I want to dock to Panel1. But it overlaps entire Panel1 and hides memo filed. I want to overlap only part of Panel1 (width of the form) and move Panel2 to the rigth or to te left.
Main form:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Panel1: TPanel;
Panel2: TPanel;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses Unit2;
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Show;
end;
end.
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 333
ClientWidth = 754
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
PixelsPerInch = 96
TextHeight = 15
object Button1: TButton
Left = 288
Top = 271
Width = 153
Height = 41
Caption = 'Show dockable form'
TabOrder = 0
OnClick = Button1Click
end
object Panel1: TPanel
Left = 8
Top = 8
Width = 729
Height = 257
Caption = 'Panel1'
DockSite = True
TabOrder = 1
object Panel2: TPanel
Left = 1
Top = 1
Width = 727
Height = 255
Align = alClient
Caption = 'Panel2'
TabOrder = 0
ExplicitLeft = 360
ExplicitTop = 8
ExplicitWidth = 337
ExplicitHeight = 241
object Memo1: TMemo
Left = 1
Top = 1
Width = 725
Height = 253
Align = alClient
Lines.Strings = (
'Memo1')
TabOrder = 0
ExplicitWidth = 216
ExplicitHeight = 144
end
end
end
end
Docking form:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
Label1: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
end.
object Form2: TForm2
Left = 0
Top = 0
Caption = 'Form2'
ClientHeight = 103
ClientWidth = 273
Color = clBtnFace
DragKind = dkDock
DragMode = dmAutomatic
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
PixelsPerInch = 96
TextHeight = 15
object Label1: TLabel
Left = 22
Top = 24
Width = 227
Height = 45
Caption = 'Dockable form'
DragKind = dkDock
DragMode = dmAutomatic
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -33
Font.Name = 'Segoe UI'
Font.Style = []
ParentFont = False
end
end
The dynamics soon become difficult to master with changing dock sites and alignment changes, but I think the following will do what you ask for.
Change the hierarchy of Form1 to the following and note that Panel2 and Memo1 both are childs of Panel1. Panel2 will act as the docking target. I have set Panel2.Width to 8, to have a visual area where to drop Form2. Here are the essential properties:
object Form1: TForm1
object Button1: TButton
object Panel1: TPanel
Caption = 'Panel1'
object Panel2: TPanel
Width = 8
Align = alRight
Caption = 'Panel2'
DockSite = True
OnDockDrop = Panel2DockDrop
OnDockOver = Panel2DockOver
OnUnDock = Panel2UnDock
end
object Memo1: TMemo
Align = alClient
end
end
end
When Form2 is dragged over Panel2 the OnDockOver event is triggered. Panel2 sets its width to half the width of Panel1, which in turn reduces the Memo1 width with the same amount. (Change as you need)
procedure TForm1.Panel2DockOver(Sender: TObject; Source: TDragDockObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Panel2.Width := Panel1.Width div 2;
end;
When Form2 is dropped on Panel2, the DockRect of the form is set to the rect of Panel2.
procedure TForm1.Panel2DockDrop(Sender: TObject; Source: TDragDockObject; X, Y: Integer);
begin
Source.DockRect := Rect(Panel2.Width, Panel2.Top, Panel2.Width, Panel2.Height);
end;
When Form2 is undocked from Panel2 it reduces its width to the 8 pixels, which again widen the Memo1 to its original width.
procedure TForm1.Panel2UnDock(Sender: TObject; Client: TControl; NewTarget: TWinControl;
var Allow: Boolean);
begin
Panel2.Width := 8;
end;
Here's the complete .dfm of Form1:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 333
ClientWidth = 425
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
OldCreateOrder = True
PixelsPerInch = 96
TextHeight = 15
object Button1: TButton
Left = 120
Top = 284
Width = 153
Height = 41
Caption = 'Show dockable form'
TabOrder = 0
OnClick = Button1Click
end
object Panel1: TPanel
Left = 8
Top = 8
Width = 400
Height = 257
Caption = 'Panel1'
TabOrder = 1
object Panel2: TPanel
Left = 391
Top = 1
Width = 8
Height = 255
Align = alRight
Caption = 'Panel2'
DockSite = True
TabOrder = 0
OnDockDrop = Panel2DockDrop
OnDockOver = Panel2DockOver
OnUnDock = Panel2UnDock
ExplicitLeft = 394
end
object Memo1: TMemo
Left = 1
Top = 1
Width = 390
Height = 255
Align = alClient
Lines.Strings = (
'Memo1')
TabOrder = 1
ExplicitWidth = 725
ExplicitHeight = 253
end
end
end

VCL style theme cutting form borders when maximized

Delphi version: 10.1 update 2 (Berlin)
When applying a vcl theme to an application. When maximizing the form, it removes the borders causing border-aligned objects like: labels, menus, buttons to have a "cut" part.
In the example in question, the theme has a 6-pixel border (left, right, bottom).
You may also notice that the title bar appears with the top "cut off".
Is there any way to get around this situation, and make sure that the measures of borders and title bar are respected as specified in the theme?
Normal Window
Maximized Window
Unit1.pas
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.Menus;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
este1: TMenuItem;
este2: TMenuItem;
este3: TMenuItem;
este4: TMenuItem;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
end.
Unit1.dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 441
ClientWidth = 704
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
Menu = MainMenu1
OldCreateOrder = False
Position = poScreenCenter
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 0
Top = 0
Width = 704
Height = 13
Align = alTop
Caption = 'Novo Teste'
end
object Label2: TLabel
Left = 0
Top = 428
Width = 704
Height = 13
Align = alBottom
Caption = 'Novo Teste'
end
object Label3: TLabel
Left = 0
Top = 13
Width = 704
Height = 13
Align = alTop
Alignment = taRightJustify
Caption = 'Novo Teste'
end
object Label4: TLabel
Left = 0
Top = 415
Width = 704
Height = 13
Align = alBottom
Alignment = taRightJustify
Caption = 'Novo Teste'
end
object MainMenu1: TMainMenu
Left = 384
Top = 152
object este1: TMenuItem
Caption = 'Teste'
object este2: TMenuItem
Caption = 'Teste'
end
object este3: TMenuItem
Caption = 'Teste'
end
object este4: TMenuItem
Caption = 'Teste'
end
end
end
end

Why do hidden forms and controls stop painting correctly after switching themes twice?

I hate the title of this question. Anyway:
If you call TForm.Show with a custom theme (Windows10 Dark in this case), then close that form, then change the theme to the system Windows theme, then change back to the Windows10 Dark theme, and finally call TForm.Show on that form again, the border renders incorrectly and certain controls do not render properly, like a TComboBox.
I have a test project below, and a "fix" of sorts. But I do not like my fix and the reason for this question is that I do not really understand what is happening here that causes the form to render incorrectly only if it was hidden while the theme changed, and only if the theme is changed away from, and then back to, Windows10 Dark.
My fix is to track the theme change. If the condition I describe above occurs, I intercept the CM_SHOWINGCHANGED message, ignore it, then force the window to be recreated and then process the inherited CM_SHOWINGCHANGED the next time around. It is a very brittle fix and obviously not the way to go, so I am hoping someone can show me what is actually happening so I can fix it "for real."
Incidentally, I have submitted this as a bug to Embarcadero already. https://quality.embarcadero.com/browse/RSP-33977
Here is the test code. You'll need to add Windows10 Dark to the application's styles, obviously.
unit Unit22;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Unit23, Vcl.Themes;
type
TForm22 = class(TForm)
Panel1: TPanel;
ComboBox1: TComboBox;
RadioGroup1: TRadioGroup;
ButtonShow: TButton;
Memo1: TMemo;
procedure ButtonShowClick(Sender: TObject);
procedure RadioGroup1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
FAllowChange: Boolean;
public
{ Public declarations }
end;
var
Form22: TForm22;
implementation
{$R *.dfm}
procedure TForm22.ButtonShowClick(Sender: TObject);
begin
Form23.Show;
end;
procedure TForm22.FormShow(Sender: TObject);
begin
if StyleServices.Name = 'Windows10 Dark' then
RadioGroup1.ItemIndex := 1
else
RadioGroup1.ItemIndex := 0;
FAllowChange := True;
end;
procedure TForm22.RadioGroup1Click(Sender: TObject);
begin
if not FAllowChange then
exit;
if RadioGroup1.ItemIndex = 0 then
TStyleManager.SetStyle('Windows');
if RadioGroup1.ItemIndex = 1 then
TStyleManager.SetStyle('Windows10 Dark');
end;
end.
Unit 22 DPR:
object Form22: TForm22
Left = 0
Top = 0
ActiveControl = Memo1
Caption = 'Form22'
ClientHeight = 305
ClientWidth = 511
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 511
Height = 305
Align = alClient
BevelEdges = []
BevelOuter = bvNone
Caption = 'Panel1'
ShowCaption = False
TabOrder = 0
object ComboBox1: TComboBox
Left = 16
Top = 8
Width = 145
Height = 21
Style = csDropDownList
ItemIndex = 0
TabOrder = 0
Text = 'one'
Items.Strings = (
'one'
'two'
'three')
end
object RadioGroup1: TRadioGroup
Left = 16
Top = 48
Width = 185
Height = 105
Caption = 'RadioGroup1'
Items.Strings = (
'windows'
'dark')
TabOrder = 1
OnClick = RadioGroup1Click
end
object ButtonShow: TButton
Left = 16
Top = 159
Width = 75
Height = 25
Caption = 'ButtonShow'
TabOrder = 2
OnClick = ButtonShowClick
end
object Memo1: TMemo
Left = 207
Top = 8
Width = 274
Height = 281
Lines.Strings = (
'Always start in dark.'
''
'Steps to reproduce:'
'1.'#9'Click ButtonShow.'
'2.'#9'Close the window that opened.'
'3.'#9'Click Windows (change to system them).'
'4.'#9'Click Dark (change back to dark VCL style).'
'5.'#9'Click ButtonShow again. The controls are '
'not properly painted. Combobox text is black and form '
'is wrong until resize.'
''
'Hacky fix:'
'1.'#9'Click ButtonShow.'
'2.'#9'Check the '#8220'Fix'#8221' button in the window that '
'opened, then close it.'
'3.'#9'Click Windows (change to system)'
'4.'#9'Click Dark (change back to vcl dark)'
'5.'#9'Click ButtonShow. See comments in source.'
'')
ReadOnly = True
TabOrder = 3
end
end
end
Unit23:
unit Unit23;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Themes;
type
TForm23 = class(TForm)
Panel1: TPanel;
ComboBox1: TComboBox;
ComboBox2: TComboBox;
ComboBox3: TComboBox;
Button1: TButton;
CheckBox1: TCheckBox;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
FFixing: Boolean;
FNeedFix: String;
FShowedStyle: String;
protected
procedure DoShow; override;
public
{ Public declarations }
procedure CMStyleChanged(var Message: TMessage); message CM_STYLECHANGED;
procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED;
end;
var
Form23: TForm23;
implementation
{$R *.dfm}
procedure TForm23.Button1Click(Sender: TObject);
begin
PostMessage(Handle, CM_RECREATEWND, 0, 0);
end;
procedure TForm23.CMShowingChanged(var Message: TMessage);
var
DoFix: Boolean;
begin
if not Showing then
inherited
else
begin
// if the theme changed away from dark, then back to dark, while we were
// not visible, then we need to force the window to be recreated again
// before showing.
// This is a really bad hack but basically I am just preventing the
// normal response to CMShowingChanged and then setting up a message
// queue that will recreate the window and then process the CM_SHOWINGCHANGED
// message again. This will probably break the universe but it appears to work
// in this test.
FShowedStyle := StyleServices.Name;
Panel1.Caption := FShowedStyle;
DoFix := not FFixing and (FNeedFix <> '') and (FNeedFix = FShowedStyle);
FNeedFix := '';
if DoFix and CheckBox1.Checked then
begin
FFixing := True;
// SendMessage(Handle, WM_SETREDRAW, Winapi.Windows.WPARAM(LongBool(False)), 0);
PostMessage(Handle, CM_RECREATEWND, 0, 0);
// PostMessage(Handle, CM_SHOWINGCHANGED, Message.WParam, Message.LParam);
// do not allow inherited.
end else
begin
FFixing := False;
inherited;
end;
end;
end;
procedure TForm23.CMStyleChanged(var Message: TMessage);
begin
FNeedFix := FShowedStyle;
inherited;
end;
procedure TForm23.DoShow;
var
DoFix: Boolean;
begin
inherited;
exit;
end;
end.
Unit23 DPR:
object Form23: TForm23
Left = 0
Top = 0
Caption = 'Form23'
ClientHeight = 253
ClientWidth = 360
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 360
Height = 253
Align = alClient
Alignment = taRightJustify
BevelEdges = []
BevelOuter = bvNone
Caption = 'Panel1'
TabOrder = 0
object ComboBox1: TComboBox
Left = 16
Top = 32
Width = 145
Height = 21
Style = csDropDownList
ItemIndex = 0
TabOrder = 0
Text = 'one'
Items.Strings = (
'one'
'two'
'three')
end
object ComboBox2: TComboBox
Left = 16
Top = 59
Width = 145
Height = 21
Style = csDropDownList
ItemIndex = 1
TabOrder = 1
Text = 'two'
Items.Strings = (
'one'
'two'
'three')
end
object ComboBox3: TComboBox
Left = 16
Top = 86
Width = 145
Height = 21
Style = csDropDownList
ItemIndex = 2
TabOrder = 2
Text = 'three'
Items.Strings = (
'one'
'two'
'three')
end
object Button1: TButton
Left = 16
Top = 136
Width = 75
Height = 25
Caption = 'RecreateWnd'
TabOrder = 3
OnClick = Button1Click
end
object CheckBox1: TCheckBox
Left = 16
Top = 167
Width = 273
Height = 17
Caption = 'Fix with CM_SHOWINGCHANGED hack'
TabOrder = 4
end
end
end

How to correctly use TGridPanel when display PPI scaling is active?

I have written a small test VCL application with a monitor PPI of 96.
The application has a TGridPanel on it with a absolute pixel sized column.
On that column I placed a TComboBox and aligned it alClient.
Here is the DFM code:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 182
ClientWidth = 514
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object GridPanel1: TGridPanel
Left = 0
Top = 0
Width = 514
Height = 182
Align = alClient
Caption = 'GridPanel1'
ColumnCollection = <
item
Value = 100.000000000000000000
end
item
SizeStyle = ssAbsolute
Value = 150.000000000000000000
end>
ControlCollection = <
item
Column = 0
Control = Button1
Row = 0
end
item
Column = 1
Control = ComboBox1
Row = 0
end
item
Column = 0
Control = Edit1
Row = 1
end>
RowCollection = <
item
Value = 50.000000000000000000
end
item
Value = 50.000000000000000000
end>
TabOrder = 0
object Button1: TButton
Left = 1
Top = 1
Width = 362
Height = 21
Align = alTop
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object ComboBox1: TComboBox
Left = 363
Top = 1
Width = 150
Height = 21
Align = alClient
TabOrder = 1
Text = 'ComboBox1'
end
object Edit1: TEdit
Left = 1
Top = 91
Width = 362
Height = 21
Align = alTop
TabOrder = 2
Text = 'Edit1'
end
end
end
and the PAS code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TForm1 = class(TForm)
GridPanel1: TGridPanel;
Button1: TButton;
ComboBox1: TComboBox;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
PPI: Integer;
begin
PPI := Integer.Parse(Edit1.Text);
GridPanel1.ScaleForPPI(PPI);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
Edit1.Text := Screen.PixelsPerInch.ToString;
end;
end.
I then changed the custom scaling factor to 125 in Windows 10 in the advanced scaling settings.
After signing off and signing on again when I run the application again the drop down button of the combo box is not visible any more.
How do you deal with this problem?
I tried to call GridPanel1.ScaleForPPI(96) which restores the drop down button on the combo box. This kind of defeats the purpose of PPI scaling though, doesn't it?
The problem is gone in Delphi 10.3.1.
So this is a bug in at least Delphi 10.1 (and possible other older versions).

How to create a 'Fake Loading Screen' in Delphi

I've been stretching my brain trying to create a loading screen in Delphi but I just can't find help anywhere.
I am creating a game for a school project and I would like to implement a form that mimics a loading screen.
I want to move a shape across the screen and I want it to leave behind a trail (imitate a progress bar). I know you use timer to smooth it's progression but I'm not sure about how to use a timer correctly with a shape.
I would appreciate it if anyone would show me what code/functions I have to use to do this.
Sincerely,
Kuzon.
To move a shape with a timer and leave a trail:
Each time the timer event fires, adjust the shape position.
The trail is also made with a shape here, by adding the width each timer tick.
unit MoveShape;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
type
TFormMoveShape = class(TForm)
Shape1: TShape;
Timer1: TTimer;
Shape2: TShape;
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FormMoveShape: TFormMoveShape;
implementation
{$R *.dfm}
const
cMoveIncrement = 2;
procedure TFormMoveShape.Timer1Timer(Sender: TObject);
begin
if (Shape1.Left + Shape1.Width < Self.ClientWidth - cMoveIncrement) then
begin
Shape1.Left := Shape1.Left + cMoveIncrement;
Shape2.Width := Shape2.Width + cMoveIncrement;
end
else
begin
Shape1.Left := 8;
Shape2.Width := 8;
end;
end;
end.
object FormMoveShape: TFormMoveShape
Left = 0
Top = 0
Caption = 'Form27'
ClientHeight = 336
ClientWidth = 635
Color = clBtnFace
DoubleBuffered = True
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Shape2: TShape
Left = 8
Top = 112
Width = 8
Height = 41
Brush.Color = clAqua
Shape = stRoundRect
end
object Shape1: TShape
Left = 8
Top = 112
Width = 137
Height = 41
Shape = stRoundRect
end
object Timer1: TTimer
Interval = 50
OnTimer = Timer1Timer
Left = 512
Top = 24
end
end

Resources