|
"Post-it". Ventanas ajustables sin borde ni título
Copyright © 2000 Alirio A. Gavidia B.. Para ver más de
mis artículos visite Programación Orientada en Delphi
¿Por qué un formulario sin borde ni título?
Cuatro hechos son fundamentales en el presente ensayo: El primero de ellos
la existencia de programas que imitan los Post-it (aquellas paginitas adheribles
que vienen en libretas e inundan las oficinas con colores vistosos). Segundo,
el uso de cierta función indocumentada en Windows para arrastrar formularios.
Tercero, el problema de cómo crear una aplicación dónde el formulario principal
no es visible. Y cuarto, utilizar la barra de tareas de Windows.
Los programas que imitan a los Post-it que tomo como referencias son
fundamentalmente TurboNote en Windows y el del KDE de Linux.
Ambos programas son fácilmente adquiribles y hacen más que el que aquí muestro.
Sin embargo considero este un buen ejercicio docente.
Se me ocurre otro programa que se presentan sin título: El reloj redondo (analógico) que
viene con Power Toys de Windows. Será propuesta para
un próximo artículo.
Entremos en calor
Evolutivamente lo obvio es imitar la nota post-it de alguna manera.
El primer pensamiento es un formulario con una barra de título sin los
botones usuales (minimizar, maximizar, etc) y completamente con un color
plano. Esta alternativa es posible y requiere una manejo de Windows algo
especializado. En particular he preferido una alternativa más económica
en cuanto a código (o al menos eso creo); ella es eliminar la barra de
título y bordes del formulario (como se haría con un "splash-screen")
y colocar un panel y un memo en un formulario, el primero alineado al tope
y el segundo al área cliente.
Manejando un formulario sin borde ni barra de título
El formulario con sólo panel y memo responderá sin inconveniente a las
acciones de teclado, pero es obvio que no responderá al arrastre y
cambios de tamaño con el ratón (sin borde por dónde agarrarlo). El
arrastre se resuelve con una acción indocumentada, al menos así era
algún tiempo atrás, a través del mensaje SysCommand. La
línea aquí mostrada, ejecutada desde un formulario, coloca a este en
estado de arrastre.
Perform(wm_SysCommand, $f012, 0)
El asunto con los mensajes no se limita a esto. Los modos de resize también
se definen de esta manera. A tal efecto definimos un conjunto de constantes
con los valores adecuados empezando con el $f012.
sc_DragMove = $f012;
sc_Leftsize = $f001;
sc_Rightsize = $f002;
sc_Upsize = $f003;
sc_UpLeftsize = $f004;
sc_UpRightsize = $f005;
sc_Dnsize = $f006;
sc_DnLeftsize = $f007;
sc_DnRightsize = $f008;
| Nota: Si usted se pregunta de donde salieron estos valores,
bueno, que diré. El primero lo conocí por un Libro de Marco Cantú llamado
"Delphi Developers' Handbook", los demás surgen de ensayo y
error. Déjeme comentarle así mismo que $f040 produce un resultado
desastroso. Además $f020 y $f030 minimizan y maximizan respectivamente,
esto último no es necesario para nuestra aplicación. Aunque no estaría
de más. No me consta si algo de esto funciona fuera de Windows 98 |
Adicionalmente en el evento MouseDown del panel y del memo, dónde
enviamos los mensajes, es necesario llamar ReleaseCapture antes de
enviar los mensajes señalados.
No me extenderé mucho con el código. Es más sencillo analizarlo
desde Delphi que desde una página HTLM o DOC. Pero nótese que hay que
distinguir dónde se hizo clic en el panel para saber si se requiere
un resize o sólo arrastrar el formulario. Algo similar sucede con
el memo para el resize de los lados e inferior.
procedure TfrmNota.Panel1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
ReleaseCapture;
if y<2 then
begin
if x<4 then
Perform(wm_SysCommand, sc_UpLeftsize, 0)
else if x>(Panel1.ClientWidth-4) then
Perform(wm_SysCommand, sc_UpRightsize, 0)
else
Perform(wm_SysCommand, sc_UpSize, 0)
end
else
Perform(wm_SysCommand, sc_DragMove, 0)
end;
Los fuentes son partes de este artículo y usted podrá ver
detalladamente el funcionamiento de la nota.
En el ejemplo anexo, además del manejo de eventos de ratón se hace algún
manejo del registro de Windows para almacenar información relativa a la nota
en sí (como su posición, tipo de letra y color). Presumo que si entiende lo
de lo que se ha realizado hasta aquí no requiere explicación del uso del
registro de Windows (en cualquier caso parece estar bien documentado en Delphi
el uso del registro y hay ejemplos e información al respecto en Internet).
Un formulario principal invisible
Cuando hice este programa no había pensado en un problema realmente tonto.
El usuario debe poder crear y destruir notas, pero que no destruir el formulario
inicial porque eso termina la aplicación. Bien, entonces simplemente con no
mostrar el formulario principal debe bastar. Si quiere usted entender este
tonto problema cree una aplicación en la que además del formulario principal
exista un formulario secundario; luego logre que la aplicación arranque sólo
mostrando el formulario secundario. Mis intentos resultaron en fracaso. Ahora
sé que eso se logra con
Application.ShowMainForm := False;
en el programa principal antes de crear los formularios, pero en aquel
entonces no lo sabía y aproveché el problema para darle una solución original...
No es una falla, es una ventaja
Dándole vuelta al asunto la solución fue convertir la pantalla principal en
una pantalla de presentación que dura una cierta cantidad de tiempo y luego
se oculta. En un timer se ejecuta este código:
procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
SaveNotes(Sender);
if not oculto then
begin
oculto := True;
ShowWindow(Handle, SW_HIDE);
ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong( Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE)
or WS_EX_TOOLWINDOW and not WS_EX_APPWINDOW);
ShowWindow(Application.Handle, SW_SHOW);
end
end;
Con ello queda oculta la ventana principal (la línea inmediata a la asignación
de la variable "oculto", que por cierto por defecto es falsa) y la presencia
de la aplicación en la barra de tareas.
El resto del código es relativamente simple y contiene algunas rutinas
para salvar y recuperar el contenido y configuración de las notas (lo
primero a disco y lo segundo al registro). Creo que no amerita una
discusión profunda por ello dejo al lector la interpretación de la rutina.
El proyecto como tal contiene cinco formularios:
note, es la nota con el panel y el memo.
GenConfig, el formulario de configuración de la aplicación, actualmente sólo configura formatos y contador del reloj.
Config, configuración de la nota (color, alineación y fuentes)
Main, es el formulario principal, además de eso funciona como pantalla de presentación.
Acerca, Versión del programa.
Como comentario aparte nótese que debido a la presencia de
un Timer es posible obligar un auto save (autoguardar) para el texto
en las notas. Esto se logra recorriendo todos los formularios del tipo nota y
guardando su contenido desde el memo. La rutina llamada SaveNotes se
ejecuta desde el timer (teniendo cuidado de no salvar lo que no ha cambiado).
Cada nota tiene la variable pública Changed, la cual es modificada al
salvar y en el evento OnChange del memo.
"Pop-up menu"
La aplicación no cuenta con un menú principal porque no tiene un formulario principal.
Toda las acciones distintas a la interacción con el campo memo y movimientos usando
ratón se realizan por menúes tipo Pop-up. Hay dos: uno en la nota y otro en el
ícono de la barra de tarea (junto al reloj). Para éste utilizamos las RX. Así que al
menos para esta versión será necesario que usted tenga las RX para poder
compilar estos fuentes. Si no las conoce,
entonces debe hacerlo, son gratuitas ("freeware") y muy buenas.
|