marzo 07, 2006

Alternativas a los skins

Existen muchos y muy variadas herramientas en el mercado que permiten hacer cosas bastante llamativas con las interfaces de los programas, VCLSkin, SuiSkin, entre otros con sus ventajas y desventajas entre ellos

Algunos usan sus propios componentes, es decir su propio TEditSkineado, TPanelEskineado, etc, el problema con eso es que obviamente cuando los usas te obligas a reescribir tu aplicacion o al menos pasarle un experto que cambie los nombres de las clases de los controles que ahora serán del nuevo tipo; y tambien con eso ya te estas jodiendo comprometiendo de alguna manera con ese proveedor de la herramienta, pues algunas características que ofrezcan estos componentes no se encontrarán en las clases genéricas.

Otros como VCLSkin son mas flexibles, con solo poner un simple componente en tu formulario principal te cambia la apariencia de la aplicación por completo, abarcando incluso cuadros de dialogo y demás chunches propios de windows

Aqui vamos a discutir una alternativa al uso de estas herramientas que son de pago (aunque algunas realmente valen lo que cuestan si la apariencia es tan importante para ti), y lo haremos usando una técnica ancestral que nos provee Delphi y a la que se le llama "Clases Sobrepuestas". Es increible cuanto mejora la apariencia de una ventana tan solo usando colores agradables y redondeando las esquinas de los componentes contenedores, aqui va el ejemplo vamos a estudiar como hacer que esto en diseño:

se vea en ejecución como esto:

Sin usar componentes de terceros y sin tener que cambiar los componentes que ahora estas usando en tu aplicación común y corriente de Delphi.

La idea es muy simple, vamos a hacer una unidad que será la que pongas al final de la sección uses de tu forma en la parte de la interfase; ahora, esto es importante, la unidad que contenga las clases sobrepuestas debe ser la última en la lista del uses, sino podria no funcionar el truco, por lo menos no para las clases que esten contenidas en las unidades que se encuentren despues de la nuestra.

Primero creamos una nueva unidad llamada GTPaneles.pas, y definamos algunos parámetros configurables al gusto de cada quien...

const
CnstArco = 35;
CnstBotonNormal = clMoneyGreen;
CnstBotonPress = clGray;
CnstTagDisable = 99;


la constante CnstArco definirá que tan pronunciados queremos que sean los arcos de las esquinas redondeadas, las siguientes dos constantes definen los colores que tendrán los botones en los estados normal y cuando estan presionados, y por último definimos un valor para diferenciar a aquellos componentes a los que no quisieramos que se les aplique el proceso de "embellecimiento", esta técnica la usa VCLSkin, donde si a algun panel no quisieras que se le aplique el Skin solo basta con poner el valor 99 en la propiedad Tag de dicho componente.

Yo suelo utilizar la propiedad Tag de los componentes para algunos trucos al nivel de la interface de las aplicaciones, si ese es tu caso tambien tendrías que hacer algunos malabares para que esto no te ocasione problemas


Para ejemplificar el trabajo lo haremos con la clase TPanel, y mostraremos la parte mas simple del asunto, aqui hacemos la sobreposición de la clase:


TPanel = class(ExtCtrls.TPanel)
protected
procedure Paint; override;
end;


Definamos un par de procedimientos genéricos que usaremos despues en la implementacion de los métodos de las clases sobrepuestas:

Este procedimiento borra el fondo donde se ha de dibujar el control y prepara el terreno para dibujar nuestra obra de arte.

