Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2010-04

New autosize sucks

14 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Hans-Peter Diettrich

Is it only me, or have more users noticed the many changes, required to make existing code work without -dOldAutoSize?

I understand that it may take some more time to finish the autosize redesign, so let me report some observations:

Resize requests occur much too often, eventually causing excessive flicker. It's not a good idea to fix this in application code, since this will result in flames like:

Delphi applications have a GUI, Lazarus applications a light show.

It would help to trigger any layout actions only on changes of the Width or Height of a control, and not when e.g. a form only is moved. It's simpler to add an Invalidate or other update statement to existing code, when the GUI or a widgetset fails to reflect changes automatically, than preventing not required changes by fragile pairs of Disable/Enable calls, all over in LCL and application code.

Thanks to Mattias for his attempts to reduce the flicker in the docking examples, but I had to drop all modifications to the dockmanager code, in order to make it *work* as before. Flicker and other inconveniences have to be fixed in the LCL, not in user code.

Since the new autosize changes broke the dockmanager examples in many ways, I suggest to use them with -dOldAutoSize. Once the general autosize problems have been solved, we can look again at eventual remaining docking-related problems.

Last not least let me refrain my opinion about layout managers, that becomes stronger with every new-autosize problem:

Separate layout management from general LCL code, so that different layout managers (e.g. anchored layouts, docksite layout) can be used *when needed*. This would simplify the development of e.g. new autosize a lot, when only the (default) layout manager must be changed in a project, instead of the currently required rebuild of the entire LCL. This redesign also could reveal many layout (manager) specific hacks, crowding the current Controls unit, that should be removed for two reasons:
1) increase the LCL maintainability
2) reduce consequently required workarounds in user code

DoDi

Reply by email

Mattias Gaertner
to Hans-Peter Diettrich
Is it only me, or have more users noticed the many changes, required to make existing code work without -dOldAutoSize?

AFAIK you and me are the only one working on docking.

I understand that it may take some more time to finish the autosize redesign, so let me report some observations:

According to the bug tracker there is only one issue left and no developer can reproduce it:
http://bugs.freepascal.org/view.php?id=16184

Resize requests occur much too often, eventually causing excessive flicker.

According to the irc all your windows flickers, not only lazarus forms.

Show quoted text 11 lines
It's not a good idea to fix this in application code, since this will result in flames like: Delphi applications have a GUI, Lazarus applications a light show. It would help to trigger any layout actions only on changes of the Width or Height of a control, and not when e.g. a form only is moved. It's simpler to add an Invalidate or other update statement to existing code, when the GUI or a widgetset fails to reflect changes automatically, than preventing not required changes by fragile pairs of Disable/Enable calls, all over in LCL and application code.

With "resize requests" I guess you mean the ResetBounds. This has been changed.

Thanks to Mattias for his attempts to reduce the flicker in the docking examples, but I had to drop all modifications to the dockmanager code, in order to make it *work* as before. Flicker and other inconveniences have to be fixed in the LCL, not in user code.

The changes combined the many moves to one. The LCL only does what your code tells it. You created a visible form and move it several times in MakeSite.lpi. That causes the flicker.
Some widgetsets/window managers don't like such moves and therefore the the oldautosize code had some workarounds to ignore some moves. This made restoring window positions and autosizing unreliable. The new autosize code does not have this workarounds, so the many moves are now shown. It dedends on widgetset and windowmanager how much flickering you see. You can avoid the flickering by a) set the Visible property to true *after* moving it b) by using Disable/EnableAutoSizing

Since the new autosize changes broke the dockmanager examples in many ways, I suggest to use them with -dOldAutoSize. Once the general autosize problems have been solved, we can look again at eventual remaining docking-related problems.

The dockmanager examples should be fixed. See my changes you undid.

Show quoted text 12 lines
Last not least let me refrain my opinion about layout managers, that becomes stronger with every new-autosize problem: Separate layout management from general LCL code, so that different layout managers (e.g. anchored layouts, docksite layout) can be used *when needed*. This would simplify the development of e.g. new autosize a lot, when only the (default) layout manager must be changed in a project, instead of the currently required rebuild of the entire LCL. This redesign also could reveal many layout (manager) specific hacks, crowding the current Controls unit, that should be removed for two reasons: 1) increase the LCL maintainability 2) reduce consequently required workarounds in user code

The new autosize code was an important step towards layout managers. We only need to agree on the events/methods. See the mail thread two weeks ago.

Mattias

Reply by email

