Apply text style of a RichEdit in Delphi

1

I need to bold some words within a RichEdit in Delphi, it works normally when there is no line break. But when I enter a line break I can not correctly select the word to apply the style.

All the examples I found have the same problem.

In this example it stores the words that will be blacked out and the color they will have and typing it will apply the formatting. But it only works until you insert a line break.

procedure TfrmRichEdit.RichEdit1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
const
   LetrasValidas = ['a'..'z', 'A'..'Z', '0'..'9', '<', '>', '!', '='];

var
   iPosIni: Integer;
   iPosFim: Integer;
   iSelStart: Integer;
   iSelLength: Integer;
   iLoopFor: Integer;
   sText: string;
begin
   LockWindowUpdate(RichEdit1.Handle);

   // Guardaremos a posição inicial
   iSelStart := RichEdit1.SelStart;
   iSelLength := RichEdit1.SelLength;

   sText := RichEdit1.Text;

   // Acharemos o inicio da palavra
   iPosIni := iSelStart;
   if sText[iPosIni] in LetrasValidas then
   begin
      for iLoopFor := iSelStart - 1 downto 0 do
      begin
         if sText[iLoopFor] in LetrasValidas then
            iPosIni := iLoopFor
         else
            Break;
      end;
   end;

   // Acharemos o final da palavra
   iPosFim := iSelStart;
   for iLoopFor := iSelStart + 1 to Length(RichEdit1.Text) do
   begin
      if RichEdit1.Text[iLoopFor] in LetrasValidas then
         iPosFim := iLoopFor
      else
         Break;
   end;

   // Selecionaremos a palavra
   RichEdit1.SelStart := iPosIni - 1;
   RichEdit1.SelLength := (iPosFim) - RichEdit1.SelStart;

   // setaremos a cor original e estilo original
   RichEdit1.SelAttributes.Color := clBlack;
   RichEdit1.SelAttributes.Style := [];

   // Atribuiremos a nova cor e estilo caso encontre a palavra
   for iLoopFor := 0 to High(APalavras) do
   begin
      if UpperCase(APalavras[iLoopFor].DS_PALAVRA) = UpperCase(RichEdit1.SelText) then
      begin
         RichEdit1.SelAttributes.Color := APalavras[iLoopFor].VR_COR;
         RichEdit1.SelAttributes.Style := APalavras[iLoopFor].ESTILO;
         Break;
      end;
   end;

   // Posicionaremos o cursor na posição original
   RichEdit1.SelStart := iSelStart;
   RichEdit1.SelLength := iSelLength;

   LockWindowUpdate(0);
end;
    
asked by anonymous 28.04.2017 / 16:48

1 answer

1

As far as I can identify when debugging the code you put in, the problem is that each line break adds a special invisible character (the character CR or #13#10 ).

With each new line break the count is getting lost more and more. The workaround is to count how many line breaks exist from the beginning of the text to the current cursor position and then subtract that number of line breaks from the value of RichEdit1.SelStart .

To do this count I created a method (I created it fast, so I know it would improve kkk) and use it within OnKeyUp .

METHOD CONCERNING LINE BREAKS:

function TfrmRichEdit.GetCrCount(const pText: string; const pPosition: Integer): Integer;
var
  lPos: Integer;
begin
  Result := 0;

  lPos := 1;
  while lPos < pPosition do
  begin
    lPos := Pos(#13#10, pText, lPos);
    if (lPos = 0) or (lPos >= pPosition) then
    begin
      Break;
    end
    else
    begin
      inc(Result);
      inc(lPos);
    end;
  end;
end;

Now that we have the method that counts line breaks, we'll apply their usage to the OnKeyUp you've posted:

WARNING: The changes I've made are indicated with these bookmarks:

<<<<< PONTO 1 >>>>> <<<<< PONTO 2 >>>>> <<<<< PONTO 3 >>>>>

YOUR METHOD ONKEYUP () WITH MY CHANGES

procedure TfrmRichEdit.RichEdit1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
const
  LetrasValidas = ['a'..'z', 'A'..'Z', '0'..'9', '<', '>', '!', '='];

var
  iPosIni: Integer;
  iPosFim: Integer;
  iSelStart: Integer;
  iSelLength: Integer;
  iLoopFor: Integer;
  lCountCr: Integer;
  sText: string;
begin
  LockWindowUpdate(RichEdit1.Handle);

  // Guardaremos a posição inicial
  iSelStart := RichEdit1.SelStart;
  iSelLength := RichEdit1.SelLength;

  sText := RichEdit1.Text;

  // Acharemos o inicio da palavra
  iPosIni := iSelStart;
  if sText[iPosIni] in LetrasValidas then
  begin
    for iLoopFor := iSelStart - 1 downto 0 do
    begin
      if sText[iLoopFor] in LetrasValidas then
        iPosIni := iLoopFor
      else
        Break;
    end;
  end;

  // Acharemos o final da palavra
  iPosFim := iSelStart;
  for iLoopFor := iSelStart + 1 to Length(RichEdit1.Text) do
  begin
    if RichEdit1.Text[iLoopFor] in LetrasValidas then
      iPosFim := iLoopFor
    else
      Break;
  end;

  // <<<<< PONTO 1 >>>>>
  // ***( OBTENHO A CONTAGEM DE QUEBRAS DE LINHA )***
  lCountCr := GetCrCount(sText, iSelStart);
  // ************************************************

  // Selecionaremos a palavra
  // <<<<< PONTO 2 >>>>>
  // ***( SUBTRAIO O NÚMERO DE QUEBRAS DE LINHA )***
  //RichEdit1.SelStart := iPosIni - 1;
  //RichEdit1.SelLength := (iPosFim) - RichEdit1.SelStart;
  RichEdit1.SelStart := iPosIni - 1 - lCountCr;
  RichEdit1.SelLength := (iPosFim) - RichEdit1.SelStart - lCountCr;
  // ***********************************************

  // setaremos a cor original e estilo original
  RichEdit1.SelAttributes.Color := clBlack;
  RichEdit1.SelAttributes.Style := [];

  // Atribuiremos a nova cor e estilo caso encontre a palavra
  for iLoopFor := 0 to High(APalavras) do
  begin
    // <<<<< PONTO 3 >>>>>
    // ***( REMOVO OS ESPAÇOS ANTES E DEPOIS PARA OTIMIZAR A COMPARAÇÃO )***
    //if UpperCase(APalavras[iLoopFor].DS_PALAVRA) = UpperCase(RichEdit1.SelText) then
    if Trim(UpperCase(APalavras[iLoopFor].DS_PALAVRA)) = Trim(UpperCase(RichEdit1.SelText)) then
    // *********************************************************************
    begin
      RichEdit1.SelAttributes.Color := APalavras[iLoopFor].VR_COR;
      RichEdit1.SelAttributes.Style := APalavras[iLoopFor].ESTILO;
      Break;
    end;
  end;

  // Posicionaremos o cursor na posição original
  RichEdit1.SelStart := iSelStart;
  RichEdit1.SelLength := iSelLength;

  LockWindowUpdate(0);
end;
    
09.05.2017 / 17:47