Recent

Author Topic: Faster Attribute Collection  (Read 4419 times)

rick2691

  • Sr. Member
  • ****
  • Posts: 444
Faster Attribute Collection
« on: August 06, 2023, 04:02:43 pm »
Is there a faster way get an inline character size than the following:
Code: Pascal  [Select][+][-]
  1. RTFmemo.GetTextAttributes(i,SelFontFormat); // visual format
  2. SizeVal:= SelFontFormat.Size; // active font-size
  3.  
Windows 11, LAZ 2.0.10, FPC 3.2.0, SVN 63526, i386-win32-win32/win64, using windows unit

Thaddy

  • Hero Member
  • *****
  • Posts: 15544
  • Censorship about opinions does not belong here.
Re: Faster Attribute Collection
« Reply #1 on: August 06, 2023, 05:23:29 pm »
No. What's your problem with the speed? Maybe you just need to use beginupdate/endupdate.
My great hero has found the key to the highway. Rest in peace John Mayall.
Playing: "Broken Wings" in your honour. As well as taking out some mouth organs.

rick2691

  • Sr. Member
  • ****
  • Posts: 444
Re: Faster Attribute Collection
« Reply #2 on: August 06, 2023, 06:10:20 pm »
I am iterating a text line to check the font size of each character ... it takes too long.
Windows 11, LAZ 2.0.10, FPC 3.2.0, SVN 63526, i386-win32-win32/win64, using windows unit

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Faster Attribute Collection
« Reply #3 on: August 06, 2023, 07:55:33 pm »
I am iterating a text line to check the font size of each character ... it takes too long.
per-character is indeed way too long.
You might want to use GetStyleRange()
The method returns the "length" (in characters) of the same style used in text.

Thus instead of using going by each character, you can skip the chars that are using the same style.

rick2691

  • Sr. Member
  • ****
  • Posts: 444
Re: Faster Attribute Collection
« Reply #4 on: August 07, 2023, 02:09:11 pm »
OK. I have trouble with seeing how that would be applied to my objective.
I am searching for the highest character within a specific page line.
The reason is to know the actual line height.

The following is the code I am using:
Code: Pascal  [Select][+][-]
  1. var LI,LL,j,k,SizeVal,LastVal,LineHgt: integer;
  2.  
  3. try
  4.   LI:= SendMessage(HgtRTF.Handle,EM_LINEINDEX,ScanLine,0); // first inline character
  5.   LL:= SendMessage(HgtRTF.handle,EM_LINELENGTH,LI,0); // total inline length
  6.  
  7.   j:= LI; // Scan-Start position (INDEX)
  8.   k:= LI + LL - 1; // Scan-Stop position (LENGTH)
  9.  
  10.   HgtRTF.GetTextAttributes(j,SelFontFormat);
  11.   SizeVal:= SelFontFormat.Size;
  12.   LastVal:= SizeVal;
  13.  
  14.   for i:= j to k do  
  15.        begin    
  16.        HgtRTF.GetTextAttributes(i,SelFontFormat);
  17.        SizeVal:= SelFontFormat.Size;
  18.        if (SizeVal>LastVal) then LastVal:= SizeVal;  // update highest value
  19.        end;
  20.   finally
  21.   LineHgt:= round (LastVal * 1.628 * ZoomStat);  // ZoomStat is the RTF zoom factor
  22.   end; // end of try                                                 // 1.628 is the line padding factor
  23.  

Note: Either make LineHgt a global variable or transmit LineHgt as a result.
Then use it as a scroll factor ... PageMemoWheel.

Code: Pascal  [Select][+][-]
  1. procedure TCmdForm.PageMemoWheel(Sender: TObject;    
  2.                                                                Shift: TShiftState;
  3.                                                                WheelDelta: Integer;
  4.                                                                MousePos: TPoint;
  5.                                                                var Handled: Boolean
  6.                                                                );
  7. var WheelRTF: TRichMemo;
  8.  
  9. begin
  10. WheelRoll:= 'XX';  // global variable
  11. if WheelDelta>0 then WheelRoll:= 'UP';
  12. if WheelDelta<0 then WheelRoll:= 'DN';
  13.  
  14. case WheelRoll of
  15.  
  16.          'UP': begin   // ** TEXT ROLLS UP & SCROLLBAR GOES DOWN ***
  17.                  WheelRTF.VertScrollBar.Position:=
  18.                            WheelRTF.VertScrollBar.Position - LineHgt;
  19.                  Handled:= true;
  20.                  end;
  21.  
  22.          'DN': begin   // ** TEXT ROLLS DOWN & SCROLLBAR GOES UP ***
  23.                  WheelRTF.VertScrollBar.Position:=
  24.                            WheelRTF.VertScrollBar.Position + LineHgt;
  25.                  Handled:= true;
  26.                  end;
  27.  
  28.          'XX': begin
  29.                  Handled:= true;
  30.                 end;
  31. end;
  32.  

