Forum > Packages and Libraries

EC-Controls - TECImageMenu

(1/2) > >>

chrv:
Hi all,

I use a nice components library (EC-Controls). I'm trying to use a control called TECImageMenu. Everything is working fine except for fonts colors.
There are 2 properties for this : CaptionFontOptions.FontColor and Font.Color.
Neither seems to work (see attachment), neither at writetime nor at runtime.
Do I miss something ?

dsiders:

--- Quote from: chrv on January 10, 2022, 10:29:29 pm ---Hi all,

I use a nice components library (EC-Controls). I'm trying to use a control called TECImageMenu. Everything is working fine except for fonts colors.
There are 2 properties for this : CaptionFontOptions.FontColor and Font.Color.
Neither seems to work (see attachment), neither at writetime nor at runtime.
Do I miss something ?

--- End quote ---

No, you haven't missed anything. Neither color value is used when it draws the menu items. ThemeServices is used to draw the text for the menu items, and it uses its own color values.

chrv:
Are you saying there is no way to change CaptionFontOptions.FontColor and Font.color just by Property Editor in EC-Controls ?
If so, (i don't know anything about themes), could anybody please give a (very) simple example.
(I am providing a very simple attachment as exemple. Could you "theme" it please ?)

dsiders:

--- Quote from: chrv on January 12, 2022, 09:28:59 am ---Are you saying there is no way to change CaptionFontOptions.FontColor and Font.color just by Property Editor in EC-Controls ?

--- End quote ---

You can change the properties in the Object Inspector all you like. The code which draws the menu items does not use them.

wp:
Ready for your first component?

If you want to change some particular behaviour of a component you should try to create a descendent class and modify what you need. Of course, this is not always easy because it depends on how well-prepared the component is for this purpose. In this case it is easy. You only want to modify the painting routine. TECImageMenu descends from TCustomListbox, and every item in the listbox is painted by a method DrawItem. This method is "virtual" - this means that it is made for overriding because at runtime the new method in the descendent class will be used rather than the original method.

At first we must learn what the original code is doing. Hold the ctrl-key down and left-click in the word "TECImageMenu" in your source. This way the IDE opens the unit (ECImageMenu) at the place where TECImageMenu is declared/implemented. Find the method TCustomECImageMenu.DrawItem. When you are in the interface part of the unit click on this word and press "SHIFT+CTRL+Down arrow" to move to the implementation of the DrawItem method. The method is rather long and runs over several screen pages. But when you study it you will notice that it calls "ThemeServices" to output the icon and the menu text. We must avoid these calls and use the the standard LCL methods for this purpose.

The icon currently is painted by this call:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---      ThemeServices.DrawIcon(Canvas, aDetails, aImagePoint, Images, MenuItems[Index].ImageIndex);Images here is an ImageList, and you probably know that an image list can paint its images by its own method Draw:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---      Images.Draw(Canvas, aImagePoint.X, aImagePoint.Y, MenuItems[Index].ImageIndex);
And the text is painted by

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---      ThemeServices.DrawText(Canvas, aDetails, MenuItems[Index].Description,        aTextRect, aFlags or DT_NOPREFIX or DT_VCENTER, 0);      This can be replaced by the standard LCLIntf procedure DrawText which has almost the same arguments.

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---      DrawText(Canvas.Handle, PChar(MenuItems[Index].Description), Length(MenuItems[Index].Description),        aTextRect, aFlags or DT_NOPREFIX or DT_VCENTER);
That's the idea...

How to implement this?

You could create a new unit with a descendent class which overrides the DrawItem method:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  TMyECImageMenu = class(TECImageMenu)  protected    procedure DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState); override;  end;and in the implementation part you copy the DrawItem code from the original TEDImageMenu whch you must modify as described above.

You should put this unit into a separate package which you can install, and then you could use the TMyECImageMenu instead of the original component.

But since we only want to replace a single procedure there is an easier way: subclassing. In the unit in which the ECImageMenu is used redeclare TECImageMenu as a descendent of ECImageMenu.TECImageMenu:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  TECImageMenu = class(ECImageMenu.TECImageMenu)  protected    procedure DrawItem(Index: Integer; ARect: TRect; State: TOwnerDrawState); override;  end;    This is a trick to fool the compiler. Qualitication of the ancestor component by its unit name tells the compiler exactly which component is meant. The new component has the same name as the old component and is name is valid only in the current unit. So, whenever the compiler sees "TECImageMenu" in the current unit it assumes that it is the new component.

With this trick there is no need to install a new component. Simply put the new component into your main form unit in which the ECImageMenu is used. If you need the new menu at several places in your project, you can also put it into a separate unit, but then it is important that you list this unit at the end of your uses list so that the compiler sees only the new TECImageMenu instead of the original TEDImageMenu.

Lots of ugly tricks, I know...

Have a look at the attached modified version of your demo in which this lengthy description is applied. I just commented out the original source lines so that you can see what is changed in the original code.

Navigation

[0] Message Index

[#] Next page

Go to full version