zeljko
to Mattias Gaertner
Mattias Gaertner wrote 14 lines
On Thu, 22 Apr 2010 17:36:36 +0200 Hans-Peter Diettrich wrote: > Is it only me, or have more users noticed the many changes, required to > make existing code work without -dOldAutoSize? AFAIK you and me are the only one working on docking. > I understand that it may take some more time to finish the autosize > redesign, so let me report some observations: According to the bug tracker there is only one issue left and no developer can reproduce it: http://bugs.freepascal.org/view.php?id=16184

I'm still using -dOldAutoSize for my projects since newAutoSize isn't compatibile with K3/D7 way of resizing.
eg. I'm resizing stringgrid columns in form OnResize event.

With such example (new autosize) , maximize form -> nothing happens with grid,
now restore form size, columns are resized but grid thinks that form is
maximized.
var
  i: integer;
  j: integer;
begin
  i := MyGrid.Width;
  for j := 0 to MyGrid.ColCount - 1 do
    MyGrid.ColWidths[j] := i div 10;
end;

1.It works correct with -dOldAutoSize
2.It works correct with Delphi/Kylix
3.It does not work correct with new autosize.

I have not enough time to investigate what's wrong but I know that my tstringgrid columns aren't resized in form's OnResize event in case of new autosize.Probably resize events are in opposite order so Form.OnResize is triggered first but children are not resized yet. Yes, I know that you wrote somewhere that we should use OnResize() for each control we want to track, but what should I do now with my project which have > 400 forms, open each form add OnResize to each control I need to resize ? Maybe I'm wrong (didn't look into), but seem that delphi/kylix triggers form.OnResize when everything is resized inside form (children), or it triggers twice : at form resize, and then after all children resized.

zeljko

Reply by email

Mattias Gaertner
to zeljko
Show quoted text 19 lines
On Thursday 22 April 2010 23:36, Mattias Gaertner wrote: > On Thu, 22 Apr 2010 17:36:36 +0200 > > Hans-Peter Diettrich wrote: > > Is it only me, or have more users noticed the many changes, required to > > make existing code work without -dOldAutoSize? > > AFAIK you and me are the only one working on docking. > > > I understand that it may take some more time to finish the autosize > > redesign, so let me report some observations: > > According to the bug tracker there is only one issue left and no > developer can reproduce it: > http://bugs.freepascal.org/view.php?id=16184 I'm still using -dOldAutoSize for my projects since newAutoSize isn't compatibile with K3/D7 way of resizing. eg. I'm resizing stringgrid columns in form OnResize event.

I added a FAQ:
http://wiki.lazarus.freepascal.org/Autosize_/_Layout#Resizing_stringgrid_columns_in_form.27s_OnResize_event_does_not_work

Show quoted text 14 lines
With such example (new autosize) , maximize form -> nothing happens with grid, now restore form size, columns are resized but grid thinks that form is maximized. var i: integer; j: integer; begin i := MyGrid.Width; for j := 0 to MyGrid.ColCount - 1 do MyGrid.ColWidths[j] := i div 10; end; 1.It works correct with -dOldAutoSize

Only sometimes, not always, as explained in the FAQ.

2.It works correct with Delphi/Kylix

I doubt that it always works, unless OnResize fires too often, even if the bounds have not changed.
See here
http://docwiki.embarcadero.com/VCL/en/Controls.TControl.OnResize

"Occurs immediately after the control is resized."

3.It does not work correct with new autosize.

Formerly the OnResize was often triggered after the LCL did some autosizing, so for simple form layouts the above worked sometimes. The OnResize is now triggered earlier. I can change it to trigger the event later, but as OnResize can trigger new autosizes, it can't be guaranteed that it will be the final event.
The right solution is to use the TStringGrids OnResize.

I will change the event so that it works for some simple cases, but I recommend that you fix your code.

Show quoted text 7 lines
I have not enough time to investigate what's wrong but I know that my tstringgrid columns aren't resized in form's OnResize event in case of new autosize.Probably resize events are in opposite order so Form.OnResize is triggered first but children are not resized yet. Yes, I know that you wrote somewhere that we should use OnResize() for each control we want to track, but what should I do now with my project which have > 400 forms, open each form add OnResize to each control I need to resize ?

I know what you mean.
But also think about the many new forms that are created and the many users who wants a reliable and fast LCL. It can not be guaranteed that form's OnResize comes after all the children are resized (unless it triggers far too often and then we get bug reports). And the later OnResize is triggered the more overhead it creates.

Maybe I'm wrong (didn't look into), but seem that delphi/kylix triggers form.OnResize when everything is resized inside form (children), or it triggers twice : at form resize, and then after all children resized.

