Files
godot-docs/tutorials/gui/custom_gui_controls.rst

243 lines
7.1 KiB
ReStructuredText

.. _doc_custom_gui_controls:
Custom GUI controls
===================
So many controls...
-------------------
Yet there are never enough. Creating your own custom controls that act
just the way you want them is an obsession of almost every GUI
programmer. Godot provides plenty of them, but they may not work exactly
the way you want. Before contacting the developers with a pull-request
to support diagonal scrollbars, at least it will be good to know how to
create these controls easily from script.
Drawing
-------
For drawing, it is recommended to check the :ref:`doc_custom_drawing_in_2d` tutorial.
The same applies. Some functions are worth mentioning due to their
usefulness when drawing, so they will be detailed next:
Checking control size
~~~~~~~~~~~~~~~~~~~~~
Unlike 2D nodes, "size" is important with controls, as it helps to
organize them in proper layouts. For this, the
:ref:`Control.rect_size <class_Control_rect_size>`
member variable is provided. Checking it during _draw() is vital to ensure
everything is kept in-bounds.
Checking focus
~~~~~~~~~~~~~~
Some controls (such as buttons or text editors) might provide input
focus for keyboard or joypad input. Examples of this are entering text
or pressing a button. This is controlled with the
:ref:`Control.focus_mode <class_Control_focus_mode>`
member variable. When drawing, and if the control supports input focus, it is
always desired to show some sort of indicator (highlight, box, etc) to
indicate that this is the currently focused control. To check for this
status, the :ref:`Control.has_focus() <class_Control_has_focus>` method
exists. Example
.. tabs::
.. code-tab:: gdscript GDScript
func _draw():
if has_focus():
draw_selected()
else:
draw_normal()
.. code-tab:: csharp
public override void _Draw()
{
if(HasFocus())
{
DrawSelected()
}
else
{
DrawNormal();
}
}
Sizing
------
As mentioned before, size is important to controls. This allows
them to lay out properly, when set into grids, containers, or anchored.
Controls most of the time provide a *minimum size* to help to properly
lay them out. For example, if controls are placed vertically on top of
each other using a :ref:`VBoxContainer <class_VBoxContainer>`,
the minimum size will make sure your custom control is not squished by
the other controls in the container.
To provide this callback, just override
:ref:`Control.get_minimum_size() <class_Control_get_minimum_size>`,
for example:
.. tabs::
.. code-tab:: gdscript GDScript
func get_minimum_size():
return Vector2(30, 30)
.. code-tab:: csharp
public override Vector2 _GetMinimumSize()
{
return new Vector2(20, 20);
}
Or alternatively, set it via function:
.. tabs::
.. code-tab:: gdscript GDScript
func _ready():
set_custom_minimum_size(Vector2(30, 30))
.. code-tab:: csharp
public override void _Ready()
{
SetCustomMinimumSize(new Vector2(20, 20));
}
Input
-----
Controls provide a few helpers to make managing input events much easier
than regular nodes.
Input events
~~~~~~~~~~~~
There are a few tutorials about input before this one, but it's worth
mentioning that controls have a special input method that only works
when:
- The mouse pointer is over the control.
- The button was pressed over this control (control always
captures input until button is released)
- Control provides keyboard/joypad focus via
:ref:`Control.focus_mode <class_Control_focus_mode>`.
This function is
:ref:`Control._gui_input() <class_Control__gui_input>`.
Simply override it in your control. No processing needs to be set.
.. tabs::
.. code-tab:: gdscript GDScript
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!")
.. code-tab:: csharp
public override void _GuiInput(InputEvent @event)
{
var mouseButtonEvent = @event as InputEventMouseButton;
if (mouseButtonEvent != null)
{
if (mouseButtonEvent.ButtonIndex == (int)ButtonList.Left && mouseButtonEvent.Pressed)
{
GD.Print("Left mouse button was pressed!");
}
}
}
// or alternatively when using C# 7 or greater we can use pattern matching
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!");
}
}
}
For more information about events themselves, check the :ref:`doc_inputevent`
tutorial.
Notifications
~~~~~~~~~~~~~
Controls also have many useful notifications for which no callback
exists, but can be checked with the _notification callback:
.. tabs::
.. code-tab:: gdscript GDScript
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_CLOSED):
pass # for modal popups, notification
# that the popup was closed
.. code-tab:: csharp
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 popups, notification that the popup was closed
break;
}
}