TTMSFMXRichEdit text color

Hi,


I've been struggling all day with runtime text coloring in an RichEdit.
I want to have a SINGLE line Richedit and the user can enter any information (s)he likes.

However if the text contains specific format(s) I want to color it.

e.g.
aaaa:bbbb cccc dddd:eeee

I want the texts before the colon colored.
aaaa:bbbb cccc dddd:eeee

For now, I use the KeyUp to Refresh the Richedit and repaint the text, because there's no OnTyping Event.

I parse the text, select the text before every colon and call SetSelectionColor.
I keep having problems with the selection and caret not returning to the right/previous position.

I saved (and restored) 
* SelStart and SelLength
* Caret (made a copy)
* Selection (also copy)

But didn't get it to work properly.
The caret is not always on the end of the text, the use might use arrow keys to change text in the middle.

Do you have any suggestions ?

Test Example (text before AND after : will be colored)


I call the RefreshEdit in the KeyUp Event of the RichEdit.

procedure TForm1.RefreshEdit;
const
  STR_REGEX = '(([A-Z]+:[A-Z0-9])|([A-Z0-9])|\s+)*';
var
  idx      : Integer;
  SelStart : Integer;
  SelLength: Integer;
  matches  : TMatchCollection;
begin
  SelStart  := TMSFMXRichEditor1.SelStart;
  SelLength := TMSFMXRichEditor1.SelLength;
  TMSFMXRichEditor1.BeginUpdate;
  try
    matches := TRegEx.matches(TMSFMXRichEditor1.PlainText, STR_REGEX, [roIgnoreCase]);

    for idx := 0 to matches.Count - 1 do
    begin
      if matches[idx].Value.Contains(':') then
      begin
        TMSFMXRichEditor1.SelStart  := matches[idx].Index - 1;
        TMSFMXRichEditor1.SelLength := matches[idx].Length;
        TMSFMXRichEditor1.SetSelectionColor(TAlphaColorRec.DarkBlue);
      end;
    end;
  finally
    TMSFMXRichEditor1.SelLength := SelLength;
    TMSFMXRichEditor1.SelStart  := SelStart;
    TMSFMXRichEditor1.EndUpdate;
  end;
end;

If you comment out the 3 red lines, the caret and selection both work just fine.

Hi, 


The responsible developer will look into this as soon as possible upon return to the office on friday.

Ok, thank Pieter

Hopefully (s)he can fix it quickly.

Maybe the TTMSFMXRichEditor CaretTimer is causing the problems ?!?
Or maybe I should use a different event instead of KeyUp.
A OnTyping Event would be nice to have :-)

Please use following code:


procedure TForm1.RefreshEdit;
const
  STR_REGEX = '(([A-Z]+:[A-Z0-9])|([A-Z0-9])|\s+)*';
var
  idx      : Integer;
  SelStart : Integer;
  SelLength: Integer;
  matches  : TMatchCollection;
  cx,cy: integer;

begin
  TMSFMXRichEditor1.CaretToSelection;
  SelStart  := TMSFMXRichEditor1.SelStart;
  SelLength := TMSFMXRichEditor1.SelLength;

  TMSFMXRichEditor1.BeginUpdate;
  try
    matches := TRegEx.matches(TMSFMXRichEditor1.PlainText, STR_REGEX, [roIgnoreCase]);

    for idx := 0 to matches.Count - 1 do
    begin
      if matches[idx].Value.Contains(':') then
      begin
        TMSFMXRichEditor1.SelStart  := matches[idx].Index - 1;
        TMSFMXRichEditor1.SelLength := matches[idx].Length;
        TMSFMXRichEditor1.SetSelectionColor(TAlphaColorRec.Red);
      end;
    end;
  finally
    TMSFMXRichEditor1.SelStart  := SelStart;
    TMSFMXRichEditor1.SelLength := SelLength;
    TMSFMXRichEditor1.SelectionToCaret;
    TMSFMXRichEditor1.EndUpdate;
  end;
end;

Bruno Fierens2017-08-25 14:35:00
Hi Bruno,

Thanks for the response.
I just want to color the Text in the RichEdit on-the-fly while the user is typing.
Unfortunately you suggested change still doesn't work and gives unwanted results.
I've included the most recent version of my routine.

Even if I remove the call to RefreshEdit from my KeyUp event,
and call it manually (from pushing a button), the selection and caret aren't the restored correctly.

procedure TForm1.RefreshEdit;
const
  STR_REGEX = '([A-Z]+\s*\:)|([A-Z0-9]+)+|\s*';
var
  idx    : Integer;
  ss     : Integer;
  sl     : Integer;
  matches: TMatchCollection;
begin
  TMSFMXRichEditor1.CaretToSelection;
  ss := TMSFMXRichEditor1.SelStart;
  sl := TMSFMXRichEditor1.SelLength;

  TMSFMXRichEditor1.BeginUpdate;
  try
    matches := TRegEx.matches(TMSFMXRichEditor1.PlainText, STR_REGEX, [roIgnoreCase]);

    for idx := 0 to matches.Count - 1 do
    begin
      TMSFMXRichEditor1.SelStart  := matches[idx].Index - 1;
      TMSFMXRichEditor1.SelLength := matches[idx].Length;

      if matches[idx].Value.Contains(':') then
        TMSFMXRichEditor1.SetSelectionColor(TAlphaColorRec.Red)
      else
        TMSFMXRichEditor1.SetSelectionColor(TAlphaColorRec.Black);
    end;
  finally
    TMSFMXRichEditor1.SelLength := ss;
    TMSFMXRichEditor1.SelStart  := sl;
    TMSFMXRichEditor1.SelectionToCaret;
    TMSFMXRichEditor1.EndUpdate;
  end;
end;

You did modify the code incorrectly.


it should be (as in my original code)

 TMSFMXRichEditor1.SelLength := sl;
 TMSFMXRichEditor1.SelStart  := ss;

You are so right Bruno, sorry for that.

I fixed it accordingly, but it still doesn't work.

After call RefreshEdit, the last part of the text stays Selected.
This is easily displayed when calling RefreshEdit from a button.

If I call RefreshEdit from the KeyUp event, the Richedit will contain only 1 character.
Namely the last, because the previous was Selected and replaced by the new key.

It looks like the Caret is at the right spot.
But the "sticky" selection is causing issues.

Hopefully, you have a solution for this...

Thanks in advance.

Do you use the latest version of the TTMSFMXRichEditor component as I cannot see an issue here.
Test project can be downloaded here:

http://www.tmssoftware.net/public/RichEditorExprColorSrc.zip

Hi Bruno,


Thanks for the test project.
But unfortunately it doesn't work over here.
Every time I type a char, it gets selected, and the next char i type will override it. 

Here a my system details:
Windows 10
Delphi 10.1 Berlin Update 2
TMS FMX 3.6.3.0
TMS FMX RichEditor 1.2.21.1

I cannot see this behavior here. Can you test with TMS FMX UI Pack v3.6.4.0 we released today so you use the same TTMSFMXRichEditor we used here for testing.

Yes, just downloaded the newest version of the FMX UI components.

Problem still exists with your test project.

Tried to use OnChange instead of KeyUp for the Refresh, 
but that doesn't make a difference.

Here a my system details again:
Windows 10
Delphi 10.1 Berlin Update 2
TMS FMX 3.6.4.0
TMS FMX RichEditor 1.2.21.1

It seems like the versionnumber of the TMS FMX Richeditor component is not updated.

I'm sorry but I see no problem.

I test with the test project http://www.tmssoftware.net/public/RichEditorExprColorSrc.zip
I just compile and run this app and I can type characters in the richeditor as-is. When I enter a sequence like aaaa: it is colored red. The caret is positioned after the character typed.
Please double-check everything that you do only compile our test app.

I downloaded the project again.

Started in a clean directory.
Compiled and run.

Still the same.
The problem isn't solved.

You can view a very short video illustrating this problem.
The video will be available for 48 hours only.
https://expirebox.com/download/b4c3b14a508b8db3f3059a3303dbafa0.html

Maybe it is due to the windows or Delphi version.

It is beyond my understanding why you see a different behavior.

You can download our compiled test app here:
http://www.tmssoftware.net/public/RichEditorExprCompiled.zip 

I tried your version of the executable and it works just fine.

So I tried compiling for debug and release over here.
No difference.
Also running in or out the Delphi environment, it's all the same.

I've sent the executable via WeTransfer to you info email address.
So you can take a look a what I'm experiencing.

There appears to be a difference in handling the key between Berlin and Tokyo.

When I change the code to:


procedure TForm3.RefreshEdit;
const
  STR_REGEX = '([A-Z]+\s*:)|([A-Z0-9]+)+|\s*';
var
  idx    : Integer;
  ss     : Integer;
  sl     : Integer;
  matches: TMatchCollection;
begin
  TMSFMXRichEditor1.CaretToSelection;
  ss := TMSFMXRichEditor1.SelStart;
  sl := TMSFMXRichEditor1.SelLength;

  TMSFMXRichEditor1.BeginUpdate;
  try
    matches := TRegEx.matches(TMSFMXRichEditor1.PlainText, STR_REGEX, [roIgnoreCase]);

    for idx := 0 to matches.Count - 1 do
    begin
      TMSFMXRichEditor1.SelStart  := matches[idx].Index - 1;
      TMSFMXRichEditor1.SelLength := matches[idx].Length;

      if matches[idx].Value.Contains(':') then
        TMSFMXRichEditor1.SetSelectionColor(TAlphaColorRec.Red)
      else
        TMSFMXRichEditor1.SetSelectionColor(TAlphaColorRec.Black);
    end;
  finally
    TMSFMXRichEditor1.SelStart  := ss;
    TMSFMXRichEditor1.SelLength := sl;
    TMSFMXRichEditor1.SelectionToCaret;
    TMSFMXRichEditor1.EndUpdate;
  end;
end;

it starts to work for Delphi 10.1 Berlin.

Hi Bruno,


YES, it works over here also now.
I really had to look 3x to find the change you made.

Very strange this makes a difference on Delphi Berlin compared to Tokyo.
It shouldn't, but it does.

Great you found the solution.
Thanks for all your effort.