Recent

Author Topic: [New Component] ExtTabCtrl - A custom, feature-rich tab control (LGPL)  (Read 6328 times)

ovidio

  • Jr. Member
  • **
  • Posts: 68
Hi everyone,

I wanted to share a component I’ve been working on called ExtTabCtrl.

Please, note that I am not a professional programmer. This component started as a hobby project written for my own personal use, but I've decided to share it here in the hope that someone else in the community might find it useful. It has been developed to the best of my (somewhat limited) ability, and it comes with absolutely no warranty.

Indeed, this is my very first post on the forum, although I have been using it quietly for many years as an invaluable source of knowledge. So, I thank this community for all the help and insights I've found here over the years!

What is ExtTabCtrl?
It is a custom tab control component for Lazarus / Free Pascal designed to be a replacement for the standard TPageControl and TTabControl. It uses purely LCL units with no platform-specific {$IFDEF} blocks.

Some of its key features include:
  • Five Visual Styles: Supports Flat, Button, Delphi, Chrome, and macOS looks.
  • Tab Positions: The component can be positioned Top, Bottom, Left, or Right (with automatic text and image rotation for vertical tabs).
  • Close & Add Buttons: Includes per-tab close buttons (visible on active/hovered tabs) and a built-in "Add" button for adding new tabs.
  • Drag-to-Reorder: Tabs can be rearranged by dragging, with a visual drop indicator.
  • Per-Tab Customization: Each tab can have its own unique background color, accent stripe color, standalone bitmap, visibility state, and font style overrides.
  • Navigation: Fully supports mouse-wheel scrolling through overflowing tabs, keyboard arrow navigation, and optional middle-click closing.

Compatibility & Testing
Because it relies entirely on standard LCL code, it is supposed to be multiplatform across Windows, Linux, and macOS. However, because of my current setup, I have only been able to personally test it under Windows and macOS.

Feedback Welcome!
Since I haven't been able to test it on Linux (GTK2/GTK3/Qt5) myself, I would be incredibly grateful if any Linux users could drop it into a test project and let me know how it renders and behaves!

Of course, general feedback, constructive criticisms, suggestions, or bug reports from Windows and macOS users are also highly welcome. Please keep in mind that since this is a hobby project, I might not always be able to provide immediate fixes, but I will do my best.

Source Code / Download:
https://github.com/ovidiopr/ExtTabCtrl

Thank you for your time, and I hope someone finds it helpful!

Ovidio

ALLIGATOR

  • Sr. Member
  • ****
  • Posts: 457
  • I use FPC [main] 💪🐯💪
Code: [Select]
Note for application developers:
The LGPL explicitly allows this component to be linked into commercial,
proprietary, and closed-source applications without requiring
the source code of your overall application to be disclosed,
provided that modifications to the ExtTabCtrl library itself
remain open source under the LGPL.

I don't understand what you're saying about the license. Are you sure the LGPL allows that?

If so, why does FPC include a special addendum to the LGPL in its library license - one that permits static linking?
https://wiki.lazarus.freepascal.org/licensing
I may seem rude - please don't take it personally

ovidio

  • Jr. Member
  • **
  • Posts: 68
As far as I understand, it does, but I am not sure. In any case, it is my intention to allow it (maybe I will have to add also this exception).

ovidio

  • Jr. Member
  • **
  • Posts: 68
