Respondiendo al teclado.
En el capítulo III vimos como podíamos responder a la pulsación
con el ratón sobre nuestro componente (Click) este método
estaba definido en el ancestro de nuestro componente TControl, si vais
a la ayuda de delphi de esta clase lo podéis ver.
En nuestro componente queremos, además, que cuando se pulse la
barra espaciadora o la tecla Return, el control se comporte como si se
hubiera pulsado con el ratón, es decir, que se hubiera hecho Click.
Si, como vimos, ya existía un método definido para el ratón,
podemos intuir que también lo puede haber para la pulsación
del teclado, para ello buscamos en sus ancestros un método que
pueda servirnos y hayamos que en TWinControl tenemos tres métodos
que podrían servirnos :
procedure KeyDown(var Key: Word; Shift: TShiftState); dynamic;
procedure KeyUp(var Key: Word; Shift: TShiftState); dynamic;
procedure KeyPress(var Key: Char); dynamic;
A nosotros nos basta con saber si las teclas Return y Barra espaciadora
se han pulsado, independientemente de si las teclas de mayúsculas,
control o Alt están también pulsadas, así que lo
que haremos será sobreescribir el método KeyPress para que
responda a la pulsación de estas dos teclas y haga caso omiso al
resto.
protected
...
procedure KeyPress(var Key: Char);override;
...
implementation
...
procedure TPanelSel.KeyPress(var Key: Char);
begin
if (Key=#13) or (Key=#32) then
Click;
Key:=#0;
end;
...
Como podéis ver, lo único que hace este procedimiento es
comprobar si se ha pulsado Return (#13) o espacio (#32), si es así
se llama al método Click. Después se pone a nulo (#0) la
tecla pulsada.
Alineación del texto.
Hasta este momento el texto de las propiedades Caption y Text sólo
está alineado a la izquierda, y si nos fijamos en el método
Paint vemos que esto lo fija el Flag DT_LEFT, yendo a la ayuda vemos que
además existe DT_RIGHT y DT_CENTER, por lo que usando cualquiera
de estos flags podemos cambiar la justificación del texto. Por
otra parte si nos fijamos en las propiedades del control Label, podemos
observar que tiene una propiedad Alignment que es la misma que nos interesa
a nosotros, así que utilizaremos su misma denominación y
tipo para nuestro componente, esta propiedad es de tipo TAlignment que
delphi define así :
TAlignment = (taLeftJustify, taRightJustify, taCenter);
En nuestro componente deberemos tener dos propiedades distintas, una
para Caption y otra para Text, la de Caption la llamaremos Alignment y
la de Text la denominaremos TextAlign las dos del tipo TAlignment :
private
...
FAlignment,
FTextAlign: TAlignment;
...
procedure SetAlignment(Value: TAlignment); procedure SetTextAlign(Value: TAlignment); ...
published
...
property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
property TextAlign: TAlignment read FTextAlign write SetTextAlign default taLeftJustify;
...
implementation
...
constructor TPanelSel.Create(AOwner:TComponent); begin inherited;
...
Alignment:=taLeftJustify;
TextAlign:=taLeftJustify;
...
end;
...
procedure TPanelSel.SetAlignment(Value: TAlignment); begin if FAlignment<>Value then begin FAlignment:=Value; Invalidate; end; end; procedure TPanelSel.SetTextAlign(Value: TAlignment); begin if FTextAlign<>Value then begin FTextAlign:=Value; Invalidate; end; end;
...
Ya tenemos definidas las propiedades, ahora nos falta tenerlas en cuenta
cuando dibujamos el texto en el método Paint de nuestro control.
Debemos hacer algo para transformar los valores de las propiedades a los
que nosotros necesitamos, o sea, de taLeftJustity a DT_LEFT e igual para
el resto. Una forma sería mediante la estructura 'Case var
of' de delphi, pero en este caso que tenemos dos propiedades que hacen
lo mismo, nos veríamos duplicar código o sacar el código
a una función y hacer dos llamadas, una con cada una de las propiedades.
Hay una forma más 'elegante' de hacerlo :
const
AAlignment : array[taLeftJustify..taCenter] of uchar=(DT_LEFT,DT_RIGHT,DT_CENTER);
¿Qué hace esto?, pues define una constante que es un array
unidimensional con tres valores, los índices de este array son
los contenidos de las propiedades de Alineación y los valores del
mismo son los Flags que nosotros necesitamos, así que bastará
hacer AAlignment[TextAlign]
para obtener el flag que haga que el texto contenido en la propiedad Text
esté justificado de acuerdo a esa propiedad:
procedure TPanelSel.Paint;
const
AAlignment : array[taLeftJustify..taCenter] of uchar=(DT_LEFT,DT_RIGHT,DT_CENTER);
var
X, Y, W, H: Integer;
...
...
Flags:=AAlignment[Alignment] or DT_NOPREFIX or DT_END_ELLIPSIS;
R:=Rect(posxText+desph,despv,width-desph,height-despv);
Font:=self.Font;
Drawtext(handle,PChar(caption),-1,R,flags);
Flags:=DT_WORDBREAK or AAlignment[TextAlign] or DT_NOPREFIX;
R:=Rect(posxText+desph,TextHeight(Caption)+despv,width-despv,height-despv);
Font:=self.TextFont;
DrawText(Handle, PChar(Text), -1, R, Flags);
...
end;
...
Figura 10
Podemos pensar que ya hemos conseguido lo que queríamos, pero
nos falta un detalle, si observamos las propiedades de nuestro componente,
vemos una propiedad que tiene que ver con la alineación del texto
y que nosotros no hemos tenido en cuenta : BiDiMode (Bidirectional Mode)
esta propiedad ajusta la apariencia del texto cuando el componente se
ejecuta en un país en el que se lee de derecha a izquierda, por
ello, en el caso de que esto sea así, lo que nosotros llamaremos
justificado a la izquierda para otros países será a la derecha
y viceversa. Vea los cambios hechos en el código para tener en
cuenta esta propiedad :
unit PanelSel;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, Graphics;
type
TPanelSel = class(TCustomControl)
private
FPicture:TPicture;
FColors:array[0..5] of TColor;
FOnMouseEnter,
FOnMouseLeave:TNotifyEvent;
FBorder:Boolean;
FBorderWidth:Integer;
FOver:Boolean;
FPosXPicture:Word;
FText:TCaption;
FTextFont:TFont;
FPosXText:Word;
FAlignment,
FTextAlign: TAlignment;
procedure SetPicture(Value:TPicture);
procedure SetColors(Index:Integer;Value:TColor);
function GetColors(Index:integer):TColor;
procedure SetBorder(Value:Boolean);
procedure SetBorderWidth(Value:integer);
procedure SetPosXPicture(Value:Word);
procedure SetText(Value:TCaption);
procedure SetPosXText(Value:Word);
procedure SetTextFont(Value:TFont);
procedure FontChanged(Sender: TObject);
procedure SetAlignment(Value: TAlignment);
procedure SetTextAlign(Value: TAlignment);
{ Private declarations }
protected
procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
procedure WMKillFocus(var Message: TWMSetFocus); message WM_KILLFOCUS;
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure Paint; override;
procedure KeyPress(var Key: Char);override;
procedure Click;override;
{ Protected declarations }
public
constructor Create(AOwner:TComponent);override;
destructor Destroy;override;
property Colors[Index:Integer]:TColor read GetColors Write SetColors;
Procedure SetAllTexts(S:String);
{ Public declarations }
published
property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
property TextAlign: TAlignment read FTextAlign write SetTextAlign default taLeftJustify;
property Picture:TPicture read FPicture Write SetPicture;
property Border:Boolean read FBorder Write SetBorder default True;
property BorderWidth:integer read FBorderWidth Write SetBorderWidth default 1;
property Color:TColor Index 0 read GetColors Write SetColors default clBtnFace;
property BorderColor:TColor Index 1 read GetColors Write SetColors default clBlack;
property FocusedColor:TColor Index 2 read GetColors Write SetColors default clBtnHighlight;
property FocusedBorderColor:TColor Index 3 read GetColors Write SetColors default clBlack;
property OverColor:TColor Index 4 read GetColors Write SetColors default clBtnShadow;
property OverBorderColor:TColor Index 5 read GetColors Write SetColors default clBlack;
property PosXPicture:Word read FPosXPicture Write SetPosXPicture default 10;
property PosXText:Word read FPosXText Write SetPosXText default 50;
property Caption;
property Text:TCaption read FText Write SetText;
property TextFont:TFont read FTextFont Write SetTextFont;
property Font;
property Tabstop;
property BiDiMode;
property TabOrder;
property Action;
property Align;
property Anchors;
property Visible;
property Enabled;
property Constraints;
property DragCursor;
property DragKind;
property DragMode;
property ParentBiDiMode;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property OnMouseEnter:TNotifyEvent read FOnMouseEnter Write FOnMouseEnter;
property OnMouseLeave:TNotifyEvent read FOnMouseLeave Write FOnMouseLeave;
property OnClick;
property OnContextPopup;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
property OnEnter;
property OnExit;
{ Published declarations }
end;
Function _BiDiMode(Alignment: TAlignment;BiDi:TBiDiMode):TAlignment;
procedure Register;
implementation
Function _BiDiMode(Alignment: TAlignment;BiDi:TBiDiMode):TAlignment;
begin
Result :=Alignment;
if (SysLocale.MiddleEast) and (BiDi= bdRightToLeft) then
case Alignment of
taLeftJustify: Result := taRightJustify;
taRightJustify: result := taLeftJustify;
end;
end;
Procedure TPanelSel.SetAllTexts(S:String);
begin
if S='' then
begin
Caption:='';
Text:='';
end
else
if pos('|',S)>0 then
begin
caption:=copy(S,1,pos('|',S)-1);
text:=copy(S,pos('|',S)+1,length(S));
end
else
Caption:=S;
end;
constructor TPanelSel.Create(AOwner:TComponent);
begin
inherited;
FOver:=False;
Tabstop:=True;
FBorder:=True;
FBorderWidth:=1;
FColors[0]:= clBtnFace;
FColors[1]:=clBlack;
FColors[2]:=clBtnHighlight;
FColors[3]:=clBlack;
FColors[4]:= clBtnShadow;
FColors[5]:=clBlack;
FPicture:=TPicture.Create;
FTextFont:=TFont.Create;
FTextFont.OnChange:=FontChanged;
FPosXPicture:=10;
FPosXText:=50;
FText:='';
Font.Style:=[fsBold];
FAlignment:=taLeftJustify; FTextAlign:=taLeftJustify;
end;
destructor TPanelSel.Destroy;
begin
FTextFont.Free;
FPicture.Free;
inherited;
end;
procedure TPanelSel.CMTextChanged(var Message: TMessage);
begin
inherited;
invalidate;
end;
procedure TPanelSel.WMSetFocus(var Message: TWMSetFocus);
begin
inherited;
Invalidate;
end;
procedure TPanelSel.WMKillFocus(var Message: TWMSetFocus);
begin
inherited;
Invalidate;
end;
procedure TPanelSel.CMMouseEnter(var Message: TMessage);
begin
inherited;
if Assigned(FOnMouseEnter) then FOnMouseEnter(Self);
FOver:=True;
Invalidate;
end;
procedure TPanelSel.CMMouseLeave(var Message: TMessage);
begin
inherited;
if Assigned(FOnMouseLeave) then FOnMouseLeave(Self);
FOver:=False;
Invalidate;
end;
procedure TPanelSel.SetPicture(Value:TPicture);
begin
FPicture.Assign(Value);
repaint;
end;
procedure TPanelSel.SetPosXPicture(Value:Word);
begin
if FPosXPicture<>Value then
if value>0 then
begin
FPosXPicture:=Value;
invalidate;
end;
end;
procedure TPanelSel.SetPosXText(Value:Word);
begin
if FPosXText<>Value then
if Value>0 then
begin
FPosXText:=Value;
invalidate;
end;
end;
procedure TPanelSel.SetText(Value: TCaption);
begin
if FText<>Value then
begin
FText:=Value;
invalidate;
end;
end;
procedure TPanelSel.SetTextFont(Value:TFont);
begin
FTextFont.Assign(Value);
end;
procedure TPanelSel.FontChanged(Sender: TObject);
begin
invalidate;
end;
procedure TPanelSel.SetBorder(Value:Boolean);
begin
if FBorder<>Value then
begin
FBorder:=Value;
Invalidate;
end;
end;
procedure TPanelSel.SetBorderWidth(Value:integer);
begin
if FBorderWidth<>Value then
begin
if Value>0 then
FBorderWidth:=Value;
Invalidate;
end;
end;
procedure TPanelSel.SetColors(Index:Integer;Value:TColor);
begin
if FColors[Index]<>Value then
begin
FColors[Index]:=Value;
Invalidate;
end;
end;
Function TPanelSel.GetColors(Index:Integer):TColor;
begin
Result:=FColors[Index];
end;
procedure TPanelSel.Click;
begin
inherited;
SetFocus;
end;
procedure TPanelSel.KeyPress(var Key: Char);
begin
if (Key=#13) or (Key=#32) then
Click;
Key:=#0;
end;
procedure TPanelSel.SetAlignment(Value: TAlignment);
begin
if FAlignment<>Value then
begin
FAlignment:=Value;
Invalidate;
end;
end;
procedure TPanelSel.SetTextAlign(Value: TAlignment);
begin
if FTextAlign<>Value then
begin
FTextAlign:=Value;
Invalidate;
end;
end;
procedure TPanelSel.Paint;
const
AAlignment : array[taLeftJustify..taCenter] of uchar=(DT_LEFT,DT_RIGHT,DT_CENTER);
var
X, Y, W, H: Integer;
r:TRect;
Flags:Cardinal;
despv,desph:Integer;
begin
despv:=3;
Desph:=0;
if border then
begin
despv:=despv+BorderWidth;
Desph:=BorderWidth-1;
end;
with Canvas do
begin
setbkmode(Handle,TRANSPARENT);
Pen.Width:=BorderWidth;
Pen.Color:=BorderColor;
Brush.Style:=bsSolid;
Brush.Color:=Color;
X := Pen.Width div 2;
Y := X;
W := Width - Pen.Width + 1;
H := Height - Pen.Width + 1;
if Focused then
begin
Pen.Color:=FocusedBorderColor;
Brush.Color:=FocusedColor;
end;
if FOver then
begin
Pen.Color:=OverBorderColor;
Brush.Color:=OverColor;
end;
FillRect(ClientRect);
Brush.Style:=bsClear;
if Assigned(Picture.Graphic) then
begin
Picture.Graphic.Transparent:=true;
Draw(BorderWidth+PosXPicture,((Height-Picture.Graphic.Height) div 2),Picture.Graphic);
end;
if Border then Rectangle(X, Y, X + W, Y + H);
Flags:=AAlignment[_BiDiMode(Alignment,BiDiMode)] or DT_NOPREFIX or DT_END_ELLIPSIS;
R:=Rect(posxText+desph,despv,width-desph,height-despv);
Font:=self.Font;
Drawtext(handle,PChar(caption),-1,R,flags);
Flags:=DT_WORDBREAK or AAlignment[_BiDiMode(TextAlign,BiDiMode)] or DT_NOPREFIX;
R:=Rect(posxText+desph,TextHeight(Caption)+despv,width-despv,height-despv);
Font:=self.TextFont;
DrawText(Handle, PChar(Text), -1, R, Flags);
end;
end;
procedure Register;
begin
RegisterComponents('Ejemplo', [TPanelSel]);
end;
end.
|