Recent

Author Topic: Creating component from scratch  (Read 13339 times)

pascal111

  • Sr. Member
  • ****
  • Posts: 423
  • Un trabajo en equipo para programas serias.
Creating component from scratch
« on: May 21, 2021, 04:47:51 pm »
كيف يُمكن تصميم component من الصفر ولنأخذ مثالاً الـ TLabel فكيف نُصمم Label بالخصائص الأساسيّة للـ TLabel ليكون تطبيقاً عمليّاً على فهم تصميم الـ components في Lazarus بالرغم من نصيحة أحد الأصدقاء أنّ تصميم component في C++ أفضل ﻷنّكـ ستطلع على تفاصيل كلّ شيء ولكن كان رأيي أنّ البداية بـ Lazarus أفضل للمبتدئين ﻷنّ الـ C++ مُعقّدة؟

google translate:

"How can component be designed from scratch? Let us take the TLabel example. How do we design Label with the basic characteristics of TLabel to be a practical application to understand the design of components in Lazarus despite a friend’s advice that component design in C ++ is better because you will see the details of everything, but my opinion was that the beginning is with Lazarus is better for beginners because C ++ is complicated?"
La chose par la chose est rappelé.

Handoko

  • Hero Member
  • *****
  • Posts: 5150
  • My goal: build my own game engine using Lazarus
Re: Creating component from scratch
« Reply #1 on: May 21, 2021, 06:43:35 pm »
Component design is one of the topics I like. If you want to learn how to write a component in detail, I think the explanation will be very long. So I won't tell you the steps for doing it but only give you some points that you need to know.

1. Choosing the ancestor


The very first thing to do - I think - is choosing where your component should be inherited from. For example, if you want to write a visual component that shows a clickable text on the screen and when clicked, it will open an URL using the default web browser. For this case, it will be easier to use TLabel as the base of the component. So, the class definition can be like this:

TMyURLText = class(TLabel)

The definition above basically means TMyURLText will have all the features of TLabel.

But sometimes, you do not need to inherit the component from other available one. So you write:

TMyURLText = class

Without features inherited from TLabel, you have to manually handle its internal data. And (if it is a visual component) you have to manually handle the painting of the component, do you use TCanvas, OpenGL or other graphics library.

It is useful if you want to write a high performance visual component because using or inherited from a default visual component usually have poor graphics performance. Or if it is a non-visual component, there maybe no suitable ancestor for it. For example you want to write an AI engine for TicTacToe game.

2. Plan about what the 'things' are visible to the public


For example, if the component has 2 color settings, one is the normal color and then the color when mouse hovering on it. They can be: NormalColor and HoverColor. For this case, it is more suitable to use property and you need to understand what are setter and gettter.

You may want to set some functions or procedures that are public to programmers. Maybe a procedure called ExecuteTheURL, so the programmer can run the URL without need the user clicking on it.

For these features recently mentioned, you can check the document on these topics: public, published, property, getter, setter.

 


To be able to write a component professionally, you need to know a lot of things. But don't think too much. Start it now, pick a thing for the exercise. Maybe a clickable URL text, a color changing label, a pause and resume-able alarm or anything interesting but simple.

You don't have to, and I don't use it, but for beginners it will be easier to use the new component creation wizard/tool, which is a feature of the Lazarus IDE. First you have to create a package, then you can create the component. This is the tutorial:
https://wiki.freepascal.org/How_To_Write_Lazarus_Component

I believe you have already read it but here is the documentation for Classes:
https://www.freepascal.org/docs-html/ref/refch6.html

Don't go too deep, the documentation explains it very detailed. Many of the topics can be ignored if you're a beginners.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Creating component from scratch
« Reply #2 on: May 21, 2021, 07:30:11 pm »
Here I'll use a different approach. I'll reduce the class for TLabel to bare minimum so it could have a "visible" effect.

Create a unit named uSimple, and copy the following code:
Code: Pascal  [Select][+][-]
  1. unit uSimple;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes,    //<-- needed for the "owner" class, TComponent
  9.   SysUtils,    //<-- Not needed, optional
  10.   Controls,   //<-- needed for the ancestor class TGraphicControl
  11.   Graphics;  //<--- needed for the color clYellow, and brush style bsSolid
  12.  
  13. type
  14.  
  15.   { TSimpleControl }
  16.  
  17.   TSimpleControl = class(TGraphicControl)
  18.   public
  19.     constructor Create(TheOwner: TComponent); override;
  20.     procedure Paint; override;
  21.   end;
  22.  
  23. implementation
  24.  
  25. { TSimpleControl }
  26.  
  27. constructor TSimpleControl.Create(TheOwner: TComponent);
  28. begin
  29.   inherited Create(TheOwner);
  30.   SetInitialBounds(10,10,100,30);//<--- we need to give the component a location (Top and Left) and some dimensions (Width and Height)
  31. end;
  32.  
  33. procedure TSimpleControl.Paint;
  34. var
  35.   R: TRect;
  36. begin
  37.   R := Rect(0,0,Width,Height);
  38.   with Canvas do
  39.   begin
  40.     Brush.Color := clYellow;
  41.     Brush.Style:=bsSolid;
  42.     FillRect(R); //<--- this line does the work based on the color and style used above
  43.   end;
  44. end;
  45.  
  46. end.

To use this component, add the unit to the USES section of some form and a button with the following code:
Code: Pascal  [Select][+][-]
  1. uses
  2.  uSimple;
  3.  
  4. procedure TForm1.Button1Click(Sender: TObject);
  5. var
  6.   test: TSimpleControl;
  7. begin
  8.   test:=TSimpleControl.Create(Self); //<--- "self" is the form itself,  will be the "owner" of the  component. It will free the component when the form is closed.
  9.   test.Parent:=Self; //<--- where this component sits. Again, on the form. A panel could be used.
  10. end;

Handoko

  • Hero Member
  • *****
  • Posts: 5150
  • My goal: build my own game engine using Lazarus
Re: Creating component from scratch
« Reply #3 on: May 21, 2021, 07:50:39 pm »
Engkin's code is a good example for beginners. It's short but has some technical things you need to understand.

1. Override


What is override, when to use it, and what happens if you remove it? It is a bit too technical, but needed. Check the documentation to learn about it.

3. Inherited

You will use it often if you do OOP style programming. Do you know why we need to use it? Make sure you really know when to use it and when not to use it.

3. Owner and Parent

What are they? Visual components that appear on the screen on runtime, need a Parent. You don't have to but usually we set Parent := Owner. Do you know what are the differences between Owner and Parent?

4. Using TCanvas

TCanvas is relatively to use, especially when writing GUI components. Check the tutorial to understand how to use it.

5. Self

What is it? If you don't know, check the documentation.

6. TGraphicsControl

He chose TGraphicsControl as the base of the component. Why? Because TGraphicsControl is a lightweight component but still provide a canvas, so programmer can draw something to the screen.
« Last Edit: May 21, 2021, 08:12:47 pm by Handoko »

pascal111

  • Sr. Member
  • ****
  • Posts: 423
  • Un trabajo en equipo para programas serias.
Re: Creating component from scratch
« Reply #4 on: May 21, 2021, 08:31:54 pm »
Engkin's code is a good example for beginners. It's short but has some technical things you need to understand.

1. Override


What is override, when to use it, and what happens if you remove it? It is a bit too technical, but needed. Check the documentation to learn about it.

3. Inherited

You will use it often if you do OOP style programming. Do you know why we need to use it? Make sure you really know when to use it and when not to use it.

3. Owner and Parent

What are they? Visual components that appear on the screen on runtime, need a Parent. You don't have to but usually we set Parent := Owner. Do you know what are the differences between Owner and Parent?

4. Using TCanvas

TCanvas is relatively to use, especially when writing GUI components. Check the tutorial to understand how to use it.

5. Self

What is it? If you don't know, check the documentation.

6. TGraphicsControl

He chose TGraphicsControl as the base of the component. Why? Because TGraphicsControl is a lightweight component but still provide a canvas, so programmer can draw something to the screen.

قُمتُ بتطبيق مثال @engkin وكانت هذه هي النتيجة كما في الصورة ،مُستطيلٌ أصفر. أظنُّ أنّ الـ Override هو كإعادة تعريف method في صنف سلف للصنف الحالي ،وInherited لإستيراث تعريف method من صنف سلف للصنف الحالي ،وOwner and Parent لتحديد الكائن الحاوي والكائن الذي يتبعه الكائن الذي نقوم بإنشائه ،ولكنّني لا أعرف الـ TCanvas ،وأفهمُ Self بعض الشيء فهي تنوب عن الـ form أو الصنف نفسه ،بينما الـ TGraphicsControl فقد شاهدتهُ في مثال @engkin.

google translate:

"I applied the @engkin example and this was the result like in the image, a yellow rectangle. I think Override is like redefining a method in "of" an ancestor class for the current class, and Inherited to import a method definition from an ancestor class for the current class, and Owner and Parent to define the container object and the object that the object we are creating follows, but I don't know the TCanvas, and I understand Self a little bit because it works (in place of) (deputes), for the form or class itself, while the TGraphicsControl I saw it in the @engkin example."

