Search code examples
delphitrichedit

TRichEdit color problems


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.


Solution

  • 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;