Recent

Author Topic: [SOLVED] Set horizontal scroll position in a TMemo does not work (Windows only)  (Read 4002 times)

Hartmut

  • Hero Member
  • *****
  • Posts: 1131
I want a feature, that the content of a Memo can be replaced by a new content, but the previous cursor- and scroll-positions in the Memo shall be unchanged (restored). For problems to restore the vertical scroll position on Linux I had another Topic last week and found a solution. Now I have problems to restore the horizontal scroll position on Windows and hope to find a solution/workaround here.

This is a short demo for my current code:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, LCLType;
  9.  
  10. type
  11.  TForm1 = class(TForm)
  12.   Memo1: TMemo;
  13.   procedure FormCreate(Sender: TObject);
  14.   procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  15.  end;
  16.  
  17. var Form1: TForm1;
  18.  
  19. implementation
  20.  
  21. {$R *.lfm}
  22.  
  23. procedure TForm1.FormCreate(Sender: TObject);
  24.    var s: string;
  25.    begin
  26.    s:='1234qwert' + LineEnding        {fill the Memo with demo text: }
  27.     + 'xxx enable/disable deprecated unixutil.EpochToLocal() + unixutil.LocalToEpoch(): ]'
  28.     + LineEnding;
  29.  
  30.    Memo1.ScrollBars:=ssAutoBoth;
  31.    Memo1.Text:=s;
  32.    end;
  33.  
  34. procedure wait_ms_AppProcMsg(ms: word);
  35.    {waits some ms and calls Application.ProcessMessages repeatedly}
  36.    const Step = 125;
  37.    var n: word;
  38.    begin
  39.    while ms > 0 do
  40.       begin
  41.       n:=Step; if n > ms then n:=ms;
  42.       sleep(n);
  43.       Application.ProcessMessages;
  44.       dec(ms,n);
  45.       end;
  46.    end;
  47.  
  48. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  49.    {Key 'F5' shows a changed content in the Memo and the previous cursor- and
  50.     scroll-positions in the Memo shall be unchanged (restored)}
  51.    var curpos,vpos,hpos,dummy: longint;
  52.    begin
  53.    if Key <> VK_F5 then exit;
  54.  
  55.    curpos:=Memo1.SelStart;             {remember previous cursor position}
  56.    vpos:=Memo1.VertScrollBar.Position; {remember previous scroll position}
  57.    hpos:=Memo1.HorzScrollBar.Position; {remember previous scroll position}
  58.    writeln('curpos=', curpos, ' vpos=', vpos, ' hpos=', hpos);
  59.  
  60.    Memo1.Text:=Memo1.Text + 'hello' + LineEnding; {simulate Memo content changed}
  61.  
  62.    dummy:=Memo1.VertScrollBar.Position; // update scroll cache (?)
  63.    dummy:=Memo1.HorzScrollBar.Position;
  64.    Memo1.VertScrollBar.Position:=vpos;  {restore vertical scroll position}
  65. // write('1---'); wait_ms_AppProcMsg(4000); writeln('1'); {wait 4 s}
  66.    Memo1.HorzScrollBar.Position:=hpos;  {restore horizontal scroll position}
  67. // write('2---'); wait_ms_AppProcMsg(4000); writeln('2'); {wait 4 s}
  68.    Memo1.SelStart:=curpos;              {restore cursor position}
  69.    writeln('hpos=', Memo1.HorzScrollBar.Position);
  70.    end;
  71.  
  72. end.
  73.  
It works to restore the cursor position and vertical scroll position, but for the horizontal scroll position there can be 2 different wrong behaviours.

How to produce wrong behaviour #1:
 - compile and run attached project
 - scroll horizontally until "1234" disappears left and "qwert" is still visible
 - set the cursor behind "qwert" and press key F5
Result: the Memo is scrolled completely to the left (as if 'hpos' would be '0', but 'hpos' is about 24).

How to produce wrong behaviour #2:
 - scroll again horizontally until "1234" disappears left and "qwert" is still visible
 - set the cursor in line 2 directly left of "EpochToLocal" and press key F5
Results:
 - the Memo is scrolled completely to the right (as if 'hpos' would be about 110, but 'hpos' is about 24)
 - but the horizontal ScrollBar itself has the correct position
 - if you click on this ScrollBar, then the Memo scrolls correctly to it's position before pressing F5

