Recent

Author Topic: TMyStatusBar with ProgressBar whatever - Statusbar for everyone for everybody!  (Read 5559 times)

kefealo

  • New Member
  • *
  • Posts: 15
Hello!

Many developers are looking for a way to add a TProgressBar to a TStatusBar, display an image, or customize its colors. Well, I got tired of searching... so I created one!

TMyStatusBar is an advanced, custom-drawn status bar component for Lazarus, built from the ground up using TCustomControl. It provides a modern, flexible, and feature-rich alternative to the standard TStatusBar, offering extensive customization options for each panel.
This component was designed to be a complete, self-contained solution, handling all its drawing internally without relying on wrapping a standard TStatusBar.
Features: Multiple Panel
Styles: Each panel can be individually configured to display different types of information.
 - psText: Displays standard text with custom font and color for each panel.
 - psProgress: A highly customizable progress bar with several animation styles.
 - psImage: Displays an image from a TPicture property.
 - psKeyIndicator: Shows the status of keyboard keys like Caps Lock, Num Lock, etc.
 - psOwnerDraw: Allows for completely custom drawing.

Advanced Progress Bar: The psProgress style offers much more than a simple bar:
Position, Min, Max, and Step properties, just like a standard TProgressBar.
Animated Marquee Styles:
 - pbstNormal: A standard progress bar.
 - pbstMarquee: A scrolling block for indeterminate processes.
 - pbstMarqueeBounce: A "bouncing ball" animation.
 - pbstMarqueeKITT: A "Knight Rider" scanner effect with a fading trail. :-)

Full Customization:
 - Set the background Color for the entire status bar or for each panel individually.
 - Set the Font (including text color, bold, italic, etc.) for each panel.
 - Customize the color of the panel separators with the SeparatorColor property.
 - Customize the color of the progress bar itself with the ProgressBarColor property for each panel.
 - Enable or disable the SizeGrip at the corner.

How to Use
Installation: After installing the component package, you will find TMyStatusBar on the 'MyPack3' tab of the component palette.

Placement: Drop a TMyStatusBar component onto your form. It will automatically align to the bottom.

Adding Panels:
Select the TMyStatusBar component.
In the Object Inspector, find the Panels property and click the (...) button to open the collection editor.
Click "Add" to create new panels.
Customizing Panels:
 - Select a panel in the collection editor.
 - In the Object Inspector, you can now set its Width, Text, and all the custom properties like Style, ProgressStyle, Color, Font, etc.
 - Changes are visible in the form designer at design-time.

This component is designed to be a versatile and visually appealing replacement for the standard status bar, giving developers full control over its appearance and functionality.

Updates by wp:

I am attaching a version of your unit, along with a small test project, in which some possible short-comings related to LCL-scaling are avoided
  • Introduced an imagelist to provide the images for the psImage-style panels; the old Picture property is kept alternatively.
  • Some hard-coded lengths were replaced by variables calculated for the current pixel density (p8 = 8px at 96ppi --> 12px at 144ppi)
  • I removed the newly declared AutoSize property. Its functionality already exists in TControl and needs not be implemented again. It is sufficient to just override the CalculatePreferredSize method. I refined this method to consider also the size of the images and the text height specific for individual panels having a different font than the statusbar itself. I assumed that the psText panels are drawn with the panel.Font, but maybe this is wrong - maybe this should apply to the psCustom panels.

The provided project was written at 96 ppi. It looked reasonable also at 144 ppi, including scaling of the image panel.

UPDATED ON 2025-09-15 - small modifications

The latest version is: Here

« Last Edit: September 17, 2025, 04:54:51 pm by kefealo »

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4631
  • I like bugs.
Sounds like a useful component. However you should consider changing its name to something more unique. The "My..." prefix is used in many experimental components by many people.
Once your component is tested by few people, it could be added to OPM (with a new unique name).

I personally cannot test now. I am in a cottage with a tablet computer. Will test later.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

wp

  • Hero Member
  • *****
  • Posts: 13195
I played with the component a bit and found it very interesting. I agree with Juha that the "My" prefix should be replaced by something else. Your initials maybe ("TZStatusbar")? Or "Flex" ("flexible")? Or "Adv" ("advanced" - but I think this is already used somewhere...).

Looking through the code I found some places which I would recommend to change:
  • Removing the units "windows" and "messages" helps to make the component cross-platform (unit "Themes" is not needed either). Add unit LCLIntf (in addition to LCLType) to make some funtionality of the windows unit available in a cross-platform way. The funtion GetKeyboardState, however, is not available in LCLIntf. With a small adaption in your code you can replace it by GetKeyState:
