Recent

Author Topic: Strange behavior of TGraphicControl objects inside a TScrollingWinControl  (Read 3734 times)

simsee

  • Full Member
  • ***
  • Posts: 194
Inside a form I have a TScrollingWinControl which is the parent of a series of TGraphicControl objects, which are lines drawn on the canvas of the parent control, all created at run time.

I develop at office with a desktop computer and at home with a laptop. When I compile the latest version of the application code written on one of the two computers, and compile it on the other computer, the lines are not shown. At this point, if I enter in the IDE, press the F12 key, slightly resize the form editor with the mouse, then recompile the project, the lines are visible.

Difficult to provide the code, because it is quite large. Based on this summary description, do you have any plausible explanation for this strange phenomenon?

I'm on Microsoft Windows 10, with the latest release of Lazarus. Thank you.
« Last Edit: September 22, 2023, 04:31:35 pm by simsee »

simsee

  • Full Member
  • ***
  • Posts: 194
Re: Strange behavior of TGraphicControl objects inside a TScrollingWinControl
« Reply #1 on: September 22, 2023, 09:16:32 pm »
Comparing the .lfm files generated in the two machines (which have different versions of lazarus on board), in the first machine I have:

LCLVersion = '2.2.6.0'

while in the second:

LCLVersion = '2.2.0.4'.

Furthermore in the first I have

DesignTimePPI = 144

This setting is not present in the lfm file of the second machine.

In both machines, however, in the object inspector the DesignTimePPI property is set to 144.

wp

  • Hero Member
  • *****
  • Posts: 12466
Re: Strange behavior of TGraphicControl objects inside a TScrollingWinControl
« Reply #2 on: September 22, 2023, 10:31:10 pm »
When you save a form file the IDE writes the DesignTimePPI to the lfm file as a unit of measurement to define at which resolution the form was designed. It is like with kilometers and miles. When you say: "The distance between two towns to 10" the number has no meaning because it has no unit; only when you say "The distance is 10 miles" or "10 kilometers" it is clear.

Since the default setting for the screen resolution is 96ppi, however, the DesignTimePPI is not written when your system runs at this resolution. So, when you do not find a DesignTimePPI entry in the lfm file you know that the length dimensions are meant to be for 96ppi. Suppose your form contains an edit control which is 100 pixels wide. It will extend on the screen over approximately 1 inch. When you load this form on a system running at 192 ppi (and LCLscaling is activated in the project options) the IDE will notice that it will have to scale-up all lengths by a factor 2 and double the pixels for the edit's Width (still it will be 1 inch on the monitor because the monitor has twice pixel densitiy now). And when you save the form on that 192ppi system, the IDE will add the DesigntimePPI=192 line - and save the edit with Width 200.

simsee

  • Full Member
  • ***
  • Posts: 194
Re: Strange behavior of TGraphicControl objects inside a TScrollingWinControl
« Reply #3 on: September 22, 2023, 11:59:36 pm »
Thanks for the very thorough explanation. I had sensed that this was the problem, although without going to this depth.

What I don't understand is why in the object inspector, in both cases, the property is always 144.

The other question is: how to avoid this phenomenon? Disable LCLscaling?

wp

  • Hero Member
  • *****
  • Posts: 12466
Re: Strange behavior of TGraphicControl objects inside a TScrollingWinControl
« Reply #4 on: September 23, 2023, 12:49:11 am »
What is the screen resolution for these two machines? To my knowledge the DesignTimePPI displayed in the object inspector is always the resolution of the current machine. When you open a 96ppi lfm file with a 144ppi compuler the length values in the file (which are for 96ppi) are automatically scaled to 144 ppi (i.e. multiplied by factor 1.5) so that the DesigntimePPI fo 144 is justified although the input file is for 96ppi.

Note that when you create controls at runtime you must take care of the scaling yourself. Suppose you create a button at runtime, with Left=10, Top=10, Width=100, Height = 100. The LCL will use these number at the current resolution, i.e. the button will be 100 pixels wide at all resolutions. But the other controls, created at designtime, will be scaled automatically to increase their size accordingly. You can imagine that this can cause trouble. If you want to scale the runtime-created button in the same way you should call one of the ScaleXXX functions of TControl:
Code: Pascal  [Select][+][-]
  1.     //scale support
  2.     function ScaleDesignToForm(const ASize: Integer): Integer;
  3.     function ScaleFormToDesign(const ASize: Integer): Integer;
  4.     function Scale96ToForm(const ASize: Integer): Integer;
  5.     function ScaleFormTo96(const ASize: Integer): Integer;
  6.     function Scale96ToFont(const ASize: Integer): Integer;
  7.     function ScaleFontTo96(const ASize: Integer): Integer;
  8.     function ScaleScreenToFont(const ASize: Integer): Integer;
  9.     function ScaleFontToScreen(const ASize: Integer): Integer;
  10.     function Scale96ToScreen(const ASize: Integer): Integer;
  11.     function ScaleScreenTo96(const ASize: Integer): Integer;

Scale96ToForm(x) means: x is understood as length for 96ppi; when the current system runs at a different resolution x will be multipied by the ratio of the current resolution to 96.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   with TButton.Create(Self) do
  4.   begin
  5.     Parent := self;
  6.     { // not scaled
  7.     Left := 10;
  8.     Top := 10;
  9.     Width := 100;
  10.     Height := 100;
  11.     }
  12.     // scaled
  13.     Left := Scale96ToForm(10);
  14.     Top := Scale96ToForm(10);
  15.     Width := Scale96ToForm(100);
  16.     Height := Scale96ToForm(100);
  17.   end;
  18. end;

I don't know if this is related to the disappearing lines, though. It really would be helpful if you could provide a small sample project showing the issue.

