* * *

Author Topic: Make TSynEdit always display a caret, even when unfocused  (Read 1185 times)

FRex

  • New member
  • *
  • Posts: 5
Make TSynEdit always display a caret, even when unfocused
« on: May 09, 2017, 12:15:55 am »
As in the title, is it possible to always display the caret in a TSynEdit text, even when it is unfocused? Right now I can't find a way to do it and Lazarus itself hides the caret when it's text area is not focused. It can blink or not, as long as it's there.

What I'm trying to do is make a search box and I want the caret to show which word is currently 'found'. Pressing enter in the edit box goes to next occurrence of the given word, but it's not visible which one it is in the syn edit text area itself because the caret is not drawn (I don't want to focus the syn edit on each enter press either).
App to keep track of notes in a single plain text file tagged by hashtags: https://github.com/FRex/botes

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 628
    • Burdjia
Re: Make TSynEdit always display a caret, even when unfocused
« Reply #1 on: May 09, 2017, 09:13:33 am »
IIRC there are a "caret" property some where you can use.  Also you would try to paint by yourself (i.e. the onPaint event).

Pascal

  • Sr. Member
  • ****
  • Posts: 477
Re: Make TSynEdit always display a caret, even when unfocused
« Reply #2 on: May 09, 2017, 10:46:57 am »
Use eoPersistentCaret in Options
laz trunk - fpc trunk 32bit - Windows 10 Pro x64

FRex

  • New member
  • *
  • Posts: 5
Re: Make TSynEdit always display a caret, even when unfocused
« Reply #3 on: May 09, 2017, 11:19:47 am »
eoPersistentCaret doesn't work on Windows when another element that has a caret is focused.
I'm also using TSynEditMarkupHighlightAllCaret, is there a way to force it to highlight the current word in different color and to highlight the word even if there are no duplicates of it? That'd work the same pretty much (I'm trying to get VS Code style search).
« Last Edit: May 09, 2017, 11:24:46 am by FRex »
App to keep track of notes in a single plain text file tagged by hashtags: https://github.com/FRex/botes

Pascal

  • Sr. Member
  • ****
  • Posts: 477
Re: Make TSynEdit always display a caret, even when unfocused
« Reply #4 on: May 09, 2017, 12:31:31 pm »
I'm trying to get VS Code style search

Use TSynEditMarkupHighlightMatches to mark all occurences.

I've done this for my COBOL-IDE for a Crossreference (reads are shades of blue, writes are shades of red).
laz trunk - fpc trunk 32bit - Windows 10 Pro x64

FRex

  • New member
  • *
  • Posts: 5
Re: Make TSynEdit always display a caret, even when unfocused
« Reply #5 on: May 09, 2017, 01:13:57 pm »
How do I tell it what has to be marked? Do you use a single instance of it for both marking red and blue or not?
App to keep track of notes in a single plain text file tagged by hashtags: https://github.com/FRex/botes

Pascal

  • Sr. Member
  • ****
  • Posts: 477
Re: Make TSynEdit always display a caret, even when unfocused
« Reply #6 on: May 09, 2017, 01:22:17 pm »
I've build a decendant of TSynEditMarkupHighlightMatches which move when editing text.

Code: Pascal  [Select]
  1. unit primeMarkup;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, SynEditMarkupHighAll, LazSynEditText, SynEditMiscClasses,
  9.   Graphics;
  10.  
  11. type
  12.   { TprimeMarkupBase }
  13.  
  14.   TprimeMarkupBase = class(TSynEditMarkupHighlightMatches)
  15.   private
  16.     procedure DoLineChange(Sender: TSynEditStrings; p_LinePos, p_BytePos, p_Count,
  17.       p_LineBrkCnt: Integer; p_Text: String);
  18.   protected
  19.     procedure SetLines(const p_new: TSynEditStrings); override;
  20.   public
  21.     constructor Create(p_SynEdit : TSynEditBase);
  22.     destructor Destroy; override;
  23.     procedure AddMark(p_Start, p_Finish: TPoint);
  24.     procedure ClearMarks;
  25.     property Matches;
  26.   end;
  27.  
  28. implementation
  29.  
  30. uses
  31.   LCLProc;
  32.  
  33. { TprimeMarkupBase }
  34.  
  35. procedure TprimeMarkupBase.DoLineChange(Sender: TSynEditStrings; p_LinePos,
  36.   p_BytePos, p_Count, p_LineBrkCnt: Integer; p_Text: String);
  37. var
  38.   i, end_BytePos: Integer;
  39.   m: TSynMarkupHighAllMatch;
  40.   abs_Count, abs_LineBrkCnt: Integer;
  41.   min, max: Integer;
  42.   changed, StartMoved, EndMoved: Boolean;
  43. begin
  44.   if Matches.Count = 0 then exit;
  45.   min := MaxInt;
  46.   max := Integer($ffffffff);
  47.   {$IFDEF primeMarkupDebug}
  48.   DebugLn('*** %s.DoLineChange l=%d x=%d c=%d crlf=%d t=%s', [ClassName, p_LinePos, p_BytePos, p_Count, p_LineBrkCnt, p_Text]);
  49.   {$ENDIF}
  50.   if (p_Count < 0)
  51.   or (p_LineBrkCnt < 0) then begin
  52.     // ggf. Marks löschen
  53.     abs_Count := Abs(p_Count);
  54.     abs_LineBrkCnt := Abs(p_LineBrkCnt);
  55.     end_BytePos := p_BytePos + abs_Count;
  56.     i := Matches.Count - 1;
  57.     while i >= 0 do begin
  58.       m := Matches[i];
  59.       changed := false;
  60.       StartMoved := false;
  61.       EndMoved := false;
  62.       if p_Count < 0 then begin
  63.         // Zeichen entfernt
  64.         if (m.StartPoint.y = p_LinePos)
  65.         and (m.StartPoint.x >= p_BytePos) then begin
  66.           // Änderung beginnt in der Zeile vor der Markierung
  67.           // -> Startposition anpassen
  68.           if (m.StartPoint.x >= end_BytePos) then begin
  69.             // komplett vor der Markierung
  70.             dec(m.StartPoint.x, abs_Count);
  71.           end else begin
  72.             // Änderung reicht in die Markierung
  73.             m.StartPoint.x := p_BytePos;
  74.           end;
  75.           Matches.StartPoint[i] := m.StartPoint;
  76.  
  77.           if (m.EndPoint.y = p_LinePos) then begin
  78.             // Ende der Markierung ist in der gleichen Zeile
  79.             if (m.EndPoint.x > end_BytePos) then begin
  80.               // und komplett vor dem Ende der Markierung
  81.               // -> also auch Endposition anpassen
  82.               dec(m.EndPoint.x, abs_Count);
  83.               Matches.EndPoint[i] := m.EndPoint;
  84.             end else begin
  85.               // und komplett nach dem Ende der Markierung
  86.               // -> Markierung entfernen
  87.               Matches.Delete(i);
  88.             end;
  89.           end;
  90.           changed := true;
  91.         end else if (m.EndPoint.y = p_LinePos)
  92.         and (m.EndPoint.x > p_BytePos) then begin
  93.           // Änderung ist in der Zeile vor dem Ende der Markierung
  94.           if (m.EndPoint.x > end_BytePos) then begin
  95.             // und komplett vor dem Ende der Markierung
  96.             // -> also auch Endposition anpassen
  97.             dec(m.EndPoint.x, abs_Count);
  98.           end else begin
  99.             // und reicht über das Ende der Markierung hinaus
  100.             // -> Endposition kürzen
  101.             m.EndPoint.x := p_BytePos;
  102.           end;
  103.           Matches.EndPoint[i] := m.EndPoint;
  104.           changed := true;
  105.         end;
  106.       end;
  107.       if p_LineBrkCnt < 0 then begin
  108.         // Zeilen entfernt
  109.         if p_Count <> 0 then DebugLn('*****'#13'***** %s.DoLineChange l=%d x=%d c=%d crlf=%d t=%s    !!! c != 0'#13'*****', [ClassName, p_LinePos, p_BytePos, p_Count, p_LineBrkCnt, p_Text]);
  110.  
  111.         if (m.StartPoint.y = p_LinePos + abs_LineBrkCnt) then begin
  112.           // Zeilenzusammenführung:
  113.           // Markup aus der nächsten Zeile wird in diese Zeile geholt
  114.           // Start
  115.           inc(m.StartPoint.x, p_BytePos - 1);
  116.           dec(m.StartPoint.y, abs_LineBrkCnt);
  117.           changed := true;
  118.           StartMoved := true;
  119.           // End
  120.           if (m.EndPoint.y = p_LinePos + abs_LineBrkCnt) then
  121.             inc(m.EndPoint.x, p_BytePos - 1);
  122.           dec(m.EndPoint.y, abs_LineBrkCnt);
  123.           EndMoved := true;
  124.         end else if (m.EndPoint.y = p_LinePos + abs_LineBrkCnt) then begin
  125.           // Zeilenzusammenführung:
  126.           // Markup aus der nächsten Zeile wird in diese Zeile geholt
  127.           inc(m.EndPoint.x, p_BytePos - 1);
  128.           dec(m.EndPoint.y, abs_LineBrkCnt);
  129.           changed := true;
  130.           EndMoved := true;
  131.         end;
  132.  
  133.         // y anpassen
  134.         if (m.StartPoint.y > p_LinePos)
  135.         and not StartMoved then begin
  136.           dec(m.StartPoint.y, abs_LineBrkCnt);
  137.           changed := true;
  138.         end;
  139.         if (m.EndPoint.y > p_LinePos)
  140.         and not EndMoved then begin
  141.           dec(m.EndPoint.y, abs_LineBrkCnt);
  142.           changed := true;
  143.         end;
  144.  
  145.         Matches.StartPoint[i] := m.StartPoint;
  146.         Matches.EndPoint[i] := m.EndPoint;
  147.       end;
  148.  
  149.       if changed then begin
  150.         if m.StartPoint.y < min then min := m.StartPoint.y;
  151.         if m.StartPoint.y > max then max := m.StartPoint.y;
  152.         if m.EndPoint.y < min then min := m.EndPoint.y;
  153.         if m.EndPoint.y > max then max := m.EndPoint.y;
  154.       end;
  155.       dec(i);
  156.     end;
  157.   end else begin
  158.     // ggf. Marks verschieben
  159.     for i := 0 to Matches.Count - 1 do begin
  160.       m := Matches[i];
  161.       changed := false;
  162.       StartMoved := false;
  163.       EndMoved := false;
  164.  
  165.       if (m.StartPoint.y = p_LinePos)
  166.       and (m.StartPoint.x >= p_BytePos) then begin
  167.         // Änderung ist in der Zeile vor der Markierung
  168.         // -> Startposition anpassen
  169.         inc(m.StartPoint.x, p_Count);
  170.         if p_LineBrkCnt > 0 then begin
  171.           inc(m.StartPoint.y, p_LineBrkCnt);
  172.           StartMoved := true;
  173.           dec(m.StartPoint.x, p_BytePos - 1);
  174.         end;
  175.         if m.EndPoint.y = p_LinePos then begin
  176.           // Ende der Markierung ist in der gleichen Zeile
  177.           // -> also auch Endposition anpassen
  178.           inc(m.EndPoint.x, p_Count);
  179.           if p_LineBrkCnt > 0 then begin
  180.             inc(m.EndPoint.y, p_LineBrkCnt);
  181.             EndMoved := true;
  182.             dec(m.EndPoint.x, p_BytePos - 1);
  183.           end;
  184.         end;
  185.         changed := true;
  186.       end;
  187.  
  188.       if (m.EndPoint.y = p_LinePos)
  189.       and (m.EndPoint.x > p_BytePos)
  190.       and (
  191.         (m.StartPoint.y <> p_LinePos)
  192.         or (m.StartPoint.x < p_BytePos)
  193.       )then begin
  194.         // Änderung ist in der Markierung
  195.         // -> also Endposition anpassen
  196.         inc(m.EndPoint.x, p_Count);
  197.         if p_LineBrkCnt > 0 then begin
  198.           if not EndMoved then begin
  199.             inc(m.EndPoint.y, p_LineBrkCnt);
  200.             EndMoved := true;
  201.           end;
  202.           dec(m.EndPoint.x, p_BytePos - 1);
  203.         end;
  204.         changed := true;
  205.       end;
  206.  
  207.       if p_LineBrkCnt > 0 then begin
  208.         // y anpassen
  209.         if not StartMoved
  210.         and (m.StartPoint.y > p_LinePos) then begin
  211.           inc(m.StartPoint.y, p_LineBrkCnt);
  212.           changed := true;
  213.         end;
  214.         if not EndMoved
  215.         and (m.EndPoint.y > p_LinePos) then begin
  216.           inc(m.EndPoint.y, p_LineBrkCnt);
  217.           changed := true;
  218.         end;
  219.       end;
  220.  
  221.       Matches.StartPoint[i] := m.StartPoint;
  222.       Matches.EndPoint[i] := m.EndPoint;
  223.  
  224.       if changed then begin
  225.         if m.StartPoint.y < min then min := m.StartPoint.y;
  226.         if m.StartPoint.y > max then max := m.StartPoint.y;
  227.         if m.EndPoint.y < min then min := m.EndPoint.y;
  228.         if m.EndPoint.y > max then max := m.EndPoint.y;
  229.       end;
  230.     end;
  231.   end;
  232.   if min <= max then begin
  233.     {$IFDEF primeMarkupDebug}
  234.     DebugLn('### InvalidateSynLines(%d, %d)', [min, max]);
  235.     {$ENDIF}
  236.     InvalidateSynLines(min, max);
  237.   end;
  238. end;
  239.  
  240. procedure TprimeMarkupBase.SetLines(const p_new: TSynEditStrings);
  241. var
  242.   l_old: TSynEditStrings;
  243. begin
  244.   l_old := Lines;
  245.   if (l_old <> p_new)
  246.   and Assigned(l_old) then begin
  247.     // alten ChangeHandler entfernen
  248.     l_old.RemoveEditHandler(@DoLineChange);
  249.   end;
  250.   inherited SetLines(p_new);
  251.   if (l_old <> p_new)
  252.   and Assigned(p_new) then begin
  253.     // neuen ChangeHandler eintragen
  254.     p_new.AddEditHandler(@DoLineChange);
  255.   end;
  256. end;
  257.  
  258. constructor TprimeMarkupBase.Create(p_SynEdit: TSynEditBase);
  259. begin
  260.   inherited Create(p_SynEdit);
  261. end;
  262.  
  263. destructor TprimeMarkupBase.Destroy;
  264. begin
  265.   Lines := nil;
  266.   inherited Destroy;
  267. end;
  268.  
  269. procedure TprimeMarkupBase.AddMark(p_Start, p_Finish: TPoint);
  270. var
  271.   r: TSynMarkupHighAllMatch;
  272. begin
  273.   r.StartPoint := p_Start;
  274.   r.EndPoint := p_Finish;
  275.   Matches[Matches.Count] := r;
  276. end;
  277.  
  278. procedure TprimeMarkupBase.ClearMarks;
  279. begin
  280.   Matches.Count := 0;
  281. end;
  282.  
  283. end.
  284.  


Then i have a decendant for UseMarkups:
Code: Pascal  [Select]
  1. { TprimeUseMarkup }
  2.   TprimeUseMarkup = class(TprimeMarkupBase)
  3.   public
  4.     constructor Create(ASynEdit : TSynEditBase; AColor: TColor; AFontColor: TColor = clWhite);
  5.   end;
  6. .
  7. .
  8. .
  9. { TprimeUseMarkup }
  10.  
  11. constructor TprimeUseMarkup.Create(ASynEdit: TSynEditBase; AColor: TColor; AFontColor: TColor = clWhite);
  12. begin
  13.   inherited Create(ASynEdit);
  14.   MarkupInfo.Clear;
  15.   MarkupInfo.Background := AColor;
  16.   MarkupInfo.BackPriority := 49;
  17.   MarkupInfo.FrameColor := clBlack;
  18.   MarkupInfo.FrameEdges := sfeAround;
  19.   MarkupInfo.FrameStyle := slsSolid;
  20.   MarkupInfo.FrameAlpha := 80;
  21.   MarkupInfo.Foreground := AFontColor;
  22.   MarkupInfo.ForePriority := 49;
  23.   Matches.Capacity := 1000;
  24. end;
  25.  
  26.  

In my decendant of TSynEdit:
Code: Pascal  [Select]
  1. constructor TprimeBaseEdit.Create(AOwner: TComponent);
  2. begin
  3.   inherited Create(AOwner);
  4. .
  5. .
  6. .
  7.   fDWMarkup := TprimeUseMarkup.Create(self, clVariableDirectWrite);
  8.   fISuperWMarkup := TprimeUseMarkup.Create(self, clVariableIndirectWriteSuper);
  9.   fISubWMarkup := TprimeUseMarkup.Create(self, clVariableIndirectWriteSub);
  10.   fDRMarkup := TprimeUseMarkup.Create(self, clVariableDirectRead);
  11.   fISuperRMarkup := TprimeUseMarkup.Create(self, clVariableIndirectReadSuper);
  12.   fISubRMarkup := TprimeUseMarkup.Create(self, clVariableIndirectReadSub);
  13.   fBranchMarkup := TprimeUseMarkup.Create(self, clBranch);
  14.   fRefOnlyMarkup := TprimeUseMarkup.Create(self, clRefOnly);
  15.   fDefMarkup := TprimeUseMarkup.Create(self, clVariableDef);
  16. .
  17. .
  18. .
  19.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fDWMarkup);
  20.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fISuperWMarkup);
  21.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fISubWMarkup);
  22.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fDRMarkup);
  23.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fISuperRMarkup);
  24.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fISubRMarkup);
  25.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fDefMarkup);
  26.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fBranchMarkup);
  27.   TSynEditMarkupManager(MarkupMgr).AddMarkUp(fRefOnlyMarkup);
  28. .
  29. .
  30. .
  31. end;
  32.  
laz trunk - fpc trunk 32bit - Windows 10 Pro x64

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 4323
    • wiki
Re: Make TSynEdit always display a caret, even when unfocused
« Reply #7 on: May 09, 2017, 04:29:03 pm »
eoPersistentCaret doesn't work on Windows when another element that has a caret is focused.
.

You can add multi caret. and then set the eoPersistentCaret too.

or
FCaret.ChangePainter(TSynEditScreenCaretPainterInternal)
but you need to subclass SynEdit to get access to that.

Also in both cases, SynEdit paints its own caret, and that does not follow system blinkrate.

windows itself only has one caret per application.

Pascal

  • Sr. Member
  • ****
  • Posts: 477
Re: Make TSynEdit always display a caret, even when unfocused
« Reply #8 on: May 10, 2017, 08:18:45 pm »
@FRex: Did you manage to get it working?
laz trunk - fpc trunk 32bit - Windows 10 Pro x64

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus