Files
Kalulu/addons/godot-form-validator/form_validator.gd
T
2024-02-20 16:08:30 +01:00

126 lines
3.7 KiB
GDScript

@tool
extends Control
class_name FormValidator
signal control_validated(control, passed, messages)
class ValidatorInfo extends RefCounted:
var control: Control
var validator: Validator
@export var auto_validate: bool = false
@export var validation_method: Validation.Method = Validation.Method.BATCH:
set(value):
validation_method = value
_update_validation_methods()
var _control_validator_map: Dictionary = {}
var _control_messages_map: Dictionary = {}
func _ready() -> void:
Validation.validator_added.connect(_on_validator_added)
Validation.validator_removed.connect(_on_validator_removed)
_find_validators(self)
func get_messages() -> Dictionary:
return _control_messages_map
func get_messages_for_control(control: Control) -> PackedStringArray:
if not _control_messages_map.has(control):
return PackedStringArray()
return _control_messages_map[control]
func validate() -> bool:
_control_messages_map.clear()
var list = _get_validator_info_list()
var valid = true
for info in list:
if info.validator.skip_validation:
continue
var passed = info.validator.validate(info.control)
var messages = info.validator.get_messages()
if not passed:
_control_messages_map[info.control] = messages
control_validated.emit(info.control, passed, messages)
valid = valid and passed
if not valid and validation_method == Validation.Method.IMMEDIATE:
return valid
return valid
func _get_validator_info_list() -> Array[ValidatorInfo]:
var list: Array[ValidatorInfo] = []
for control in _control_validator_map.keys():
var validator = _control_validator_map[control]
if not validator:
continue
var info = ValidatorInfo.new()
info.control = control
info.validator = validator
list.append(info)
list.sort_custom(func (a: ValidatorInfo, b: ValidatorInfo):
return a.validator.validation_order < b.validator.validation_order
)
return list
func _update_validation_methods() -> void:
var validators = _control_validator_map.values()
for item in validators:
var validator = item as Validator
if not validator:
continue
validator.validation_method = validation_method
func _find_validators(node: Node) -> void:
for child in node.get_children():
var control_validator = child as ControlValidator
if control_validator:
control_validator._on_validator_added()
_find_validators(child)
func _auto_validate(control: Control) -> void:
if not auto_validate:
return
var validator = _control_validator_map[control] as Validator
if not validator:
return
var passed = validator.validate(control)
control_validated.emit(control, passed, validator.get_messages())
func _on_validator_added(control: Control, validator: Validator) -> void:
if not control:
return
_control_validator_map[control] = validator
if not control.focus_exited.is_connected(_on_control_focus_exited):
control.focus_exited.connect(_on_control_focus_exited.bind(control))
# Special case to handle range-type controls that don't respond to focus
# events for editing.
if control.has_signal("value_changed"):
if not control.value_changed.is_connected(_on_control_value_changed):
control.value_changed.connect(_on_control_value_changed.bind(control))
func _on_validator_removed(control: Control) -> void:
_control_validator_map.erase(control)
if control.focus_exited.is_connected(_on_control_focus_exited):
control.focus_exited.disconnect(_on_control_focus_exited)
if control.has_signal("value_changed"):
if control.value_changed.is_connected(_on_control_value_changed):
control.value_changed.disconnect(_on_control_value_changed)
func _on_control_focus_exited(control: Control) -> void:
_auto_validate(control)
func _on_control_value_changed(value: float, control: Control) -> void:
_auto_validate(control)