GUI
All info i learned about building graphical user interfaces in Godot.
Nodes
Here is a list of common Control nodes with their name next to them:

Most used nodes
- TextureRect: a simple image, uses a Texture2d as the UI image.
Unhandled input
Once an InputEvent didn't interact with the GUI, it is forwarded to the _unhandled_input method:
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("lmb"):
on_lmb_pressed.emit( get_mouse_position_ray_result() )
if event.is_action_released("lmb"):
on_lmb_released.emit( get_mouse_position_ray_result() )
Tipps and Tricks
GUI Tipps and Tricks
Split container
HSplitContainer and VSplitContainer allow a pretty flexible layout.
From Reddit

split_style.gd
extends SplitContainer
var draggers: Array[Control]
var idx_cur: int = 0
var is_dragging: bool = false
var is_mouse_enter: bool = false
func _ready() -> void:
theme_type_variation = &"ScSplitSection"
drag_started.connect(on_drag_started)
drag_ended.connect(on_drag_ended)
deffered_ready.call_deferred()
func deffered_ready() -> void:
var idx: int = 0
for child in get_children(true):
if child.get_class() == "SplitContainerDragger":
draggers.push_back(child)
child.mouse_entered.connect(on_mouse_entered.bind(idx))
child.mouse_exited.connect(on_mouse_exited)
idx += 1
update_view()
func update_view() -> void:
for dragger: Control in draggers:
dragger.self_modulate.a = 0.0
if is_dragging:
draggers[idx_cur].self_modulate.a = 1.0
elif is_mouse_enter:
draggers[idx_cur].self_modulate.a = 0.36
func on_drag_started() -> void:
is_dragging = true
update_view()
func on_drag_ended() -> void:
is_dragging = false
if GlobalMng.is_touch:
is_mouse_enter = false
update_view()
func on_mouse_entered(idx: int) -> void:
if !is_dragging:
idx_cur = idx
is_mouse_enter = true
update_view()
func on_mouse_exited() -> void:
is_mouse_enter = false
update_view()
split_with_parent.gd
extends "res://scene/shot_analysis/sections/split_style.gd"
var is_left: bool = true
var parent_split: SplitContainer = get_parent().get_parent() as SplitContainer
var idx_near: int = 0 if is_left else 1
var idx_far: int = 1 if is_left else 0
var ori_mouse_pos: float
var ori_offset: int
var ori_parent_offsets: PackedInt32Array = [0,0]
func _ready() -> void:
super()
drag_started.connect(on_drag_started2)
await get_tree().process_frame
func on_drag_started2() -> void:
ori_mouse_pos = get_global_mouse_position()[1 if vertical else 0]
ori_offset = split_offsets[0]
ori_parent_offsets[0] = parent_split.split_offsets[0]
ori_parent_offsets[1] = parent_split.split_offsets[1]
func _input(event: InputEvent) -> void:
if !(is_dragging && event is InputEventMouseMotion):
return
get_viewport().set_input_as_handled()
var par_diff: float = parent_split.split_offsets[idx_near] - ori_parent_offsets[idx_near]
var mouse_delta: float = event.global_position[1 if vertical else 0] - ori_mouse_pos
var desired_offset: int = ori_offset + int(mouse_delta+par_diff*0.5)
split_offsets[0] = desired_offset
clamp_split_offset(0)
var overflow_near: int = desired_offset - split_offsets[0]
if (overflow_near<0) == is_left:
overflow_near = 0
var next_near_offset: int = ori_parent_offsets[idx_near] + overflow_near
var overflow_far: int = next_near_offset - ori_parent_offsets[idx_far]
if (overflow_far<0) == is_left:
overflow_far = 0
parent_split.split_offsets[idx_near] = next_near_offset
parent_split.split_offsets[idx_far] = ori_parent_offsets[idx_far] + overflow_far