La mejor manera de decirle programación si Leyenda de un TLabel se recorta (es decir dibujado usando puntos suspensivos)?
Pregunta
Tengo un TLabel
con juego EllipsisPosition
a epEndEllipsis
y tengo que ser capaz de decir si el texto se recorta o no actualmente. Además de calcular el área requerida para visualizar el texto mismo y su comparación con las dimensiones reales de la etiqueta, nadie ha llegado con una forma más fácil / más elegante de hacer esto?
En realidad, el cálculo de la superficie necesaria de una manera a prueba de fallos también no parece ser tan sencillo como suena ... Por ejemplo TCanvas.GetTextHeight
no toma en cuenta los saltos de línea.
TCustomLabel.DoDrawText
utiliza internamente o bien DrawTextW
o DrawThemeTextEx
with la bandera DT_CALCRECT
para determinar si se debe utilizar la elipsis o no. Hay una gran cantidad de código que participan allí, todos los cuales se declara private
. Simplemente duplicar todo ese código no calificaría exactamente como "elegante" en mi libro ...
¿Alguna idea?
(estoy usando Delphi 2010, en caso de que alguien se le ocurre una solución de Delphi-versión específica)
Actualización 1: ahora descubrió que sólo puedo llamar TCustomLabel.DoDrawText(lRect, DT_CALCRECT)
directamente (que se limitó a declarar protected
) para permitir que la etiqueta realice el cálculo del tamaño requerido sin tener que duplicar su código. Sólo tengo que asegurarse de que sea temporal conjunto EllipsisPosition
a epNone
o utilizar una instancia de etiqueta temporal por completo. Esto no es realmente tan malo y que sólo podría ir con él si nadie puede pensar en una solución aún más simple.
Actualización 2: Ahora he añadido mi solución como una respuesta por separado. Que resultó ser bastante más recta de avance de lo que esperaba por lo que probablemente hay mejor manera más fácil / para hacerlo, pero voy a dejar esta pregunta abierta por un tiempo más largo de todos modos por si acaso.
Solución
Fwiw, esto es lo que se me ocurrió (este es un método de un TLabel
-descendiente personalizado):
function TMyLabel.IsTextClipped: Boolean;
const
EllipsisStr = '...';
var
lEllipBup: TEllipsisPosition;
lRect: TRect;
begin
lRect := ClientRect;
Dec(lRect.Right, Canvas.TextWidth(EllipsisStr));
lEllipBup := EllipsisPosition;
EllipsisPosition := epNone;
try
DoDrawText(lRect, DT_CALCRECT or IfThen(WordWrap, DT_WORDBREAK));
finally
EllipsisPosition := lEllipBup;
end;
Result := ((lRect.Right - lRect.Left) > ClientWidth)
or ((lRect.Bottom - lRect.Top) > ClientHeight);
end;
Como esta ahora utiliza exactamente la misma lógica que TCustomLabel.DoDrawText
(especialmente el relleno artificial y el ajuste correcto WordWrap) también se ocupa correctamente con varias líneas y textos de entrada de palabras envueltas. Tenga en cuenta que "correctamente" en este caso significa "siempre devuelve True
cuando el TLabel
se dibuja con un pie de foto recortada y False
lo contrario".
Mientras que el código anterior hace lo que le pedía originalmente para probablemente no vuelva a usarlo de esta manera - pero esto es más debido a las deficiencias de TLabel
sí: Especialmente con texto de varias líneas que a menudo no se comporta de la manera que yo hubiera querido a, por ejemplo, cuando no hay suficiente espacio para múltiples líneas, siempre se truncará la última palabra de la primera línea, incluso si toda esa línea, más los puntos suspensivos se han instalado.
Otros consejos
Como punto de partida, se puede usar
function DrawStringEllipsis(const DC: HDC; const ARect: TRect; const AStr: string): boolean;
var
r: TRect;
s: PChar;
begin
r := ARect;
GetMem(s, length(AStr)*sizeof(char) + 8);
StrCopy(s, PChar(AStr));
DrawText(DC, PChar(s), length(AStr), r, DT_LEFT or DT_END_ELLIPSIS or DT_MODIFYSTRING);
result := not SameStr(AStr, s);
FreeMem(s);
end;
Ejemplo de uso:
procedure TForm1.FormClick(Sender: TObject);
begin
Caption := 'Clipped ' + BoolToStr(DrawStringEllipsis(Canvas.Handle, Rect(10, 100, 50, 50), 'This is a text.'), true);
end;
No sería difícil escribir un componente TExtLabel
que tiene una propiedad WasClipped
usando esta técnica. De hecho, el componente TLabel
es uno de los componentes más simples del VCL -. Simplemente dibuja una cadena