At the request of a reader of my blog, this article examines in detail the algorithm for displaying the application launch progress.

In the article “Border Splash Screen” I talked about how to create a splash form that should appear before the main form and remain on the screen for some time. And at this very time, the program performs actions related to preparation for work: initializes variables and arrays, loads graphic resources – in a word, prepares the application for work. This is especially in demand for complex applications in which forms are created or modified using scripts. This technique is also used in projects that use the MySQL DBMS, since it becomes possible to initialize components before the main form is launched.

The splash form is created by scripts. It contains a background image (1), the project name with a shadow (2), the version number (3), and a launch progress bar (4).

Splash.pas

The “Developer’s Handbook” project uses a modular structure – scripts required to perform a certain class of tasks are stored in separate files – modules. The splash.pas module contains the function for creating a splash form Splash_Create() and the procedure for displaying a form with information about the loading progress Splash_ShowProgress(), as well as constants that determine the operation of the splash form.

The Splash.pas module references other modules: AppConstVar.pas, Label.pas and License.pas, which are located in the Script\VClass folder. That is, to use this module, your project must also contain modules associated with it. In addition to explicit references, there are implicit dependencies on other modules, such as System\Utils.pas. All source files of the project can be found in the program description.

Below is the module’s table of contents with detailed comments and links to related modules.

// Splash
// 05.05.2023
// function Splash_Create:TForm; - create a splash form
// procedure Splash_ShowProgress( AText: string ); - display loading progress

uses 'VClass\AppConstVar.pas', 'VClass\Label.pas', 'VClass\License.pas';

const
  SPLASH_FORM_NAME = 'frmSplash'; // form name
  SPLASH_DELAY_MIN = 2000; // minimum splash display time;
  SPLASH_LOGO_WIDTH = 350; // Image width
  SPLASH_LOGO_HEIGHT = 169; // Image height
  SPLASH_BORDER = 2; // image border thickness
  SPLASH_BORDER_COLOR = clBlack; // frame 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

procedure Splash_ShowProgress( AText: string );
// show loading progress
var
  tmpLabel: TLabel;
  tmpForm: TForm;
begin
  // find the splash form by name; this search allows you to do without a global variable
  tmpForm := GetFormByName(SPLASH_FORM_NAME); // {System\Utils.pas}
  // find the label component on the form; the search is also performed by the component name
  FindC(tmpForm,'labProgress',tmpLabel); // {System\Utils.pas}
  // write the progress text into the label caption; the space at the end is added for aesthetics,
  // since the label content is aligned to the right.
  tmpLabel.Caption := AText + ' ';
  // call the method that allows you to process all system messages;
  // this is necessary for drawing the new content of the label caption
  Application.ProcessMessages;
end;

function Splash_Create: TForm;
// create a splash form
var
  tmpImage: TImage;
  tmpLabel: TLabel;
  tmpImageFileName: string;
begin
  Result := TForm.Create(Application);
  with Result do 
  begin 
    Name := SPLASH_FORM_NAME;
    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 an 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 not found ' + tmpImageFileName) else Picture.LoadFromFile(tmpImageFileName);
  end;
  // add a 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;
    // the application name is taken from the constant APP_NAME
    Caption := APP_NAME; // {VClass\AppConstVar.pas}
    Font.Size := SPLASH_FONT_SIZE;
    Font.Color := SPLASH_FONT_COLOR
  end;
  // add shadow
  Label_AddShadow(tmpLabel, SPLASH_SHADOW_COLOR); // {VClass\Label.pas}
  // add version number
  tmpLabel := TLabel.Create(Result);
  with tmpLabel do
  begin
    Name := 'AppVer';
    Parent := Result;
    // the version number is associated with the licensing system; in addition to the number, it may contain a demo version flag
    Caption := License_GetVersion; // {VClass\License.pas}
    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;
  // add a shadow Label_AddShadow(tmpLabel, SPLASH_SHADOW_COLOR, 1); // {VClass\Label.pas} // add a progress line tmpLabel := TLabel.Create(Result);
  with tmpLabel do 
  begin Name := 'labProgress';
    Parent := Result;
    Caption := '';
    StyleElements := 0;
    Font.Size := SPLASH_VER_FONT_SIZE;
    Font.Color := SPLASH_VER_FONT_COLOR;
    Align := alBottom;
    Alignment := taRightJustify;
    Layout := tlTop;
    Autosize := False;
    Height := Height + Height div 2;
  end;
end;

begin 
end.Code language: Delphi (delphi)

Usage

The chronology of running an application created in My Visual Database looks like this:

#ProcessTime
1Loading an application (*.exe) into memoryUnchanged. Depends on computer performance
2Loading graphic resources (graphics.dll) into memoryDepends on the number of static images in the project – the size of the graphics.dll file
3Create all forms created in the form designer (forms.xml)Depends on the number and complexity of the project forms – the size of the forms.xml file.
4Initialization of scriptsExecution of code written in the begin end section of the script.pas module and all connected modules
5Connecting to a databaseDepends on the number of TdbComboBox components.
6Display authentication and authorization form (optional)The form is displayed if the standard access rights setting system is enabled.
7Displaying the main form

Thus, the splash screen can be displayed either in stage 4 or in stage 7. In the example below, the splash screen is displayed in the main form’s display stage, in the OnShow event handler.

