A single click is sometimes enough to launch an application. But sometimes the startup time is longer than the expected one or two seconds, and then the user may have the feeling that the program has frozen and something has gone wrong. This can be avoided by reducing the time until the first window appears, which is called the splash screen.

To do this, all actions to prepare the program for work are performed after the splash screen is displayed. And the screensaver itself becomes the boundary separating the gray everyday life of a Windows user from the fantasy world of your application.

Visual part

The splash screen is the application’s calling card. There are no uniform rules for its design and cannot be. And if it is necessary to adhere to certain rules and conventions when designing the design of the main application, then when displaying the splash screen, you can not restrain yourself and allow yourself to show your creative talents to the fullest: use bright palettes or black and white sketches, add animation or make the splash screen translucent, refuse from a rectangular shape. But, if you are used to restraint and economical use of resources, I can offer a solution that uses scripts and an image file to implement it.

The image in the Images\Logo folder is used as the background image, and the name of the application is displayed over the background. The title has a black shadow that improves the readability of the text. The version number of the program is displayed in the lower left corner. Since the application initialization time depends on the performance of the computer, it is desirable to display the splash screen for the time specified in the setting, but no more than.

Scripts

The Splash_Create() function is used to create a splash form. It returns the generated form as a result. Creation parameters have been moved to constants.

const
  SPLASH_LOGO_WIDTH = 350; // Image Width
  SPLASH_LOGO_HEIGHT = 169; // Image Height
  SPLASH_BORDER = 2; // thickness of the frame around the image
  SPLASH_BORDER_COLOR = clBlack; // border color
  SPLASH_FILE_NAME = 'Images\Logo\logo_350x169.jpg'; // background image file
  SPLASH_FONT_SIZE = 28; // font size for title
  SPLASH_FONT_COLOR = clWhite; // color for title
  SPLASH_SHADOW_COLOR = clBlack; // shadow color
  SPLASH_VER_FONT_SIZE = 11; // font size for version
  SPLASH_VER_FONT_COLOR = clWhite; // font color for version
  SPLASH_DELAY_MIN = 2000; // minimum splash screen display time;
  // other project constants
  APP_NAME = 'Intro'; // Project name
  APP_VERSION = '1.0'; // project version
  LABEL_SHADOW_SHIFT_DEF = 2; // default shadow shift
  SX_SHADOW = '_Shadow'; // shadow
  APP_NAME = 'Splash'; // Project name
  APP_VERSION = '1.0'; // Project version

function Splash_Create:TForm;
var
  tmpImage:TImage;
  tmpLabel:TLabel;
  tmpImageFileName:string;
begin
  Result := TForm.Create(Application);
  with Result do
  begin
    Width := SPLASH_LOGO_WIDTH + SPLASH_BORDER*2;
    Height := SPLASH_LOGO_HEIGHT + SPLASH_BORDER*2;
    BorderStyle := bsNone;
    Position := poScreenCenter;
    Color := SPLASH_BORDER_COLOR;
    StyleElements := 0;
  end;
  // add image
  tmpImage := TImage.Create(Result);
  with tmpImage do
  begin
    parent := result;
    Width := SPLASH_LOGO_WIDTH;
    Height := SPLASH_LOGO_HEIGHT;
    Top := SPLASH_BORDER;
    Left := SPLASH_BORDER;
    tmpImageFileName := ExtractFilePath(Application.ExeName)+SPLASH_FILE_NAME;
    if not FileExists(tmpImageFileName) then
      RaiseException('Splash_Create() File '+tmpImageFileName not found)
    else
     Picture.LoadFromFile(tmpImageFileName);
  end;
  // add title
  tmpLabel := TLabel.Create(Result);
  with tmpLabel do
  begin
    Name := 'AppName';
    parent := result;
    top := 0;
    Left := 0;
    Autosize := False;
    Width := Result.ClientWidth;
    Height := Result.ClientHeight;
    Alignment := taCenter;
    Layout := tlCenter;
    WordWrap := True;
    StyleElements := 0;
    Caption := APP_NAME;
    Font.Size := SPLASH_FONT_SIZE;
    Font.Color := SPLASH_FONT_COLOR
  end;
  Label_AddShadow( tmpLabel, SPLASH_SHADOW_COLOR ); // add shadow
  // add version number
  tmpLabel := TLabel.Create(Result);
  with tmpLabel do
  begin
    Name := 'AppVer';
    parent := result;
    Caption := APP_VERSION;
    StyleElements := 0;
    Font.Size := SPLASH_VER_FONT_SIZE;
    Font.Color := SPLASH_VER_FONT_COLOR;
    Left := Height div 2;
    Top := Result.ClientHeight - Height - (Height div 2);
  end;
  Label_AddShadow( tmpLabel, SPLASH_SHADOW_COLOR, 1 ); // add shadow
end;

Code language: Delphi (delphi)

Let’s take a closer look at it. Line sixty-third calls the Label_AddShadow() procedure, which adds a shadow to the specified text.

A shadow is an exact copy of a label component, usually with black text color (any color specified, darker than the main one). It is located with a slight offset behind the given mark. For unknown reasons, MVDB does not support the Assign() method for the TLabel component, so a lot of code had to be written to obtain a copy:

procedure Label_AddShadow( ALabel:TLabel; AColor:TColor = 0; AShift: integer = 0 );
var
  tmpLabel:TLabel;
