2
ans:= RichEdit1.Text     
for i:=1 to Length(ans) do
begin
   RichEdit1.SelStart :=  i-1;
   RichEdit1.SelLength:= 1;
   if ans[i] = correct[i] then
      RichEdit1.SelAttributes.Color := clRed
   else
      RichEdit1.SelAttributes.Color := clBlue;  

If the letter in ans matches the letter in the same position as the letter in correct string, it is colored red otherwise, it is blue.

My problem is when I type again the whole RichEdit1 text is colored as the same as the first letter (if the first letter of RichEdit1 is blue then the whole text becomes blue).

By the way, this is not the the actual code I just simplified it because there are mutiple TRichEdits.
The TRichEdits are read-only and I assign the letters by something like RichEdit1.Text := RichEdit1.Text+Key; (doing this because it's a multiple keyboard program and I need to separate user inputs)

Is this the correct behavior? How can I stop my color changes from overriding the default color?

update: Solved it... in a sloppy way (applying the default color whenever someone types), but I'm keeping this open in case someone comes up with a better solution.

Dian
  • 1,187
  • 3
  • 18
  • 35
  • First, your code doesn't even compile... second, when are you calling this subroutine? I did a small example based on your code, added corrections and refinements and it is working very good for me. – jachguate Sep 22 '10 at 03:09
  • RichEdit1..SelAttributes.Color := clBlue;(you've put an extra . between richedit1 and selattributes.color) – Omair Iqbal Sep 22 '10 at 05:12
  • @jachguate: that's not the whole code, just a little snippet btw. I call it onkeypress. – Dian Sep 22 '10 at 05:22
  • @omair: Oh, just a typo. Thanks for pointing it out. Gonna go fix it now. – Dian Sep 22 '10 at 05:22
  • I'm calling it on the OnChange event. Not every KeyPress changes the Edit content. As said, it works, with flickering, but I'm sure it can be optimized to make less changes. I know no way to prevent the control to repaint on every single attribute change, thinking on something like BeginUpdate/EndUpdate pair. – jachguate Sep 23 '10 at 01:20
  • @jachguate: I can't change it to OnChange because it cannot get what key is pressed(it's a read only RichEdit, reasons stated in question). I already have a temporary fix but it's really bad programming. – Dian Sep 23 '10 at 02:09
  • @Dian, you edited your question after I first read it. If the richedit is too simplified (no cut/paste, no backspace, etc.), you can just color the new char and not all the richedit. Anyway your description seems still very incomplete to make a good (or smart) suggestion. – jachguate Sep 23 '10 at 02:27
  • @jachguate: There's no cut/paste but there is backspace. I do color the new char with the default color (as I stated in the question) but I'm looking for an alternative solution. What else should I include in the question? Other details seem irrelevant. – Dian Sep 23 '10 at 05:15

1 Answers1

5

As you already discovered, you have to reset the default color when you are done, eg:

ans := RichEdit1.Text;
for i := 1 to Length(ans) do 
begin 
  RichEdit1.SelStart := i-1; 
  RichEdit1.SelLength := 1; 
  if ans[i] = correct[i] then 
    RichEdit1.SelAttributes.Color := clRed 
  else 
    RichEdit1.SelAttributes.Color := clBlue;
end;
RichEdit1.SelStart := RichEdit1.GetTextLen;
RichEdit1.SelLength := 0;
RichEdit1.SelAttributes.Color := RichEdit1.Font.Color;

There are more efficient ways to handle this than coloring one letter at a time, eg:

const
  colors: array[Boolean] of TColor = (clRed, clBlue);
var
  ans: string;
  start, len: Integer;
  cur_state: Boolean;

  procedure ColorRange(AStart, ALength: Integer; AColor: TColor);
  begin
    RichEdit1.SelStart := AStart;
    RichEdit1.SelLength := ALength;
    RichEdit1.SelAttributes.Color := AColor;
  end;

begin
  RichEdit1.Lines.BeginUpdate;
  try
    ans := RichEdit1.Text;
    start := 0;
    len := 0;
    cur_start := False;

    for i := 1 to Length(ans) do 
    begin 
      if (ans[i] = correct[i]) = cur_state then
        Inc(len)
      else begin
        if len > 0 then
          ColorRange(start, len, colors[cur_state]);
        start := i-1;
        len := 1;
        cur_state := not cur_state;
      end;
    end;
    if len > 0 then
      ColorRange(start, len, colors[cur_state]);
    ColorRange(RichEdit1.GetTextLen, 0, RichEdit1.Font.Color);
  finally
    RichEdit1.Lines.EndUpdate;
  end;
end;

Also, using the Text property to append a single Char is very inefficient. Use the SelText property instead, eg:

RichEdit1.SelStart := RichEdit1.GetTextLen;
RichEdit1.SelLength := 0;
RichEdit1.SelAttributes.Color := ...; // optional
RichEdit1.SelText := Key;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770