Tutorial: Crear un Control personalizado de Botón en Xojo (Segunda Parte)

A continuación encontrarás traducido al Castellano el artículo escrito por Gabriel Ludosanu y publicado originalmente en el Blog de Xojo.

En la primera parte de este tutorial creamos un control básico personalizado utilizando para ello la clase DesktopCanvas, proporcionándole un aspecto y funcionalidad personalizados para la gestión de los eventos de ratón con una retroalimentación visual también personalizada, así como lanzar el evento Pressed cuando se pulsa.

En esta segunda parte mejoraremos nuestro CanvasButton añadiendo una mayor personalización con nuevas propiedades para:

  • Definir los colores del botón para diferentes estados (por defecto, cuando el apuntador está sobre él, y pulsado).
  • Ajustar el radio de las esquinas del botón.
  • Implementar el estado de desactivado.

¡Comencemos!

Añadiendo nuevas propiedades de personalización

Precisamos añadir varias propiedades a nuestra clase CanvasButton con el objeto de referenciar colores personalizados para diferentes estados, el color del border, el color del texto, el radio de curvatura de las esquinas del botón y el estado de activado.

  • En el Navegador de Proyecto, selecciona la clase CanvasButton.
  • Dirígete a Insert > Property (o accede al menú contextual sobre la clase y selecciona la opción Add to “CanvasButton” > Property).
  • Añade las siguientes propiedades con los nombres, tipos y valores por defecto indicados:
  • Nombre: BackgroundColor; Tipo: Color; Valor por defecto: &c5e2d8b
  • Nombre: HoverColor; Tipo: Color; Valor por defecto: &c729fcf
  • Nombre: HighlightColor; Tipo: Color; Valor por defecto: &c628eff
  • Nombre: BorderColor; Tipo: Color; Valor por defecto: &c525252
  • Nombre: TextColor; Tipo: Color; Valor por defecto: &ceeeeee
  • Nombre: CornerRadius; Tipo: Integer; Valor por defecto: 4

Estas propiedades contendrán los valores que determinan el aspecto y comportamiento del botón.

Actualizando los Manejadores de Evento para el estado Enabled

Hemos de modificar los manejadores de evento existentes (MouseDown, MouseEnter, MouseExit, MouseUp) para comprobar si el botón está activo antes de procesar cualquier acción del ratón.

Selecciona cada uno de los siguientes manejadores de evento en la clase CanvasButton y actualiza el código tal y como se muestra:

MouseDown(x As Integer, y As Integer) As Boolean

#Pragma unused x
#Pragma unused y
 
// Sólo se procesa si el botón está activado.
If Enabled Then
  // Define el estado interno para indicar que se está pulsando el botón.
  IsPressed = True
  // Actualiza el control para que se muestre visualmente el estado de pulsado.
  Refresh(False)
  // Devuelve True para indicar que se ha manejado el evento.
  Return True
Else
  // Si está desactivado, no manejamos el evento.
  Return False
End If

MouseEnter()

// Sólo se procesa si el botón está activado.
If Enabled Then
  // Define el estado interno para indicar que el apuntador está sobre el botón.
  IsHovered = True
  // Actualiza el control para mostrar visualmente el estado.
  Refresh(False)
End If

MouseExit()

// Sólo se procesa si el botón está activado.
If Enabled Then
  // Define el estado interno para indicar que el apuntador ya no está sobre el control.
  IsHovered = False
  // Actualiza el estado de pulsado si el apuntador deja de estar sobre el control mientras que se ha pulsado (previene clics accidentales).
  IsPressed = False
  // Actualiza el control para revertir su estado.
  Refresh(False)
End If

MouseUp(x As Integer, y As Integer) As Boolean

#Pragma unused x
#Pragma unused y
 
// Sólo se procesa si el botón está activado.
If Enabled Then
  // Comprueba si se ha pulsado el botón y el apuntador aun se encuentra sobre él.
  If IsPressed And IsHovered Then
    // Si es así, se ha hecho clic sobre el botón y se lanza el evento personalizado Pressed.
    RaiseEvent Pressed
  End If
  // Resetea el estado de pulsado independientemente de que el clic se haya realizado con éxito.
  IsPressed = False
  // Actualiza el control para revertir su estado de pulsado.
  Refresh(False)
End If

En estos manejadores de evento actualizados hemos añadido un “If Enabled Then” al inicio. Si el botón no se encuentra activado, el código dentro del bloque no se ejecuta evitando que el botón reaccione a las interacciones con el apuntador. También hemos añadido #Pragma con los parámetros no utilizados x e y en los eventos MouseDown y MouseUp, dado que no se utilizan en el código, lo cual ayuda a evitar los Warning del compilador. En MouseExit hemos añadido IsPressed = False para asegurarnos de resetear el estado de pulsado en el caso de que el apuntador abandone el control mientras que se mantiene pulsado el botón del ratón.

Actualizando el Evento Paint para mejorar el aspecto

los cambios más significativos estarán en el evento Paint, donde utilizaremos las nuevas propiedades para dibujar el botón basándonos en su estado actual (activado/desactivado, si el apuntador está o no sobre el botón, y también si está o no pulsado).

Selecciona el manejador de evento Paint(g As Graphics, areas() As Rect) y sustituye su contenido con el siguiente código:

#Pragma unused areas
 
// Utiliza la propiedad del radio de redondeo para las esquinas.
Var currentCornerRadius As Integer = CornerRadius
 