Disappearing lines can occur also when they are not drawn in the paint cycle (OnPaint event). This has nothing to do with LCL scaling.

simsee

  • Full Member
  • ***
  • Posts: 194
Re: Strange behavior of TGraphicControl objects inside a TScrollingWinControl
« Reply #5 on: September 23, 2023, 08:43:01 am »
Very interesting explanation.  I will do some experiments and try to come up with a smaller project that makes the problem repeatable externally too.  The lines are drawn inside the OnPaint event, so that's not the problem.

simsee

  • Full Member
  • ***
  • Posts: 194
Re: Strange behavior of TGraphicControl objects inside a TScrollingWinControl
« Reply #6 on: September 23, 2023, 09:28:59 am »
The strange thing is in trying to obtain a smaller project, even with a trivial modification of the code, by recompiling on the other machine, the problem is solved.

simsee

  • Full Member
  • ***
  • Posts: 194
I apologize for the time that has passed. I hope that despite this, someone still has the patience to help me solve this problem that is driving me crazy.

I finally had the time to extrapolate from a very complex application, a self-compilable portion that allows me to reproduce my problem. For this reason there is also a contour code which is superfluous in this context, but necessary in the original one.

In the code that I show and attach as Lazarus project, on machines with PPI=96 a segment is shown, while on machines with PPI=144 nothing appears (I work on Windows 64 bit, with the latest stable version of Lazarus).

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs;
  9.  
  10. type
  11.  
  12.   TGraph=TScrollBox;
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class(TForm)
  17.     ScrollBox1: TScrollBox;
  18.     procedure FormCreate(Sender: TObject);
  19.   private
  20.     Graph : TGraph;
  21.   public
  22.  
  23.   end;
  24.  
  25.   { TGraphObject }
  26.  
  27.   TGraphObject=class(TGraphicControl)
  28.     private
  29.       fGraphOwner : TGraph;
  30.     public
  31.       Pt1, Pt2 : TPoint;
  32.       constructor Create(Graph : TGraph; Rect : TRect); virtual; overload;
  33.       constructor Create(Graph : TGraph; X1,Y1,X2,Y2 : integer); virtual; overload;
  34.   end;
  35.  
  36.   { TLine }
  37.  
  38.   TLine=class(TGraphObject)
  39.     protected
  40.       procedure Paint; override;
  41.     public
  42.       constructor Create(Graph: TGraph; Rect: TRect); override; overload;
  43.       constructor Create(Graph : TGraph; X1,Y1,X2,Y2 : integer); override; overload;
  44.   end;
  45.  
  46.  
  47. var
  48.   Form1: TForm1;
  49.  
  50. implementation
  51.  
  52. {$R *.lfm}
  53.  
  54. { TForm1 }
  55.  
  56. procedure TForm1.FormCreate(Sender: TObject);
  57. begin
  58.   Graph:=TGraph.Create(Self);
  59.   Graph.Parent:=Self;
  60.   Graph.Align:=alClient;
  61.   TLine.Create(Graph,Rect(200,200,300,400));
  62. end;
  63.  
  64. { TGraphObject }
  65.  
  66. constructor TGraphObject.Create(Graph: TGraph; Rect: TRect);
  67. begin
  68.   inherited Create(Graph);
  69.   fGraphOwner:=Graph;
  70.   Rect.NormalizeRect;
  71.   with Rect do
  72.     SetBounds(Left,Top,Width,Height);
  73.   Parent:=Graph;
  74. end;
  75.  
  76. constructor TGraphObject.Create(Graph: TGraph; X1, Y1, X2, Y2: integer);
  77. begin
  78.   Create(Graph,Rect(X1,Y1,X2,Y2));
  79. end;
  80.  
  81. { TLine }
  82.  
  83. procedure TLine.Paint;
  84. begin
  85.   inherited;
  86.   Canvas.Line(Pt1.X-Left,Pt1.Y-Top,Pt2.X-Left,Pt2.Y-Top);
  87. end;
  88.  
  89. constructor TLine.Create(Graph: TGraph; Rect: TRect);
  90. begin
  91.   inherited Create(Graph, Rect);
  92.   Pt1:=Rect.TopLeft;
  93.   Pt2:=Rect.BottomRight;
  94. end;
  95.  
  96. constructor TLine.Create(Graph: TGraph; X1, Y1, X2, Y2: integer);
  97. begin
  98.   Create(Graph,Rect(X1,Y1,X2,Y2));
  99. end;
  100.  
  101. end.

Can anyone help me make this program portable between machines with screens having different resolutions (PPI)?

Thanks in advance.
« Last Edit: October 30, 2023, 06:50:52 pm by simsee »

jamie

  • Hero Member
  • *****
  • Posts: 6735
You have a scrollbox sitting on top of a scrollbox that is there at design time.
you set your parent of your control to the form which is covered by the scrollbox of the design time one.

When scrollbars start to appear, your control will disappear, in fact I am surprised it shows now..

The only true wisdom is knowing you know nothing

simsee

  • Full Member
  • ***
  • Posts: 194
I'm sorry. The scrollbox at design time is an error. But that's not the cause of the problem. You can consider this version instead of the previous one (with the design time scrollbox eliminated).

In the meantime I found a solution (even if I'm not completely convinced), modifying the following method as follows:

Code: Pascal  [Select][+][-]
  1. procedure TLine.Paint;
  2. begin
  3.   inherited;
  4.   Canvas.Line(Pt1.X-ScaleFormTo96(Left),Pt1.Y-ScaleFormTo96(Top),Pt2.X-ScaleFormTo96(Left),Pt2.Y-ScaleFormTo96(Top));
  5. end;

With this fix the line is shown both on machines with PPI=96 and with PPI=144.

 

TinyPortal © 2005-2018