Topic was automatically imported from the old Question2Answer platform.
Asked By
fish2091852127
How to make a folding menu like this:
(they looks like a drawer, usually its closes.
When one is opened, expand the window it contains and squeeze down the other drawers)
Here is an example of a custom node that works like an accordion.
The Control moves each child to the end of the previous one (only in the y direction)
extends Control
export var spacing = 10
func _draw():
var last_end_achor = Vector2.ZERO
for child in get_children():
child.rect_position = last_end_achor
last_end_achor.y = child.rect_position.y + child.rect_size.y
last_end_achor.y += spacing
rect_min_size.y = last_end_achor.y #to work with ScrollContainer
And clicking on the button “show” toggles the parent “Panel” size to the open or the closed value.
extends Panel
var is_expanded = false
func _ready():
$VBoxContainer/show.connect("pressed",self,"expand")
func expand():
is_expanded = !is_expanded
var last_rect_size = Vector2.ZERO
func _process(delta):
#snap to end
if abs(rect_size.y-rect_min_size.y) < 1:
rect_size.y = rect_min_size.y
#resize to target size
if is_expanded:
rect_size.y = lerp(rect_size.y, 70, 0.1)
else:
rect_size.y = lerp(rect_size.y, rect_min_size.y, 0.1)
#update layout
if last_rect_size != rect_size:
get_parent().update()
last_rect_size = rect_size
Wow!!!Thanks!!
fish2091852127 | 2019-03-18 08:05
I followed your instruction to make this menu. But the items in panel didn’t close. Can you help?
hoody monkey | 2019-12-06 02:29
What I have done to make this work is to make the show button a child of the root and then set “Clip Content” in the panel rect properties to true.
Now the content of the panel will shrink with the panel.
If I find the time I will post an github project with an working reusable example.
R. K. | 2020-02-11 10:22
For me that solution doesn’t work well with spinbox. It makes an overflow due to callback update. I have done a simpler script on a VBoxContainer and a button “show” as his child and it seems to work.
extends VBoxContainer
export var is_expanded = true
func _ready():
$show.connect("pressed",self,"expand")
get_parent().connect("expand",self,"resized")
func expand():
is_expanded = !is_expanded
for child in get_children():
child.visible = true if is_expanded else child == $show
These answers not work in godot 4. Just add a solution of mine.
This script is linked to the ExpandableVbox, and the vbox is saved to a branch scene. So that it can be used in other scene(just drag nodes as children)
You can call theexpand method of it by other signals like button, or just enable the _gui_input function(and replace 0 with min size)
extends VBoxContainer
@export var is_expanded = true
enum STATE {OPEN,CLOSE,OPENING,CLOSING}
var init = false
var state :STATE
var max_size : Vector2i
var last_size : Vector2i
@onready var v_box = $"."
func _ready():
state = STATE.OPEN
#func _gui_input(event):
#if event is InputEventMouseButton:
#if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
#self.expand()
func expand():
is_expanded = !is_expanded
if is_expanded:
state = STATE.OPENING
else:
state = STATE.CLOSING
func _process(delta):
if not init: # not in ready beacuse ready do not get corret size
max_size = v_box.size
last_size = v_box.size
v_box.custom_minimum_size.y = max_size.y
init = true
if state == STATE.CLOSING:
v_box.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
#print(last_size)
if v_box.custom_minimum_size.y > 0:
v_box.custom_minimum_size.y = lerp(last_size.y,0,0.1)
last_size = v_box.custom_minimum_size
elif v_box.custom_minimum_size.y == 0:
v_box.size_flags_vertical = Control.SIZE_FILL
for child in v_box.get_children():
child.visible = true if is_expanded else child == $show
state = STATE.CLOSE
elif state == STATE.OPENING:
v_box.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
for child in v_box.get_children():
child.visible = true if is_expanded else child == $show
if v_box.custom_minimum_size.y < max_size.y:
v_box.custom_minimum_size.y = lerp(last_size.y,max_size.y,0.1)
last_size = v_box.custom_minimum_size
elif v_box.custom_minimum_size.y == max_size.y:
v_box.size_flags_vertical = Control.SIZE_FILL
state = STATE.OPEN