Code: Pascal  [Select][+][-]
  1. const
  2.   KEY_INDICATORS: array[TMyKeyIndicatorType] of string = ('CAPS', 'NUM', 'SCROLL', 'INS');
  3.   ...
  4.     case Panel.Style of
  5.        ...
  6.        psKeyIndicator:
  7.         begin
  8.           case Panel.KeyIndicator of
  9.             kiCaps  : IsKeyActive := GetKeyState(VK_CAPITAL) = 1;
  10.             kiNum   : IsKeyActive := GetKeyState(VK_NUMLOCK) = 1;
  11.             kiScroll: IsKeyActive := GetKeyState(VK_SCROLL) = 1;
  12.             kiInsert: IsKeyActive := GetKeyState(VK_INSERT) = 1;
  13.           end;
  14.           KeyText := KEY_INDICATORS[Panel.KeyIndicator];
  15.           if IsKeyActive then
  16.             Canvas.Font.Color := clWindowText
  17.           else
  18.             Canvas.Font.Color := clGrayText;
  19.  
  20.           Canvas.TextRect(FillRect, FillRect.Left, (FillRect.Top + FillRect.Bottom - Canvas.TextHeight('Tg')) div 2, KeyText);
  21.           Canvas.Font.Assign(Self.Font);
  22.         end;
  23.  
  • The text of the key indicators is not aligned vertically with the normal status text. Use Canvas.TextOut like you do for psText or center the text manually as done above. Do the same with the psOwnerDraw case.
  • Avoid hard-coded pixel locations because your users having retina displays will see a very tiny size grip then... Instead of "Canvas.Pixels[Width-5, Height-5] := ..." use a variable p5 which you calculate as "p5 := Scale96ToFont(5)" - this uses the current pixel-per-inch of the control's Font to scale the 5 pixel-distance according to the current resolution
  • An image defined by a TPicture property is difficult to scale to a different monitor resolution. A simpler way would be to replace it by an ImageList (which has built-in scaling capabilities) and the ImageIndex of the image to be displayed.
  • For scaling the Width of a statusPanel you should override the method DoAutoAdjustLayout which is called whenever the screen resolution changes (must probably be done also for the FKnightRiderTrail):
Code: Pascal  [Select][+][-]
  1. procedure TMyStatusBar.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
  2.   const AXProportion, AYProportion: Double);  // override; in protected section
  3. var
  4.   i: Integer;
  5. begin
  6.   inherited;
  7.   if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
  8.   begin
  9.     for i := 0 to Panels.Count-1 do
  10.       Panels[i].Width := round(Panels[i].Width * AXProportion);
  11.   end;
  12. end;
  • In the constructor you should also specify the Width of the component, even if you align it to alBottom. Because otherwise the statusbar will disappear, when a user sets Align to alNone.
  • The default value, 23, of the Height will certainly cause trouble when the user changes the font size (Or goes to an operating system with larger fonts). You could add an AutoSize property. For this to work you only must override the method CalculatePreferredSize where you measure the text height and return this as argument PreferredHeight; PreferredWidth should be 0 because you probably do not intend to autosize the width also:
Code: Pascal  [Select][+][-]
  1. procedure TMyStatusbar.CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer; WithThemeSpace: Boolean);
  2. var
  3.   canv: TControlCanvas;
  4. begin
  5.   PreferredWidth:=0;
  6.   canv := TControlcanvas.Create;
  7.   try
  8.     canv.Control := Self;
  9.     PreferredHeight := canv.TextHeight('Tg') + 2*Scale96ToFont(3);  // + 3 pixels margin at top and bottom
  10.   finally
  11.     canv.Free;
  12.   end;
  13. end;


kefealo

  • New Member
  • *
  • Posts: 15
Sounds like a useful component. However you should consider changing its name to something more unique. The "My..." prefix is used in many experimental components by many people.

 ;D
will change to "Mine" :) Or sometihing like that...

Have to know the myPack started with delphi 2... with My... But it's ok! Why Not... It would be a great time to rename to "zsezo" :D

LOL!

Have a nice day!

NickyTi

  • New Member
  • *
  • Posts: 11
Code: Pascal  [Select][+][-]
  1.             pbstMarquee:
  2.               begin
  3.                 Canvas.Brush.Style := bsSolid;
  4.                 Canvas.Pen.Style := psSolid;
  5.                 Canvas.Pen.Color := clBtnShadow;
  6.                 Canvas.Rectangle(ProgRect);
  7.                 MarqueeRect := ProgRect;
  8.                 if FMarqueePos > (MarqueeRect.Right - MarqueeRect.Left) then
  9.                   FMarqueePos := -40;
  10.                 MarqueeRect.Left := ProgRect.Left + FMarqueePos;
  11.                 MarqueeRect.Right := MarqueeRect.Left + 40;
  12.                 if MarqueeRect.Right > ProgRect.Right then MarqueeRect.Right := ProgRect.Right;
  13.                 if MarqueeRect.Left < ProgRect.Left then MarqueeRect.Left := ProgRect.Left; // <-- it's need add this line
  14.                 if MarqueeRect.Left < ProgRect.Right then
  15.                 begin
  16.                   Canvas.Brush.Color := Panel.ProgressBarColor;
  17.                   Canvas.FillRect(MarqueeRect);
  18.                 end;
  19.               end;
  20.  

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4631
  • I like bugs.
