Recent

Author Topic: Question about generics/changing component types  (Read 568 times)

geraldholdsworth

  • Sr. Member
  • ****
  • Posts: 265
Question about generics/changing component types
« on: October 11, 2025, 11:25:29 am »
This is kind of a question not to do with generics, but it may hold the answer...

I've recently become aware of the generics types - something else I've missed in my 30-odd year Delphi/Lazarus journey.

I was wondering if these could be used to have different component types, based on the setting of a varilable (set by the user). In other words, having an application which can be custom styled. This would need a button to be two different types, depending on the setting.

So, for my custom, retro-styling, application I have written my own class (based on TGraphicControl) for a button. But, if the user wanted to have native OS styling, then this would need to be a TButton, instead of my own.

Of course, the button needs to be declared either as my own TGJHCustomButton or a TButton...but I can't declare it as both. There's also the problem of creating said control...OK, if the user changes the setting, then they would need to restart the application - not a biggie (these controls are created on the fly, and not placed on the form).

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12536
  • FPC developer.
Re: Question about generics/changing component types
« Reply #1 on: October 11, 2025, 12:19:03 pm »
Generics are purely a compiletime vehicle.   They don't provide any runtime  change.

cdbc

  • Hero Member
  • *****
  • Posts: 2473
    • http://www.cdbc.dk
Re: Question about generics/changing component types
« Reply #2 on: October 11, 2025, 12:25:14 pm »
Hi
I would probably think: A common CORBA interface (no need for refcount in GUI code), then implement that interface in a TButton descendant and in your own component...
Just a thought  ;D
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

geraldholdsworth

  • Sr. Member
  • ****
  • Posts: 265
Re: Question about generics/changing component types
« Reply #3 on: October 11, 2025, 01:59:52 pm »
I would probably think: A common CORBA interface (no need for refcount in GUI code), then implement that interface in a TButton descendant and in your own component...
Does that mean basing my component on a TButton?
I think I may have considered that, but I need to paint the entire button - I think that is where it fell flat for me. It will have the advantage of having all the properties, methods and events already available, and then being to just switch with an extra provided property.

I'll have another play with it.

cdbc

  • Hero Member
  • *****
  • Posts: 2473
    • http://www.cdbc.dk
Re: Question about generics/changing component types
« Reply #4 on: October 11, 2025, 02:08:42 pm »
Hi
Quote
Does that mean basing my component on a TButton?
No not necessarily they both have to implement the interface, how they do it is up to you, that's why I said 'you'll have to make a descendant from TButton', because you can't impose an interface on a written class, only on a descendant. In your own component, you're free to do whatever you desire, as long as it implements the interface...
And since we're dealing with corba, there's no "bolting on refcount-code"  ;D
Have fun
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

geraldholdsworth

  • Sr. Member
  • ****
  • Posts: 265
Re: Question about generics/changing component types
« Reply #5 on: October 11, 2025, 02:32:58 pm »
I have actually been so stupid...I based my custom button on TGraphicControl. Never looked at TSpeedButton, which is based on....TGraphicControl!!!

cdbc

  • Hero Member
  • *****
  • Posts: 2473
    • http://www.cdbc.dk
