Automatycznie wstawienie #13#10 gdy string jest za długi

Automatycznie wstawienie #13#10 gdy string jest za długi
MR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 119
1
Kopiuj
lines := TStringList.Create;
lines.Text := 'linia1 krótka#13#10Linia2 długa, która nie mieści się w całości na canvasie i trzeba ją podzielić#13#10Linia3 krótka';

textSize := canvas.TextExtent('LOZL!');
blockHeightL := 6 + textSize.cy * lines.Count;

for n := 0 to lines.Count - 1 do
  begin
    textSize := canvas.TextExtent(lines[n]);
    canvas.TextOut(_Left+7,_Top+2 + (textSize.cy * n),lines[n]);
  end;

Mam taki fragment kodu, który na canvasie rysuje kolejne linie ze stringlisty.
I to działa dobrze.
Ale mam problem, jeśli któraś z linii jest za długa. (np powyżej 200px)
Chciałbym (w tym przypadku do drugiej linii) wstawić #13#10 w miejscu spacji która już zmieści się na canvasie, a resztę przenieść do tej string listy do kolejnej linii.
Jak to można najprościej?

MI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 49
1

Jeśli Twój Canvas to zwykły Canvas zawierający HDC to proponuję użyć DrawText z parametrem DT_WORDBREAK
https://learn.microsoft.com/pl-pl/windows/win32/api/winuser/nf-winuser-drawtext?redirectedfrom=MSDN

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1

@michalgw Cancas okna i wszelkich kontrolek to tylko abstrakcja zbudowana na HDC. 😉

To co podałeś to jak najbardziej prawidłowe rozwiązanie, choć zaznaczyć trzeba, że klasa TCanvas posiada metodę TextRect, która jest wysokopoziomową (nieco łatwiejszą w użyciu) i przeznaczona jest właśnie do renderowania tekstu w zadanym obszarze, dając dostęp do formatowania i clippingu. Założę się, że jest to prostu wrapper na funkcję DrawText z Win32 API (w Lazarusie jest).

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10227
0

Czyli w zasadzie chcesz dodać zwijanie wierszy podczas rysowania tekstu na canvas. Rozwiązanie od @flowCRANE wydaje się najlepsze do tego celu.

MR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 119
0

Ale muszę znać wysokość obszaru który zajął tekst bo pod nim będę rysować coś innego. Czy jeśli użyję automatycznego zawijania wierszy to będę znał wysokość?

Riddle
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 10227
1

Przeczytaj dokumentację podlinkowaną przez @flowCRANE: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-drawtext Zwłaszcza zwróć uwagę na parametr format oraz wartość DT_CALCRECT. Czytamy w dokumentacji:

Determines the width and height of the rectangle. If there are multiple lines of text, DrawText uses the width of the rectangle pointed to by the lpRect parameter and extends the base of the rectangle to bound the last line of text. If the largest word is wider than the rectangle, the width is expanded. If the text is less than the width of the rectangle, the width is reduced. If there is only one line of text, DrawText modifies the right side of the rectangle so that it bounds the last character in the line. In either case, DrawText returns the height of the formatted text but does not draw the text.

To pozwoli Ci uzyskać jaką wysokość będzie miał zwijany tekst kiedy będzie wyświetlony. Ponieważ wywołanie funkcji z parametrem nie wyświetla tekstu będziesz musiał wywołać funkcję dwa razy: raz z parametrem DT_CALCRECT żeby obliczyć wysokość, a potem drugi raz bez parametru, żeby wyświetlić tekst.

MR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 119
0
Kopiuj
                ARect.Left:=60;
                ARect.Right:=300;
                ARect.Top:=2;
                ARect.Bottom:=20;

                DrawText(Canvas.Handle,PChar(S),Length(S),ARect,DT_LEFT or DT_WORDBREAK or DT_CALCRECT);
                DrawText(Canvas.Handle,PChar(S),Length(S),ARect,DT_LEFT or DT_WORDBREAK);

screenshot-20250121100629.png
DrawText rzeczywiście robi to co chciałem, ale nie do końca dobrze
Jak widać ostatnie słowa które nie mieszczą się na wyznaczonym obszarze są obcinane, A powinny być przeniesione do nowej linii.

podobnie Canvas.TextRect

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
2

Nie wiem na czego płótnie ten tekst jest malowany, ale wątpię, aby systemowa funkcja DrawText posiadała bugi i nieprawidłowo obliczała rozmiar tekstu. Użyj metody TextRect z klasy TCanvas, zamiast surowego Win32 API — pisałem, będzie łatwiej.

Sprawdziłem w Lazarausie, tam jest metoda TCanvas.TextRect, która wewnętrznie wywołuje systemową funkcję DrawText i działa prawidłowo, nie ma żadnych problemów z ucinaniem tekstu:

Kopiuj
procedure TForm1.FormPaint(Sender: TObject);
const
  SOME_TEXT = 'PIERWSZA BARDZO DŁUGA LINIA PISANA DUŻYMI LITERAMI'#13#10'druga linia'#13#10'i jeszcze trzecia trochę dłuższa, która też się nie mięsci w obszarze.';
var
  TextStyle: TTextStyle;
begin
  Form1.Canvas.Brush.Color := clWhite;
  Form1.Canvas.FillRect(Form1.ClientRect);

  TextStyle := Default(TTextStyle);
  TextStyle.Wordbreak := True;

  Form1.Canvas.TextRect(Form1.ClientRect, 0, 0, SOME_TEXT, TextStyle);
end;

Efekt:

screenshot-20250121181843.gif

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.