Recent

Author Topic: [solved--so far]Scrollbox -- why are controls not drawn in the order created?  (Read 1476 times)

indydev

  • Full Member
  • ***
  • Posts: 116
I have a Lazarus client that interacts with several different AI models. Questions and Responses are placed on TPanels in a TScrollbox. Loading sessions that have more than 4 TPanels begin to be displayed out of order (different from how they are created and assigned to the scrollbox), though not always.  The more TPanels the greater the chance of this "out of order" display. (See attachments: second one is the out of order display).

Is the drawing method for TScrollbox threaded? Is there a way for me to force the drawing order of the TPanels?
« Last Edit: July 18, 2024, 04:18:00 am by indydev »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #1 on: July 17, 2024, 01:16:53 am »
How to you create the panels?

Do you set the "Top" by hand? Or do you alTop? Or...?

One way may be to add them with AnchorSides. Then you can specify to which panel they should anchor (setting AnchorSide.Control).

Another may be to set the parent up to do "ChildSizing". And the one panel per row. Then they will be kept in the order in which they were created (I don't know how well that works with a scrollbox / or if you need an extra layer...)

indydev

  • Full Member
  • ***
  • Posts: 116
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #2 on: July 17, 2024, 01:32:04 am »
Ok. Yes I calculate the Top, by the bottom of the previous control. I'll look into using Anchors.

indydev

  • Full Member
  • ***
  • Posts: 116
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #3 on: July 17, 2024, 07:07:21 pm »
No improvements to this issue after trying several different ways to get at it. However, I am able to isolate more precisely when this happens.

First some code to show what I tried (not all my attempts):

Code: Pascal  [Select][+][-]
  1. // TDiscssionPanel is an extension of TPanel and is in my Discussion unit.
  2. constructor TDiscussionPanel.Create(AOwner: TComponent; DText: string; DTop: integer);
  3. begin
  4.   inherited Create(AOwner);
  5.   Self.Parent := AOwner as TWinControl;
  6.   Self.Top := DTop;
  7.  
  8.   // added this if block after posting about the problem in this forum but no change in results.
  9.   if Parent.ControlCount > 1 then
  10.   begin
  11.     Self.AnchorSide[akTop].Control := Parent.Controls[Parent.ControlCount-2]; //assign sibling
  12.     Self.AnchorSide[akTop].Side := asrBottom;  //anchor top side to bottom of sibling
  13.   end;
  14.  
  15.   Self.Align := alTop;
  16.  
  17.   // some settings (Panel background colors etc.) removed for conciseness.
  18.  
  19. end:
  20.  

The DiscussionPanel Constructor method is called from the main form unit:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.CreateTxtDisplay(AText: string);
  2. var
  3.   newDiscussionPanel:TDiscussionPanel;
  4.   newTop: Integer;
  5. begin
  6.   if Scrollbox1.ControlCount = 0 then newTop := 0
  7.      else newTop := Scrollbox1.Controls[Scrollbox1.ControlCount-1].Top +
  8.                     Scrollbox1.Controls[Scrollbox1.ControlCount-1].Height;
  9.   try
  10.     // Create a new Discussion Panel
  11.     newDiscussionPanel := TDiscussionPanel.Create(Scrollbox1, Atext, newTop);
  12.   except
  13.     on E: Exception do
  14.     begin
  15.       newDiscussionPanel.Free;
  16.       raise;
  17.     end;
  18.   end;
  19. end;
  20.  

Other attempts included adding a "wait" flag to the TScrollbox in my (already existing) helper unit and setting it on when a call to create a TDisucssionPanel is made and not calling another create method until it is off.  ...but, when do you turn it off? Turning it off at the end of the CreateTxtDisplay procedure didn't change anything. Turning it off at the end of the TDiscussionPanel.Create method itself, didn't change anything. Turning it off in the OnPaint method of the TDiscussionPanel created an infinite loop.

With all of that said, this problem ONLY occurs on loading a session that has more than 4 panels AND when they do not fit in the ScrollBox display window.  If I make the Form full screen, I can get more panels fitting in the ScrollBox. If they all fit this issue does not occur. If I shrink the Form and reload, the problem reccurs. If the Panels load fine, no problems occur on redraws of the ScrollBox from resizing, changing visibility etc. Adding Panels during a session doesn't create any problems (but they are added one at a time so not surprising).

Edit: In my previous response I indicated that I didn't set any alignment. Although I calculate the Top of the panel myself, I did already set the alignment. Removing my calculation makes the situation worse--they never load in order (Top to bottom, but some mishmash of bottom to top).
« Last Edit: July 17, 2024, 07:12:36 pm by indydev »

wp

  • Hero Member
  • *****
  • Posts: 12457
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #4 on: July 17, 2024, 07:30:13 pm »
See attached mini project which works for me (tested on Windows and Linux). But note: you will run into a "Position overflow error" when the total height of all panels exceeds the SmallInt maximum (32767) - when you add "many" panels this limit is reached faster than expected...

indydev

  • Full Member
  • ***
  • Posts: 116
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #5 on: July 17, 2024, 07:54:18 pm »
Maybe my description doesn't describe better what is happening. See the attached images.  My panels are loading from a database and each panel needs to convert Markdown text (of varying complexity) into the appropriate display.

Your example works because you are adding one panel at a time (by a button), and your panels have nothing in them. My panels work fine in that situation as well.

Edit: I can see how my description could be misleading. When I load a session, I call into a database and grab the Markdown, a loop calls the CreateTxtDisplay procedure until all the messages from the session are loaded. Each panel has to add the converted Markdown Text (done prior to creating the Discussion Panel) into the Display. Thus the Panels can get pretty complicated.
« Last Edit: July 17, 2024, 08:09:27 pm by indydev »

wp

  • Hero Member
  • *****
  • Posts: 12457
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #6 on: July 17, 2024, 08:20:40 pm »
A modified version of the demo in which random text is added to the panel along with an icon, and in which multiple panels can be added - panels are added in order.

VisualLab

  • Hero Member
  • *****
  • Posts: 569
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #7 on: July 18, 2024, 12:02:18 am »
I have a Lazarus client that interacts with several different AI models. Questions and Responses are placed on TPanels in a TScrollbox. Loading sessions that have more than 4 TPanels begin to be displayed out of order (different from how they are created and assigned to the scrollbox), though not always.  The more TPanels the greater the chance of this "out of order" display. (See attachments: second one is the out of order display).

Is the drawing method for TScrollbox threaded? Is there a way for me to force the drawing order of the TPanels?

If there are a lot of these panels, the program will load RAM and CPU quite heavily (TPanel is a class derived from TWinControl, which is quite extensive). In the screenshot shown, you can see that each section contains a frame with text and a small simple raster graphic. Maybe a better solution would be to create a simple class derived from TCustomControl that would draw subsequent sections, one below the other. The difficult part would be writing the code to handle vertical scrolling of the content.

indydev

  • Full Member
  • ***
  • Posts: 116
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #8 on: July 18, 2024, 12:03:00 am »
Thank you for your example. After playing with your example, cutting out part of my code and inserting yours, here is what I found.

Without calculating the top of the panel the panels always display from the bottom to the top. I don't know why that is. It does not matter if I always set Top to 0 or to High(SmallInt), the panels display from the bottom up. If I use your example outside of my code, it displays top to bottom.

If I calculate the top (i.e. use DTop) and use your example code (cutting out my display calculations etc.) I get the result you see in your example. 

But this tends to confirm my position that the calculations are taking too long, causing the drawing to happen out of order.

My previous posting of the code stripped out what I thought would be unnecessary. Here is the full code block in all of its ugliness:

Code: Pascal  [Select][+][-]
  1. constructor TDiscussionPanel.Create(AOwner: TComponent; DText, KyWord: string;
  2.   DTop, MLeft, MWidth, SgNum: Integer; CodeBlock, Q, Err: Boolean; const Pics: TBmpRecord);
  3. const
  4.   border = 8;
  5. begin
  6.   inherited Create(AOwner);
  7.   Self.Parent := AOwner as TWinControl;
  8.   Self.Align := alTop;
  9.   Self.Top := DTop; //High(SmallInt);
  10.  
  11.   BevelOuter := bvNone;
  12.   if Q then Color := TColor($00544644) else Color := TColor($00413534);
  13.   ParentBackground := False;
  14.   ParentColor := False;
  15.   FIsQuestion := Q;  
  16.   FUsePics := Pics.Use;
  17.   FPanelNum := Parent.ControlCount - 1;
  18.   FSegNum := SgNum;
  19.    case KyWord of
  20.        'EQ':begin
  21.               BlockType := btEquation;
  22.               CreateEquation;
  23.             end;
  24.     'TABLE':begin
  25.               BlockType := btTable;
  26.               FisCodeBlock := FALSE;
  27.               CreateTable(MLeft, MWidth, DText);
  28.             end;
  29.    'StdTxt':begin
  30.               BlockType := btText;
  31.               CreateDialogue(MLeft, MWidth, DText);
  32.             end;
  33.        else begin
  34.             BlockType := btCode;
  35.             FCodeKeyword := KyWord;
  36.             FIsCodeBlock := CodeBlock;
  37.             CreateCodeBlock(MLeft, MWidth, Q, DText);
  38.        end;
  39.    end;
  40.  
  41.   FError := Err;
  42.  
  43.   // Create and configure the agent picture
  44.   if UsePics then begin
  45.     AssignPics(Pics);
  46.     Self.OnPaint:=@PaintHandler;
  47.   end else Self.OnPaint:=@PaintHandler2;
  48.  
  49.   Self.OnResize:=@Resize;
  50.   Self.OnClick:=@SelfClick;
  51.   Self.OnMouseMove:=@SelfMouseMove;
  52.   Self.OnMouseDown:=@SelfMouseDown;
  53.   Self.OnMouseUp:=@SelfMouseUp;
  54. end;
  55.  

Each block calls another method constructor based on the content needed to be displayed (which includes a highly customized TRichMemo control). While it doesn't seem slow when I run it, there is a significant amount of processing going on.

At this point I don't expect anyone to 'Debug' my code, as it gets extensive. I believe I am stressing the display process. The problem I have does not always appear, but it appears more than 50% of the time, and it only appears upon loading the session messages into the ScrollBox.  I will continue to work on it until I either solve it or have something more concrete to question.

Thanks to Martin_fr and wp for responding.

Edit: I posted before seeing your response VisualLab. Iterative coding is a death nail >:D  Will consider your advice.
« Last Edit: July 18, 2024, 12:05:51 am by indydev »

indydev

  • Full Member
  • ***
  • Posts: 116
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #9 on: July 18, 2024, 12:08:55 am »
The difficult part would be writing the code to handle vertical scrolling of the content.

...yeah, need to figure out how that would be done?

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #10 on: July 18, 2024, 01:09:04 am »
Code: Pascal  [Select][+][-]
  1.  Self.Align := alTop;
  2.   Self.Top := DTop; //High(SmallInt);

Have you tried swapping those 2 lines?

Having multiple alTop is always a bit dangerous. Afaik/IIRC, They are sorted by whatever "top" position they have before the alTop overrides the position, but then I don't know, if the next one is sorted according to the new Top of the already aligned one.... So you may have to set value to Top that are further out than expected.

The other thing to do is to do a DisableAllAutosing on the parent container before setting them.
Otherwise, if you do more than one, they will keep adjusting while you still are adjusting them => and that ain't no good.


Though I wonder, when you used/tried AnchorSide.Control... Did you remove the alTop then? Because if you didn't, then the alTop may take precedence. Otherwise the AnchorSide would force position (you then need to set the parent control as AnchorSide for Left/Right to have the width being done.

Having seen you access parent.controls[parent.controlcount-1] => Just the obvious remainder: You don't use any "BringToFront" or similar on the panels? Because afaik that changes there order in the list of Controls. (But again, not sure)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #11 on: July 18, 2024, 01:14:20 am »
Not necessary related to your issue, but how many such sub-panels are you talking about?

Because if it is in the hundreds, you might get Windows to freak out. I did once a test with a plain form (non LCL, directly created via Windows API, so everything was Windows API only). I did put around 300 or 400 buttons or panels on that form.  And at some point Windows froze... It' long aga, but IIRC it froze completely (my entire Desktop) for maybe 10 secs. (That was Windows 10, not sure if other Versions do that do, or if it is still like that at all)... I don't recall the exact details, but Windows does have limits...

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10542
  • Debugger - SynEdit - and more
    • wiki
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #12 on: July 18, 2024, 01:32:09 am »
Just one more word on the auto-sizing (including align and all else)...

And it also depends on the parent having "AutoSize = True" or not.

Autosizing, aligning, positioning, .... are all always done immediately. You change the parent => autosize, you change the Top or Height => Autosize (of all others that may depend), you change anchors, or align ....

Unless you use DisableAutoSizing.

So when you have
Quote
Code: Pascal  [Select][+][-]
  1.   Self.Parent := AOwner as TWinControl;
  2.   Self.Align := alTop;
  3.   Self.Top := DTop; //High(SmallInt);

And say the Parent has "AutoSize = True"

Setting the parent, will set Top/Left = 0 => because an autosizing parent makes space...

Setting alTop, then makes this the topmost alTop (because Top=0). Actually it could also be the 2nd alTop from the top, as 2 controls would have "Top := 0".
The other panels will got down

"Self.Top = high(smallint)" should however work.... Good question. Though: assuming it is "visible = true". Not sure what happens if it is hidden. Might be skipped.

Though, again, I am not sure what happens with "top := " if align is already set. Yet, if you set it before it depends on the Parent.Autosize.



Beware, above is from distant memory. I probably got some details wrong. But it may be giving some ideas at what to pay attention....

indydev

  • Full Member
  • ***
  • Posts: 116
Re: Scrollbox -- why are controls not drawn in the order created?
« Reply #13 on: July 18, 2024, 03:17:57 am »
Wooh hoo! :D Tentative solution for now. 

Thank you Martin_fr for asking questions. I just kept messing with it.

If I remove Align:=alTop; altogether, set Left := 0;, set Width:=Parent.Width; and just handle the width adjustment in the @onPaint method I haven't been able to get the issue to arise after many attempts.

As for # of DiscussionPanels, I don't know how many a user may generate. The longest I have seen is about 100, but most sessions  are around 50 or less. I may have to put some code in to split sessions up when they get really long. As for controls in the Panels themselves, it is usually a RichMemo control, but could also include a button and a label if it needs a code window, or a table. The image on the left is just a stretchDraw on the Panel itself. So probably up to 4 controls on a panel.

 

TinyPortal © 2005-2018