Don't forget that Delphi has a simple top down autosize algorithm. It does not even support a theme independent groupbox with buttons.

Mattias

Reply by email

zeljko
to Mattias Gaertner
Mattias Gaertner wrote 12 lines
> 1.It works correct with -dOldAutoSize Only sometimes, not always, as explained in the FAQ. > 2.It works correct with Delphi/Kylix I doubt that it always works, unless OnResize fires too often, even if the bounds have not changed. See here http://docwiki.embarcadero.com/VCL/en/Controls.TControl.OnResize "Occurs immediately after the control is resized."

Well, I don't know what docs says, just know how it works :)

Show quoted text 8 lines
> 3.It does not work correct with new autosize. Formerly the OnResize was often triggered after the LCL did some autosizing, so for simple form layouts the above worked sometimes. The OnResize is now triggered earlier. I can change it to trigger the event later, but as OnResize can trigger new autosizes, it can't be guaranteed that it will be the final event. The right solution is to use the TStringGrids OnResize.

AFAIK, OnResize() isn't in Delphi order.In Delphi OnResize is triggered after OnShow(), with LCL it's triggered before OnShow(). Would be great if all OnResize() are postponed (when real resize is happened), and sent all at once when parent and all children are resized (only when form is resizing).

I will change the event so that it works for some simple cases, but I recommend that you fix your code.

Yes, I'll do that but cannot do it now, I need few days maybe few weeks to refactor all forms where I use OnResize().

Show quoted text 16 lines
> I have not enough time to investigate what's wrong but I know that my > tstringgrid columns aren't resized in form's OnResize event in case of > new autosize.Probably resize events are in opposite order so > Form.OnResize is triggered first but children are not resized yet. Yes, I > know that you wrote somewhere that we should use OnResize() for each > control we want to track, but what should I do now with my project which > have > 400 forms, open each form add OnResize to each control I need to > resize ? I know what you mean. But also think about the many new forms that are created and the many users who wants a reliable and fast LCL. It can not be guaranteed that form's OnResize comes after all the children are resized (unless it triggers far too often and then we get bug reports). And the later OnResize is triggered the more overhead it creates.

Of course that I'm on LCL side, but as I mentioned before - cannot use new autosize at the moment , and praying God for OldAutoSize not broken at some stage until I refactor my projects.

zeljko

Reply by email

Mattias Gärtner
to zeljko: Well, I don't know what docs says, just know how it works :) AFAIK, OnResize() isn't in…

Zitat von zeljko :

Show quoted text 15 lines
[...] > > 3.It does not work correct with new autosize. > > Formerly the OnResize was often triggered after the LCL did some > autosizing, so for simple form layouts the above worked sometimes. The > OnResize is now triggered earlier. I can change it to trigger the event > later, but as OnResize can trigger new autosizes, it can't be > guaranteed that it will be the final event. > The right solution is to use the TStringGrids OnResize. AFAIK, OnResize() isn't in Delphi order.In Delphi OnResize is triggered after OnShow(), with LCL it's triggered before OnShow(). Would be great if all OnResize() are postponed (when real resize is happened), and sent all at once when parent and all children are resized (only when form is resizing).

1. If the bounds are changed after the form became visible, you will see flicker. Some widgetsets/windowmanagers combines them and you can not see it. I heard that this is often the case under windows. But see Martin's Mail, he sees every window move as flicker. Maybe that is why the VCL didn't fix it. That's why OnResize comes before OnShow in LCL.

2. OnResize is often used to resize controls. So triggering it after real resize would trigger another real resize. That would cause flicker.

Show quoted text 5 lines
> I will change the event so that it works for some simple cases, > but I recommend that you fix your code. Yes, I'll do that but cannot do it now, I need few days maybe few weeks to refactor all forms where I use OnResize().

You know, I tell people since 10 years that the VCL OnResize misuses are not portable.

Show quoted text 19 lines
> > I have not enough time to investigate what's wrong but I know that my > > tstringgrid columns aren't resized in form's OnResize event in case of > > new autosize.Probably resize events are in opposite order so > > Form.OnResize is triggered first but children are not resized yet. Yes, I > > know that you wrote somewhere that we should use OnResize() for each > > control we want to track, but what should I do now with my project which > > have > 400 forms, open each form add OnResize to each control I need to > > resize ? > > I know what you mean. > But also think about the many new forms that are created and the many > users who wants a reliable and fast LCL. It can not be guaranteed that > form's OnResize comes after all the children are resized (unless it > triggers far too often and then we get bug reports). And the later > OnResize is triggered the more overhead it creates. Of course that I'm on LCL side, but as I mentioned before - cannot use new autosize at the moment , and praying God for OldAutoSize not broken at some stage until I refactor my projects.

