77 lines
1.7 KiB
GDScript
77 lines
1.7 KiB
GDScript
class_name ClosableMenu extends Control
|
|
|
|
signal opened
|
|
signal closed
|
|
signal focusGained
|
|
signal focusLost
|
|
|
|
@export var canClose:bool = true
|
|
@export var isOpen:bool = false:
|
|
set(v):
|
|
isOpen = v
|
|
visible = v
|
|
get():
|
|
return isOpen
|
|
|
|
var _savedFocusNode:Control = null
|
|
|
|
func _enter_tree() -> void:
|
|
visible = isOpen
|
|
|
|
func _exit_tree() -> void:
|
|
visible = false
|
|
|
|
func _ready() -> void:
|
|
visible = isOpen
|
|
if canClose:
|
|
set_process_unhandled_input(false)
|
|
|
|
func open() -> void:
|
|
visible = true
|
|
if canClose:
|
|
UI.FOCUS_STACK.push(self)
|
|
isOpen = true
|
|
opened.emit()
|
|
|
|
func close() -> void:
|
|
if canClose:
|
|
UI.FOCUS_STACK.pop()
|
|
isOpen = false
|
|
closed.emit()
|
|
|
|
func toggle() -> void:
|
|
if isOpen:
|
|
close()
|
|
else:
|
|
open()
|
|
|
|
func _onFocusGained() -> void:
|
|
set_process_unhandled_input(true)
|
|
get_viewport().gui_focus_changed.connect(_onViewportFocusChanged)
|
|
if _savedFocusNode != null and is_instance_valid(_savedFocusNode):
|
|
_savedFocusNode.grab_focus()
|
|
else:
|
|
_grabInitialFocus()
|
|
var currentFocus:Control = get_viewport().gui_get_focus_owner()
|
|
if currentFocus != null and is_ancestor_of(currentFocus):
|
|
_savedFocusNode = currentFocus
|
|
focusGained.emit()
|
|
|
|
func _onFocusLost() -> void:
|
|
_savedFocusNode = get_viewport().gui_get_focus_owner()
|
|
if get_viewport().gui_focus_changed.is_connected(_onViewportFocusChanged):
|
|
get_viewport().gui_focus_changed.disconnect(_onViewportFocusChanged)
|
|
set_process_unhandled_input(false)
|
|
focusLost.emit()
|
|
|
|
func _onViewportFocusChanged(control:Control) -> void:
|
|
if control == null or is_ancestor_of(control):
|
|
return
|
|
if _savedFocusNode != null and is_instance_valid(_savedFocusNode):
|
|
_savedFocusNode.grab_focus()
|
|
else:
|
|
_grabInitialFocus()
|
|
|
|
func _grabInitialFocus() -> void:
|
|
pass
|