procedure QuitarFondo(Control: TControl; Dest: TCanvas);
var
SaveIndex: Integer;
DC: HDC;
Position: TPoint;
begin
with Control do
begin
if Parent = nil then
Exit;
DC := Dest.Handle;
SaveIndex := SaveDC(DC);
{$IFDEF PDJ_2}
GetViewportOrgEx(DC, @Position);
{$ELSE}
GetViewportOrgEx(DC, Position);
{$ENDIF}
SetViewportOrgEx(DC, Position.X - Left,
Position.Y - Top, nil);
IntersectClipRect(DC, 0, 0, Parent.ClientWidth,
Parent.ClientHeight);
Parent.Perform(WM_ERASEBKGND, Integer(DC), Integer(0));
Parent.Perform(WM_PAINT, Integer(DC), Integer(0));
RestoreDC(DC, SaveIndex);
end;
end;


Este otro dibuja ya formalmente los rectangulos redondeados de modo que de la perspectiva 3D.

procedure DibujaRedondeado(const C: TCanvas; R1:
TRect; Color: TColor);
var R2: TRect;
begin
//Guardamos el Rectangulo original
R2 := R1;
with C do
begin
Pen.Width := 1;
//Dibujamos la sombra
inc(R1.left, Pen.Width);
inc(R1.top, Pen.Width);
pen.Color := clbtnhighlight;
Brush.Style := bsSolid;
Brush.Color := Color;
RoundRect(R1.Left, r1.top, r1.Right, r1.Bottom,
CnstArco, CnstArco);

pen.Color := color;
Brush.Style := bsclear;
inc(R1.left, Pen.Width);
inc(R1.top, Pen.Width);
RoundRect(R1.Left, r1.top, r1.Right, r1.Bottom,
CnstArco, CnstArco);

//Dibujamos la base...
pen.Color := clBlack;
Brush.Style := bsclear;
r1 := R2; //Recuperamos el rectangulo original...
RoundRect(R1.Left, r1.top, r1.Right, r1.Bottom,
CnstArco, CnstArco);
Brush.Style := bsSolid;
end;
end;


Despues de eso solo basta con codificar la nueva versión del método Paint del componente mandando a llamar a los dos métodos que ya vimos:

procedure TPanel.Paint;
begin
if (Tag <> CnstTagDisable) then
begin
QuitarFondo(Self, Canvas);
DibujaRedondeado(Canvas, GetClientRect, Color);
end else inherited;
end;


Para el caso de los botones y los TGroupBoxes el procedimiento es similar con la variante de desplegar el texto o el caption del componente en cuestión, e incluso se puede hacer una variante que dibuje tambien los Glyphs de los botones, pero yo por lo menos mi versión la he dejado hasta ahi; si alguien continua con este desarrollo hagamelo saber. También sobre BDS 2006 se pueden hacer uso de los nuevos eventos OnMouseEnter y OnMouseLeave para dar más efectos a los componentes.

En la sección de archivos de este blog esta disponible para descargar un demo completo de lo aqui explicado, seguro que con un poco mas de dedicación se puede ampliar la unidad para que soporte mas clases y con mejor apariencia.

4 comentarios:

  1. Ya tengo un nuevo código sobre esta técnica, queda muy parecido a los vclSkin, pero solo de los 3 componentes modificables.
    Lo tengo desde ayer, pero lamentablemente la página del clubdelphi.com no me carga.

    ResponderEliminar
  2. Enviamelo si quieres a mi correo personal y lo publicamos en la sección de archivos del blog como una actualización, en cgarciagl en gmail.com

    ResponderEliminar
  3. Disculpa que no te halla respondido antes, pero el proyecto del rinconcito me ha tenido muy limitado de tiempo, aquí está el zip http://chrids.rinconcitodelphi.com/Download/CSkin.zip
    Revisalo y dame tu opinión, chrids@rinconcitodelphi.com

    ResponderEliminar
  4. ah se me olvidaba, el botón está dando problemas, ya que está sobre el panel y entonces toca dibujar doble, lo que hace que dure un poco, o que parpade, no me acuerdo bien, pero el botón que no está en el panel, funciona a las mil maravillas, creo que el problema se deriva de la técnica, hay que rediseñar un poco para que no tenga que pintar tanto, bueno, esa es mi humilde opinión.

    ResponderEliminar