When you activate lines 65 and 67 than you can see:
 - after 'Memo1.VertScrollBar.Position:=vpos' the Memo is scrolled to the left (the default after it's content was changed)
 - directly after 'Memo1.HorzScrollBar.Position:=hpos' the Memo is scrolled to the right end. So this does not occur by 'Memo1.SelStart:=curpos'.

I tested on Win10 64-bit and Win7 32-bit and compiled (32-bit) with Lazarus/FPC 3.6.0/3.2.2 and 2.0.10/3.2.0 (same result).

Question 1
Is it possible to "refresh" the Memo at the end in any kind, so that it is scrolled to it's correct position accordingly to 'hpos'?
I tried Memo1.Update, .Refresh, .Repaint and .Invalidate without success.

Question 2
When I click on the horizontal ScrollBar (after "wrong behaviour #2"), then the Memo scrolls to it's correct position. Can somebody point me to the Sources, where this mouse-click is handled and the "trigger" for the Memo is activated, so that it scrolls corecctly?
Maybe I could call this code (or copy it and call it).

Or has somebody another / better idea? Thanks in advance.
« Last Edit: April 05, 2026, 05:21:28 pm by Hartmut »

jamie

  • Hero Member
  • *****
  • Posts: 7661
reset the cursor Pos first before the scrollbar reset.

Code: Pascal  [Select][+][-]
  1.    Memo1.SelStart:=curpos;
  2.    Memo1.HorzScrollBar.Position:=hpos; {restore horizontal scroll position}
  3.    Application.ProcessMessages;                                          
  4.  

Jamie
The only true wisdom is knowing you know nothing

Hartmut

  • Hero Member
  • *****
  • Posts: 1131
Hello jamie, I tried your suggestion but it does not work for me:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  2.    {Key 'F5' shows a changed content in the Memo and the previous cursor- and
  3.     scroll-positions in the Memo shall be unchanged (restored)}
  4.    var curpos,vpos,hpos,dummy: longint;
  5.    begin
  6.    if Key <> VK_F5 then exit;
  7.  
  8.    curpos:=Memo1.SelStart;             {remember previous cursor position}
  9.    vpos:=Memo1.VertScrollBar.Position; {remember previous scroll position}
  10.    hpos:=Memo1.HorzScrollBar.Position; {remember previous scroll position}
  11.    writeln('curpos=', curpos, ' vpos=', vpos, ' hpos=', hpos);
  12.  
  13.    Memo1.Text:=Memo1.Text + 'hello' + LineEnding; {simulate changed Memo content}
  14.  
  15.    dummy:=Memo1.HorzScrollBar.Position; // update scroll cache (?)
  16.  
  17.    Memo1.SelStart:=curpos;             {restore cursor position}
  18.    Memo1.HorzScrollBar.Position:=hpos; {restore horizontal scroll position}
  19.    Application.ProcessMessages;
  20.    end;

I skipped restoring vertical scroll position temporarily (because you skipped it and that it does not disturb).

With command 'dummy:=Memo1.HorzScrollBar.Position' now both tests (see behaviour #1 and #2 above) scroll the Memo completely to the right.
Without command 'dummy:=Memo1.HorzScrollBar.Position' now both tests (see behaviour #1 and #2 above) scroll the Memo completely to the left.

Did you test your suggestion? Which detailed Lazarus/FPC-Version did you use? And which OS-Version?

jamie

  • Hero Member
  • *****
  • Posts: 7661
Yes of corse i tested it, i down loaded your test app and used laz 4.6 64 bit windows 10
4.6 also has the tabcontrol patch in it.
Btw, if u havent yet you should put that patch in.
Jamie
The only true wisdom is knowing you know nothing

Hartmut

  • Hero Member
  • *****
  • Posts: 1131
Thank you jamie for testing and your reply. That sounds that Laz 4.6 might have a bugfix that it works and with Laz 3.6 it not works. I searched in Bugtracker for "Memo scroll" and got 8 matches, but nothing what might apply to my problem. So I'm afraid that installing 4.6 might be useless, if the issue might be related to something else.

Do you have an older Laz install (e.g. something between 3.6 and 2.0.10) and want to start my demo there, to see if you get the issue then?

4.6 also has the tabcontrol patch in it.
I never heard about this patch. Do you have more infos about it? How is something with "tab control" related to memo scrolling?

Quote
Btw, if u havent yet you should put that patch in.
Does that mean, that this patch is not *always* included in any 4.6 version?

jamie

  • Hero Member
  • *****
  • Posts: 7661
No, the patch isn't in the recent release of 4.6.

if you go to the page where the release was anounced, you can see the changes list, in it it shows changes after the release and you can fix it yourself.

It's in the TabControl.inc file. Correct it and rebuild the ide.

Anyone interested can simply drop a TTabControl on the form and then try to delete it from the IDE to see what happens.

I can't say the patch that fixed here really was the issue but appears that maybe a TabControl is getting freed twice and safeguards were not in place to work around it.

Jamie

https://gitlab.com/freepascal.org/lazarus/lazarus/-/commits/fixes_4/
« Last Edit: March 08, 2026, 01:43:02 pm by jamie »
The only true wisdom is knowing you know nothing

Hartmut

  • Hero Member
  • *****
  • Posts: 1131
No, the patch isn't in the recent release of 4.6.
Do you mean this patch? https://gitlab.com/freepascal.org/lazarus/lazarus/-/commit/30ad5cc722d7a90d4f999984b6aa6e4726f64a5f
From what I see there, I can't imagine, that this patch is anyhow involved in my Memo scrolling issue...

Because you mentioned a "tabcontrol patch" in combination with my Memo scrolling issue: if you mean an other patch and if this other patch would be the "fix" for my issue - and not Laz 4.6, wherefore I found no Memo scrolling fix in bugtracker -  then it would be in vail to install Laz 4.6 and maybe it would be better to try, if I can apply the correct patch in Laz 3.6

Even if there would be a fix for my Memo scrolling issue in Laz 4.6, it would not help me directly, because I would not want the effort to migrate this project from 3.6 to 4.6 only for this "small issue". In this case, I would hope to detect the "fix" in 4.6 and try to patch it into my 3.6 if possible.

Does anybody know about this fix? Or has a better idea to find it in bugtracker?
Or has someone an idea for a solution or workaround which works in Laz 3.6?

jamie

  • Hero Member
  • *****
  • Posts: 7661
I never implied that it has anything to do with your problem of the Tmemo.

I merely suggested that you could apply that patch because it causes issues due to the nature of its existence with other code.

I assumed by this time you may have moved up a little in versions. I know I stuck to one for some time, along with the compiler version due to newer versions of the compiler not allowing some code practices and some newer releases of Lazarus and the compiler too, generating code in a DLL that does not work correctly anymore. My solution was to keep the DLL at the lower version where I know it works and moved up to a newer level of compiler and IDE for the rest of it.

 As for the TMEMO, it isn't a control of one you think you can rely on to maintain its stature because it is a windows control and subjected to changes vis the OS.

 The Last time I checked, the TMemo on other widgets they do not implement the TMemo the way windows does and thus may even work better to your likely.

 The windows memo does not have a line-by-line association with the Lines property verses the view on the screen.

 I don't know what your intent use is, but I believe you would be better off using the Tsynedit object.

 You can do whatever you wish, it's your code.

 Jamie

 
The only true wisdom is knowing you know nothing

Hartmut

  • Hero Member
  • *****
  • Posts: 1131
Hello jamie, I appreciate that you try to help me but for me it is difficult to understand you.

As for the TMEMO, it isn't a control of one you think you can rely on to maintain its stature because it is a windows control and subjected to changes vis the OS.

The Last time I checked, the TMemo on other widgets they do not implement the TMemo the way windows does and thus may even work better to your likely.

The windows memo does not have a line-by-line association with the Lines property verses the view on the screen.
Sorry, I don't see how this can help me to solve the scroll issue...

Quote
I don't know what your intent use is, but I believe you would be better off using the Tsynedit object.
To replace the TMemo by a TSynedit would be no option in this project.

My intent is what I wrote:
Quote
I want a feature, that the content of a Memo can be replaced by a new content, but the previous cursor- and scroll-positions in the Memo shall be unchanged (restored).

rvk

  • Hero Member
  • *****
  • Posts: 6989
How to produce wrong behaviour #1:
 - compile and run attached project
 - scroll horizontally until "1234" disappears left and "qwert" is still visible
 - set the cursor behind "qwert" and press key F5
Result: the Memo is scrolled completely to the left (as if 'hpos' would be '0', but 'hpos' is about 24).
I just curious... how do you scroll, on line 2, to the right with having qwert still in view?
When I do this, at first scroll, 1234qwert completely scroll out of view.

Nevertheless... you can't minipulate the scrollbars and expect the memo-view to follow. They are separate things in Lazarus. You need to use the scroll functions of Windows itself.

Was something like this your desired result??? (I only did horizontal here). Begin/EndUpdate eliminates the fickering.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
  2. var
  3.   CurPos: TPoint;
  4.   ScrollPos: integer;
  5. begin
  6.   if Key <> VK_F5 then exit;
  7.  
  8.   Memo1.Lines.BeginUpdate;
  9.   try
  10.     ScrollPos := GetScrollPos(Memo1.Handle, SB_HORZ);
  11.     CurPos := Memo1.CaretPos;
  12.     Memo1.Text := Memo1.Text + 'hello' + LineEnding; {simulate changed Memo content}
  13.     Memo1.CaretPos := CurPos;
  14.     SendMessage(Memo1.Handle, WM_HSCROLL, MakeLong(SB_THUMBPOSITION, ScrollPos), 0);
  15.     SendMessage(Memo1.Handle, WM_HSCROLL, SB_ENDSCROLL, 0);
  16.   finally
  17.     Memo1.Lines.EndUpdate;
  18.   end;
  19. end;

BTW. If you also want to keep the selected text (if any) you can also save the SelStart and Length and set it back instead of CaretPos. But you need to save/restore both, not just SelStart as you did.
« Last Edit: March 08, 2026, 06:38:23 pm by rvk »

rvk

  • Hero Member
  • *****
  • Posts: 6989
Re: Set horizontal scroll position in a TMemo does not work (Windows only)
« Reply #10 on: March 08, 2026, 06:38:34 pm »
Updated version with selected text and vertical scroll (Windows only):

Code: Pascal  [Select][+][-]
  1. uses Windows;
  2.  
  3. procedure TForm1.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
  4. var
  5.   ScrollPos: TPoint;
  6.   OldSelStart, OldSelLength: Integer;
  7. begin
  8.   if Key <> VK_F5 then exit;
  9.  
  10.   Memo1.Lines.BeginUpdate;
  11.   try
  12.     ScrollPos.X := GetScrollPos(Memo1.Handle, SB_VERT);
  13.     ScrollPos.Y := GetScrollPos(Memo1.Handle, SB_HORZ);
  14.     OldSelStart := Memo1.SelStart;
  15.     OldSelLength := Memo1.SelLength;
  16.     Memo1.Text := Memo1.Text + 'hello' + LineEnding; {simulate changed Memo content}
  17.     Memo1.SelStart := OldSelStart;
  18.     Memo1.SelLength := OldSelLength;
  19.     SendMessage(Memo1.Handle, WM_VSCROLL, MakeLong(SB_THUMBPOSITION, ScrollPos.X), 0);
  20.     SendMessage(Memo1.Handle, WM_VSCROLL, SB_ENDSCROLL, 0);
  21.     SendMessage(Memo1.Handle, WM_HSCROLL, MakeLong(SB_THUMBPOSITION, ScrollPos.Y), 0);
  22.     SendMessage(Memo1.Handle, WM_HSCROLL, SB_ENDSCROLL, 0);
  23.   finally
  24.     Memo1.Lines.EndUpdate;
  25.   end;
  26. end;

Hartmut

  • Hero Member
  • *****
  • Posts: 1131
Re: Set horizontal scroll position in a TMemo does not work (Windows only)
« Reply #11 on: March 08, 2026, 07:09:08 pm »
How to produce wrong behaviour #1:
 - compile and run attached project
 - scroll horizontally until "1234" disappears left and "qwert" is still visible
 - set the cursor behind "qwert" and press key F5
Result: the Memo is scrolled completely to the left (as if 'hpos' would be '0', but 'hpos' is about 24).
I just curious... how do you scroll, on line 2, to the right with having qwert still in view?
When I do this, at first scroll, 1234qwert completely scroll out of view.

Maybe you mean "behaviour #2"? There:
 - I scroll to the left, until "1234" disappears left (and "qwert" is still visible)
 - after this I set the cursor in line 2 directly left of "EpochToLocal"
 - after this I press key F5
 - after this *the program* scrolles the Memo completely to the right (and then "qwert" is not longer visible). Sorry if my description was not clear enough.

Currently I had only time for a quick test of your 1st demo on Win7. Seems that it works perfectly!! Must stop for today. Will test your 2nd demo tomorrow more detailed on Win7+Win10 and report later.

Thank you very much for these demos rvk. I assume that the problem is solved :-((

rvk

  • Hero Member
  • *****
  • Posts: 6989
Re: Set horizontal scroll position in a TMemo does not work (Windows only)
« Reply #12 on: March 08, 2026, 07:17:42 pm »
- I scroll to the left, until "1234" disappears left (and "qwert" is still visible)
I meant that for me, the 1234qwert completely scrolled right out of view on the first scroll. Maybe you have a setting where it scrolls per character but for me it scrolled about 10 characters so 1234qwert was directly out of view.

I can only set qwerty back in view with manually taking the scrollbars and scrolling with the mouse.

But yes... my last example should fix the scrolling and set the exact position (including horizontal and vertical scroll position and selected text) back after adding text through code.

Hartmut

  • Hero Member
  • *****
  • Posts: 1131
Re: Set horizontal scroll position in a TMemo does not work (Windows only)
« Reply #13 on: March 09, 2026, 11:09:47 am »
I meant that for me, the 1234qwert completely scrolled right out of view on the first scroll. Maybe you have a setting where it scrolls per character but for me it scrolled about 10 characters so 1234qwert was directly out of view.
Hmm, I never saw this behavior on my systems. I always had after pressing key F5 the scrolling results which I had described in my 1st post.

Quote
Maybe you have a setting where it scrolls per character
Do you mean, that horizontal scrolling is *selectable*, whether to scroll per character or per pixel? Currently your code uses horizontal scrolling per pixel. But during tests in the past I had sometimes the impression, that horizontal scrolling worked per character (but I did not look deeper).
If per character or per pixel is selectable: how/where can I select this?

Meanwhile I integrated your 2nd demo in my "real program" and tested more detailed on Win10 and Win7 and everything works perfectly. I'm very happy that this nasty issue could be solved with so small effort.

BTW: you used 'ScrollPos.X' for 'SB_VERT' and 'ScrollPos.Y' for 'SB_HORZ', I switched this.

Again thank you very much rvk for your valuable help.

rvk

  • Hero Member
  • *****
  • Posts: 6989
Re: Set horizontal scroll position in a TMemo does not work (Windows only)
« Reply #14 on: March 09, 2026, 11:25:38 am »
Quote
Maybe you have a setting where it scrolls per character
Do you mean, that horizontal scrolling is *selectable*, whether to scroll per character or per pixel? Currently your code uses horizontal scrolling per pixel. But during tests in the past I had sometimes the impression, that horizontal scrolling worked per character (but I did not look deeper).
If per character or per pixel is selectable: how/where can I select this?
I'm not sure if scrolling is selectable. But for me, when I put the cursor on the second line and scrolled to the right with the cursor, so with keyboard, there comes a point where the complete view scrolls. And for me it scrolled about 10 characters so the 1234qwert was completely out of view. Scrolling back, it came back in view all the way. So the only way I could get 1234 out of view and qwert inside the view was with my mouse.

Could you reproduce this without touching your mouse? If you scroll on the second line, how much does the scrollbar jump in your example you uploaded?
It could be that your own program has different sizes for the component. That will also give different behavior and maybe there you can get it to 1234 out of view and qwert inside, with just the keyboard.

BTW: you used 'ScrollPos.X' for 'SB_VERT' and 'ScrollPos.Y' for 'SB_HORZ', I switched this.
Woops... yeah that's what you get, coding just out of hand  ;)

Again thank you very much rvk for your valuable help.
Glad it's working in your program.

 

TinyPortal © 2005-2018