(https://i.postimg.cc/VSgbs0DP/Screenshot-at-2021-05-21-20-20-54.png)
« Last Edit: May 21, 2021, 08:35:33 pm by pascal111 »
La chose par la chose est rappelé.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9857
  • Debugger - SynEdit - and more
    • wiki
Re: Creating component from scratch
« Reply #5 on: May 21, 2021, 08:40:00 pm »
despite a friend’s advice that component design in C ++ is better because you will see the details of everything,
What are you supposedly not seeing in "Lazarus".
Actually, the language is "Pascal". So if you compare with C++ then it should be compared to Pascal.


There is actually something that is "hard to find". And that is, the interaction with gtk or the Win32 api.

That has nothing to do with C++ vs Pascal. That has something to do, that the LCL (Lazarus Component Library) is available for Win,gtk and Mac.
And if you created such a library in C++, you would at some point have to divide between the LCL api, and the "Win,gtk or Mac" api.

So the question is: Will your new component need to directly call Win,gtk AND Mac api?
Or can you use the interface provided by the LCL? (e.g. TCanvas etc).

If you stick to LCL, then you can easily look through the code, and see how existing components are done.


Last thing is: If you want to use your component with the Form designer in the IDE, then you need to register it (RegisterComponents).
You need to create a package, that you can install into the IDE.

pascal111

  • Sr. Member
  • ****
  • Posts: 423
  • Un trabajo en equipo para programas serias.
Re: Creating component from scratch
« Reply #6 on: May 21, 2021, 08:43:16 pm »

4. Using TCanvas

TCanvas is relatively to use, especially when writing GUI components. Check the tutorial to understand how to use it.


ربّما تَعني TCanvas الذي يخص TGraphicsControl ،الآن عرفته في المثال.

google translate:

"Maybe (you mean) TCanvas for TGraphicsControl, now you "I" know it in the example."
La chose par la chose est rappelé.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9857
  • Debugger - SynEdit - and more
    • wiki
Re: Creating component from scratch
« Reply #7 on: May 21, 2021, 08:44:09 pm »
I think Override is like redefining a method in "of" an ancestor class for the current class, and Inherited to import a method definition from an ancestor class for the current class, and Owner and Parent to define the container object

If you want to know more about override / inherited and others:
http://www.delphibasics.co.uk/Article.asp?Name=OO
http://www.delphibasics.co.uk/Article.asp?Name=Inherit

Owner and Parent are LCL (and VCL) concepts.

Parent is the visual parent (e.g. the TPanel in which you want your control to dislpay)

Owner is in most cases the Form. (other owners are advanced topics).
Setting an Owner means you do not need to call Free/Destroy. The owner will do that.
(Yet you can call it yourself, but only before you destroy the owner)

pascal111

  • Sr. Member
  • ****
  • Posts: 423
  • Un trabajo en equipo para programas serias.
Re: Creating component from scratch
« Reply #8 on: May 21, 2021, 08:59:45 pm »
despite a friend’s advice that component design in C ++ is better because you will see the details of everything,
What are you supposedly not seeing in "Lazarus".
Actually, the language is "Pascal". So if you compare with C++ then it should be compared to Pascal.

أنا لا أعترض ولكن كما تعلم فبعض مبرمجي الـ C++ يتبجحون بأنّهم يعلمون تفاصيل كلّ شيء وأنّ لديهم معرفة أكبر.

google translate:

"I don't object, but as you know some C ++ programmers brag that they know the details of everything and that they have more knowledge."

There is actually something that is "hard to find". And that is, the interaction with gtk or the Win32 api.

That has nothing to do with C++ vs Pascal. That has something to do, that the LCL (Lazarus Component Library) is available for Win,gtk and Mac.
And if you created such a library in C++, you would at some point have to divide between the LCL api, and the "Win,gtk or Mac" api.

So the question is: Will your new component need to directly call Win,gtk AND Mac api?
Or can you use the interface provided by the LCL? (e.g. TCanvas etc).

If you stick to LCL, then you can easily look through the code, and see how existing components are done.

يبدو الأمر مُعقداً على فهمي فلم أبلغ بعد هذا المستوى من معرفة الفرق بين طريقة عمل الـ Win والـ Mac أو حتّى ما هو الـ gtk ،أظنّني كمبتدئ أن ألزم الطريق الأوضح وهو الـ LCL.

google translate:

"It seems complicated to my understanding that I have not yet reached this level of knowledge of the difference between how Win and Mac work or even what is gtk, I think as a beginner I should stick to the clearest path, which is LCL."

Last thing is: If you want to use your component with the Form designer in the IDE, then you need to register it (RegisterComponents).
You need to create a package, that you can install into the IDE.

ممتاز! سأنظُر في خطوة تسجيل الـ component في بيئة Lazarus بعدما أُكمل بناء component جيّد.

google translate:

"Excellent! I will look at the component registration step in the Lazarus environment after I have completed building a good component."
La chose par la chose est rappelé.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9857
  • Debugger - SynEdit - and more
    • wiki
Re: Creating component from scratch
« Reply #9 on: May 21, 2021, 09:05:24 pm »
"I don't object, but as you know some C ++ programmers brag that they know the details of everything and that they have more knowledge."
That is because: They do not know Pascal. ;)

