Sign in Join

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

Mailing lists / lazarus / 2016-08

Collapse/Expand "bug" in VirtualTreeView "fixed".

6 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

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

Jürgen Hestermann

I have investigated a bit more into the "bug" in VirtualTreeView where clicking on the plus or minus left to a node to collapse or expand it only works when this (main) column is the most left column.
When moving this main column away from the most left position (either in object inspector or on the fly on program run) then expanding/collapsing with the mouse no longer works.

After a lot of debugging the whole afternoon I think I now know the reason of this behaviour:

First step was the VTV procedure DetermineHitpositionLTR. The first code line is:

MainColumnHit := HitInfo.HitColumn = FHeader.MainColumn;

When, for example, the main column is 5 and I move this (main) column to the most left of all columns then both, HitInfo.HitColumn and FHeader.MainColumn are set to 5. So both match and MainColumnHit is true.
In this case everything works fine and the node is collapsed/expanded as expected.

But when I have the tree unchanged so that the main column is still at position 5 then HitInfo.HitColumn suddenly becomes 0!

Going further and checking where HitInfo.HitColumn is defined led me to HandleMouseDown.
This routine again uses GetHitTestInfoAt to set HitInfo:

-----------------------------------------------------------
HitInfo.HitColumn := FHeader.Columns.GetColumnAndBounds(Point(X, Y), ColLeft, ColRight, False);  // Here HitColumn is correct!
// If auto column spanning is enabled then look for the last non empty column.
if toAutoSpanColumns in FOptions.FAutoOptions then
begin
   InitialColumn := HitInfo.HitColumn;
   // Search to the left of the hit column for empty columns.
   while (HitInfo.HitColumn > NoColumn) and ColumnIsEmpty(HitInfo.HitNode, HitInfo.HitColumn) do // this is true...
   begin
     NextColumn := FHeader.FColumns.GetPreviousVisibleColumn(HitInfo.HitColumn); // ... so HitColumn will be decreased down to 0!
     if NextColumn = InvalidColumn then
       Break;
     HitInfo.HitColumn := NextColumn;
[...]
-----------------------------------------------------------

Here HitColumn starts with the correct value 5 but then, depending on ColumnIsEmpty, is decreased to zero!

Looking at ColumnIsEmpty shows this comment:

// Returns True if the given column is to be considered as being empty. This will usually be determined by
// descendants as the base tree implementation has not enough information to decide.

Its body is:

-----------------------------------------------------------
begin
Result := True;
if Assigned(FOnGetCellIsEmpty) then
    FOnGetCellIsEmpty(Self, Node, Column, Result);
end;
-----------------------------------------------------------

So as default, all cells are considered to be empty! But when adding an event routine OnGetCellsEmpty that always sets

IsEmpty := false;

then suddenly everything works as expected and the bug is gone!

I am wondering whether this is all as intended.
Why is the column changed in GetColumnAndBounds
depending on whether a cell is empty?
It makes no sense to me.

Reply by email

Jürgen Hestermann
to Jürgen Hestermann
Jürgen Hestermann wrote
I am wondering whether this is all as intended. Why is the column changed in GetColumnAndBounds depending on whether a cell is empty? It makes no sense to me.

I think the code in GetHitTestInfoAt needs changes (is buggy).

For example, the searching to the right for an non-empty column has no effect as it does not change HitInfo.HitColumn:

-----------------------------------------------------------
// Search to the right of the hit column for empty columns.
repeat
    InitialColumn := FHeader.FColumns.GetNextVisibleColumn(InitialColumn);
    if (InitialColumn = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, InitialColumn) then
       Break;
    Inc(ColRight,FHeader.FColumns[InitialColumn].Width);
until False;
-----------------------------------------------------------

It only changes InitialColumn but not HitInfo.HitColumn.

In the end, when *all* cells (columns) of a row are empty (as ist the case as default if no event routine OnGetCellsEmpty is defined by the programmer) then GetHitTestInfoAt should report the original column and not change this default (correct) column value. In this case the bug would be avoided even without the workaround of defining an event routine OnGetCellsEmpty with IsEmpty := false.

Another option would be to set

Result := False;

in ColumnIsEmpty.
As this routine does not know about the state anyway setting it to true is not good. Setting it to false would avoid the issue above too.

Reply by email

Luiz Americo Pereira Camara
to Jürgen Hestermann: I have investigated a bit more into the "bug" in VirtualTreeView where clicking on the…

2016-08-23 12:11 GMT-03:00 Jürgen Hestermann :

Show quoted text 6 lines
I have investigated a bit more into the "bug" in VirtualTreeView where clicking on the plus or minus left to a node to collapse or expand it only works when this (main) column is the most left column.

