Skip to content

Commit e2d5373

Browse files
René HoffmannRené Hoffmann
authored andcommitted
refix methods 'GetLastVisibleNoInit' (and 'GetLastVisible') to return correct results even if some ancestor of the last visible node is effectively not visible itself
Changes from commit 8df8d6 for issue #725 performance reintroduced a regression, that prevents method 'TBaseVirtualTree.GetLastVisibleNoInit' from returning a visible node if some of its ancestors are hidden. That is because methods 'TBaseVirtualTree.GetLastVisibleChildNoInit' and 'GetPreviousVisibleSiblingNoInit' both only consider direct children of given 'Node' but ignore other descendant nodes' visibility. - completely revised method 'TBaseVirtualTree.GetLastVisibleNoInit' - kept the top-down-traversal algorithm of commit 8df8d6 in particular for optimal performance - iteratively checked expansion state (but not visibility) of every ancestor of given 'Node' in the first place to prevent a false-positive result in an effectively collapsed subtree - added conditional recursion for expanded nodes having some visible children (i.e. using fast 'vsAllChildrenHidden' state) as a local function 'IterateChildren' - apply 'ChildrenAbove' option on the conditions' precedence: - for top-down trees prefer visible children over their visible parent - for bottom-up trees prefer a visible node over its visible children - therefore extracted main condition as a local function 'GetNodeIsVisible' and recursion condition as a local function 'GetNodeHasVisibleChildren' ! thus a visible subnode under some effectively invisible but expanded ancestor will be found again
1 parent 63e73ef commit e2d5373

File tree

1 file changed

+63
-15
lines changed

1 file changed

+63
-15
lines changed

Source/VirtualTrees.pas

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28067,25 +28067,73 @@ function TBaseVirtualTree.GetLastVisibleNoInit(Node: PVirtualNode = nil;
2806728067
ConsiderChildrenAbove: Boolean = True; IncludeFiltered: Boolean = False): PVirtualNode;
2806828068

2806928069
// Returns the very last visible node in the tree while optionally considering toChildrenAbove.
28070+
// Note that the visibility of all ancestor nodes of the resulting node must not be considered.
2807028071
// No initialization is performed.
28071-
var
28072-
Next: PVirtualNode;
2807328072

28074-
begin
28075-
Result := GetLastVisibleChildNoInit(Node, IncludeFiltered);
28076-
if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then
28077-
while Assigned(Result) and (vsExpanded in Result.States) do
28073+
//--------------- local functions -------------------------------------------
28074+
28075+
function GetNodeIsVisible(ChildNode: PVirtualNode): Boolean;
28076+
begin
28077+
Result := (vsVisible in ChildNode.States) and
28078+
(IncludeFiltered or not IsEffectivelyFiltered[ChildNode]);
28079+
end;
28080+
28081+
function GetNodeHasVisibleChildren(ChildNode: PVirtualNode): Boolean;
28082+
begin
28083+
Result := (vsHasChildren in ChildNode.States) and
28084+
(vsExpanded in ChildNode.States) and
28085+
not (vsAllChildrenHidden in ChildNode.States);
28086+
end;
28087+
28088+
function IterateChildren(ParentNode: PVirtualNode): PVirtualNode;
28089+
var
28090+
Run: PVirtualNode;
28091+
begin
28092+
Result := nil;
28093+
28094+
Run := GetLastChildNoInit(ParentNode); // Do not use 'GetLastVisibleChildNoInit' here (see above).
28095+
while Assigned(Run) do
2807828096
begin
28079-
// Test if there is a next last child. If not keep the node from the last run.
28080-
// Otherwise use the next last child.
28081-
Next := GetLastChildNoInit(Result);
28082-
if Assigned(Next) and (not (vsVisible in Next.States) or
28083-
(not IncludeFiltered and IsEffectivelyFiltered[Next])) then
28084-
Next := GetPreviousVisibleSiblingNoInit(Next, IncludeFiltered);
28085-
if Next = nil then
28086-
Break;
28087-
Result := Next;
28097+
if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then
28098+
begin
28099+
if GetNodeIsVisible(Run) then
28100+
Result := Run
28101+
else if GetNodeHasVisibleChildren(Run) then
28102+
Result := IterateChildren(Run);
28103+
end else
28104+
begin
28105+
if GetNodeHasVisibleChildren(Run) then
28106+
Result := IterateChildren(Run)
28107+
else if GetNodeIsVisible(Run) then
28108+
Result := Run;
28109+
end;
28110+
28111+
if Assigned(Result) then
28112+
break;
28113+
28114+
Run := GetPreviousSiblingNoInit(Run);
2808828115
end;
28116+
end;
28117+
28118+
//--------------- end local functions ---------------------------------------
28119+
28120+
var
28121+
Run: PVirtualNode;
28122+
28123+
begin
28124+
Result := nil;
28125+
28126+
// First, check wether the given node and all its parents are expanded.
28127+
// If not, there can not be any visible child node.
28128+
Run := Node;
28129+
while Assigned(Run) and (Run <> RootNode) do
28130+
begin
28131+
if not (vsExpanded in Run.States) then
28132+
exit;
28133+
Run := Run.Parent;
28134+
end;
28135+
28136+
Result := IterateChildren(Node);
2808928137
end;
2809028138

2809128139
//----------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)