|
Formas no rectangulares - Parte I
Copyright © 2000 Alirio A. Gavidia B.. Para ver más de
mis artículos visite Programación Orientada en Delphi
Introducción
El presente ensayo pretende demostrar las posibilidades de usos de las
formas no rectangulares bajo Windows. El uso es simple pero las
posibilidades parecen ser interesantes. Adicionalmente, en particular en
esta primera parte, se hará uso de dos elementos
importantes: Canvas (lienzo) y Timer (temporizador).
El primer ejemplo
En el artículo anterior (ver "Post-it". Ventanas
ajustables sin borde ni título) hice referencia a la posibilidad
de crear un reloj analógico redondo como lo hace el "PowerToys" de
Windows. Hacer un formulario redondo es simple y se limita a llamar
a dos procedimientos. El reloj debe además dibujar agujas (lo que
requiere cierto cálculo matemático) y actualizarse unas 60 veces por
minuto (para ello el TTimer).
Nota: Reloj Analógico por contraposición
a Digital. El término NO es correcto porque el reloj que aquí
creamos aún cuando tenga agujas es digital. Para que el reloj imitara
un comportamiento realmente analógico tendría que tener un movimiento
continuo, el que aquí creamos se mueve de segundo en segundo por lo que
tiene un número de estados definidos y finitos eso lo califica de digital.
Parece ser que el término más adecuado es "reloj de agujas".
Sin título
El reloj se basa en un formulario simple sin borde; con ello se elimina
la barra de título, que resulta en un estorbo al tener un formulario
redondo. Este genera adicionalmente el problema de traslado del
formulario. Por este motivo la operación de arrastre debe ser atendida
desde el área cliente del formulario. Para resolver este inconveniente
hay varios métodos. Opto por el aplicado en el artículo anterior (uso
del wm_Syscommand $f012):
procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
ReleaseCapture;
Perform(wm_Syscommand, $f012, 0)
end;
Otra técnica es responder al Wm_NcHitTest y retornar como resultado
htCaption. Esto simplemente es comunicarle a Windows que se hizo clic
sobre el área de título. Me parece que hay al menos otra técnica más
pero no es objetivo de este ensayo.
El "timer"
En el formulario se incluye un control TTimer. Este control dispara un
evento de manera aproximada cada cierto intervalo (por la propiedad
interval dada en milisegundos). Esta bien documentado su uso en
Delphi, pero debo agregar algo: no es exacto.
El evento asociado al timer ejecuta tres acciones: eliminar agujas de
posición anterior, tomar la hora del sistema y colocar agujas en una
nueva posición.
El "Canvas"
El Canvas provee capacidades gráficas para controles gráficos y
formularios. Brinda posibilidades para dibujar líneas, elipses,
polígonos, textos y otros. Su manejo es simple y práctico.
Hacemos uso de los siguientes elementos del Canvas:
Pen, para establecer color y modo de trazado de línea.
MoveTo, para establecer el punto inicial de trazado de una línea.
LineTo, para trazar una línea desde la última posición a la nueva.
Rectangle, para trazar un rectángulo según los parámetros
especificados.
Es de notar particularmente la propiedad Canvas.Pen.Mode que nos
permite utilizar valores como pmXor o pmCopy (entre
muchos otros).
Nota: El uso del Xor como operador lógico tiene una
propiedad interesante y es que aplicada dos veces se obtiene el valor
original, esto es: x xor y resulta en z si
a z se le aplica xor y nuevamente resulta
en x. Esto es muy útil para cifrados simples donde queremos
aplicar una clave para cifrar y la misma
clave y el mismo algoritmo para descifrar. También es muy útil para
casos donde queremos dibujar algo y luego borrarlo porque para
borrarlo sólo se requiere volver a dibujar aplicando xor entre el
fondo y el pixel a dibujar.
El evento Paint del formulario ciertamente hace uso de la
propiedad Mode y Color de la plumilla obteniendo
un curioso efecto. El resultado, al menos para mi, no fue el esperado, sin
embargo fue agradable. Todo ello parece ser consecuencia de la manera en
que se crea un rectángulo, el mismo es rellenado y luego se trazan los bordes
superior e izquierdo, que bajo el operador Xor generan un color
blanco, la imagen resultante genera un efecto 3D. Sugiero al lector
comentar las lineas de Mode y Color para obtener sus propias
conclusiones.
Como comentario adicional Pen.Color sólo afecta al borde del
rectángulo. El relleno es definido por la
propiedad Brush del Canvas la cual, en este caso,
permanece con su valor por defecto.
procedure TForm1.FormPaint(Sender: TObject);
Var
X1, Y1, i : integer;
Angle : double;
begin
Canvas.Pen.Mode := pmXor;
Canvas.Pen.Color := clBtnFace;
for i:=1 to 12 do
begin
Angle := i * 2 * Pi / 12;
X1 := trunc(X0 + Len * Sin(Angle));
Y1 := trunc(Y0 - Len * Cos(Angle));
Canvas.Rectangle(X1-4,Y1-4,X1+4,Y1+4)
end;
Canvas.Pen.Mode := pmCopy;
Canvas.Pen.Color := clBlack;
end;
De aritmética
El sistema de coordenadas tiene el punto cero en la esquina superior
izquierda. Para determinar la posición de la aguja necesitamos el centro
y el extremo. El centro es la mitad del ancho y la altura. Para la
longitud de las agujas tomamos una fracción del mínimo entre el ancho y
la altura.
Conocido el centro y largo de la aguja sólo resta conocer la posición de
su extremo, para ello se determina el ángulo. Aquí terminamos recurriendo
a la trigonometría. El ángulo es determinado con cero en el 12, 90
grados a las 3, 180 grados a las 6 hasta 360 a las 12. El seno del
referido ángulo nos da la apertura en la coordenada "X", el coseno la
apertura en la coordenada "Y". Como nota adicional el sistema de
coordenadas en el eje "Y" esta invertido con respecto a los sistemas
trigonométricos usuales. Todos los ángulos deben ser expresados como
Radianes para efectos de la función seno y coseno. El
procedimiento DrawWatchHand efectúa los cálculos (también
en FormPaint)
procedure Tform1.DrawWatchHand;
:
:
// Now draw Second
Canvas.MoveTo(X0, Y0);
Angle := S * 2 * Pi / 60;
X1 := trunc(X0 + 9*Len * Sin(Angle)/10);
Y1 := trunc(Y0 - 9*Len * Cos(Angle)/10);
Canvas.LineTo(X1, Y1);
:
:
Nótese que cada aguja utiliza una proporción del valor de Len. Así los
segundos son 9/10 de Len, los minutos 3/4 y las horas 1/2.
Redondo
Hasta ahora se ha utilizado el Canvas para dibujar, se ha eliminado el
título y sustituido su funcionalidad, se ha efectuado cierta aritmética
y se ha hecho uso de un timer. El punto central que da título a este
artículo es crear una forma no rectangular. Bien esto es tremendamente
simple y se resuelve con dos llamadas a Windows en el FormCreate" del
formulario principal:
hRegion := CreateEllipticRgnIndirect(R);
SetWindowRgn(handle, hRegion, true);
CreateEllipticRgnIndirect es una versión de CreateEllipticRgn con
los parámetros (un rectángulo) empaquetados en una sola estructura. Esta
rutina retorna un "handle" (manejador) a una región elíptica circunscrita
al rectángulo dado. Si se quiere se pueden definir regiones no
circulares usando CreatePolygonRgn para
polígonos, CreateRectRgn para rectángulos (sin sentido en el
caso que nos atañe, pero es posible), CreateRoundRectRgn para
rectángulos de esquina redondeadas, o CombineRgn para combinar
los resultados de otras regiones.
SetWindowRgn define el área en la cual es visible
una ventana. Es interesante que bajo este concepto la ventana rectangular
sigue existiendo y sólo hemos definido algo así como un agujero por donde
verla.
Un "Bug", un segundo ejemplo
Mi primer intento de forma no rectangular fue hace un par de años en
Delphi 3. Fue una travesura motivado a que conozco personas que detestan
terriblemente a las cucarachas (y aquí en el trópico las hay espectaculares).
Así que creé una cucaracha que se pasea por el escritorio de Windows.
La forma de la misma es elíptica y yo fundamentalmente creé
unos dibujos para distintos ángulos (cuatro: dos verticales y dos
horizontales). El resultado es algo tosco pero disfruté al crear este
"bicho". Sé sobradamente que hay mejores maneras de hacer esto pero
viene al caso por el tema de este artículo.
Lo siguiente
Bien, esto abre posibilidades de uso interesantes (como ya antes
mencioné). Creo que la que más adecuada parece ser la de "Skins" como
los de algunas aplicaciones. Para referencia están "Winamp",
"NeoPlanet", "Windows Media Player" (versión nueva). La técnica que
permite formas no rectangulares es aplicable a botones y otros
controles.
Bibliografía
Delphi Developer's Handbook, Marco Cantù, Tim Gooch, John F. Lam
SYBEX ISBN: 0-7821-1987-5 1.998
La Biblia de Delphi 5 Marco Cantù
ANAYA ISBN: 84-415-0994-8 2.000
Los fuentes de este artículo están a su disposición para descargar.
|