Recent

Author Topic: [SOLVED] TStringGrid - making focused cell visible again  (Read 483 times)

alpine

  • Hero Member
  • *****
  • Posts: 660
[SOLVED] TStringGrid - making focused cell visible again
« on: February 06, 2023, 06:02:28 pm »
It is probably a trivial one, but for the last couple of hours I just can't figure it out. In the TStringGrid, when the focused cell has gone out of sight (by scrolling with the scrollbars), how it can be made visible again?

There is IsCellVisible() but no MakeCellVisible().

Thanks in advance!
« Last Edit: February 08, 2023, 10:17:35 am by alpine »
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Blaazen

  • Hero Member
  • *****
  • Posts: 3156
  • POKE 54296,15
    • Eye-Candy Controls
Re: TStringGrid - making focused cell visible again
« Reply #1 on: February 06, 2023, 07:14:24 pm »
You can use property TopRow, which is the first visible row. The same is for columns: property LeftCol.
Code: Pascal  [Select][+][-]
  1.   if not StringGrid1.IsCellVisible(StringGrid1.Col, StringGrid1.Row) then
  2.     begin
  3.       StringGrid1.TopRow:=StringGrid1.Row;
  4.       StringGrid1.LeftCol:=StringGrid1.Col;
  5.     end;
So the selected cell will become top-left cell.
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

alpine

  • Hero Member
  • *****
  • Posts: 660
Re: TStringGrid - making focused cell visible again
« Reply #2 on: February 06, 2023, 07:38:37 pm »
You can use property TopRow, which is the first visible row. The same is for columns: property LeftCol.
Code: Pascal  [Select][+][-]
  1.   if not StringGrid1.IsCellVisible(StringGrid1.Col, StringGrid1.Row) then
  2.     begin
  3.       StringGrid1.TopRow:=StringGrid1.Row;
  4.       StringGrid1.LeftCol:=StringGrid1.Col;
  5.     end;
So the selected cell will become top-left cell.
Thanks for the reply!
However, I would prefer not to scroll in unnecessary directions, making it top-left will be fine when the focused cell is currently towards NW direction. In all other (7) it will be inappropriate - consider the focused cell is the farthest bottom-right and it is out of sight.

Edit:
Actually, there is a goScrollKeepVisible option, but with that on, the grid changes its Row and/or Col when scrolling with the scrollbars. I don't want this to happen because it will trigger some internal updates, etc.
« Last Edit: February 06, 2023, 07:46:53 pm by alpine »
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Blaazen

  • Hero Member
  • *****
  • Posts: 3156
  • POKE 54296,15
    • Eye-Candy Controls
Re: TStringGrid - making focused cell visible again
« Reply #3 on: February 06, 2023, 08:07:29 pm »
OK, there are also properties VisibleRowCount and VisibleColCount.
So you can do:
Code: Pascal  [Select][+][-]
  1. StringGrid1.TopRow:=StringGrid1.Row-(StringGrid1.VisibleRowCount div 2);
and selected cell will be in the middle (vertically).

Also, instead of IsCellVisible() you can use:
Code: Pascal  [Select][+][-]
  1. if (Row>=TopRow) and (Row<(TopRow+VisibleRowCount)) then
and you can manage vertical and horizontal shift separately.
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

alpine

  • Hero Member
  • *****
  • Posts: 660
Re: TStringGrid - making focused cell visible again
« Reply #4 on: February 08, 2023, 10:14:23 am »
OK, there are also properties VisibleRowCount and VisibleColCount.
So you can do:
Code: Pascal  [Select][+][-]
  1. StringGrid1.TopRow:=StringGrid1.Row-(StringGrid1.VisibleRowCount div 2);
and selected cell will be in the middle (vertically).
Not quite. VisibleRowCount could change depending on the different row heights (same with VisibleColCount and col widths).

Also, instead of IsCellVisible() you can use:
Code: Pascal  [Select][+][-]
  1. if (Row>=TopRow) and (Row<(TopRow+VisibleRowCount)) then
and you can manage vertical and horizontal shift separately.

Trying to cope with variable widths/heights, I've tried a "crawling" approach:
Code: Pascal  [Select][+][-]
  1. procedure MakeVisible(AGrid: TStringGrid; ACol, ARow: Integer);
  2. begin
  3.   AGrid.BeginUpdate; // Comment it to work properly
  4.   try
  5.      if ARow < AGrid.TopRow then
  6.        AGrid.TopRow := ARow
  7.      else
  8.        while AGrid.TopRow + AGrid.VisibleRowCount <= ARow do
  9.          AGrid.TopRow := AGrid.TopRow + 1;
  10.      if ACol < AGrid.LeftCol then
  11.        AGrid.LeftCol := ACol
  12.      else
  13.        while AGrid.LeftCol + AGrid.VisibleColCount <= ACol do
  14.          AGrid.LeftCol := AGrid.LeftCol + 1;
  15.   finally
  16.     AGrid.EndUpdate(True);  // Comment it to work properly
  17.   end;
  18. end;
But it doesn't work well when enclosed in BeginUpdate/EndUpdate - seems that VisibleXXXCount doesn't update properly. Commenting them out makes it plausible, but then you can visually see how the grid gradually gets there.

I finally came to the following solution:
Code: Pascal  [Select][+][-]
  1. procedure MakeVisible(AGrid: TStringGrid; ACol, ARow: Integer);
  2. var
  3.   L: Integer;
  4. begin
  5.   AGrid.BeginUpdate;
  6.   try
  7.     if ARow < AGrid.TopRow then
  8.      AGrid.TopRow := ARow  // towards NW, N, NE
  9.     else if AGrid.TopRow + AGrid.VisibleRowCount < ARow then
  10.     begin
  11.      if AGrid.FixedRows > 0
  12.        then L := AGrid.CellRect(0, Pred(AGrid.FixedRows)).Bottom
  13.        else L := 0;
  14.      Inc(L, AGrid.RowHeights[ARow]);
  15.      while ARow > AGrid.FixedRows do
  16.      begin
  17.        Inc(L, AGrid.RowHeights[Pred(ARow)]);
  18.        if L > AGrid.ClientHeight
  19.          then Break
  20.          else Dec(ARow)
  21.      end;
  22.      AGrid.TopRow := ARow; // towards SW, S, SE
  23.     end;
  24.     if ACol < AGrid.LeftCol then
  25.      AGrid.LeftCol := ACol  // towards NW, W, SW
  26.     else if AGrid.LeftCol + AGrid.VisibleColCount < ACol then
  27.     begin
  28.      if AGrid.FixedCols > 0
  29.        then L := AGrid.CellRect(Pred(AGrid.FixedCols), 0).Right
  30.        else L := 0;
  31.      Inc(L, AGrid.ColWidths[ACol]);
  32.      while ACol > AGrid.FixedCols do
  33.      begin
  34.        Inc(L, AGrid.ColWidths[Pred(ACol)]);
  35.        if L > AGrid.ClientWidth
  36.          then Break
  37.          else Dec(ACol)
  38.      end;
  39.      AGrid.LeftCol := ACol; // towards NE, E, SE
  40.     end;
  41.   finally
  42.     AGrid.EndUpdate(True);
  43.   end;
  44. end;  
Which scans backward to find how many cols/rows can fit into the scrollable area. It works fairly well, though it is not ideal, since it cannot set the topleft origin of the scrollable area in the middle of a cell - something that can be observed when you have columns wider than the grid itself.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

 

TinyPortal © 2005-2018