Work in progress
The content of this page was not yet updated for Godot4.4
and may beoutdated. If you know how to improve this page or you can confirm that it's up to date, feel free toopen a pull request.
Custom GUI controls
So many controls...
Yet there are never enough. Creating your own custom controls that actjust the way you want them to is an obsession of almost every GUIprogrammer. Godot provides plenty of them, but they may not work exactlythe way you want. Before contacting the developers with a pull-requestto support diagonal scrollbars, at least it will be good to know how tocreate these controls easily from script.
Drawing
For drawing, it is recommended to check theCustom drawing in 2D tutorial.The same applies. Some functions are worth mentioning due to theirusefulness when drawing, so they will be detailed next:
Checking control size
Unlike 2D nodes, "size" is important with controls, as it helps toorganize them in proper layouts. For this, theControl.sizeproperty is provided. Checking it during_draw()
is vital to ensureeverything is kept in-bounds.
Checking focus
Some controls (such as buttons or text editors) might provide inputfocus for keyboard or joypad input. Examples of this are entering textor pressing a button. This is controlled with theControl.focus_modeproperty. When drawing, and if the control supports input focus, it isalways desired to show some sort of indicator (highlight, box, etc.) toindicate that this is the currently focused control. To check for thisstatus, theControl.has_focus() methodexists. Example
func_draw():ifhas_focus():draw_selected()else:draw_normal()
publicoverridevoid_Draw(){if(HasFocus()){DrawSelected()}else{DrawNormal();}}
Sizing
As mentioned before, size is important to controls. This allowsthem to lay out properly, when set into grids, containers, or anchored.Controls, most of the time, provide aminimum size to help properlylay them out. For example, if controls are placed vertically on top ofeach other using aVBoxContainer,the minimum size will make sure your custom control is not squished bythe other controls in the container.
To provide this callback, just overrideControl._get_minimum_size(),for example:
func_get_minimum_size():returnVector2(30,30)
publicoverrideVector2_GetMinimumSize(){returnnewVector2(20,20);}
Alternatively, set it using a function:
func_ready():set_custom_minimum_size(Vector2(30,30))
publicoverridevoid_Ready(){CustomMinimumSize=newVector2(20,20);}
Input
Controls provide a few helpers to make managing input events much easierthan regular nodes.
Input events
There are a few tutorials about input before this one, but it's worthmentioning that controls have a special input method that only workswhen:
The mouse pointer is over the control.
The button was pressed over this control (control alwayscaptures input until button is released)
Control provides keyboard/joypad focus viaControl.focus_mode.
This function isControl._gui_input().To use it, override it in your control. No processing needs to be set.
extendsControlfunc_gui_input(event):ifeventisInputEventMouseButtonandevent.button_index==MOUSE_BUTTON_LEFTandevent.pressed:print("Left mouse button was pressed!")
publicoverridevoid_GuiInput(InputEvent@event){if(@eventisInputEventMouseButtonmbe&&mbe.ButtonIndex==MouseButton.Left&&mbe.Pressed){GD.Print("Left mouse button was pressed!");}}
For more information about events themselves, check theUsing InputEventtutorial.
Notifications
Controls also have many useful notifications for which no dedicated callbackexists, but which can be checked with the _notification callback:
func_notification(what):matchwhat: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.
publicoverridevoid_Notification(intwhat){switch(what){caseNotificationMouseEnter:// Mouse entered the area of this control.break;caseNotificationMouseExit:// Mouse exited the area of this control.break;caseNotificationFocusEnter:// Control gained focus.break;caseNotificationFocusExit:// Control lost focus.break;caseNotificationThemeChanged:// Theme used to draw the control changed;// update and redraw is recommended if using a theme.break;caseNotificationVisibilityChanged:// Control became visible/invisible;// check new status with is_visible().break;caseNotificationResized:// Control changed size; check new size with get_size().break;caseNotificationModalClose:// For modal pop-ups, notification that the pop-up was closed.break;}}