Do you know what is the behavior with Delphi?

Can you open a issue with a sample project?

Luiz
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20160823/1e2fdf18/attachment.html>

Reply by email

Jürgen Hestermann
to Luiz Americo Pereira Camara
Luiz Americo Pereira Camara wrote 5 lines
2016-08-23 12:11 GMT-03:00 Jürgen Hestermann : I have investigated a bit more into the "bug" in VirtualTreeView where clicking on the plus or minus left to a node to collapse or expand it only works when this (main) column is the most left column. Do you know what is the behavior with Delphi?

No, unfortunately I am not using Delphi.
But if the same code is used with Delphi, why should it behave differently?

Can you open a issue with a sample project?

What do you mean?
Open a bug report?
I am not sure how to do that...

I have changed my VirtualTreeView.pas file now and the bug is gone. So if anybody encounters the same problem (and IMO it must be everybody who uses hierarchical nodes when the main column is *not* the most left one) can use the mentioned workaround or even modify his VirtualTreeView.pas file too.
Of course, it would be better, if this can be fixed in the official sources too but I am not able to do that.

Another question is, why GetHitTestInfoAt searches for a non-empty cell while VTV does not even know how such cells are defined. I don't see any rational behind it.

And even if that makes sense it does not make sense to set the default Result in ColumnIsEmpty to true.
IMO it has to be false.
If the programmer adds an event routine OnGetCellsEmpty he can override this behaviour but the default should be false. It took me years to find out that I can circumvent this bug by adding an OnGetCellsEmpty event routine.

Reply by email

Jürgen Hestermann
to Jürgen Hestermann
Jürgen Hestermann wrote
I have changed my VirtualTreeView.pas file now and the bug is gone. So if anybody encounters the same problem (and IMO it must be everybody who uses hierarchical nodes when the main column is *not* the most left one) ...

{...]

Another question is, why GetHitTestInfoAt searches for a non-empty cell while VTV does not even know how such cells are defined. I don't see any rational behind it.

It seems that I have overlooked that all my observations only apply if (toAutoSpanColumns in FOptions.FAutoOptions) is true. That may explain why this issue has not popped up earlier. I have set toAutoSpanColumns a long time ago but meanwhile do all the drawing myself so it does not matter to me whether it is set or not.

Nevertheless, the searching for non-empty cells is only useful if the event routine OnGetCellIsEmpty has been defined by the programmer. If he did not do this, all cells are considered to be empty which makes no sense. Therefore IMO this if-request in GetHitTestInfoAt

if toAutoSpanColumns in FOptions.FAutoOptions then

needs to be enhanced by checking the OnGetCellIsEmpty routine:

if (toAutoSpanColumns in FOptions.FAutoOptions) and assigned(FOnGetCellIsEmpty) then

If FOnGetCellIsEmpty=nil, then the checking for empty cells should be skipped in the same way as if toAutoSpanColumns is not set. It makes no sense to check for empty cells when all cells are always considered to be empty.

And IMO the bug still exists that *if* the code for searching non-empty cells is run, then it does not do this correctly when searching to the right as HitColumn is not changed in this case.

And also I still think that the result of ColumnIsEmpty should default to false, not true. It would avoid confusion and unexpected results.

Reply by email

Luiz Americo Pereira Camara
to Jürgen Hestermann: No, unfortunately I am not using Delphi. But if the same code is used with Delphi, why…

2016-08-24 6:04 GMT-03:00 Jürgen Hestermann :

Show quoted text 13 lines
Am 2016-08-23 um 18:36 schrieb Luiz Americo Pereira Camara: > 2016-08-23 12:11 GMT-03:00 Jürgen Hestermann < >: > I have investigated a bit more into the "bug" in VirtualTreeView where clicking > on the plus or minus left to a node to collapse or expand it only works when > this (main) column is the most left column. > Do you know what is the behavior with Delphi? No, unfortunately I am not using Delphi. But if the same code is used with Delphi, why should it behave differently?

This can be already fixed in Delphi master repository and we could simply port the changes to 4,8 and 5.5 versions

Show quoted text 6 lines
> Can you open a issue with a sample project? What do you mean? Open a bug report?

Yes.
With a sample project attached so it can be reproduced

I am not sure how to do that...

http://wiki.freepascal.org/How_do_I_create_a_bug_report

Select Lazarus CCR <http://bugs.freepascal.org/set_project.php?project_id=9> project and VirtualTreeView category

or https://github.com/blikblum/VirtualTreeView-Lazarus/issues

Luiz
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20160824/d93340d4/attachment.html>

Reply by email