155 lines
3.8 KiB
GDScript3
155 lines
3.8 KiB
GDScript3
|
extends TextEdit
|
||
|
class_name ContentFitTextEdit
|
||
|
|
||
|
signal confirmed(content)
|
||
|
|
||
|
|
||
|
const PLACEHOLDER_VISIBILITY = 0.35
|
||
|
const MAIN_THEME = preload("res://user_interface/main.theme")
|
||
|
|
||
|
export(NodePath) var expansion_target = @"."
|
||
|
export(int) var height_addition = 0
|
||
|
export(int) var min_height = 0
|
||
|
export(int) var max_height = 0
|
||
|
export(bool) var expand_width = false
|
||
|
export(int) var width_addition = 0
|
||
|
export(int) var min_width = 0
|
||
|
export(int) var max_width = 488
|
||
|
export(String) var placeholder = ""
|
||
|
export(bool) var confirm_on_focus_exit = false
|
||
|
|
||
|
|
||
|
var reference_label: Label
|
||
|
var width_reference_label: Label
|
||
|
|
||
|
|
||
|
func _ready():
|
||
|
reference_label = Label.new()
|
||
|
reference_label.name = "@@9"
|
||
|
|
||
|
width_reference_label = Label.new()
|
||
|
width_reference_label.name = "@@10"
|
||
|
width_reference_label.theme = MAIN_THEME
|
||
|
width_reference_label.self_modulate.a = 0.0
|
||
|
width_reference_label.set_as_toplevel(true)
|
||
|
|
||
|
reference_label.margin_top = 2.0
|
||
|
reference_label.anchor_right = 1.0
|
||
|
reference_label.margin_right = -9.0
|
||
|
reference_label.autowrap = true
|
||
|
|
||
|
add_child(reference_label)
|
||
|
add_child(width_reference_label)
|
||
|
|
||
|
_check_for_placeholder(false)
|
||
|
|
||
|
# warning-ignore:return_value_discarded
|
||
|
connect("text_changed", self, "_on_text_changed")
|
||
|
# warning-ignore:return_value_discarded
|
||
|
connect("resized", self, "_on_resized")
|
||
|
# warning-ignore:return_value_discarded
|
||
|
connect("focus_entered", self, "_on_focus_entered")
|
||
|
# warning-ignore:return_value_discarded
|
||
|
connect("focus_exited", self, "_on_focus_exited")
|
||
|
|
||
|
|
||
|
func _input(event):
|
||
|
if has_focus():
|
||
|
if event is InputEventKey and event.is_pressed():
|
||
|
if event.scancode == KEY_ENTER and not event.shift:
|
||
|
get_tree().set_input_as_handled()
|
||
|
_confirm()
|
||
|
|
||
|
|
||
|
func _on_text_changed():
|
||
|
adjust_size()
|
||
|
if expand_width:
|
||
|
# I honestly don't know why, but the second call helps with refreshing.
|
||
|
adjust_size()
|
||
|
|
||
|
|
||
|
func _on_resized():
|
||
|
_adjust_height()
|
||
|
|
||
|
|
||
|
func _on_focus_entered():
|
||
|
if has_focus():
|
||
|
_check_for_placeholder(true)
|
||
|
|
||
|
|
||
|
func _on_focus_exited():
|
||
|
_check_for_placeholder(false)
|
||
|
if confirm_on_focus_exit:
|
||
|
_confirm()
|
||
|
|
||
|
|
||
|
func _confirm():
|
||
|
emit_signal("confirmed", text)
|
||
|
|
||
|
|
||
|
func adjust_size():
|
||
|
if expand_width:
|
||
|
width_reference_label.rect_size.x = 1
|
||
|
width_reference_label.text = text
|
||
|
_adjust_width()
|
||
|
reference_label.text = text
|
||
|
_refresh()
|
||
|
_adjust_height()
|
||
|
|
||
|
|
||
|
func _adjust_width():
|
||
|
if is_connected("resized", self, "_on_resized"):
|
||
|
disconnect("resized", self, "_on_resized")
|
||
|
|
||
|
var target = get_node(expansion_target)
|
||
|
|
||
|
# I swear I'm not doing this for fun.
|
||
|
width_reference_label.visible = false
|
||
|
width_reference_label.visible = true
|
||
|
|
||
|
var width = width_reference_label.rect_size.x
|
||
|
target.rect_min_size.x = clamp(width, min_width, max_width) + width_addition
|
||
|
|
||
|
if not is_connected("resized", self, "_on_resized"):
|
||
|
# warning-ignore:return_value_discarded
|
||
|
connect("resized", self, "_on_resized")
|
||
|
|
||
|
|
||
|
func _adjust_height():
|
||
|
if is_connected("resized", self, "_on_resized"):
|
||
|
disconnect("resized", self, "_on_resized")
|
||
|
|
||
|
var target = get_node(expansion_target)
|
||
|
|
||
|
var height = reference_label.rect_size.y + height_addition
|
||
|
if max_height > 0:
|
||
|
height = min(height, max_height)
|
||
|
height = max(height, min_height)
|
||
|
target.rect_min_size.y = height
|
||
|
|
||
|
if not is_connected("resized", self, "_on_resized"):
|
||
|
# warning-ignore:return_value_discarded
|
||
|
connect("resized", self, "_on_resized")
|
||
|
|
||
|
|
||
|
func _check_for_placeholder(focus: bool):
|
||
|
if !focus and text.empty():
|
||
|
reference_label.text = placeholder
|
||
|
reference_label.self_modulate.a = PLACEHOLDER_VISIBILITY
|
||
|
else:
|
||
|
reference_label.self_modulate.a = 0.0
|
||
|
|
||
|
|
||
|
# Control sizes sometimes don't refresh properly, but this can be forces, by
|
||
|
# turning visibility off and on again.
|
||
|
func _refresh():
|
||
|
var had_focus = has_focus()
|
||
|
disconnect("focus_exited", self, "_on_focus_exited")
|
||
|
visible = false
|
||
|
visible = true
|
||
|
if had_focus:
|
||
|
grab_focus()
|
||
|
_check_for_placeholder(had_focus)
|
||
|
# warning-ignore:return_value_discarded
|
||
|
connect("focus_exited", self, "_on_focus_exited")
|