OTOH we need good bug reports.

Mattias

Reply by email

Alexander Klenin
to Mattias Gaertner: On Thu, 22 Apr 2010 17:36:36 +0200 AFAIK you and me are the only one working on docking.…
Mattias Gaertner wrote 8 lines
On Thu, 22 Apr 2010 17:36:36 +0200 > I understand that it may take some more time to finish the autosize > redesign, so let me report some observations: According to the bug tracker there is only one issue left and no developer can reproduce it: http://bugs.freepascal.org/view.php?id=16184

What about
http://bugs.freepascal.org/view.php?id=16212
http://bugs.freepascal.org/view.php?id=16319
?

Alexander S. Klenin

Reply by email

Hans-Peter Diettrich
to Mattias Gaertner: On Fri, 23 Apr 2010 08:14:37 +0200 I added a FAQ:…
Show quoted text 6 lines
> Maybe I'm wrong (didn't look into), but seem that delphi/kylix triggers > form.OnResize when everything is resized inside form (children), or it > triggers twice : at form resize, and then after all children resized. Don't forget that Delphi has a simple top down autosize algorithm. It does not even support a theme independent groupbox with buttons.

Why requires theme support to break that stable processing?

I'd accept a two-pass resize, with the determination of the min/max sizes in the first pass, and the distribution of available space in the second pass.

How does the new autosize work?

DoDi

Reply by email

Hans-Peter Diettrich
to Mattias Gärtner: Zitat von zeljko : 1. If the bounds are changed after the form became visible, you will…
Show quoted text 5 lines
1. If the bounds are changed after the form became visible, you will see flicker. Some widgetsets/windowmanagers combines them and you can not see it. I heard that this is often the case under windows. But see Martin's Mail, he sees every window move as flicker. Maybe that is why the VCL didn't fix it. That's why OnResize comes before OnShow in LCL.

I understand that window creation is a somewhat special case. It could be solved in special handling of the Visible property, that should be honored only after any other initialization. I.e. Visible could be decoupled from Show, indicating the *designed* state, to be honored when nothing else is left to do.

2. OnResize is often used to resize controls. So triggering it after real resize would trigger another real resize. That would cause flicker.

That's why I suggested a two-pass resize. I have no idea how it would affect Delphi compatibility, though. OnResize could be split into two handlers, OnBeforeResize (or OnQueryResize) and OnAfterResize, called in either pass. When only a single OnResize handler exists, it will be called in both passes, but programmatical changes to the size should be ignored in the second pass.

DoDi

Reply by email

Mattias Gaertner
to Alexander Klenin: What about http://bugs.freepascal.org/view.php?id=16212…
Show quoted text 14 lines
On 2010-04-23, Mattias Gaertner wrote: > On Thu, 22 Apr 2010 17:36:36 +0200 >> I understand that it may take some more time to finish the autosize >> redesign, so let me report some observations: > > According to the bug tracker there is only one issue left and no > developer can reproduce it: > http://bugs.freepascal.org/view.php?id=16184 What about http://bugs.freepascal.org/view.php?id=16212 http://bugs.freepascal.org/view.php?id=16319 ?

Both fixed.

Mattias

Reply by email

Mattias Gaertner
to Hans-Peter Diettrich: Why requires theme support to break that stable processing? I'd accept a two-pass resize,…
Show quoted text 10 lines
Mattias Gaertner schrieb: >> Maybe I'm wrong (didn't look into), but seem that delphi/kylix triggers >> form.OnResize when everything is resized inside form (children), or it >> triggers twice : at form resize, and then after all children resized. > > Don't forget that Delphi has a simple top down autosize algorithm. It > does not even support a theme independent groupbox with buttons. Why requires theme support to break that stable processing?

It is not that "stable". It is simple and limited to simple layouts.

Maybe you don't know how the VCL resizes:
As soon as a size or position changes the layout must recomputed. This is done by resizing the control itself and telling the parent to realign its children. This can trigger other controls and so forth, a random walk until nothing changes anymore. A control can resize/position several times this way. And every time the new bounds are sent to the winapi.

