Recent

Author Topic: Overload assignment operator of TPanel  (Read 2242 times)

chamfay

  • Newbie
  • Posts: 6
Overload assignment operator of TPanel
« on: August 26, 2025, 11:51:46 am »
Hello everyone. :)

This is my first post here, so I want to say thank you to all members.

I need a panel with title so I made a custom TPanel class with TLabel and TFlowPanel inside it.

Code: Pascal  [Select][+][-]
  1. unit LPanel;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, StdCtrls, ExtCtrls, Graphics, Controls;
  9.  
  10. type
  11.  
  12.   { TLabeledPanel }
  13.  
  14.   TLabeledPanel = class(TPanel)
  15.     FLabel: TLabel;
  16.     FFlowPanel: TFlowPanel;
  17.   public
  18.     constructor Create(TheOwner: TComponent); override;
  19.     destructor Destroy; override;
  20.   end;
  21.  
  22. implementation
  23.  
  24. { TLabeledPanel }
  25.  
  26. constructor TLabeledPanel.Create(TheOwner: TComponent);
  27. begin
  28.   inherited Create(TheOwner);
  29.  
  30.   FLabel := TLabel.Create(self);
  31.   FLabel.Parent := self;
  32.   FLabel.Caption:='Panel title';
  33.   FLabel.Align:= alTop;
  34.   FLabel.Alignment:=taCenter;
  35.  
  36.   FFlowPanel := TFlowPanel.Create(self);
  37.   FFlowPanel.Parent := self;
  38.   FFlowPanel.Align:= alClient;
  39. end;
  40.  
  41. destructor TLabeledPanel.Destroy;
  42. begin
  43.   FLabel.Free;
  44.   FFlowPanel.Free;
  45.   inherited Destroy;
  46. end;
  47.  
  48. end.

Code: Pascal  [Select][+][-]
  1. unit ulpanel;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     procedure FormCreate(Sender: TObject);
  16.   private
  17.  
  18.   public
  19.  
  20.   end;
  21.  
  22. var
  23.   Form1: TForm1;
  24.  
  25. implementation
  26.  
  27. uses
  28.   LPanel;
  29.  
  30.   {$R *.lfm}
  31.  
  32.   { TForm1 }
  33.  
  34. procedure TForm1.FormCreate(Sender: TObject);
  35. var
  36.   myPan: TLabeledPanel;
  37.   btn: TButton;
  38.   i: integer;
  39. begin
  40.   myPan := TLabeledPanel.Create(Self);
  41.   myPan.Width := 200;
  42.   myPan.Height := 200;
  43.   myPan.Left := 10;
  44.   myPan.top := 10;
  45.   myPan.Parent := Self;
  46.  
  47.   for i := 0 to 10 do
  48.   begin
  49.     btn := TButton.Create(myPan);
  50.     btn.Parent := myPan.FFlowPanel;
  51.     btn.Caption:='Button ' + IntToStr(i);
  52.   end;
  53. end;
  54.  
  55. end.

Now I need to add some buttons in TFlowPanel outside TLabledPanel class
for example I did:

Code: Pascal  [Select][+][-]
  1. btn.Parent := myPan.FFlowPanel;

but I need to overload assignment operator of myPan to add buttons directly
for example like this
Code: Pascal  [Select][+][-]
  1. btn.Parent := myPan;

Thanks. ;)
« Last Edit: August 26, 2025, 12:04:14 pm by chamfay »

jamie

  • Hero Member
  • *****
  • Posts: 7486
Re: Overload assignment operator of TPanel
« Reply #1 on: August 26, 2025, 11:24:48 pm »
The instant variable for your label control needs to be living in the TFORM1 so that you can gain access to it later on, instead of scanning the Components list of owned components, which you can btw.


 Then in the Label class, you put a Procedure in the public section that deals with adding a control to it.

Jamie
The only true wisdom is knowing you know nothing

chamfay

  • Newbie
  • Posts: 6
Re: Overload assignment operator of TPanel
« Reply #2 on: August 27, 2025, 07:19:57 am »
Hello.
Maybe I don't understand you, but I need to use this TlabeledPanel many times (5 instances), and each instance with a label and many controls inside TFlowPanel; for that I defined a Label inside.

BTW this is the class in my real program