The other side of this is that you use the "top line" of the screen if you are scrolling up.
Or you use the "top line minus one" for the off screen line to scroll down.

That is why I want to speed up the collection.
My thumb turns the wheel faster than the data can be acquired.
So how would I work GetStyleRange() into the code to find the highest character?

If I can't make the code as fast as my thumb ...
having it is nice, but nevertheless useless to me.
Windows 11, LAZ 2.0.10, FPC 3.2.0, SVN 63526, i386-win32-win32/win64, using windows unit

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Faster Attribute Collection
« Reply #5 on: August 07, 2023, 03:52:22 pm »
does the code works slow WITHOUT for i loop?
Code: Pascal  [Select][+][-]
  1. for i:= j to k do  
  2.        begin    
  3.        HgtRTF.GetTextAttributes
  4.  

rick2691

  • Sr. Member
  • ****
  • Posts: 444
Re: Faster Attribute Collection
« Reply #6 on: August 07, 2023, 05:16:46 pm »
I haven't tried that, but I have always found the Get/SetAttributes method to be slow if applied to more than a couple words. I think it would not be noticeable on one character. They just stack up on a list.

I always thought that it was because it was designed for handling too many attributes at the same time. However, doing loops have also been slow ... but doing a search is often quick. What is the difference between doing a SEARCH or ITERATION?
Windows 11, LAZ 2.0.10, FPC 3.2.0, SVN 63526, i386-win32-win32/win64, using windows unit

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Faster Attribute Collection
« Reply #7 on: August 08, 2023, 05:01:54 am »
I haven't tried that, but I have always found the Get/SetAttributes method to be slow if applied to more than a couple words. I think it would not be noticeable on one character. They just stack up on a list.
I always thought that it was because it was designed for handling too many attributes at the same time.
SetAttributes might be slow, as it's applying attributes in as-needed manner. Meaning any attributes that are not being modified has to be read in the first place.
GetAttributes performance is close to native, as the values are expected to read from only one character.

However, doing loops have also been slow ... but doing a search is often quick. What is the difference between doing a SEARCH or ITERATION?
I'm not sure if text search operation is any relative to reading characters look properties.

rick2691

  • Sr. Member
  • ****
  • Posts: 444
Re: Faster Attribute Collection
« Reply #8 on: August 08, 2023, 01:23:50 pm »
OK. The screen has all the lines showing, and they arrayed by line height. That line height should be knowable, since it had to be set for the display. Isn't there a method for querying for that (database) as a line attribute? Then I wouldn't have to do the loop or attribute search.
Windows 11, LAZ 2.0.10, FPC 3.2.0, SVN 63526, i386-win32-win32/win64, using windows unit

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Faster Attribute Collection
« Reply #9 on: August 09, 2023, 03:21:29 am »
OK. The screen has all the lines showing, and they arrayed by line height. That line height should be knowable, since it had to be set for the display. Isn't there a method for querying for that (database) as a line attribute? Then I wouldn't have to do the loop or attribute search.
The short answer is "no". There's no method for querying the attribute of a "line".