The frmMain_OnShow procedure contains many conventions related to the operation of the application, but you need to pay attention to the sequence of calls to the Splash_Create() function and the Splash_ShowProgress() method.

procedure frmMain_OnShow(Sender: TObject; Action: string);
// display the main form
var
  tmpForm: TForm;
  tmpStartTime: TTime;
  tmpSleepTime: integer;
begin
  //
  tmpForm := Splash_Create;
  tmpForm.Show;
  Application.ProcessMessages;
  tmpStartTime := Time();
  //
  // all sorts of initialization should be here, which can take a long time
  App_InitSystemVar; // initialization of system variables
  Resource_Init();
  Resource_CreateMenu(frmMain);
  Images_Init; // initialization of the image loading subsystem
  Style_Init; // style activation
  Style_AddToMenu(frmMain);
  UserApp_InitForm; // create forms
  UserApp_InitVar; // set up variables
  CodeHL_Init; // set up code highlighting
  // initialization that occurs after setting up variables
  CE_Init(MainForm); // the main menu is on the main form, but is displayed where needed
  Hotkey_Init(MainForm); // enable hotkeys
  UserApp_InitBase; // initialize the database
  //
  // guaranteed delay, but no more than specified in APP_SPLASH_DELAY
  tmpSleepTime := SPLASH_DELAY_MIN - Trunc((Time() - tmpStartTime) * 24 * 60 * 60 * 1000);
  if tmpSleepTime > 0 then
    Sleep(tmpSleepTime);
  tmpForm.Close;
  tmpForm.Free;
  //
  // display the program name and version in the main form header
  frmMain.Caption := R('APP_NAME', APP_NAME) + ' ' + License_GetVersion;
  frmMain.pgcMain.ActivePage := frmMain.tshNavigation; // open the "Navigation" tab
  frmMain.pgcNavigation.ActivePage := frmMain.tshTask; // open the "Tasks" tab
end;

procedure UserApp_InitForm;
// initializing forms
var
  tmpItem: TMenuItem;
begin
  try
    Splash_ShowProgress('Initializing forms...' );
    // App_SetDoubleBuffer;
    UserApp_AddFrame;
    // adding splitters and alignment
    Splitter_Create(frmMain.panUnit, frmMain.pgcUnitProc, alTop); //
    Splitter_Create(frmMain.panProject, frmMain.pgcProject, alLeft); //
    Splitter_Create(frmMain.panClass, frmMain.pgcClass, alLeft); // classes
    Splitter_Create(frmMain.panType, frmMain.pgcType, alLeft); // types
    Splitter_Create(frmMain.panFunction, frmMain.pgcFunction, alLeft); // functions
    Splitter_Create(frmMain.pgcMethodParamView, frmMain.panMethod, alBottom); // methods
    Splash_ShowProgress('Creating forms...' );
    DTF_CreateForms; // load forms from form settings file
    Splash_ShowProgress('Preparing...' );
    frmMain.panProjectToolbar.Align := alBottom;
    // static forms
    Form_ShowOnWinControl(frmSearchResult, frmMain.tshSearchResult);
    Form_ShowOnWinControl(frmExampleView, frmMain.tshExample);
    //
    UserApp_InitAboutForm;
  except 
    RaiseException('UserApp_InitForm() ' + ExceptionMessage);
  end;
end;

procedure DTF_CreateForms;
// creating dynamic forms from the list located in the dforms.ini file 
var 
  tmpIniFile: TIniFile;
  tmpFormList: TStringList;
  i:integer;
  tmpForm: TForm;
  tmpSName: string;

  function S(AName: string): string;
  begin 
    Result := tmpIniFile.ReadString(tmpFormList.Strings(i), AName, '');
  end;

begin 
  try 
    tmpIniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + DTF_FORMS_INI_FILE);
    tmpFormList := TStringList.Create;
    tmpIniFile.ReadSections(tmpFormList);
    for i := 0 to tmpFormList.count - 1 do 
    begin 
      Splash_ShowProgress('Creating a form for the table '+ S('table') );
      tmpSName := 'DTF_' + UpperCase(tmpFormList.Strings(i)) + '_CAPTIONS'; // resource format tmpForm := DTF_Create(tmpFormList.Strings(i), S('table'), S('fields'), R(tmpSName, S('captions')), S('sort'), S ('parentField'), S('masterField'), S('filter'), S('isDetail') = 'True');
      Form_ShowOnWinControl(tmpForm, TWinControl(FindCF(S('parentControl'))));
    end;
    tmpFormList.Free;
    tmpIniFile.Free;
  except 
    RaiseException('DTF_CreateForms() - ' + ExceptionMessage);
  end;
end;
Code language: Delphi (delphi)

Line 9 creates the splash form and then makes it visible (line 10). Various actions are performed next, and where the delays are really long, calls to the Splash_ShowProgress() procedure are used: lines 48, 58, 60, and 93. After the initialization is complete, the splash form is destroyed (lines 33-34).

Display time

The splash screen display time is set by the constant SPLASH_DELAY_MIN. This is the minimum guaranteed display time in milliseconds. Of course, if the actual initialization time is longer than the declared one, the splash screen will be displayed until the initialization is complete.

Line 12 remembers the splash screen display start time, and after initialization, it is compared with the current one and, if necessary, an additional delay is made (lines 30-32) to ensure the specified splash screen display time.

Leave a Reply

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