For theme support you need "bottom up" too:
For example a Control with Align=alBottom is anchored to the left and right, so its width is defined by its Parent. When Parent.AutoSize is true it should resize itself according to its children. Now you have a circle dependency.
The bottom up part is done in the LCL with the CalculatePreferredSize. This was already done with the old autosizing code. It also used the random walk approach.

The random walk has (at least) three disadvantages:
1. it is random. A control can resize several times. This is especially true for the LCL, because it has more autosize properties than the VCL, has additionally bottom up, runs on different widgetsets with different order of WMSize messages. This makes this "stable" algorithm go nuts and causes a lot of flickering.
2. it is not sufficient for bottom up. Only checking the parent and sibling is not enough if you have a two level dependency. For example a label in a groupbox in a notebook.
3. It does not work well with some special phases. For example when a form is set to visible, all handles must be created and turned visible. Creating a handle means applying all properties to the handle - a lot of changes. This starts the random walk many times - more flickering.

Show quoted text 5 lines
I'd accept a two-pass resize, with the determination of the min/max sizes in the first pass, and the distribution of available space in the second pass. How does the new autosize work?

There were several changes:
- the update is now done for the whole form, allowing arbitrary nesting levels. With the random walk approach this would cause more flickering.
- the autosize now uses a two-pass resize with the determination of the preferred sizes in the first pass, and the distribution of available space in the second pass. Because of OnResizes, overrides and non linear dependencies this might need some repeats.
- the changes are not immediately sent to the widgetset, but collected and sent after all the computation.
- to further reduce flickering the visible flags are delayed until the handles are created and all bounds have been computed.
- you can now delay autosizing and visibility for the whole form. This is important for docking were you have to work with arbitrary forms.
- as side effect many workarounds of the old autosize code to reduce the random walk problems are no longer needed and were removed.
- with the removal of the workarounds it is now possible to think about custom layout managers that can be written without being an LCL expert.

Mattias

Reply by email

Mattias Gaertner
to Hans-Peter Diettrich: I understand that window creation is a somewhat special case. It could be solved in…
Show quoted text 13 lines
Mattias G?rtner schrieb: > 1. If the bounds are changed after the form became visible, you will see > flicker. Some widgetsets/windowmanagers combines them and you can not > see it. I heard that this is often the case under windows. But see > Martin's Mail, he sees every window move as flicker. Maybe that is why > the VCL didn't fix it. That's why OnResize comes before OnShow in LCL. I understand that window creation is a somewhat special case. It could be solved in special handling of the Visible property, that should be honored only after any other initialization. I.e. Visible could be decoupled from Show, indicating the *designed* state, to be honored when nothing else is left to do.

Visible is even in the VCL decoupled. The VCL also tries to delay turning a form visible, by using the variable "Showing". The problem is that this attribute is updated by UpdateShowing which is triggered to early by some properties (e.g. WindowState). This is ok for the VCL which internals are optimized for the winapi. The LCL needed a better approach. Now UpdateShowing is no longer triggered immediately.

Show quoted text 9 lines
> 2. OnResize is often used to resize controls. So triggering it after > real resize would trigger another real resize. That would cause flicker. That's why I suggested a two-pass resize. I have no idea how it would affect Delphi compatibility, though. OnResize could be split into two handlers, OnBeforeResize (or OnQueryResize) and OnAfterResize, called in either pass. When only a single OnResize handler exists, it will be called in both passes, but programmatical changes to the size should be ignored in the second pass.

OnResize is a very important event. See the mails from zeljko. It now works somewhat like OnAfterResize, which should be quite Delphi compatible.

The name OnAfterResize would be misleading. It was always triggered after the control was resized. But for Delphi compatibility it should be triggered after all other have reacted to this resize. That's not even true for Delphi, but that is how a lot of code uses it.

Mattias

Reply by email

Hans-Peter Diettrich
to Mattias Gaertner
Show quoted text 8 lines
OnResize is a very important event. See the mails from zeljko. It now works somewhat like OnAfterResize, which should be quite Delphi compatible. The name OnAfterResize would be misleading. It was always triggered after the control was resized. But for Delphi compatibility it should be triggered after all other have reacted to this resize. That's not even true for Delphi, but that is how a lot of code uses it.

While OnResize should be kept for Delphi compatibility, I still would suggest to add further events, according to the layout recalculation passes. Then OnResize could be deprecated, or get the new usage documented, while true Lazarus applications and components should use the new events.

DoDi

Reply by email