The problem is with what is a "line". Especially when WordWrap method is enabled as a single line (defined by the presence of end-of-line character) turns into mulitple lines (as they're broken to fit the width of the control).

As a result, the only to query text attributes is by text offsets. Does this text visual resides on the same line as the previous character (taking into consideration WordWrap, the width of the control, DPI of the screen) or not... the information is not available through RichMemo, as it is not available via underlying APIs.

Now, for WinAPI there's a method to query lines (as they're shown in your sample). There are no methods to query those "lines" for any other platform. Thus RichMemo doesn't interpret a "line" as a visual line (caused by wordwrapping). Instead it considers text paragraph as a single "line".
---
As a subsequent result... one might note the difference in scrolling between Memo and RichMemo on windows. Where Memo's scrolling is done based on the line height (it's easy.. all lines are the same height). While on RichMemo scrolling is done on pixels (which matches all other platforms)
---
Back to the original example. There's already WinAPI involved in getting the info regarding the characters of the line. The line created by the word-wrapping.

What could speed up the process is querying the minimal amount of characters in the line for its height. (instead of every character).
For this purpose GetStyleRange method can be used.
It could provide the information, if all characters in the line are sharing the same style.

As a result, the number of GetTextAttribute calls can be reduced to minimum to determine the "line height". Instead of querying each character in the line, the number of calls necessary can be 1.
(of if the style changes several times - the exact number of style changes, rather than the amount of characters).
« Last Edit: August 09, 2023, 03:29:50 am by skalogryz »

rick2691

  • Sr. Member
  • ****
  • Posts: 444
Re: Faster Attribute Collection
« Reply #10 on: August 10, 2023, 08:22:58 pm »
I have resolved the problem, and greatly simplified it as well. It works extremely very well, and as with the former, it leaves the caret position alone. Plus it is very fast.

Code: Pascal  [Select][+][-]
  1. // Get first & last visible lines of a RichMemo screen; Author: Thomas Stutz
  2. function TCmdForm.GetRTF_FirstLine(TempMemo:TRichmemo): Integer; // ** WORKS **
  3. begin
  4.   Result:= SendMessage(TempMemo.handle,EM_GETFIRSTVISIBLELINE, 0, 0);
  5. end;
  6.  
  7. procedure TCmdForm.PageMemoWheel(Sender: TObject;      // ** GOOD **
  8.                                  Shift: TShiftState;
  9.                                  WheelDelta: Integer;
  10.                                  MousePos: TPoint;
  11.                                  var Handled: Boolean
  12.                                  );
  13. var WheelRTF: TRichMemo;
  14.     MemoSet: boolean;
  15.     LineHgt: integer;
  16.     Ndx1, Ndx2, Ndx3: integer;
  17.     Pos1, Pos2, Pos3: TPoint;
  18.     HgtUP, HgtDN: integer;
  19. begin
  20.   Handled:= false;
  21.   MemoSet:= false;
  22.  
  23.   WheelRoll:= 'XX';  // global variable
  24.   if WheelDelta>0 then WheelRoll:= 'UP';
  25.   if WheelDelta<0 then WheelRoll:= 'DN';
  26.  
  27.   // assign active editor to WheelRTF
  28.   if (PageMemoOn) and (not PagePassive) then // global editor flags
  29.      begin
  30.      WheelRTF:= PageMemo;
  31.      MemoSet:= true;
  32.      end;
  33.   if SearchBoxOn then // global editor flag
  34.      begin
  35.      WheelRTF:= SearchBox;
  36.      MemoSet:= true;
  37.      end;
  38.   if ReplaceBoxOn then // global editor flag
  39.      begin
  40.      WheelRTF:= ReplaceBox;
  41.      MemoSet:= true;
  42.      end;
  43.  
  44.   if MemoSet then
  45.      begin
  46.      try
  47.        WheelRTF.Lines.BeginUpdate;  // suspend screen update
  48.  
  49.          // first visible line of screen
  50.          TopLin:= GetRTF_FirstLine(WheelRTF);
  51.  
  52.          // first character of line ** FIX FOR TOPLIN=0 & TOPLIN ONLY **
  53.          Ndx1:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin-1,0);
  54.          Ndx2:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin,0);
  55.          Ndx3:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin+1,0);
  56.  
  57.          // coordinate of line
  58.          WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos1),Ndx1);
  59.          WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos2),Ndx2);
  60.          WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos3),Ndx3);
  61.  
  62.          // height of line
  63.          HgtUP:= Pos2.y - Pos1.y;
  64.          HgtDN:= Pos3.y - Pos2.y;
  65.  
  66.          case WheelRoll of
  67.          'UP': begin   // ** TEXT ROLLS DOWN ** NEW LINE APPEARS AT TOP
  68.                LineHgt:= HgtUP;
  69.                if LineHgt>100 then LineHgt:= 20; // ** MUST REFINE **
  70.                WheelRTF.VertScrollBar.Position:=
  71.                         WheelRTF.VertScrollBar.Position - LineHgt;
  72.                Handled:= true;
  73.                end;
  74.          'DN': begin   // ** TEXT ROLLS UP ** NEW LINE APPEARS AT BOTTOM
  75.                LineHgt:= HgtDN;
  76.                if LineHgt>100 then LineHgt:= 20; // ** MUST REFINE **
  77.                WheelRTF.VertScrollBar.Position:=
  78.                         WheelRTF.VertScrollBar.Position + LineHgt;
  79.                Handled:= true;
  80.                end;
  81.          'XX': begin
  82.                Handled:= true;
  83.                end;
  84.          end; // end-of-case
  85.  
  86.      finally
  87.      WheelRTF.Lines.EndUpdate;  // restore screen update
  88.      end; // end try
  89.      end; // end MemoSet
  90. end; // end PageMemoWheel    
  91.  

