Controles GUI personalizados

Tantos controles...

Sin embargo, nunca hay suficientes. Crear tus propios controles personalizados que actúen como tú quieres es una obsesión de casi todos los programadores de GUI. Godot proporciona muchos de ellos, pero puede que no funcionen exactamente de la manera que quieres. Antes de contactar a los desarrolladores con una solicitud de soporte para barras de desplazamiento diagonales, al menos será bueno saber cómo crear estos controles fácilmente desde el script.

Dibujando

Para dibujar, se recomienda consultar el tutorial Dibujos personalizados en 2D. Lo mismo se aplica. Algunas funciones son dignas de mención por su utilidad a la hora de dibujar, por lo que se detallarán a continuación:

Comprobando el tamaño del control

A diferencia de los nodos 2D, el "tamaño" es importante con los controles, ya que ayuda a organizarlos en diseños adecuados. Para ello, se proporciona la propiedad Control.rect_size. Revisarla durante el draw() es vital para asegurar que todo se mantenga dentro de los límites.

Comprobando el enfoque

Algunos controles (como los botones o los editores de texto) pueden proporcionar un enfoque de entrada para el teclado o el joypad de entrada. Ejemplos de ello son la introducción de texto o la pulsación de un botón. Esto se controla con la propiedad Control.focus_mode. Cuando se dibuja, y si el control soporta el enfoque de entrada, siempre se desea mostrar algún tipo de indicador (resaltado, cuadro, etc.) para indicar que este es el control actualmente enfocado. Para comprobar este estado, existe el método Control.has_focus(). Ejemplo

func _draw():
    if has_focus():
         draw_selected()
    else:
         draw_normal()
public override void _Draw()
{
    if (HasFocus())
    {
        DrawSelected()
    }
    else
    {
        DrawNormal();
    }
}

Escalando

Como se mencionó anteriormente, el tamaño es importante para los Control. Esto les permite colocarse correctamente, cuando se colocan en rejillas, contenedores o cuando están anclados. La mayoría de las veces, proporcionan un tamaño mínimo para ayudar a colocarlos correctamente. Por ejemplo, se colocan verticalmente uno encima del otro usando un VBoxContainer, el tamaño mínimo se asegurará de que su control personalizado no sea aplastado por los otros controles en el contenedor.

Para proporcionar esta llamada, simplemente anula Control.get_minimum_size(), por ejemplo:

func get_minimum_size():
    return Vector2(30, 30)
public override Vector2 _GetMinimumSize()
{
    return new Vector2(20, 20);
}

Alternativamente, configúralo usando una función:

func _ready():
    set_custom_minimum_size(Vector2(30, 30))
public override void _Ready()
{
    SetCustomMinimumSize(new Vector2(20, 20));
}

Entrada

Los controles proporcionan unos cuantos ayudantes para que la gestión de los eventos de entrada sea mucho más fácil que la de los nodos regulares.

Eventos de entrada

Hay algunos tutoriales sobre la entrada de datos antes de este, pero vale la pena mencionar que los controles tienen un método especial de entrada de datos que sólo funciona cuando:

  • El puntero del mouse está sobre el control.

  • El botón fue presionado sobre este control (el control siempre captura la entrada hasta que se suelta el botón)

  • Control proporciona el foco del teclado/joypad a través de Control.focus_mode.

Esta función es is Control._gui_input(). Simplemente sobrescríbelo en tu Control. No es necesario establecer ningún procesamiento.

extends Control

func _gui_input(event):
   if event is InputEventMouseButton and event.button_index == BUTTON_LEFT and event.pressed:
       print("Left mouse button was pressed!")
public override void _GuiInput(InputEvent @event)
{
    if (@event is InputEventMouseButton mbe && mbe.ButtonIndex == (int)ButtonList.Left && mbe.Pressed)
    {
        GD.Print("Left mouse button was pressed!");
    }
}

Para obtener más información sobre los eventos propios, consulte el tutorial Using InputEvent.

Notificaciones

Los nodos Control también tienen muchas notificaciones útiles para los que no existe ninguna llamada de retorno, pero que se pueden verificar con la llamada de retorno _notification:

func _notification(what):
    match what:
        NOTIFICATION_MOUSE_ENTER:
            pass # Mouse entered the area of this control.
        NOTIFICATION_MOUSE_EXIT:
            pass # Mouse exited the area of this control.
        NOTIFICATION_FOCUS_ENTER:
            pass # Control gained focus.
        NOTIFICATION_FOCUS_EXIT:
            pass # Control lost focus.
        NOTIFICATION_THEME_CHANGED:
            pass # Theme used to draw the control changed;
            # update and redraw is recommended if using a theme.
        NOTIFICATION_VISIBILITY_CHANGED:
            pass # Control became visible/invisible;
            # check new status with is_visible().
        NOTIFICATION_RESIZED:
            pass # Control changed size; check new size
            # with get_size().
        NOTIFICATION_MODAL_CLOSE:
            pass # For modal pop-ups, notification
            # that the pop-up was closed.
public override void _Notification(int what)
{
    switch (what)
    {
        case NotificationMouseEnter:
            // Mouse entered the area of this control.
            break;

        case NotificationMouseExit:
            // Mouse exited the area of this control.
            break;

        case NotificationFocusEnter:
            // Control gained focus.
            break;

        case NotificationFocusExit:
            // Control lost focus.
            break;

        case NotificationThemeChanged:
            // Theme used to draw the control changed;
            // update and redraw is recommended if using a theme.
            break;

        case NotificationVisibilityChanged:
            // Control became visible/invisible;
            // check new status with is_visible().
            break;

        case NotificationResized:
            // Control changed size; check new size with get_size().
            break;

        case NotificationModalClose:
            // For modal pop-ups, notification that the pop-up was closed.
            break;
    }
}