Recent

Author Topic: [Solved] TShellTreeView scroll to selected folder  (Read 1273 times)

speter

  • Sr. Member
  • ****
  • Posts: 487
[Solved] TShellTreeView scroll to selected folder
« on: February 21, 2025, 03:30:56 am »
G'Day Folks

I am using TShellTreeView is a program; I am setting the "path", but when the program runs, the selected folder is not in view.

What do I need to do, to scroll the View to the selected node?

At present, I am simply setting .path in the formcreate event:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   ShellTreeView1.path := 'c:\windows';
  4.   memo1.append('path = '+ShellTreeView1.Path);
  5. end;

I am attaching a simple project to show what I mean.
Lazarus 3.2; FPC 3.2.2. Windows 11.

cheers
S.




« Last Edit: February 23, 2025, 10:49:57 pm by speter »
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

dsiders

  • Hero Member
  • *****
  • Posts: 1525
Re: TShellTreeView scroll to selected folder
« Reply #1 on: February 21, 2025, 04:45:59 am »
G'Day Folks

I am using TShellTreeView is a program; I am setting the "path", but when the program runs, the selected folder is not in view.

What do I need to do, to scroll the View to the selected node?

At present, I am simply setting .path in the formcreate event:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   ShellTreeView1.path := 'c:\windows';
  4.   memo1.append('path = '+ShellTreeView1.Path);
  5. end;

I am attaching a simple project to show what I mean.
Lazarus 3.2; FPC 3.2.2. Windows 11.

cheers
S.

I'm on Linux, so I changed the OnCreate to the following:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   //ShellTreeView1.path := 'c:\windows';
  4.   ShellTreeView1.Path := '/home/don/lazarus/projects/ShellTreeMkVis';
  5.   // either of the following work... depending on where you want the selected node to appear
  6.   //if (ShellTreeView1.Selected <> Nil) then ShellTreeView1.TopItem := ShellTreeView1.Selected;
  7.   if (ShellTreeView1.Selected <> Nil) then ShellTreeView1.Selected.MakeVisible;
  8.   memo1.append('path = '+ShellTreeView1.Path);
  9. end;
« Last Edit: February 21, 2025, 04:55:54 am by dsiders »

speter

  • Sr. Member
  • ****
  • Posts: 487
Re: TShellTreeView scroll to selected folder
« Reply #2 on: February 22, 2025, 08:12:31 am »
Thanks very much for the reply dsiders!

I changed my formcreate to:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   ShellTreeView1.path := 'c:\windows';
  4.   memo1.append('path = '+ShellTreeView1.Path);
  5.  
  6.   if (ShellTreeView1.Selected = Nil) then
  7.     memo1.append('Selected = Nil')
  8.   else
  9.     begin
  10.       ShellTreeView1.Selected.MakeVisible;
  11.       memo1.append('Selected.MakeVisible')
  12.     end;
  13. end;

Unfortunately, that only resulted in the tree view scrolling one line, and the selected folder _still_ not showing (see attached image).

I guess it could be a windows *thing*!?

cheers
S.
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

wp

  • Hero Member
  • *****
  • Posts: 13353
Re: TShellTreeView scroll to selected folder
« Reply #3 on: February 22, 2025, 11:41:42 am »
Your code in the initial post is working when the font size is reset to 0 (default size). Maybe the node height is not considered correctly. (*) Please post a bug report.

[EDIT]
(*) Yes. ANode.Height is 20, independently of Font.Size. There is a DefaultItemHeight from which the Node.Height is calculated and which in my opinion should react to font change, but the corresponding UpdateDefaultItemHeight method always exits prematurely because the handles are not allocated. Seems to be a bit tricky to resolve...
« Last Edit: February 22, 2025, 01:23:36 pm by wp »

jcmontherock

  • Sr. Member
  • ****
  • Posts: 336