Perhaps someone can help me with its last issue. In the section of the "case WheelRoll of", I change the LineHgt to 20, if LineHgt is greater 100. Doing so is an assumption that a taller image might be that large or larger, and exceed the screen view. Which is a problem if you want look at everything in the image. So I have reduced the scroll factor to move it up slowly.

I would prefer to sense the image as an object, and then modify the LineHgt factor in a more intelligent way. Unfortunately, I am not studied enough to code that process.

Can anyone show me a cleaner way to get that done?
« Last Edit: August 10, 2023, 08:38:08 pm by rick2691 »
Windows 11, LAZ 2.0.10, FPC 3.2.0, SVN 63526, i386-win32-win32/win64, using windows unit

rick2691

  • Sr. Member
  • ****
  • Posts: 444
Re: Faster Attribute Collection
« Reply #11 on: August 15, 2023, 12:51:41 pm »
This is a tweak to make image scrolling smoother. It is a decent work around, but I would still like to either identify that the top scroll line contains text or an image. Knowing either of them would allow us to manipulate the scroll factor with precision.

Code: Pascal  [Select][+][-]
  1. procedure TCmdForm.PageMemoWheel(Sender: TObject;    
  2.                                  Shift: TShiftState;
  3.                                  WheelDelta: Integer;
  4.                                  MousePos: TPoint;
  5.                                  var Handled: Boolean
  6.                                  );
  7. var WheelRTF: TRichMemo;
  8.     MemoSet: boolean;
  9.     LineHgt, FullHgt, MaxHgt, DefHgt: integer;
  10.     Ndx1, Ndx2, Ndx3: integer;
  11.     Pos1, Pos2, Pos3: TPoint;
  12.     HgtUP, HgtDN: integer;
  13.     StorePos, StoreRun: integer;
  14. begin
  15.   Handled:= false;
  16.   MemoSet:= false;
  17.  
  18.   WheelRoll:= 'XX';  // global variable
  19.   if WheelDelta>0 then WheelRoll:= 'UP';
  20.   if WheelDelta<0 then WheelRoll:= 'DN';
  21.  
  22.   // assign active editor to WheelRTF
  23.   if (PageMemoOn) and (not PagePassive) then // global editor flags
  24.      begin
  25.      WheelRTF:= PageMemo;
  26.      MemoSet:= true;
  27.      end;
  28.   if SearchBoxOn then // global editor flag
  29.      begin
  30.      WheelRTF:= SearchBox;
  31.      MemoSet:= true;
  32.      end;
  33.   if ReplaceBoxOn then // global editor flag
  34.      begin
  35.      WheelRTF:= ReplaceBox;
  36.      MemoSet:= true;
  37.      end;
  38.  
  39.   if MemoSet then
  40.      begin
  41.      try
  42.        {
  43.        GetLin:= SendMessage(ScanRTF.Handle,EM_LINEFROMCHAR,WheelRTF.SelStart,0); // Line Data
  44.        BtmLin:= GetRTF_LastLine(WheelRTF); // last visible line
  45.        }
  46.  
  47.        // get first visible line on screen
  48.        TopLin:= SendMessage(WheelRTF.handle,EM_GETFIRSTVISIBLELINE, 0, 0);
  49.  
  50.        // get index of line
  51.        if (TopLin-1)<0
  52.           then Ndx1:= 0
  53.           else Ndx1:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin-1,0);
  54.  
  55.        Ndx2:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin,0);
  56.  
  57.        if (TopLin+1) > (WheelRTF.Lines.Count)  // TopLin+1 > total lines
  58.           then Ndx3:= TopLin
  59.           else Ndx3:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin+1,0);
  60.  
  61.        // get coordinate of index
  62.        WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos1),Ndx1);
  63.        WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos2),Ndx2);
  64.        WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos3),Ndx3);
  65.  
  66.        // get height of line
  67.        HgtUP:= Pos2.y - Pos1.y;
  68.        HgtDN:= Pos3.y - Pos2.y;
  69.  
  70.        MaxHgt:= round(36 * 1.628 * ZoomStat); // height * padding * Zoom // 72
  71.        DefHgt:= round(12 * 1.628 * ZoomStat); // height * padding * Zoom // 10
  72.  
  73.        case WheelRoll of
  74.        'UP': begin   // ** TEXT ROLLS DOWN ** NEW LINE APPEARS AT TOP
  75.              LineHgt:= HgtUP;
  76.              if LineHgt>MaxHgt then LineHgt:= DefHgt; // ** MUST REFINE **
  77.              WheelRTF.VertScrollBar.Position:=
  78.                       WheelRTF.VertScrollBar.Position - LineHgt;
  79.              Handled:= true;
  80.              end;
  81.        'DN': begin   // ** TEXT ROLLS UP ** NEW LINE APPEARS AT BOTTOM
  82.              LineHgt:= HgtDN;
  83.              if LineHgt>MaxHgt then LineHgt:= DefHgt; // ** MUST REFINE **
  84.              WheelRTF.VertScrollBar.Position:=
  85.                       WheelRTF.VertScrollBar.Position + LineHgt;
  86.              Handled:= true;
  87.              end;
  88.        'XX': begin
  89.              Handled:= true;
  90.              end;
  91.        end; // end-of-case
  92.  
  93.        finally
  94.        end; // end try
  95.      end; // end MemoSet
  96. end; // end PageMemoWheel
  97.  
Windows 11, LAZ 2.0.10, FPC 3.2.0, SVN 63526, i386-win32-win32/win64, using windows unit

rick2691

  • Sr. Member
  • ****
  • Posts: 444
Re: Faster Attribute Collection
« Reply #12 on: August 25, 2023, 01:15:27 pm »
I have enhanced the scrolling for PageMemoWheel, which is a stand alone scroller. It now allows you to hold down the control key while turning the wheel. Doing so makes it scroll very slowly.

I have also added some additional scroll functions that are useful, but not needed for PageMemoWheel.

At this point, I don't think I need to identify the page content. It is doing what I need.

Code: Pascal  [Select][+][-]
  1. procedure TCmdForm.ScrollMemo(ScrollRTF:TRichMemo; BarType:string; BarTgt:longint);
  2. var BarPos: LongInt;                                      // ** WORKS **
  3. // Example: ScrollMemo(PageMemo, 'VRT', 12452);
  4. // Example: ScrollMemo(PageMemo, 'VRT', VrtBarPos);
  5. begin
  6. if upcase(BarType)='VRT' then  // vertical
  7.    begin
  8.    BarPos:= GetScrollPos(ScrollRTF.handle,SB_VERT);
  9.    if BarPos>BarTgt then  // if in view this scrolls document to previous position
  10.       Begin               // if not in view it scrolls document to previous cursor
  11.       while (BarTgt<GetScrollPos(ScrollRTF.handle,SB_VERT)) do
  12.              begin
  13.              ScrollRTF.VertScrollBar.Position:=
  14.                        ScrollRTF.VertScrollBar.Position - 1;
  15.              end;
  16.       end else
  17.           Begin
  18.           while (BarTgt>GetScrollPos(ScrollRTF.handle,SB_VERT)) do
  19.                  begin
  20.                  ScrollRTF.VertScrollBar.Position:=
  21.                            ScrollRTF.VertScrollBar.Position + 1;
  22.                  end;
  23.           end; // end of else
  24.    end; // end VERTICAL BarType
  25.  
  26. if upcase(BarType)='HRZ' then  // horizontal
  27.    begin
  28.    BarPos:= GetScrollPos(ScrollRTF.handle,SB_HORZ);
  29.    if BarPos>BarTgt then  // scrolls document to last cursor position
  30.       Begin                      // as per its previous screen view
  31.       while (BarTgt<GetScrollPos(ScrollRTF.handle,SB_HORZ)) do
  32.              begin
  33.              ScrollRTF.HorzScrollBar.Position:=
  34.                        ScrollRTF.HorzScrollBar.Position - 1;
  35.              end;
  36.       end else
  37.           Begin
  38.           while (BarTgt>GetScrollPos(ScrollRTF.handle,SB_HORZ)) do
  39.                  begin
  40.                  ScrollRTF.HorzScrollBar.Position:=
  41.                            ScrollRTF.HorzScrollBar.Position + 1;
  42.                  end;
  43.           end; // end of else
  44.    end; // end of HORIZONTAL BarType
  45. end; // end proc
  46.  
  47. procedure GotoLine(FindRTF:TRichMemo; FindLine:integer);  // ** WORKS **
  48. var Index: integer;  // EXAMPLE: GotoLine(ScanRTF,TopLin);
  49. begin
  50.   Index:= SendMessage(FindRTF.Handle,EM_LINEINDEX,FindLine,0); // first character
  51.   FindRTF.SelStart:= Index; // EM_SCROLLCARET
  52.   FindRTF.SelLength:= 0;  // EM_SETSEL
  53.   // Notepad uses EM_LINEINDEX (then EM_SETSEL and EM_SCROLLCARET)
  54. end;
  55.  
  56. // Get first & last visible lines of a RichMemo screen; Author: Thomas Stutz
  57. function TCmdForm.GetRTF_FirstLine(TempMemo:TRichmemo): Integer; // ** WORKS **
  58. begin
  59.   Result:= SendMessage(TempMemo.handle,EM_GETFIRSTVISIBLELINE, 0, 0);
  60. end;
  61.  
  62. //  Get last visible line of RichMemo screen; Author: Thomas Stutz
  63. function TCmdForm.GetRTF_LastLine(TempMemo:TRichmemo): Integer;  // ** WORKS **
  64. const
  65.   EM_EXLINEFROMCHAR = WM_USER + 54;
  66. var
  67.   r: TRect;
  68.   i: Integer;
  69. begin
  70.   SendMessage(TempMemo.handle, EM_GETRECT, 0, Longint(@r));
  71.   r.Left:= r.Left + 1;
  72.   r.Top:= r.Bottom - 2;
  73.   i:= SendMessage(TempMemo.handle, EM_CHARFROMPOS, 0, Integer(@r.topleft));
  74.   Result:= SendMessage(TempMemo.handle, EM_EXLINEFROMCHAR, 0, i); // ** GOOD **
  75. end;
  76.  
  77. procedure TCmdForm.PageMemoWheel(Sender: TObject;      // ** GOOD **
  78.                                  Shift: TShiftState;
  79.                                  WheelDelta: Integer;
  80.                                  MousePos: TPoint;
  81.                                  var Handled: Boolean
  82.                                  );
  83. var WheelRTF: TRichMemo;
  84.     MemoSet: boolean;
  85.     LineHgt, FullHgt, MaxHgt, DefHgt: integer;
  86.     Ndx1, Ndx2, Ndx3: integer;
  87.     Pos1, Pos2, Pos3: TPoint;
  88.     HgtUP, HgtDN, CtrHgt: integer;
  89.     StorePos, StoreRun: integer;
  90. begin
  91.   Handled:= false;
  92.   MemoSet:= false;
  93.  
  94.   WheelRoll:= 'XX';  // global variable
  95.   if WheelDelta>0 then WheelRoll:= 'UP';
  96.   if WheelDelta<0 then WheelRoll:= 'DN';
  97.  
  98.   // assign active editor to WheelRTF
  99.   if (PageMemoOn) and (not PagePassive) then // global editor flags
  100.      begin
  101.      WheelRTF:= PageMemo;
  102.      MemoSet:= true;
  103.      end;
  104.   if SearchBoxOn then // global editor flag
  105.      begin
  106.      WheelRTF:= SearchBox;
  107.      MemoSet:= true;
  108.      end;
  109.   if ReplaceBoxOn then // global editor flag
  110.      begin
  111.      WheelRTF:= ReplaceBox;
  112.      MemoSet:= true;
  113.      end;
  114.  
  115.   if MemoSet then
  116.      begin
  117.      try
  118.        {
  119.        // line functions ** not needed ** GetLin,BtmLn,LN,LI,LL: integer;
  120.        GetLin:= SendMessage(WheelRTF.Handle,EM_LINEFROMCHAR,WheelRTF.SelStart,0); // Line Data
  121.        BtmLin:= GetRTF_LastLine(WheelRTF); // last visible line
  122.        LN:= SendMessage(WheelRTF.Handle,EM_LINEFROMCHAR,WheelRTF.SelStart,0); // get Line-Number
  123.        LI:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,LN,0); // get Line-Postion at Line start
  124.        LL:= SendMessage(WheelRTF.handle,EM_LINELENGTH,LI,0); // get Line-Length from Character Index
  125.        }
  126.  
  127.        // get first visible screen line
  128.        TopLin:= SendMessage(WheelRTF.handle,EM_GETFIRSTVISIBLELINE,0,0);
  129.  
  130.        // get indexes of lines above & below TopLin
  131.        if (TopLin-1)<0
  132.           then Ndx1:= 0
  133.           else Ndx1:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin-1,0);
  134.  
  135.        Ndx2:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin,0);
  136.  
  137.        if (TopLin+1) > (WheelRTF.Lines.Count)  // TopLin+1 > total lines
  138.           then Ndx3:= TopLin
  139.           else Ndx3:= SendMessage(WheelRTF.Handle,EM_LINEINDEX,TopLin+1,0);
  140.  
  141.        // get coordinate of index
  142.        WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos1),Ndx1);
  143.        WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos2),Ndx2);
  144.        WheelRTF.Perform(EM_POSFROMCHAR,WPARAM(@Pos3),Ndx3);
  145.  
  146.        // get height of line
  147.        HgtUP:= Pos2.y - Pos1.y;
  148.        HgtDN:= Pos3.y - Pos2.y;
  149.  
  150.        // ZoomStat is a "global double" to unify zoom for multiple tab frames.
  151.        ZoomStat:= WheelRTF.ZoomFactor;
  152.        MaxHgt:= round(36 * 1.628 * ZoomStat); // height * padding * Zoom
  153.        DefHgt:= round(12 * 1.628 * ZoomStat); // height * padding * Zoom
  154.        CtrHgt:= round(1 * 1.628 * ZoomStat); // height * padding * Zoom
  155.  
  156.        case WheelRoll of
  157.        'UP': begin   // ** TEXT ROLLS DOWN ** NEW LINE APPEARS AT TOP
  158.              LineHgt:= HgtUP;
  159.              if LineHgt>MaxHgt then LineHgt:= DefHgt;
  160.              if (ssCtrl in Shift) then LineHgt:= CtrHgt; // Ctrl key pressed
  161.              WheelRTF.VertScrollBar.Position:=
  162.                       WheelRTF.VertScrollBar.Position - LineHgt;
  163.              Handled:= true;
  164.              end;
  165.        'DN': begin   // ** TEXT ROLLS UP ** NEW LINE APPEARS AT BOTTOM
  166.              LineHgt:= HgtDN;
  167.              if LineHgt>MaxHgt then LineHgt:= DefHgt;
  168.              if (ssCtrl in Shift) then LineHgt:= CtrHgt; // Ctrl key pressed
  169.              WheelRTF.VertScrollBar.Position:=
  170.                       WheelRTF.VertScrollBar.Position + LineHgt;
  171.              Handled:= true;
  172.              end;
  173.        'XX': begin
  174.              Handled:= true;
  175.              end;
  176.        end; // end-of-case
  177.  
  178.        finally
  179.        end; // end try
  180.      end; // end MemoSet
  181. end; // end PageMemoWheel
  182.  
Windows 11, LAZ 2.0.10, FPC 3.2.0, SVN 63526, i386-win32-win32/win64, using windows unit

 

TinyPortal © 2005-2018