Code: Pascal  [Select][+][-]
  1. unit LabledPanel;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, ExtCtrls, StdCtrls, CommCtrl, Buttons, SysUtils, Controls, Graphics;
  9.  
  10. type
  11.  
  12.   { TLabledPanel }
  13.  
  14.   TLabledPanel = class(TPanel)
  15.   private
  16.     FLabel: TLabel;
  17.     FFlowPanel: TFlowPanel;
  18.     procedure _CreateLabel(const aCaption: TCaption);
  19.     procedure _CreatePanel;
  20.     function GetCaption: TCaption;
  21.     procedure SetCaption(ACaption: TCaption);
  22.   public
  23.     constructor Create(TheOwner: TComponent; aCaption: TCaption); overload;
  24.     destructor Destroy; override;
  25.     property FlowPanel: TFlowPanel read FFlowPanel write FFlowPanel;
  26.     property Caption: TCaption read GetCaption write SetCaption;
  27.  
  28.   end;
  29.  
  30. implementation
  31.  
  32. { TLabledPanel }
  33.  
  34. procedure TLabledPanel._CreateLabel(const aCaption: TCaption);
  35. begin
  36.   FLabel := TLabel.Create(self);
  37.   FLabel.Parent := Self;
  38.   SetCaption(aCaption);
  39.   FLabel.Align := alTop;
  40.   FLabel.Font.Style := [fsBold, fsItalic];
  41.   FLabel.BorderSpacing.Top := 4;
  42. end;
  43.  
  44. procedure TLabledPanel._CreatePanel;
  45. begin
  46.   FFlowPanel := TFlowPanel.Create(self);
  47.   FFlowPanel.Parent := self;
  48.   FFlowPanel.Align := alClient;
  49.   FFlowPanel.BevelOuter := bvNone;
  50.   FFlowPanel.BorderWidth := 2;
  51. end;
  52.  
  53. function TLabledPanel.GetCaption: TCaption;
  54. begin
  55.   Result := FLabel.Caption;
  56. end;
  57.  
  58. procedure TLabledPanel.SetCaption(ACaption: TCaption);
  59. begin
  60.   if FLabel.Caption = ACaption then Exit;
  61.   FLabel.Caption := '  ' + ACaption;
  62. end;
  63.  
  64. constructor TLabledPanel.Create(TheOwner: TComponent; aCaption: TCaption);
  65. begin
  66.   inherited Create(TheOwner);
  67.  
  68.   // init
  69.   Width := 300;
  70.   Height := 150;
  71.   BevelOuter := bvNone;
  72.   _CreateLabel(aCaption);
  73.   _CreatePanel;
  74.  
  75. end;
  76.  
  77. destructor TLabledPanel.Destroy;
  78. begin
  79.   FreeAndNil(FFlowPanel);
  80.   FreeAndNil(FLabel);
  81.   inherited Destroy;
  82. end;
  83.  
  84. end.

any advices please?

Thanks.
« Last Edit: August 27, 2025, 07:31:45 am by chamfay »

Khrys

  • Sr. Member
  • ****
  • Posts: 376
Re: Overload assignment operator of TPanel
« Reply #3 on: August 27, 2025, 07:38:26 am »
Polymorphism (as provided by Pascal classes) doesn't mix well with assignment operator overloading in this case.

When targeting the  Parent  property, the required operator would have to be  operator :=(ASelf: TLabeledPanel): TWinControl;  which is an impossible overload because it's an upcast. The necessity of invoking such an upcast operator is impossible to determine since in principle, subclasses already act equivalently to some superclass when invoking one of the latter's (non-virtual) methods.

For example:  ControlAtPos()  is defined on  TWinControl.  When invoking it on a  TLabeledPanel  instance, should the compiler first call the custom assignment operator (which may in principle have all kinds of side effects, such as rearranging child controls) or should it call the method directly? With virtual methods, how should the compiler know whether to invoke the overridden or the inherited one?

Apart from that, the existing code in the LCL has no way of knowing whether such a user-defined overload exists at all, making it impossible to be applied consistently.