Re: TShellTreeView scroll to selected folder
« Reply #4 on: February 22, 2025, 12:16:59 pm »
The only solution I found:
Code: Pascal  [Select][+][-]
  1. function TForm1.SelectNodeFromFilePath(sFilePath: String): TTreeNode;
  2. var
  3.   arPath: TStringArray;
  4.   i:      Integer;
  5. begin
  6.   Result := Nil;
  7.   arPath := Nil;
  8.   arPath := sFilePath.Split('\');
  9.   for i := 0 to High(arPath) do begin
  10.     Result := ShellTreeView1.Items.FindNodeWithText(arPath[i]);
  11.     if Assigned(Result) then begin
  12.       if (i < High(arPath)) And (Result.HasChildren = True) then Result.Expanded := True
  13.       else
  14.       if Assigned(Result) = True then Result.Selected := True;
  15.     end;
  16.   end;
  17.   if Assigned(Result) then Result.MakeVisible;
  18. end;
  19.  
Windows 11 UTF8-64 - Lazarus 4.4-64 - FPC 3.2.2

speter

  • Sr. Member
  • ****
  • Posts: 487
Re: TShellTreeView scroll to selected folder
« Reply #5 on: February 23, 2025, 01:09:16 am »
Thanks folks. jcmontherock's code "just" calls MakeVisible (which was already happening).

There is a method called EnsureNodeIsVisible (TCustomTreeView.EnsureNodeIsVisible) which seems to do the job; but
Code: Pascal  [Select][+][-]
  1. ShellTreeView1.EnsureNodeIsVisible(ShellTreeView1.Selected);
gives an error (identifier idents no member "EnsureNodeIsVisible"); so maybe it is private.

Looking at the code of the (above) method, it includes:
Code: Pascal  [Select][+][-]
  1.   if ANode.Top<ScrolledTop then
  2.     ScrolledTop:=ANode.Top
  3.   else begin
  4.     b:=ANode.Top+ANode.Height-GetNodeDrawAreaHeight;
  5.     if ScrolledTop<b then ScrolledTop:=b;
  6.   end;
but "ScrolledTop" is a protected property so I can't access it (to compare the node's Top with the control's view top).

Maybe I can use the tree's scroll bar...!?

cheers
S.
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

dsiders

  • Hero Member
  • *****
  • Posts: 1525
Re: TShellTreeView scroll to selected folder
« Reply #6 on: February 23, 2025, 08:51:29 am »
Your code in the initial post is working when the font size is reset to 0 (default size). Maybe the node height is not considered correctly. (*) Please post a bug report.

[EDIT]
(*) Yes. ANode.Height is 20, independently of Font.Size. There is a DefaultItemHeight from which the Node.Height is calculated and which in my opinion should react to font change, but the corresponding UpdateDefaultItemHeight method always exits prematurely because the handles are not allocated. Seems to be a bit tricky to resolve...

I modified TCustomTreeView.UpdateDefaultItemHeight so that Canvas.HandleAllocated is not checked before calling TextHeight(). Turns out the handle gets created when needed in TextHeight() by calling RequireState(). It works on  QT6 - but I have not tested it for other widgetsets yet.

It looks like this:


Or the diff file in the attachment.

[ Edited ]

The code introduced errors. I removed it. Sorry for the noise.

« Last Edit: February 23, 2025, 05:47:58 pm by dsiders »

wp

  • Hero Member
  • *****
  • Posts: 13353
Re: TShellTreeView scroll to selected folder
« Reply #7 on: February 23, 2025, 06:17:08 pm »
Looking at this issue again in more detail, I am convinced that there is no bug at all. It is just the place where TreeView.Selected.MakeVisible is called.

You do this in the OnCreate event of the form, and this is too early: There is no Handle for the canvas, yet, and UpdateDefaultItemHeight cannot deliver a value for the node height. dsiders fixed this by forcing creation of the handle, but this is only half of the story. Because if you are on a high resolution monitor the important step of scaling dimensions in AutoAdjustLayout has not yet been executed. So, dsiders' solution might work for normal monitors but the issue will re-appear on a high-res screen. Of course, this could probably be worked around. But: the more code, the higher the risk of introducing another bug. In particular, because there is a much simpler solution: Put your code into the OnShow or OnActivate event handler of the form. Here everything is ready, and MakeVisible works as expected.
« Last Edit: February 23, 2025, 06:53:02 pm by wp »

dsiders

  • Hero Member
  • *****
  • Posts: 1525
Re: TShellTreeView scroll to selected folder
« Reply #8 on: February 23, 2025, 06:47:51 pm »
Looking at this issue again in more detail, I am convinced that there is no bug at all. It is just the place where TreeView.Selected.MakeVisible is called.

You do this in the OnCreate event of the form, and this is too early: There is no Handle for the canvas, yet, and UpdateDefaultItemHeight cannot deliver a value for the node height. dsiders fixed this by forcing creation of the handle, but this is only half of the story. Because if you are on a high resolution monitor the important step of scaling dimensions in AutoAdjustLayout has not yet been executed. So, dsiders' solution might work for normal monitors but the issue will re-appear on a high-res screen. Of course, this could be worked around. But: the more code, the higher the risk of introducing another bug. In particular, because there is a much simpler solution: Put your code into the OnShow or OnActivate event handler of the form. Here everything is ready, and MakeVisible works as expected.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormActivate(Sender: TObject);
  2. begin
  3.   ShellTreeView1.Path := '/home/don/lazarus/projects/ShellTreeMkVis';
  4.   Memo1.Append('Path = ' + ShellTreeView1.Path);
  5.   Memo1.Append('Selected = ' + ShellTreeView1.Selected.GetTextPath);
  6. end;

Works for me. Thanks @wp.

wp

  • Hero Member
  • *****
  • Posts: 13353
Re: TShellTreeView scroll to selected folder
« Reply #9 on: February 23, 2025, 07:05:24 pm »
It should be mentioned that there's a problem with the OnActivate and OnShow events: They can be fired multiple times while OnCreate fires only once. This can lead to "surprises" for the user. Single execution, therefor, should be enforced by using a status variable:
Code: Pascal  [Select][+][-]
  1. type
  2.   TForm1 = class(TForm)
  3.     ...
  4.   private
  5.     FActivated: Boolean;
  6.     ...
  7.   end;
  8.  
  9.   procedure TForm1.FormActivate(Sender: TObject);
  10.   begin
  11.     if not FActivated then
  12.     begin
  13.       ShellTreeView1.Path := 'c:\Windows';
  14.       FActivated := true;
  15.   end;
  16. end;

dsiders

  • Hero Member
  • *****
  • Posts: 1525
Re: TShellTreeView scroll to selected folder
« Reply #10 on: February 23, 2025, 07:09:09 pm »
It should be mentioned that there's a problem with the OnActivate and OnShow events: They can be fired multiple times while OnCreate fires only once. This can lead to "surprises" for the user. Single execution, therefor, should be enforced by using a status variable:
Code: Pascal  [Select][+][-]
  1. type
  2.   TForm1 = class(TForm)
  3.     ...
  4.   private
  5.     FActivated: Boolean;
  6.     ...
  7.   end;
  8.  
  9.   procedure TForm1.FormActivate(Sender: TObject);
  10.   begin
  11.     if not FActivated then
  12.     begin
  13.       ShellTreeView1.Path := 'c:\Windows';
  14.       FActivated := true;
  15.   end;
  16. end;

Now that you've pointed out the real issue... this works too.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   Application.QueueAsyncCall(@ShowInfo, 0);
  4. end;
  5.  
  6. procedure TForm1.ShowInfo(AData: PtrInt);
  7. begin
  8.   ShellTreeView1.Path := '/home/don/lazarus/projects/ShellTreeMkVis';
  9.   Memo1.Append('Path = ' + ShellTreeView1.Path);
  10.   Memo1.Append('Selected = ' + ShellTreeView1.Selected.GetTextPath);
  11. end;
  12.  

wp

  • Hero Member
  • *****
  • Posts: 13353
Re: TShellTreeView scroll to selected folder
« Reply #11 on: February 23, 2025, 07:55:04 pm »
Yes, I can confirm this also on high-dpi.

speter

  • Sr. Member
  • ****
  • Posts: 487
Re: TShellTreeView scroll to selected folder
« Reply #12 on: February 23, 2025, 10:48:00 pm »
Thanks very much wp and dsiders.

Setting the path in FormActivate works.

cheers
S.
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

 

TinyPortal © 2005-2018