begin
  if AShift = 0 then // if the shadow shift is not specified, then we take the default value
    AShift := LABEL_SHADOW_SHIFT_DEF;
  tmpLabel := TLabel.Create( ALabel.Owner );
  // tmpLabel.Assign( ALabel ); // Unfortunately, this method does not work in MVDB :(, so you will have to copy all the properties manually....
  // everything below can be replaced with a single Assign command, but it doesn't work in MVD...
  tmpLabel.Parent := ALabel.Parent;
  tmpLabel.Name := ALabel.Name + SX_SHADOW;
  tmpLabel.Caption := ALabel.Caption;
  tmpLabel.AutoSize := ALabel.AutoSize;
  tmpLabel.Width := ALabel.Width;
  tmpLabel.Height := ALabel.Height;
  tmpLabel.Anchors := ALabel.Anchors;
  tmpLabel.Font := ALabel.Font;
  tmpLabel.StyleElements := ALabel.StyleElements;
  tmpLabel.Alignment := ALabel.Alignment;
  tmpLabel.Layout := ALabel.Layout;
  tmpLabel.WordWrap := ALabel.WordWrap;
  // set shadow offset
  tmpLabel.Top := ALabel.Top + AShift;
  tmpLabel.Left := ALabel.Left + AShift;
  // set shadow color
  tmpLabel.Font.Color := AColor;
  ALabel.BringToFront; // put the title block in front of the shadow
end;
Code language: Delphi (delphi)

Initialization and launch

Before the application’s main form frmMain (its name is marked in red on the visual designer tab) is displayed, the OnShow event occurs, which we will use to create and display splash screen, as well as performing actions to initialize the application.

procedure frmMain_OnShow(Sender: TObject; Action: string);
// display the main form
begin
  // display splash screen
  App_Splash_Start;
end;Code language: Delphi (delphi)

To initialize the application with the splash screen, the App_Splash_Start() procedure is called, in which the splash form is created and displayed, and then the application initialization procedure App_Start() is called. After its completion, the time spent on initialization is measured and, if it does not exceed the specified one, then a delay is added exactly so long that the total time for displaying the splash screen corresponds to that specified in the SPLASH_DELAY_MIN constant. After that the splash screen is destroyed, then the main form frmMain is displayed.

procedure App_Splash_Start;
// initialization with splash screen
var
   tmpForm:TForm;
   tmpStartTime: TTime;
   tmpSleepTime: integer;
begin
   tmpForm := Splash_Create;
   tmpForm.Show;
   Application.ProcessMessages;
   tmpStartTime := Time();
   App_Start;
   // guaranteed delay, but no more than specified in SPLASH_DELAY_MIN
   tmpSleepTime := SPLASH_DELAY_MIN - Trunc( (Time() - tmpStartTime)*24*60*60*1000 );
   if tmpSleepTime > 0 then
     Sleep(tmpSleepTime);
   tmpForm.Close;
   tmpForm.Free;
end;Code language: Delphi (delphi)

В App_Start() происходит инициализация подсистем приложения: управление картинками кнопок, стилями, горячими клавишами и т.д..

procedure App_Start;
// start application
begin
  // there should be any initialization here, which can take a long time
end;Code language: Delphi (delphi)

Thus, in the basic package there will be two initialization procedures: with and without splash screen display. This will allow flexibility in choosing the optimal solution. For lightweight applications, you can call App_Start(), and if the initialization is long, then it is better to use App_Splash_Start();

If you want to use a static splash created in the My Visual Database visual designer, then modify App_Splash_Start() to call your static form frmSplash:

procedure App_Splash_Start;
// initialization with splash screen
var
  tmpStartTime: TTime;
  tmpSleepTime: integer;
begin
  frmSplash.Show;
  Application.ProcessMessages;
  tmpStartTime := Time();
  App_Start;
  // guaranteed delay, but no more than specified in SPLASH_DELAY_MIN
  tmpSleepTime := SPLASH_DELAY_MIN - Trunc( (Time() - tmpStartTime)*24*60*60*1000 );
  if tmpSleepTime > 0 then
    Sleep(tmpSleepTime);
  frmSplash.Hide;
end;Code language: Delphi (delphi)

Links

Afterword

Unfortunately, the proposed solution is not 100% effective, and in some cases, the delay between clicking on the application launch icon in the operating system explorer and the splash screen appears can be several seconds. Why is this happening? To answer this question, you need to analyze the sequence of events and timings.

  1. Click on the app icon in explorer
  2. Loading an application into memory
  3. Creating Application Service Forms
  4. Reading the forms.xml file and creating custom application forms
  5. Starting the begin end section. in the script.pas file and other modules.
  6. If the built-in rights system is used, then the built-in authorization form is displayed
  7. Database connection.
  8. Connecting data to visual components.
  9. Displaying the main form of the application

Despite the solid size of the executable file (19Mb), most of the time is spent not on loading it into memory, but on creating user forms (st. 4) and connecting data to visual components (st. 8). By moving the creation and display of the splash screen form to step 5, this delay can be eliminated, but it is not possible to cope with step 4. And with a large number of forms and components on the forms, the creation time can be several seconds, which I observe in some of my projects.

I see three ways to reduce splash screen delay time.

Standalone application

A simple solution that requires two folders to implement: the main project folder and the startup (starter) project folder. Despite the bulkiness (the solution will include two identical executable files of 19MB each plus a bunch of service files for each project), it copes with the task quite effectively: the starter application contains only one form (splash screen), starts quickly and starts the main application. And after detecting the form display of the main application, it self-destructs.

Dynamic forms

A solution that is difficult in terms of implementation and development of the project, which involves programmatic creation of forms, which reduces the effectiveness of the visual development environment, turning it into a non-visual one. In practice, you can combine visual and programmatic creation of the form and components, implementing repetitive standard solutions using a script.

Refinement of My Visual Database

To do this, you need to add the ability to write a handler to an event that occurs before creating custom user forms in MVDB. But at the moment, the development of the project is suspended for an indefinite period.

Leave a Reply

Your email address will not be published. Required fields are marked *