Here are some screenshots. I have added them also on the GitHub page (https://github.com/ovidiopr/ExtTabCtrl)

wp

  • Hero Member
  • *****
  • Posts: 13630
Looks nice!

Tested on Linux: the main widgetsets are working (gtk2, gtk3, qt5, qt6). In gtk3 there seems to be some text measuring issue - see attached screenshot. Maybe - since there is lot of activity in the gtk3 widgetset now - this issue is resolved by itself in a few days/weeks.

A few ideas:
- Why is there no mouse-over effect (regarding the tab background color) on the selected tab?
- The mouse-over effect of tab background appears too strong for some Style settings (tested the demo_notebook project).
- Since you are mimicking a row of tabs in a folder I would expect to see a line at the base beyond the right-most tab reaching to the right side of the control (see 2nd screenshot where I added the baseline manually)
« Last Edit: June 06, 2026, 10:30:21 pm by wp »

ovidio

  • Jr. Member
  • **
  • Posts: 68
Dear wp, thank you for testing the component under Linux!

Tested on Linux: the main widgetsets are working (gtk2, gtk3, qt5, qt6). In gtk3 there seems to be some text measuring issue - see attached screenshot. Maybe - since there is lot of activity in the gtk3 widgetset now - this issue is resolved by itself in a few days/weeks.
I would also wait, to see if this is an issue with gtk3. I use TCanvas.TextExtent, TCanvas.TextWidth and TCanvas.TextHeight to determine the space taken by the text, so it should be correct when these functions are working as expected.

- Why is there no mouse-over effect (regarding the tab background color) on the selected tab?
This was intentional, but maybe you are right, and there should be a hover effect for the active tab too. I will review the drawing of the styles.

- The mouse-over effect of tab background appears too strong for some Style settings (tested the demo_notebook project).
- Since you are mimicking a row of tabs in a folder I would expect to see a line at the base beyond the right-most tab reaching to the right side of the control (see 2nd screenshot where I added the baseline manually)
Ok, I will review these two points too.

EDIT: WP, I have already implemented your suggestions. Please, can you check that they work as expected?

In addition, there are two issues that I don't know how to solve. Maybe somebody can help me with this.
  • The tabs appear in the component tree, as expected, and they are correctly added/removed. However, when the user rearranges the Tabs at design time (using "Move Left"/"Move Right"), I have not found the way to update this new order in the component tree.
  • I have used 'csDesignInteractive' to allow the user selecting the tabs at design time, and this works as expected. However, this has the unintended result that the user cannot drag the component to a new location. In general, this is not a huge problem, since this component should generally be aligned to the Top/Left/..., but it would be nice to have it anyway.

Thanks in advance!
« Last Edit: June 07, 2026, 11:08:09 am by ovidio »

wp

  • Hero Member
  • *****
  • Posts: 13630
I like it.

Did you ever test dark mode? I did not. But I could imagine that the colors calculated for mouse-over do not look good in dark mode.

Can the hovering effect be turned off?

[EDIT]
Found a bug: The demo_notebook comes with tab close buttons turned off. When you load the project, select one of the tabs and then turn its ShowCloseButton propery ON, the close icon will appear but the width of the tab does not increase accordingly.
« Last Edit: June 07, 2026, 12:30:28 pm by wp »

ovidio

  • Jr. Member
  • **
  • Posts: 68
Did you ever test dark mode? I did not. But I could imagine that the colors calculated for mouse-over do not look good in dark mode.

No, I haven't, but it is on my TODO list. Please, if anybody tries it, share the results.

EDIT: Well, this is just a quick test, and it doesn't look that bad.
« Last Edit: June 07, 2026, 12:38:12 pm by ovidio »

ovidio

  • Jr. Member
  • **
  • Posts: 68
Found a bug: The demo_notebook comes with tab close buttons turned off. When you load the project, select one of the tabs and then turn its ShowCloseButton propery ON, the close icon will appear but the width of the tab does not increase accordingly.

Fixed.

wp

  • Hero Member
  • *****
  • Posts: 13630
Compiled the demo projects at 96ppi and ran them at 144ppi - the inner dimensions scale correctly, fine!. With one exception: the images do not scale or are scaled by the system to become very pixelated - see screenshot.

Looking at the code of the demo_simple I see that you do not scale the user-provided imagelist. I set ImageList.Scaled to true, added two image resolutions for 24x24 and 32x32 images (in the ImageList editor) and replaced the icons by similar ones from the Lazarus icon collection (folder images/general_purpose). This changed the two user-icons on two tabs but left the internal icons for left/right arrows, close-crosses and add-tab unchanged. I guess these are the ButtonImages properties - but assigning some indices of my imagelist did not change anything. So, I am afraid that something's wrong here in image handling.

The other issue is that the user-provided images do not change their size when the projects runs under 144 ppi. You need some changes to take care of the "real" image size under High-DPI:

* In function TExtTabCtrl.GetTabImageWidth use FImages.WidthForPPI[0, Font.PixelsPerInch] to measure the image width; the "0" indicates that the default width of the images (i.a. at 96ppi) should be assumed to be the Width of the ImageList, but the images can be scaled to any other base width (normally there is an extra property "ImagesWidth" next to the "Images" property in controls using an ImageList). You may also have to apply the result of CanvasScaleFactor, but I don't have experience with it since Windows does not use it (macOS does...)

* Likewise, in GetTabImageHeight use FImages.HeightForPPI[...].

* I did not dive into your drawing code which is more complicated than usual because you are caching rotated images. But basically you should always call the XXXXForPPI methods of the imagelist to get the image at the correct size for the current resolution. Example: Draw the image of index AIndex at position AX, AY - AImageWidthAt96PPI is 0 (or FImagesWidth if you'd provide it), ATargetPPI is usually taken from the font settings (Font.PixelsPerInch), ACanvasFactor is the result of GetCanvasScaleFactor:
Code: Pascal  [Select][+][-]
  1. procedure DrawForPPI(ACanvas: TCanvas; AX, AY, AIndex: Integer;
  2.       AImageWidthAt96PPI, ATargetPPI: Integer; ACanvasFactor: Double; AEnabled: Boolean = True);

* In order to extract an image for caching you should call ImageList.ResolutionForPPI[AImageWidth, APPI: Integer; const ACanvasScaleFactor: Double].GetBitmap(Index: Integer; Image: TCustomBitmap)

In the attachment there's a version of demo_simple where everything should be set up for correct scaling.

Two more remarks:
* I am missing an imageindex entry for the close button in the ButtonImages property.
* You should declare all image index properties as TImageIndex rather than integer. Then the LCL can select the corresponding property editor which displays preview images in a combobox.
« Last Edit: June 07, 2026, 03:52:23 pm by wp »

wp

  • Hero Member
  • *****
  • Posts: 13630
Found a bug: The demo_notebook comes with tab close buttons turned off. When you load the project, select one of the tabs and then turn its ShowCloseButton propery ON, the close icon will appear but the width of the tab does not increase accordingly.
Fixed.
Yes.

I don't know, maybe there are similar one in the TabOptions property: When I turn toShowAddButton off in the Object inspector, the icon at the right does not disappear.

Shouldn't the top stripe run across the entire width of the tab? And I am a bit confused about the colored line appearing above the top of the selected tab. Sometimes it is hard to be distinguished. Is it to highlight the selected tab again? Can it be turned off?

wp

  • Hero Member
  • *****
  • Posts: 13630
In gtk3 there seems to be some text measuring issue
Was able to reproduce this outside the component and reported as a bug: https://gitlab.com/freepascal.org/lazarus/lazarus/-/work_items/42352

[EDIT]
Has been fixed already. Thank you, zeljko.
« Last Edit: June 07, 2026, 11:23:04 pm by wp »

d7_2_laz

  • Hero Member
  • *****
  • Posts: 696
I am a bit confused about the colored line appearing above the top of the selected tab. Sometimes it is hard to be distinguished. Is it to highlight the selected tab again? Can it be turned off?

I'm actually using PageControl combined with the component attabs for such purpose, but i did read this topic with interest.

My 5 pennies: a colored line appearing on top of the selected  a selected or hovered tab as well can be a very useful visual indicator resp. additional supporter to recognize better the selected tab and the tab where the mouse is actually over. Personally i won't miss such. See sample screenshots:
« Last Edit: June 08, 2026, 12:26:04 am by d7_2_laz »
Lazarus 4.8  FPC 3.2.2 Win11 64bit

wp

  • Hero Member
  • *****
  • Posts: 13630
I am a bit confused about the colored line appearing above the top of the selected tab. Sometimes it is hard to be distinguished. Is it to highlight the selected tab again? Can it be turned off?

I'm actually using PageControl combined with the component attabs for such purpose, but i did read this topic with interest.

My 5 pennies: the colored line appearing on top of the selected  a selected or hovered tab as well can be a very useful visual indicator resp. additional supporter
to recognize the selected tab and the tab were the mouse might be actually over. Personally i won't miss it. See sample screenshots:
I did not mean the thick colored stripe at the top, but the 1-pixel-thick line above and at the left and right sides of that colored stripe (light blue in the attached screenshot). This is confusing, I've never seen this anywhere else.
« Last Edit: June 08, 2026, 12:30:02 am by wp »

d7_2_laz

  • Hero Member
  • *****
  • Posts: 696
Oh yes, that's right wp (same opinion).
Lazarus 4.8  FPC 3.2.2 Win11 64bit

 

TinyPortal © 2005-2018