Recent

Author Topic: Best way to exchange data between a form and a unit  (Read 47916 times)

cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #180 on: July 01, 2024, 02:12:39 am »
Oh, I forgot to mention:
...and the Germans were 2 goals better than us buhuhuhuuuuhhh.....  :P
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 686
Re: Best way to exchange data between a form and a unit
« Reply #181 on: July 01, 2024, 07:02:17 pm »
That's not half work, you've looked at it carefully.

Quote
* why do you send visual controls along into the presenter & model?

I had started reading the MVVM Delphi book and as far as I understand so far the view should remain as empty as possible. So I tried to channel everything away from it to the model or a seperate unit.   
I was actually quite happy that it worked  :-[
   
   
Quote
* Use PathDelim from sysutils instead of hardcoded '\'.
I actually knew that but didn't think about it

Quote
* the 'chkAddProjectToRootChange' event-handler is not consistent (yet),
   contemplating ...maybe 'ForceDirestories' could do the trick in model?!?
I don't think I fully understand you, but let me look at it myself first. I didn't know ForceDirectories.   
I had to look it up: ForceDirectories tries to create any missing directories in Dir till the whole path in Dir exists

   
Quote
* the 'edtProjectLocationChange' event-handler gets called a *lot* and in a
   round-about sort of way, contemplating...
Then things have to be different. I'm going to look at it
   
Quote
* is there a reason for not letting the 'view' show a dialog, before requesting
   the presenter to validate, instead of letting the model call back into the
   'view' / dialog area for getting a path and then letting the 'view' request
   validation from the presenter?
I placed the dialog outside the view because I wanted to keep the view as empty as possible. I will first bring this back to the view. Then it is also clearer, I think.   
   
   
Quote
* would it perhaps make sense to have a 'TProjectProperties' or 'TMVPProps'
   class to hold all the collected and calculated values and connections,
   behind the scenes, instead of a record?
I always struggle with that. The idea now was to grow TProjectData into a record containing all the project information, but a class is also possible. Otherwise I'll probably end up with a class that takes the record information and uses it
   
Quote
* you know, there's a clever way to get answers from the 'view' / user, in the
   model & presenter, namely:
   notify the observer -> let the reason-handler collect the info and
   return it via call-stack-unwinding  ;)
I don't understand this one. I already notify in various places in the presenter. Could you explain that a little further?   
   
   
Quote
* color feed-back is cool, but looks a bit funny with white edit-background on
   a dark themed desktop  :D (the text-font is white)
Didn't think about it at all. :-X I never use a black theme myself. But first recording the starting position (colour) and then adjusting it is still better than what I do now. Then I can go back to the original color. Using colors is very taste sensitive. So maybe just a warning text is fine too. For now I will continue with the color, i can always remove it.
   
   
Quote
*) contemplating means that I'm thinking about / 'sleeping on' input I can
    provide you with...
Do you have any specific needs, that I can help you with
Not yet, I will try to adjust it myself first.

I had started working on a listbox for the subdirectories and had just managed to edit a line in the listbox with a double click. I have that completely hidden away and a separate unit. I was already very satisfied that this first step worked, but I now have to reconsider that.  :D Some of it may be included in the view.

What I'm going to have trouble with later on is how I'm going to put the ready to use *.pas files in the folders. I prepare *.pas files with place holders in them so that I can pick them up or I prepare some of them in memos. I have no idea about that yet.


My vacation starts next Friday, so I'm less busy with Lazarus now

added:
I'll stop for tonight. I'm having a "fight" with "chkAddProjectToRoot". I want to start with the checkmark on and when you enter a project name it becomes difficult to keep the project location "edtProjectLocation" correct.
« Last Edit: July 01, 2024, 09:07:10 pm by Hansvb »

cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #182 on: July 03, 2024, 01:31:43 pm »
Hi Hans
Just a few thoughts, I won't interfere with your holiday  :D
Quote
I had started reading the MVVM Delphi book and as far as I understand so far the view should remain as empty as possible.
Yup, I read the book and translated his code to lazarus and got the app working... with a lot of modifications! The author is a typical 'new-school' programmer, where computers have endless memory and lightning-speed cpus, which results in some useless code-blocks here and there!
He also leans towards a very 'prude' philosophy, of idiot/stupid 'view' + almighty 'viewmodel'/'presenter' & 1 or more merely providers 'model(s)'.
That got me thinking... and studying... eventually landing on Martin Fowler.
Who AMO desribes the different takes on the /gray-area/, that is the "P" in MVP, i.e.: the presenter...
After researching, I find myself leaning more towards this philosophy:https://martinfowler.com/eaaDev/SupervisingPresenter.html
Which lets the 'view' do view & view-related stuff, the presenter is controlling/supervising everything between the 'model' and the 'view' & the 'model' is king(with the ability to communicate to the view via observer, if need be). It also scales very well, e.g.: you can do 1 presenter per view if you want to or 1 to more models, if need be: I do that with app-settings f.ex.
Quote
Then things have to be different. I'm going to look at it
The philosophy I described above will help with that. I personally like to keep things as simple as possible.
Quote
I placed the dialog outside the view because I wanted to keep the view as empty as possible. I will first bring this back to the view. Then it is also clearer, I think.
+1 =^
Quote
I don't understand this one. I already notify in various places in the presenter. Could you explain that a little further?
csu ~(c)all(s)tack(u)nwinding: I'll try and prepare a /simple/ demo for you, hang on...
I like your color feedback, maybe just /ask/ the 'view'(s) what color or theme it/they is/are and adjust accordingly...
Quote
I'm having a "fight" with "chkAddProjectToRoot"
Hahaha, been there, even got a t-shirt  ;D
Quote
What I'm going to have trouble with later on is how I'm going to put the ready to use *.pas files in the folders. I prepare *.pas files with place holders in them so that I can pick them up or I prepare some of them in memos. I have no idea about that yet.
The more interaction a user can have with code-generation, the more likely errors are to occur  ::)
Anyway, you have a nice vacation and enjoy the sun, these things and I will all be here, when you return  8-)
Regards Benny
« Last Edit: July 03, 2024, 01:33:21 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #183 on: July 07, 2024, 11:20:19 am »
Hi Hans
As promised, I've attached a small simple demo of csu ~ (c)all(s)tack(u)nwinding.
It doesn't do much, but should be able to show you the principle at work.
You may have to disable 'heaptrc' in project-options, just had it to confirm: no leaks!
I've found that the technique works best, if the object you /send/ along, is allocated beforehand.
It is not threadsafe!
Have fun & happy holidays  8-)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 686
Re: Best way to exchange data between a form and a unit
« Reply #184 on: July 08, 2024, 09:03:28 am »
Thanks, I'm going to download one of these days and check it out. I brought a laptop with me :-[, but it is usually used by my son for gaming. But getting away from the PC for a while is not a bad thing. Get new ideas.

cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #185 on: July 08, 2024, 09:48:07 am »
Hi Hans
A nice vacation to you and your family  ;)
Quote
I brought a laptop with me :-[, but it is usually used by my son for gaming.
Yup, Hehehe... They will snag it away from under your nose  :D
Quote
Get new ideas.
Yeah, I hear you mate, the best ideas come from watching the world and life...
Enjoy your holiday
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #186 on: July 29, 2024, 11:04:25 pm »
Hi
For whom it may interest...
Here's the source for "MVP-Setup"  8-)
This is a preliminary test release, alpha at most   :P
So, have fun and tell me what you think about it...
Notes:
  If your hair catches fire or whatever happens:
  I'm NOT LIABLE FOR ANYTHING!!! Use at your own risk! Delivered AS IS.
  This is programmed on a Linux box, so 'winders' users might have to
  change 'PathDelim's in "model.resources.pas'  :P
 
@Hansvb: Here's your little holiday-present attached, hope you like it.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #187 on: July 29, 2024, 11:07:39 pm »
Hi
Addendum: When I think the MVP-Setup project is finished, it will be made public on Gitlab, so stay tuned...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 686
Re: Best way to exchange data between a form and a unit
« Reply #188 on: July 30, 2024, 07:50:34 pm »
Hi,

It looks good and the project being created works.
You're giving me some homework again, because it looks different than I've seen before.   :D

I have no idea how you created the .inc files. (I can't find anything with Google either).  How do you create them? I assume that these inc files contain the .pas files that are placed in the correct folders.


For what it's worth, I've added my prematue principle. What I really did like is that if I double-click on the listbox, I can immediately adjust the value without opening an extra screen  O:-). Of course this is not entirely true because I am making an Tedit on top of the selected listbox item, but it seems that way when you use it.
Furthermore, I had used a TDirectoryEdit to retrieve the Root. This freed me from the open dialogue.

Thanks for the nice setup. I'll look into it further.

cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #189 on: July 31, 2024, 12:23:21 am »
Hi Hans
First off: I borrowed the code to create includes, from @KodeZwerg here: https://forum.lazarus.freepascal.org/index.php/topic,66868.msg513506.html#msg513506 "BinToInc"
Secondly: Your app looks good, still some road to cover, eh... I see you're still stuffing GUI elements into and through the backend?!? Strictly speaking, have they got no business there, at all. Remember the Cardinal rule: No Visual Stuff in the Backend! Presenter is a barrier, that gui stuff cannot pass.
Why not just let the gui handle your "Edit-Magic" 'in-house', so to speak?!?
Otherwise, I'm looking forward to seeing you progress, keep up the good work  8)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 686
Re: Best way to exchange data between a form and a unit
« Reply #190 on: July 31, 2024, 07:34:30 pm »
I'm going to take a good look at your new setup first. I get the feeling that this one is more mature than what I've seen so far. (I've only watched it a bit). I find the logging part very interesting (was also in 1 of your earlier examples but I had ignored it). And when the weather outside doesn't get so good, I'm going to put more time into it and then... Either try to finish the setup or go back to my stranded tool. I thought my idea wasn't right, but I now think I made a wrong start and went on with it for too long.

 So plenty to do.

I now know (now) that visual things should stay in the view, but the "edit" went so well that I thought it was too much of a shame to convert it. (Bad reason, I know).


cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #191 on: August 01, 2024, 08:52:38 am »
Hi Hans
Yup, mature is right, I've decided on my /dialect/ of Martin Fowler's 'Supervising Presenter' philosophy and am consolidating it. It's practical and has a clear methodology to it, I can see where the code is going...
Quote
I now know (now) that visual things should stay in the view, but the "edit" went so well that I thought it was too much of a shame to convert it. (Bad reason, I know).

Nah, not a bad reason, more bad(ish) placement of the code...  :D
I've thought it over and one way to do it would be like:
Code: Pascal  [Select][+][-]
  1. unit vwmain;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Menus, ComCtrls, LCLType,
  9.   StdCtrls, Buttons, EditBtn, obs_prosu, vwmain.intf, vwmain.decl,
  10.   vwmain.presenter, vwmain.types;
  11.  
  12. type
  13.   {$define lbxfab-intf} //<--- HERE
  14.     {$i lbxfab.inc} //<--- HERE
  15.   {$UnDef lbxfab-intf} //<--- HERE
  16.   { TViewMain }
  17.  
  18.   TViewMain = class(TForm, IView)
  19.     bbtnAddDirToLbx: TBitBtn;
  20.     bbtnDelDirFromLbx: TBitBtn;
  21.     btnCreateProject: TButton;
  22.     btnClose: TButton;
  23.     chkAddProjectToRoot: TCheckBox;
  24.     chkDefaultDirs: TCheckBox;
  25.     dirEdtProjectRoot: TDirectoryEdit;
  26.     Edit1: TEdit;
  27.     edtAddDir: TEdit;
  28.     edtProjectName: TEdit;
  29.     gbProjectDir: TGroupBox;
  30.     ImageList1: TImageList;
  31.     lblProjectNameExtension: TLabel;
  32.     lblProjectDirs: TLabel;
  33.     lblProjectRoot: TLabel;
  34.     lblProjectName: TLabel;
  35.     lbxNewDirs: TListBox;
  36.     mmiProgramQuit: TMenuItem;
  37.     mmiProgram: TMenuItem;
  38.     mmViewMain: TMainMenu;
  39.     StatusBar1: TStatusBar;
  40.     procedure bbtnAddDirToLbxClick(Sender: TObject);
  41.     procedure btnCreateProjectClick(Sender: TObject);
  42.     procedure chkAddProjectToRootChange(Sender: TObject);
  43.     procedure chkDefaultDirsChange(Sender: TObject);
  44.     procedure dirEdtProjectRootChange(Sender: TObject);
  45.     procedure edtAddDirKeyDown(Sender: TObject; var Key: Word;
  46.       Shift: TShiftState);
  47.     procedure edtProjectNameChange(Sender: TObject);
  48.     procedure FormCreate(Sender: TObject);
  49.     procedure FormDestroy(Sender: TObject);
  50.     procedure lbxNewDirsDblClick(Sender: TObject);
  51.     procedure lbxNewDirsKeyDown(Sender: TObject; var Key: Word;
  52.       Shift: TShiftState);
  53.     procedure mmiProgramQuitClick(Sender: TObject);
  54.   private
  55.     FSubscriber : TobsSubscriber;  { Holds the Observer. }
  56.     FPresenter : TPresenter;       { Holds the Presenter. }
  57.  
  58.     FLbxItemIdx : Integer;
  59.  
  60.     procedure EditEditingDone(Sender: TObject);
  61.     procedure EditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  62.     procedure EditExit(Sender: TObject);
  63.     function get_Presenter: IPresenter;
  64.     function get_Subscriber: IobsSubscriber;
  65.     procedure set_Presenter(AValue: IPresenter);
  66.     function Obj: TObject;
  67.     procedure ValidateEntry(Sender: TObject);
  68.  
  69.   protected
  70.     procedure DoSetStaticTexts(aTextRec: PStaticTextsAll);
  71.     procedure DoSetStatusBarPaneltext(aData: Pointer);
  72.     procedure DoSetCheckEntry({%H-}aSender: TObject; aData: Pointer);
  73.     procedure DoSetProjectLocation(aSender: TObject; aData: Pointer);
  74.     procedure DoCreateDirs({%H-}anObj: TObject; aDirs: PDirectoriesRec);
  75.     procedure DoSetSubDirs({%H-}anObj: TObject; aData: PDirectoriesRec);
  76.     procedure DoLbxDblClick(anObj: TObject; aData: PListBoxFab);
  77.     procedure DoEditKeyUp(anObj: TObject; aData: PEditKeyUp);
  78.     procedure DoAddSubDirToList(anObj: TObject; aData: PDirectoriesRec);
  79.  
  80.   public
  81.     { Handle Observer Notifications }
  82.     procedure HandleObsNotify(aReason: TProviderReason; aNotifyClass: TObject; UserData: pointer);
  83.  
  84.     property Subscriber: IobsSubscriber read get_Subscriber;
  85.     property Presenter: IPresenter read get_Presenter write set_Presenter;
  86.   end;
  87.  
  88. var
  89.   ViewMain: TViewMain;
  90.  
  91. implementation
  92.  
  93. {$R *.lfm}
  94. {$define lbxfab-impl} //<--- HERE
  95.   {$i lbxfab.inc} //<--- HERE
  96. {$UnDef lbxfab-impl} //<--- HERE
  97. { TViewMain }
  98.  
  99. procedure TViewMain.mmiProgramQuitClick(Sender: TObject);
  100. begin
  101.   Close;
  102. end;
  103.  
  104. function TViewMain.get_Presenter: IPresenter;
  105. begin
  106.   Result:= FPresenter;
  107. end;
  108.  
  109. function TViewMain.get_Subscriber: IobsSubscriber;
  110. begin
  111.   Result:= FSubscriber;
  112. end;
  113.  
  114. procedure TViewMain.set_Presenter(AValue: IPresenter);
  115. begin
  116.   if aValue= nil then begin
  117.     if Assigned(FPresenter) then FPresenter.Provider.UnSubscribe(FSubscriber);
  118.     FPresenter.Free;
  119.     FPresenter:= nil;
  120.     exit;
  121.   end;
  122.   if TPresenter(aValue.Obj) <> fPresenter then begin
  123.     if Assigned(FPresenter) then fPresenter.Provider.UnSubscribe(FSubscriber); { we can't detach nil }
  124.     FPresenter.Free; { we own it }
  125.     if Assigned(aValue) then begin
  126.       FPresenter:= TPresenter(aValue.Obj);
  127.       FPresenter.Provider.Subscribe(FSubscriber);
  128.       FPresenter.GetStaticTexts(gstAll);  // Get All static texts
  129.     end;
  130.   end;
  131. end;
  132.  
  133. function TViewMain.Obj: TObject;
  134. begin
  135.   Result:= self;
  136. end;
  137.  
  138. procedure TViewMain.DoSetStaticTexts(aTextRec: PStaticTextsAll);
  139. begin
  140.   with aTextRec^ do begin
  141.     Caption:= staVwMainTitle;
  142.     mmiProgram.Caption:= staMmiProgram;
  143.     mmiProgramQuit.Caption:= staMmiProgramQuit;
  144.     gbProjectDir.Caption:= staClear;
  145.     lblProjectName.Caption:= staLblProjectName;
  146.     lblProjectRoot.Caption:= staLblProjectLocation;
  147.     lblProjectNameExtension.Caption:= staLblProjectNameExtension;
  148.     lblProjectDirs.Caption:= staLblProjectDirs;
  149.     bbtnAddDirToLbx.Caption:= staClear;
  150.     btnCreateProject.Caption:= staBtnCreateProject;
  151.     btnClose.Caption:= staBtnClose;
  152.     chkAddProjectToRoot.Caption:= staChkAddProjectToRoot;
  153.     chkDefaultDirs.Caption:= staChkDefaultDirs;
  154.  
  155.     edtProjectName.TextHint:= staEdtProjectNameTxtHint;
  156.     edtAddDir.TextHint:= staEdtAddDirHint;
  157.  
  158.     edtProjectName.Text:= staClear;
  159.     dirEdtProjectRoot.Text:= staClear;
  160.     edtAddDir.Text:= staClear;
  161.   end;
  162. end;
  163.  
  164. procedure TViewMain.DoSetStatusBarPaneltext(aData: Pointer);
  165. begin
  166.   with PStatusbarPanelText(aData)^ do begin
  167.     StatusBar1.Panels[activePanel].Text:= PanelText;
  168.   end;
  169. end;
  170.  
  171. procedure TViewMain.DoSetCheckEntry(aSender: TObject; aData: Pointer);
  172. begin
  173.   with PValidateEntry(aData)^ do begin
  174.     if veLengthProjectIsCorrect then begin
  175.       if veUsedCharProjectIsCorrect then
  176.         edtProjectName.Color:= clDefault
  177.       else
  178.         edtProjectName.Color:= clFuchsia;
  179.     end
  180.     else if not veLengthProjectIsCorrect then begin
  181.       edtProjectName.Color:= clRed;
  182.     end;
  183.  
  184.     if veLengthRootIsCorrect then begin
  185.       if veUsedCharRootIsCorrect then
  186.         dirEdtProjectRoot.Color:= clDefault
  187.       else
  188.         dirEdtProjectRoot.Color:= clFuchsia;
  189.     end
  190.     else if not veLengthRootIsCorrect then begin
  191.       dirEdtProjectRoot.Color:= clRed;
  192.     end;
  193.  
  194.     FPresenter.SetStatusbartext(veMessage, 0);
  195.     btnCreateProject.Enabled:= veSucces;
  196.   end;
  197. end;
  198.  
  199. procedure TViewMain.DoSetProjectLocation(aSender: TObject; aData: Pointer);
  200. begin
  201.   with PProjectLocation(aData)^ do begin
  202.     TEdit(aSender).Text:= ProjectRootDir;
  203.   end;
  204. end;
  205.  
  206. procedure TViewMain.DoCreateDirs(anObj: TObject; aDirs: PDirectoriesRec);
  207. begin
  208.   FPresenter.SetStatusbartext(aDirs^.dirSuccesMsg, 0);
  209. end;
  210.  
  211. procedure TViewMain.DoSetSubDirs(anObj: TObject; aData: PDirectoriesRec);
  212. var
  213.   i, j: Integer;
  214. begin
  215.   { #todo : wrong place. should be in Model or lisbox unit. }
  216.   with PDirectoriesRec(aData)^ do begin
  217.     if AddSubDirs then begin
  218.       for i:=0 to length(DefaultSubDirs)-1 do begin
  219.         // add check if exists...
  220.         lbxNewDirs.AddItem(DefaultSubDirs[i], nil);
  221.       end;
  222.     end
  223.     else begin
  224.       for i:=0 to length(DefaultSubDirs)-1 do begin
  225.         for j:= lbxNewDirs.Items.Count-1 downto 0 do begin
  226.           if DefaultSubDirs[i] = lbxNewDirs.Items[j] then begin
  227.             lbxNewDirs.Items.Delete(j);
  228.             Break;
  229.           end;
  230.         end;
  231.       end;
  232.     end;
  233.   end;
  234. end;
  235.  
  236. procedure TViewMain.EditEditingDone(Sender: TObject);
  237. begin
  238.   lbxNewDirs.Items[FLbxItemIdx]:= (Sender As TEdit).Text;
  239. end;
  240.  
  241. procedure TViewMain.EditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  242. begin
  243.   FPresenter.EdtKeyUp(Sender, Key);
  244. end;
  245.  
  246. procedure TViewMain.EditExit(Sender: TObject);
  247. begin
  248.   with Sender as TEdit do begin
  249.     Destroy;
  250.   end;
  251. end;
  252.  
  253. procedure TViewMain.DoLbxDblClick(anObj: TObject; aData: PListBoxFab);
  254. begin
  255.   if TListBox(anObj).ItemIndex >= 0 then begin
  256.     With PListBoxFab(aData)^ do begin
  257.       TEdit(edt).OnEditingDone:= @EditEditingDone;
  258.       TEdit(edt).OnKeyUp:= @EditKeyUp;
  259.       TEdit(edt).OnExit:= @EditExit;
  260.  
  261.       FLbxItemIdx:= TListBox(anObj).ItemIndex;
  262.     end;
  263.   end;
  264. end;
  265.  
  266. procedure TViewMain.DoEditKeyUp(anObj: TObject; aData: PEditKeyUp);
  267. begin
  268.   with PEditKeyUp(adata)^ do begin
  269.  
  270.   case aKey of
  271.         VK_RETURN: begin
  272.           (anObj as TEdit).OnEditingDone:= nil;
  273.           EditEditingDone(TEdit(anObj));
  274.           lbxNewDirs.SetFocus;  // keep the listbox focues. (without this an other component gets the focus).
  275.         end;
  276.         40: begin  // Arrow down
  277.           (anObj as TEdit).OnEditingDone:= nil;
  278.           EditEditingDone(TEdit(anObj));
  279.           if FLbxItemIdx < lbxNewDirs.Items.Count-1 then
  280.             lbxNewDirs.ItemIndex := FLbxItemIdx + 1;  // goto the next listbox item
  281.           lbxNewDirs.SetFocus;
  282.         end;
  283.         VK_ESCAPE: begin
  284.           (anObj as TEdit).OnEditingDone:= nil;
  285.           (anObj as TEdit).OnExit:= nil;
  286.           anObj.Destroy;
  287.           lbxNewDirs.SetFocus;
  288.         end;
  289.       end;
  290.   end;
  291. end;
  292.  
  293. procedure TViewMain.DoAddSubDirToList(anObj: TObject; aData: PDirectoriesRec);
  294. begin
  295.   with  PDirectoriesRec(adata)^ do begin
  296.   if dirNewDirnames <> '' then
  297.     lbxNewDirs.AddItem(dirNewDirnames, nil);
  298.  
  299.   end;
  300.   //if Length(edtAddDir.Text) > 0 then lbxNewDirs.AddItem(edtAddDir.Text,nil);
  301. end;
  302.  
  303. procedure TViewMain.HandleObsNotify(aReason: TProviderReason;
  304.   aNotifyClass: TObject; UserData: pointer);
  305. begin
  306.   case aReason of
  307.     prStaticTexts          : DoSetStaticTexts(UserData);
  308.     prStatusBarPanelText   : DoSetStatusBarPaneltext(UserData);
  309.     prCheckEntryLength     : DoSetCheckEntry(aNotifyClass, UserData);
  310.     prProjectLocation      : DoSetProjectLocation(aNotifyClass, UserData);
  311.     prCreateDirs           : DoCreateDirs(aNotifyClass,UserData);
  312.     prSubDirDefault        : DoSetSubDirs(aNotifyClass,UserData);
  313.     prListBoxDblClick      : DoLbxDblClick(aNotifyClass,UserData);
  314.     prEdtKeyUp             : DoEditKeyUp(aNotifyClass,UserData);
  315.     prSubDirToList         : DoAddSubDirToList(aNotifyClass,UserData);
  316.   end;
  317. end;
  318.  
  319. procedure TViewMain.FormCreate(Sender: TObject);
  320. begin
  321.   FSubscriber:= CreateObsSubscriber(@HandleObsNotify); { Delegate messages }
  322.   Presenter:= TPresenter.Create; { the view owns the presenter }
  323.  
  324.   chkDefaultDirs.Checked:= true;  // Get the default dirs.
  325.  
  326. end;
  327.  
  328. procedure TViewMain.btnCreateProjectClick(Sender: TObject);
  329. var
  330.   lRec: TCreateProjectDirectoryTransaction;
  331. begin
  332.   try
  333.     lRec := FPresenter.TrxMan.StartTransaction(msCreateDir) as TCreateProjectDirectoryTransaction;
  334.     lRec.ProjectName:= edtProjectName.Text;
  335.     lrec.RootDir:= dirEdtProjectRoot.Text;
  336.     lRec.PrjNameInDir:= chkAddProjectToRoot.Checked;
  337.  
  338.     if lbxNewDirs.Items.Count> 0 then begin
  339.       lRec.NewDirnames.AddStrings(lbxNewDirs.Items);
  340.     end;
  341.     fPresenter.TrxMan.CommitTransaction;
  342.   except
  343.     fPresenter.TrxMan.RollbackTransaction;
  344.   end;
  345. end;
  346.  
  347. procedure TViewMain.bbtnAddDirToLbxClick(Sender: TObject);
  348. var
  349.   lRec: TDirectoriesRec;
  350. begin
  351.   lRec.dirNewDirnames:= edtAddDir.Text;
  352.   FPresenter.AddDirToList(Sender, lRec);
  353. end;
  354.  
  355. procedure TViewMain.chkAddProjectToRootChange(Sender: TObject);
  356. begin
  357.   if chkAddProjectToRoot.Checked then begin
  358.     ValidateEntry(Sender);
  359.   end
  360.   else begin
  361.     edtProjectNameChange(edtProjectName);
  362.     dirEdtProjectRootChange(dirEdtProjectRoot);
  363.   end;
  364. end;
  365.  
  366. procedure TViewMain.chkDefaultDirsChange(Sender: TObject);
  367. begin
  368.   FPresenter.AddDefaultSubDirs(lbxNewDirs, TCheckBox(Sender).Checked);
  369. end;
  370.  
  371. procedure TViewMain.dirEdtProjectRootChange(Sender: TObject);
  372. begin
  373.   ValidateEntry(Sender);
  374. end;
  375.  
  376. procedure TViewMain.edtAddDirKeyDown(Sender: TObject; var Key: Word;
  377.   Shift: TShiftState);
  378. var
  379.   lRec: TDirectoriesRec;
  380. begin
  381.   case Key of
  382.     13: begin
  383.           lRec.dirNewDirnames:= edtAddDir.Text;
  384.           FPresenter.AddDirToList(Sender, lRec);
  385.           //Key:= 0;
  386.         end;
  387.   end;
  388. end;
  389.  
  390. procedure TViewMain.edtProjectNameChange(Sender: TObject);
  391. begin
  392.   // Is called when focus is left ?  that's actually unnecessary. how to prevent that?
  393.   ValidateEntry(Sender);
  394. end;
  395.  
  396. procedure TViewMain.ValidateEntry(Sender: TObject);
  397. var
  398.   lveRec : TValidateEntry;
  399. begin
  400.   lveRec.vePrjName:= edtProjectName.Text;
  401.   lveRec.veRootName:= dirEdtProjectRoot.Text;
  402.   lveRec.vePrjInRoot:= chkAddProjectToRoot.Checked;
  403.   //lveRec.veIsFolder:= IsFolder;
  404.   FPresenter.ValidateEntry(Sender, lveRec);  // Sender is not used.
  405. end;
  406.  
  407. procedure TViewMain.FormDestroy(Sender: TObject);
  408. begin
  409.   if Assigned(FPresenter) then begin
  410.     FPresenter.Provider.UnSubscribe(FSubscriber);
  411.     FSubscriber.Free;
  412.     FPresenter.Free;
  413.   end;
  414. end;
  415.  
  416. procedure TViewMain.lbxNewDirsDblClick(Sender: TObject);
  417. begin
  418.   FPresenter.LbxNewDirsDblClick(Sender);
  419. end;
  420.  
  421. procedure TViewMain.lbxNewDirsKeyDown(Sender: TObject; var Key: Word;
  422.   Shift: TShiftState);
  423. begin
  424.   case Key of
  425.     113: begin // VK_F2
  426.       lbxNewDirsDblClick(sender);
  427.     end;
  428.   end;
  429. end;
  430.  
  431. end.
  432.  
Use an include file(see att.), this way you can keep the gui-stuff in the gui-departement and still have your nifty 'Edit-thingy'  8-)
I've marked the additions with 'HERE'...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 686
Re: Best way to exchange data between a form and a unit
« Reply #192 on: August 04, 2024, 08:38:00 pm »
I'm trying to see if I understand the new MVP framework a bit. I also encountered something in Lazarus itself. Find in Files did not work in a newly generated empty project. To do this, you must load all the files of your project in the project inspector. Then Find in Files works again. I did not know that yet.

I don't understand the idea of ​​"unit istrlist".
Why is IViewMain and IPresenterMain in Model.intf? I had trouble with the name, but the old location vwmain.intf may also have been a strange location.

My impression now is that it would be better for me to continue working with the previous variant for the time being. I started to understand that. The new one has become more complicated (for me anyway).


cdbc

  • Hero Member
  • *****
  • Posts: 1496
    • http://www.cdbc.dk
Re: Best way to exchange data between a form and a unit
« Reply #193 on: August 04, 2024, 09:47:08 pm »
Hi Hans
First off:
Quote
but the old location <vwmain.intf> may also have been a strange location.
This was your invention, I just edited in the projects / code you provided first hand  :D
Quote
I don't understand the idea of ​​"unit istrlist".
<nerd(ish)> Now this is a step upwards, because it allows me to write code in the model.main, that delivers a stringlist as a *function-result*, without me worrying about, if the user of the result remembers to *Free* the stringlist after use... (Let that sink in for a bit)...
E.g.:
Code: Pascal  [Select][+][-]
  1. function TcsuModelMain.GetStaticTexts(const aSection: string; out aTarget: integer): IStringList;
  2. begin
  3.   if aSection = '' then exit(nil);
  4.   fSection:= '['+aSection+']'; fSecId:= IndexText(fSection,sects); // iterator-search
  5.   if fTextCache.Count = 0 then begin
  6.     fPresenter.Provider.NotifySubscribers(prStatus,nil,Str2Pch('(!) ERROR: Could NOT retrieve static texts!'));
  7.     exit(nil); /// maybe we should just comment this line and return an empty list, avoid AVs ///
  8.   end;
  9.   Result:= CreateStrList;
  10.   fRes:= Result; // tmp placeholder while searching, bug in TStrings-> you cant send userdata along
  11.   fTextCache.ForEach(@DoEach);{ iterate over the source-list calling 'doeach' for each item }
  12.   fRes:= nil;
  13.   aTarget:= fSecId; // for the presenter to differentiate between views
  14. end;
The resulting stringlist above,  __N e v e r__  gets free'd by me! ...but still no leaks  %)
The reason for that is, I've implemented IStringList as a COM interface and they are _Ref-Counted_, i.e.: when the last reference to it goes out of scope or gets nil'ed, it free's itself. In this use-case, that is pure gold. For the rest of the interfaces used throughout the app, we'll stay with CORBA interfaces, because they're easier to control the lifetime of. </nerd(ish)>
Quote
Why is IViewMain and IPresenterMain in Model.intf?
Model is king!
Not much goes on in an MVP-app, without the model knows about it and the Presenter is the Queen *controlling* the king  :D The 'View(s)' on the other hand is/are only "Look, I can draw pretty things", well nothing without the other 2!
Therefore, .Intf, .Decl, .Main & the /super/ .Base all belong to the 'model' and _define_ the mechanics of the application. The TrxManager has a foot in both model & presenter world, it simply needs to, to do its job.
The presenter & single transactions are sat 'rigth smack' in the middle, again to do their jobs (controlling/presenting/supervising). The structure is not new, it's long ago (1/2 year), since you saw my first simple example app  ;D What you see now is actually correct after the pattern & philosophy

The sooner you practice this, the quicker you'll adapt your thinking, that it becomes natural to you ...and you'll be hard pressed, to find *any* gui-stuff behind the presenter and in the backend   :-X

Ohh, almost forgot, the 'IViewMain' is a contract, when I put that in my 'TfrmMain', the compiler tells me exactly what I have to implement, to 'play nice' with the backend. Tip: just fill in the first 3 letters of the method-name and do (Ctrl + space), the codetools will offer to write out the full methodname to implement, nifty eh...
Pheeeww, I hope this answers your questions?
Regards Benny
« Last Edit: August 04, 2024, 09:56:23 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Hansvb

  • Hero Member
  • *****
  • Posts: 686
Re: Best way to exchange data between a form and a unit
« Reply #194 on: August 05, 2024, 05:24:55 pm »
It's probably stupid, but in the new setup I can't even find how to set a label caption using:
Code: Pascal  [Select][+][-]
  1.  TModelMain.GetStaticTexts
.

 

TinyPortal © 2005-2018