Quote
There is actually something that is "hard to find". And that is, the interaction with gtk or the Win32 api.

"It seems complicated to my understanding that I have not yet reached this level of knowledge of the difference between how Win and Mac work or even what is gtk, I think as a beginner I should stick to the clearest path, which is LCL."
And that is how it should be.

Even in the Lazarus Team, you have several people. Different people for each OS.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Creating component from scratch
« Reply #10 on: May 21, 2021, 09:29:43 pm »
Add a Panel to the form, and set its alignment to "alBottom"
Add another button and write a simple code that changes the "Parent" of the SimpleControl. Every time your click on this new button the parent switches between the form "Self" and and panel "Panel1".

When you run the code, first click on the first button to create a SimpleControl. Next, click on the second button to switch the parent.

Can you write the code for the second button, and make the right changes to the previous code?

pascal111

  • Sr. Member
  • ****
  • Posts: 423
  • Un trabajo en equipo para programas serias.
Re: Creating component from scratch
« Reply #11 on: May 21, 2021, 09:58:13 pm »
Add a Panel to the form, and set its alignment to "alBottom"
Add another button and write a simple code that changes the "Parent" of the SimpleControl. Every time your click on this new button the parent switches between the form "Self" and and panel "Panel1".

When you run the code, first click on the first button to create a SimpleControl. Next, click on the second button to switch the parent.

Can you write the code for the second button, and make the right changes to the previous code?

لم أجد "alBottom" في خصائص الـ Panel ولكنّني حاولت برمجة ما قُلتَهُ لي فكان أنّه عندما أضغط الزر الأوّل لا أرى المُستطيل الأصفر فجرّبتُ أضغط الزر الذي سمّيته switch فكان المُستطيل الأصفر يظهر فوق الـ Panel وإذا ضغطت مرّةً أخرى إختفى خلفه.

google translate:

"I did not find "alBottom" in the panel properties, but I tried to program what you said to me "it" was that when I pressed the first button, I did not see the yellow rectangle, so I tried to press the button that I called switch, so the yellow rectangle appeared above the panel and if I pressed again it disappeared behind it."

(https://i.postimg.cc/k26c4mHg/Screenshot-at-2021-05-21-21-51-32.png)

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. begin
  3.  
  4.     if test.Parent=self then
  5.     test.Parent:=panel1
  6.     else
  7.       test.Parent:=self;
  8.  
  9. end;    
  10.  
La chose par la chose est rappelé.

pascal111

  • Sr. Member
  • ****
  • Posts: 423
  • Un trabajo en equipo para programas serias.
Re: Creating component from scratch
« Reply #12 on: May 21, 2021, 10:03:55 pm »
جرّبتُ كذلكـ تحجيم الـ Panel ﻷرى ما سيحدث فكانت النتيجة أنّ عند الضغط على switch ينتقل المُستطيل الأصفر إلى داخل الـ Panel كما في الصور:

google translate:

"I also tried sizing the panel to see what would happen, and the result was that when you press"ing" the switch, the yellow rectangle moves to the inside of the panel as in the pictures:"

(https://i.postimg.cc/6y5KFHm0/Screenshot-at-2021-05-21-21-59-52.png)

(https://i.postimg.cc/V529VTvy/Screenshot-at-2021-05-21-22-00-00.png)
La chose par la chose est rappelé.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Creating component from scratch
« Reply #13 on: May 21, 2021, 10:26:49 pm »
Great!  :)

You did it right.

alBottom is the value. Look for Align in the Object Inspector, after you select the Panel.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Creating component from scratch
« Reply #14 on: May 21, 2021, 10:34:52 pm »
Here another test. Add a third button to destroy the Panel by calling Panel1.Free

Press the first button to create a SimpleComponent
Press the second button to switch its parent to Panel1
Press the third button to destroy the Panel

What do you think will happen if you press the second button, now?

 

TinyPortal © 2005-2018