It does not compile here because of Windows dependency.
This kind of component should definitely be cross-platform like LCL and the whole Lazarus are.
I will test when you make it cross-platform.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

kefealo

  • New Member
  • *
  • Posts: 15
It does not compile here because of Windows dependency.
This kind of component should definitely be cross-platform like LCL and the whole Lazarus are.
I will test when you make it cross-platform.

Based on wp suggestions, no more mswin dependency.

kefealo

  • New Member
  • *
  • Posts: 15
Hello!

First post updated! Thank you for all!

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4631
  • I like bugs.
I tested it. Very nice! Some minor notes:

- it should be
    property Align default alBottom;

- using
  DrawFlags := Default(TTextStyle);
 instead of
  FillChar(DrawFlags, SizeOf(TTextStyle), 0);
 is more clear and avoids a warning.

- wp knows more about how images should be managed.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

wp

  • Hero Member
  • *****
  • Posts: 13195
I am attaching a version of your unit, along with a small test project, in which some possible short-comings related to LCL-scaling are avoided
  • Introduced an imagelist to provide the images for the psImage-style panels; the old Picture property is kept alternatively.
  • Some hard-coded lengths were replaced by variables calculated for the current pixel density (p8 = 8px at 96ppi --> 12px at 144ppi)
  • I removed the newly declared AutoSize property. Its functionality already exists in TControl and needs not be implemented again. It is sufficient to just override the CalculatePreferredSize method. I refined this method to consider also the size of the images and the text height specific for individual panels having a different font than the statusbar itself. I assumed that the psText panels are drawn with the panel.Font, but maybe this is wrong - maybe this should apply to the psCustom panels.
The provided project was written at 96 ppi. It looked reasonable also at 144 ppi, including scaling of the image panel.
« Last Edit: August 26, 2025, 11:12:04 pm by wp »

kefealo

  • New Member
  • *
  • Posts: 15
I am attaching a version of your unit, along with a small test project, in which some possible short-comings related to LCL-scaling are avoided
  • Introduced an imagelist to provide the images for the psImage-style panels; the old Picture property is kept alternatively.
  • Some hard-coded lengths were replaced by variables calculated for the current pixel density (p8 = 8px at 96ppi --> 12px at 144ppi)
  • I removed the newly declared AutoSize property. Its functionality already exists in TControl and needs not be implemented again. It is sufficient to just override the CalculatePreferredSize method. I refined this method to consider also the size of the images and the text height specific for individual panels having a different font than the statusbar itself. I assumed that the psText panels are drawn with the panel.Font, but maybe this is wrong - maybe this should apply to the psCustom panels.
The provided project was written at 96 ppi. It looked reasonable also at 144 ppi, including scaling of the image panel.

Hey wp,

Huge thanks for jumping in and sorting that part out! You tackled the exact bit I always struggle with—trying to explain the whole sizing vs. DPI thing drives me nuts every time 😅 Super happy the component’s getting good vibes—got a bunch of messages already. Fingers crossed it keeps getting better! Big thanks to everyone!

It’ll be up in the first post too!

wp

  • Hero Member
  • *****
  • Posts: 13195
One thing that I am missing is the possibility to automatically display the long hint texts of controls over with the mouse hovers. Study the code of the standard TStatusbar to find out how this could be done (primarily add the  function TStatusbar.ExecuteAction, and from that all missing methods as notified by the compiler).

Arend041

  • New Member
  • *
  • Posts: 27
When I try to replace TStatusBar with TMySTatusBar using Change Class, I get the following error message, see attachment. And if I continue, Lazarus crashes completely.

How can I implement the new status bar without fatal errors?

PS: in a smaller, less complex program, the errors do not occur.

wp

  • Hero Member
  • *****
  • Posts: 13195
This is because your Statusbar does not provide those missing properties, ParentShowHint and SimplePanel. ParentShowHint is inherited from one of the ancestors, it probably is sufficient to add "property ParentShowHint;" to the list of published properties.

SimplePanel is specific to TStatusbar. It allows (when true) to display a text even when no panels are assigned. If you implement this feature you can provide a published SimplePanel property, and your statusbar will be even more compatible with the original one.

Arend041

  • New Member
  • *
  • Posts: 27
Perhaps I should have phrased my question differently:
Why does TMyStatusBar lack the following items: ParentShowHint and SimplePanel?
Because these items are missing, I cannot replace the standard status bar using Change Class.
I don't have the knowledge to add these elements myself, so my question is: will TMyStatusBar be further developed by its owner, kefealo, and will the missing elements be added?

 

TinyPortal © 2005-2018