Quote from: Inheritance tree of TLabeledPanel
TObject (+ IFPObserved)
┗━━ TPersistent (+ IUnknown, IInterfaceComponentReference)
    ┗━━ TComponent
        ┗━━ TLCLComponent
            ┗━━ TControl
                ┗━━ TWinControl
                    ┗━━ TCustomControl
                        ┗━━ TCustomPanel
                            ┗━━ TPanel
                                ┗━━ TLabeledPanel

Code: Pascal  [Select][+][-]
  1. Assert(Panel is TLabeledPanel);   // True
  2. Assert(Panel is TPanel);          // True
  3. Assert(Panel is TCustomPanel);    // True
  4. Assert(Panel is TCustomControl);  // True
  5. Assert(Panel is TWinControl);     // True
  6. {...}



In my opinion, defining an opaque and somewhat confusing operator overload just to save 10 keystrokes isn't worth it. I encourage you to just make  FFlowPanel  publicly accessible - this is probably the simplest solution:

Code: Pascal  [Select][+][-]
  1. TLabeledPanel = class(TPanel)
  2.   FLabel: TLabel;
  3.   FFlowPanel: TFlowPanel;
  4. public
  5.   constructor Create(TheOwner: TComponent); override;
  6.   destructor Destroy; override;
  7.   property FlowPanel: TFlowPanel read FFlowPanel;
  8. end;

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1570
    • Lebeau Software
Re: Overload assignment operator of TPanel
« Reply #4 on: August 27, 2025, 08:13:51 am »
I would suggest exposing a public method to add a button, eg:

Code: Pascal  [Select][+][-]
  1. type
  2.   TLabeledPanel = class(TPanel)
  3.     ...
  4.   public
  5.     ...
  6.     procedure AddButton(ACaption: string {; other params as needed...});
  7.   end;
  8.  
  9. ...
  10.  
  11. procedure TLabeledPanel.AddButton(ACaption: string {; ...});
  12. var
  13.   btn: TButton;
  14. begin
  15.   btn := TButton.Create(FFlowPanel);
  16.   btn.Parent := FFlowPanel;
  17.   btn.Caption := ACaption;
  18.   ...
  19. end;

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   myPan: TLabeledPanel;
  4.   i: integer;
  5. begin
  6.   ...
  7.   for i := 0 to 10 do
  8.   begin
  9.     myPan.AddButton('Button ' + IntToStr(i) {,...});
  10.   end;
  11. end;
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

chamfay

  • Newbie
  • Posts: 6
Re: Overload assignment operator of TPanel
« Reply #5 on: August 27, 2025, 08:51:32 am »
Polymorphism (as provided by Pascal classes) doesn't mix well with assignment operator overloading in this case.

When targeting the  Parent  property, the required operator would have to be  operator :=(ASelf: TLabeledPanel): TWinControl;  which is an impossible overload because it's an upcast. The necessity of invoking such an upcast operator is impossible to determine since in principle, subclasses already act equivalently to some superclass when invoking one of the latter's (non-virtual) methods.

For example:  ControlAtPos()  is defined on  TWinControl.  When invoking it on a  TLabeledPanel  instance, should the compiler first call the custom assignment operator (which may in principle have all kinds of side effects, such as rearranging child controls) or should it call the method directly? With virtual methods, how should the compiler know whether to invoke the overridden or the inherited one?

Apart from that, the existing code in the LCL has no way of knowing whether such a user-defined overload exists at all, making it impossible to be applied consistently.

Quote from: Inheritance tree of TLabeledPanel
TObject (+ IFPObserved)
┗━━ TPersistent (+ IUnknown, IInterfaceComponentReference)
    ┗━━ TComponent
        ┗━━ TLCLComponent
            ┗━━ TControl
                ┗━━ TWinControl
                    ┗━━ TCustomControl
                        ┗━━ TCustomPanel
                            ┗━━ TPanel
                                ┗━━ TLabeledPanel

Code: Pascal  [Select][+][-]
  1. Assert(Panel is TLabeledPanel);   // True
  2. Assert(Panel is TPanel);          // True
  3. Assert(Panel is TCustomPanel);    // True
  4. Assert(Panel is TCustomControl);  // True
  5. Assert(Panel is TWinControl);     // True
  6. {...}



In my opinion, defining an opaque and somewhat confusing operator overload just to save 10 keystrokes isn't worth it. I encourage you to just make  FFlowPanel  publicly accessible - this is probably the simplest solution:

Code: Pascal  [Select][+][-]
  1. TLabeledPanel = class(TPanel)
  2.   FLabel: TLabel;
  3.   FFlowPanel: TFlowPanel;
  4. public
  5.   constructor Create(TheOwner: TComponent); override;
  6.   destructor Destroy; override;
  7.   property FlowPanel: TFlowPanel read FFlowPanel;
  8. end;

Very helpful answer.  :)
Since it's impossible so I will follow your advice.

Regards.

chamfay

  • Newbie
  • Posts: 6
Re: Overload assignment operator of TPanel
« Reply #6 on: August 27, 2025, 09:01:38 am »
I would suggest exposing a public method to add a button, eg:

Code: Pascal  [Select][+][-]
  1. type
  2.   TLabeledPanel = class(TPanel)
  3.     ...
  4.   public
  5.     ...
  6.     procedure AddButton(ACaption: string {; other params as needed...});
  7.   end;
  8.  
  9. ...
  10.  
  11. procedure TLabeledPanel.AddButton(ACaption: string {; ...});
  12. var
  13.   btn: TButton;
  14. begin
  15.   btn := TButton.Create(FFlowPanel);
  16.   btn.Parent := FFlowPanel;
  17.   btn.Caption := ACaption;
  18.   ...
  19. end;

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   myPan: TLabeledPanel;
  4.   i: integer;
  5. begin
  6.   ...
  7.   for i := 0 to 10 do
  8.   begin
  9.     myPan.AddButton('Button ' + IntToStr(i) {,...});
  10.   end;
  11. end;

Good suggestion, also nice and simple function to pass many parameters at once. 8-)

Thanks.
« Last Edit: August 27, 2025, 09:15:18 am by chamfay »

chamfay

  • Newbie
  • Posts: 6
Re: Overload assignment operator of TPanel
« Reply #7 on: August 27, 2025, 12:00:39 pm »

Quote from: Inheritance tree of TLabeledPanel
TObject (+ IFPObserved)
┗━━ TPersistent (+ IUnknown, IInterfaceComponentReference)
    ┗━━ TComponent
        ┗━━ TLCLComponent
            ┗━━ TControl
                ┗━━ TWinControl
                    ┗━━ TCustomControl
                        ┗━━ TCustomPanel
                            ┗━━ TPanel
                                ┗━━ TLabeledPanel


How do I get like this tree?

cdbc

  • Hero Member
  • *****
  • Posts: 2561
    • http://www.cdbc.dk
Re: Overload assignment operator of TPanel
« Reply #8 on: August 27, 2025, 04:22:09 pm »
Hi
Walk backwards up the object tree, putting the objects in a stack.
Now, pop the entire stack and write the individual items with increasing indentation to a TMemo...
Something like this:
Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.btnWalkClick(Sender: TObject);
  2. var O: TClass;
  3. begin
  4.   fStackObj:= CreStack;
  5.   O:= lblVers.ClassType;
  6.   repeat
  7.     fStackObj.Push(O);
  8.     Memo1.Append(O.ClassName);
  9.     O:= O.ClassParent;
  10.   until O = nil;
  11.   Memo1.Append(format('-> Stack count: %d',[fStackObj.Count]));
  12. end;
  13.  
  14. procedure TfrmMain.btnTreeClick(Sender: TObject);
  15. const indnt = '  ';
  16. var li: integer = 0;
  17.   function Indent: string;var l: byte; begin Result:= ''; for l:= 0 to li-1 do Result+= indnt; end;
  18. begin
  19.   if (Assigned(fStackObj) and (fStackObj.Count > 0)) then begin
  20.     while not fStackObj.IsEmpty do begin
  21.       Memo1.Append(Indent+tclass(fStackObj.Pop).ClassName);
  22.       inc(li);
  23.     end;
  24.   end;
  25. end;
The above snippet uses 'icontainers' + friends, but any stack will do...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

chamfay

  • Newbie
  • Posts: 6
Re: Overload assignment operator of TPanel
« Reply #9 on: August 27, 2025, 11:52:37 pm »
Hi
Walk backwards up the object tree, putting the objects in a stack.
.......

Nice, thank you  :)

 

TinyPortal © 2005-2018