Re: Question about generics/changing component types
« Reply #6 on: October 11, 2025, 02:51:23 pm »
Hi
I just quickly proved the concept, and it's ONLY that...:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.   {$Interfaces CORBA}
  12.   IbtnSwapable = interface['IbtnSwapable']
  13.     function get_Caption: string;
  14.     function get_Hint: string;
  15.     function get_OnClick: TNotifyEvent;
  16.     function get_Parent: TWinControl;
  17.     function Obj: TObject;
  18.     procedure set_Caption(aValue: string);
  19.     procedure set_Hint(aValue: string);
  20.     procedure set_OnClick(aValue: TNotifyEvent);
  21.     procedure set_Parent(aValue: TWinControl);
  22.     property Caption: string read get_Caption write set_Caption;
  23.     property Hint: string read get_Hint write set_Hint;
  24.     property OnClick: TNotifyEvent read get_OnClick write set_OnClick;
  25.     property Parent: TWinControl read get_Parent write set_Parent;
  26.   end;
  27.   {$Interfaces COM}
  28.  
  29.   { TbtnSwapableLCL }
  30.  
  31.   TbtnSwapableLCL = class(TCustomButton,IbtnSwapable)
  32.   private
  33.     function get_Caption: string;
  34.     function get_Hint: string;
  35.     function get_OnClick: TNotifyEvent;
  36.     function get_Parent: TWinControl;
  37.     function Obj: TObject;
  38.     procedure set_Caption(aValue: string);
  39.     procedure set_Hint(aValue: string);
  40.     procedure set_OnClick(aValue: TNotifyEvent);
  41.     procedure set_Parent(aValue: TWinControl);
  42.   published
  43.     property Caption: string read get_Caption write set_Caption;
  44.     property Hint: string read get_Hint write set_Hint;
  45.     property OnClick: TNotifyEvent read get_OnClick write set_OnClick;
  46.     property Parent: TWinControl read get_Parent write set_Parent;
  47.   end;
  48.  
  49.   { TForm1 }
  50.   TForm1 = class(TForm)
  51.     Button1: TButton;
  52.     procedure IntfBtnClick(aSender: TObject);
  53.   private
  54.  
  55.   public
  56.     btnIntf: IbtnSwapable;
  57.     procedure AfterConstruction; override;
  58.     procedure BeforeDestruction; override;
  59.   end;
  60.  
  61. var
  62.   Form1: TForm1;
  63.  
  64. implementation
  65.  
  66. {$R *.lfm}
  67.  
  68. { TbtnSwapableLCL }
  69.  
  70. function TbtnSwapableLCL.get_Caption: string;
  71. begin
  72.   Result:= inherited Caption;
  73. end;
  74.  
  75. function TbtnSwapableLCL.get_Hint: string;
  76. begin
  77.   Result:= inherited Hint;
  78. end;
  79.  
  80. function TbtnSwapableLCL.get_OnClick: TNotifyEvent;
  81. begin
  82.   Result:= inherited OnClick;
  83. end;
  84.  
  85. function TbtnSwapableLCL.get_Parent: TWinControl;
  86. begin
  87.   Result:= inherited Parent;
  88. end;
  89.  
  90. function TbtnSwapableLCL.Obj: TObject;
  91. begin
  92.   Result:= Self;
  93. end;
  94.  
  95. procedure TbtnSwapableLCL.set_Caption(aValue: string);
  96. begin
  97.   inherited Caption:= aValue;
  98. end;
  99.  
  100. procedure TbtnSwapableLCL.set_Hint(aValue: string);
  101. begin
  102.   inherited Hint:= aValue;
  103. end;
  104.  
  105. procedure TbtnSwapableLCL.set_OnClick(aValue: TNotifyEvent);
  106. begin
  107.   inherited OnClick:= aValue;
  108. end;
  109.  
  110. procedure TbtnSwapableLCL.set_Parent(aValue: TWinControl);
  111. begin
  112.   inherited Parent:= aValue;
  113. end;
  114.  
  115. { TForm1 }
  116.  
  117. procedure TForm1.IntfBtnClick(aSender: TObject);
  118. begin
  119.   ShowMessage('Hi HAJ :o)');
  120. end;
  121.  
  122. procedure TForm1.AfterConstruction;
  123. begin
  124.   inherited AfterConstruction;
  125.   btnIntf:= TbtnSwapableLCL.Create(nil);
  126.   btnIntf.Caption:= '>ME<';
  127.   btnIntf.Parent:= Self;
  128.   btnIntf.OnClick:= @IntfBtnClick;
  129. end;
  130.  
  131. procedure TForm1.BeforeDestruction;
  132. begin
  133.   btnIntf.Obj.Free;
  134.   inherited BeforeDestruction;
  135. end;
  136.  
  137. end.    
Cheerio...  :D
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11824
  • Debugger - SynEdit - and more
    • wiki
Re: Question about generics/changing component types
« Reply #7 on: October 11, 2025, 03:20:00 pm »
I was wondering if these could be used to have different component types, based on the setting of a varilable (set by the user). In other words, having an application which can be custom styled. This would need a button to be two different types, depending on the setting.

So, for my custom, retro-styling, application I have written my own class (based on TGraphicControl) for a button. But, if the user wanted to have native OS styling, then this would need to be a TButton, instead of my own.

Mind you, any approach to this will likely (or certainly) mean that you need to create them at runtime. If you design the component in the designer, it will be very hard or impossible.

Anything loaded from the LFM resource (the data done by the form designer) will have a fixed class in it. You can see that in the generated TForm1 which will have "Button1: TButton". And then it must be a TButton (or descendant thereof).

The proposed interface means that you have 3 variables,
Code: Pascal  [Select][+][-]
  1. LclButton1: TButton; // set by your code, if you want normal buttons
  2. YourButton1: TYourClassButton; // set by your code, if you want your buttons
  3. IntfButton: IYourInterfaceToButtons; // always set with the interface.

IntfButton can be connected to either class. So no matter which class you created, once you did create it, all other code can use IntfButton and use the same calls to e.g. change the caption.




Well, "LFM resource .. have a fixed class ":

I don't know how far that can be taken. There are hooks, that can be used to resolve classnames in the LFM. But I am not sure under which conditions they will be called.
If they are called, and you get your code in there, then you can change the class....
But, then the field in the TForm1 must still exist by the correct name, and that must have a compatible class: base class to both possible classes.... (again not tested if actually possible). It must have the given name, so you can only have one field, not two. Well, IIRC it can be absent, but then you have to find the button via "Form1.Controls[]" or similar.
And if the above can actually be done, then both classes must have all the published properties that are in the LFM (and they must be of the correct type).
That is a heck of a lot of "IF"s.
« Last Edit: October 11, 2025, 03:26:08 pm by Martin_fr »

geraldholdsworth

  • Sr. Member
  • ****
  • Posts: 265
Re: Question about generics/changing component types
« Reply #8 on: October 12, 2025, 12:17:06 pm »
As my custom button was based on TGraphicControl, which is what TSpeedButton is based on, I just changed the base class for my buttons, and it all works with very little changes (plus I could remove a lot of code as that is dealt with by TSpeedButton), except for the ModalResult - but I've got that one sorted (I think).

However, I have a few other components that are custom : my own custom TCheckBox and TRadioBox. All I need is a method to repaint the control, which there are no Paint methods to override (unlike TGraphicControl), presumably because it's dealt with by the OS.

Thank you for all your advice - there's some excellent stuff there I never knew could be done, so I'll be spending a while playing with that, and seeing what I can do.

 

TinyPortal © 2005-2018