// Declara las variables para los colores utilizados en el dibujado.
Var currentBgColor As Color
Var currentBorderColor As Color
Var currentTextColor As Color
 
// Determina los colores basándose en el estado actual.
If Enabled Then
  If IsPressed Then
    // Usa el color de destacado si está pulsado.
    currentBgColor = HighlightColor
    currentBorderColor = BorderColor
    currentTextColor = TextColor
  ElseIf IsHovered Then // Comprueba si está sobre el cursor sólo si no está pulsado
    // Usa el color hover si el apuntador está sobre el control.
    currentBgColor = HoverColor
    currentBorderColor = BorderColor
    currentTextColor = TextColor
  Else
    // Usa el color de fondo para el estado por defecto.
    currentBgColor = BackgroundColor
    currentBorderColor = BorderColor
    currentTextColor = TextColor
  End If
Else
  // Usa los colores correspondientes del sistema o los grises estándar para el estado de desactivado.
  currentBgColor = Color.LightGray
  currentBorderColor = Color.Gray
  currentTextColor = Color.DisabledTextColor // usa el color de texto desactivado del sistema.
End If
 
// Define el color de dibujado y dibuja la forma de fondo con las esquinas redondeadas.
g.DrawingColor = currentBgColor
g.FillRoundRectangle(0, 0, g.Width, g.Height, currentCornerRadius, currentCornerRadius)
 
// Define el color de dibujado y el tamaño de lápiz para el borde.
g.DrawingColor = currentBorderColor
g.PenSize = 2
// Dibuja la forma del borde justo dentro del rectángulo de fondo.
g.DrawRoundRectangle(1, 1, g.Width-2, g.Height-2, currentCornerRadius, currentCornerRadius)
 
// Activa el anti-aliasing para un dibujado suavizado del texto.
g.AntiAliasMode = Graphics.AntiAliasModes.HighQuality
g.AntiAliased = True
// Calcula el ancho y altura para el texto del botón.
Var tw As Double = g.TextWidth(ButtonText)
Var th As Double = g.TextHeight
// Calcula la posición X para centrar el texto horizontalmente.
Var tx As Double = (g.Width - tw) / 2
// Calcula la posición Y para centrar verticalmente el texto, con un pequeño ajuste..
Var ty As Double = (g.Height + th) / 2 - 3
// Define el color de dibujado para el texto.
g.DrawingColor = currentTextColor
// Dibuja el texto del botón en la posición que se ha calculado..
g.DrawText(ButtonText, tx, ty)

Revisemos los cambios en el evento Paint:

  • Ahora utilizamos la propiedad CornerRadius en vez de usar una constante estática para el dibujado de los bordes redondeados en los rectángulos.
  • Hemos añadido un bloque If Enabled Then… Else.
  • Dentro del bloque If Enabled Then, hemos añadido una estructura anidada If IsPressed Then… ElseIf IsHovered Then… Else. Esto determina el color currentBgColor:
  • Si IsPressed es cierto, entonces currentBgColor se define a HighlightColor.
  • Si IsPressed es falso, pero IsHovered es cierto, entonces currentBgColor se define a HoverColor.
  • Si tanto IsPressed como IsHovered son falsos, entonces currentBgColor se define a BackgroundColor.
  • Los valores de currentBorderColor y currentTextColor se ajustan directamente desde las propiedades BorderColor y TextColor cuando el botón está activado.
  • Dentro del bloque Else (cuando Enabled es falso), definimos los colores a los valores de gris estándar (Color.LightGray para el fondo, Color.Gray para el borde, y Color.DiseabledTextColor para el texto) para dar así el aspecto de desactivado al botón.
  • Por último, los comandos de dibujado utilizan estas variables currentBgColor, currentBorderColor, currentTextColor y currentCornerRadius.

Usando el Control personalizado mejorado

Ahora que nuestro CanvasButton tiene más opciones de personalización y soporta un estado de desactivado, veamos cómo utilizar estas características.

Si aún no tienes una instancia, arrastra la clase CanvasButton desde el Navegador de Proyecto sobre la ventana en el Editor de Diseño.

Ahora puedes acceder y definir las nuevas propiedades desde código. Por ejemplo, en el evento Opening del botón, puedes añadir las siguientes líneas:

// Personaliza los colores
Me.BackgroundColor = Color.RGB(200, 50, 50) // Un tono de rojo
Me.HoverColor = Color.RGB(255, 100, 100) // Un color rojo más claro para cuando el apuntador está sobre el control
Me.HighlightColor = Color.RGB(150, 0, 0) // Un tono de rojo más oscuro para cuando se pulsa sobre el botón
Me.BorderColor = Color.Black
Me.TextColor = Color.White
// Ajusta el radio de redondeo para el borde del contorno
Me.CornerRadius = 10

Puedes experimentar con diferentes valores y ver cómo estos afectan al aspecto del botón. Para probar el estado de desactivado, puedes ajustar la propiedad Enabled a False.

Conclusión

Hemos llevado nuestro botón personalizado al siguiente nivel añadiendo más opciones de personalización para el color, así como un redondeo de contorno personalizado y un estado de desactivado.

Puedes descargar este proyecto desde GitHub desde este enlace.

¡Espero que hayas disfrutado creando tu propio control! Si bien ahora es mucho más flexible, probablemente también podríamos añadir otras capacidades, así que… ¡permanece atento!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *