Last active
December 8, 2021 22:15
-
-
Save Capital-EX/f86dee1b426bb1dbd672f6ff515f9b42 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is code to implement a text box. This | |
# was used in Godot <= 3.1 when rich text was still | |
# broken. | |
tool | |
extends PanelContainer | |
export (float, 0.0, 1.0, 0.001) var percent_visible := 1.0 setget set_percent_visible, get_percent_visible | |
export (String, MULTILINE) var text := "" setget set_text, get_text | |
export (int) var glyphs_per_line := 30 setget set_glyphs_per_line, get_glyphs_per_line | |
export (int) var lines_shown := 3 | |
export (int, "ALIGN_BEGIN", "ALIGN_CENTER", "ALIGN_END") var alignment := 0 setget set_alignment, get_alignment | |
export (float, 0.0, 3.0, 0.01) var shake_amount := 3.0 | |
export (float, 0.0, 3.0, 0.01) var wave_amplitude := 3.0 | |
export (float, 1.0, 20.0, 0.01) var wave_frequency := 2.0 | |
var labels := [] | |
var words := [] | |
var effect_labels := [] | |
var old_percent_visible := percent_visible | |
var is_ready := false | |
var longest_line_length := 0 | |
var last_word_index := 0 | |
var _wave_time := 0.0 | |
func _enter_tree(): | |
print("Enter Tree") | |
func _ready(): | |
print("Ready") | |
is_ready = true | |
_build_labels(words, float(len(text))) | |
# Process Text Effects | |
func _process(delta): | |
_wave_time += delta | |
var label_number = 0 | |
for effect_label in effect_labels: | |
if effect_label.label.visible: | |
var shake_offset = Vector2(0, 0) | |
var wave_offset = Vector2(0, 0) | |
var color = Color.white | |
for effect in effect_label.effects: | |
match effect: | |
"shk": | |
var shake_x = 1.0/effect_label.label.get_parent().rect_size.x * shake_amount | |
var shake_y = 1.0/effect_label.label.get_parent().rect_size.y * shake_amount | |
shake_offset.x += randf() * shake_x - shake_x / 2.0 | |
shake_offset.y += randf() * shake_y - shake_y / 2.0 | |
"wav": | |
var wavy_y = 1.0/effect_label.label.get_parent().rect_size.y * wave_amplitude | |
wave_offset.y += sin(wave_frequency * _wave_time + label_number) * wavy_y | |
label_number += 1 | |
effect_label.label.anchor_top = wave_offset.y + shake_offset.y | |
effect_label.label.anchor_left = shake_offset.x | |
func _parse(text: String) -> Array: | |
var in_tag := false | |
var remove_effect := false | |
var lines := [] | |
var glyphs := [] | |
var words := [] | |
var effects := [] | |
var effect := "" | |
for character_index in range(0, len(text)): | |
var character = text[character_index] | |
if character == "{": | |
in_tag = true | |
elif character == "}": | |
in_tag = false | |
if not effect.empty(): | |
var index = effects.find(effect) | |
if index == -1: | |
effects.append(effect) | |
else: | |
effects.remove(index) | |
effect = "" | |
elif character == " ": | |
if len(glyphs) > 0: | |
words.append(glyphs) | |
words.append([{character = " ", effects = []}]) | |
glyphs = [] | |
elif character == "\n": | |
if len(glyphs) > 0: | |
words.append(glyphs) | |
words.append([{character = "\n", effects = []}]) | |
glyphs = [] | |
else: | |
if in_tag: | |
effect += character | |
else: | |
glyphs.append({character = character, effects = effects.duplicate()}) | |
if not glyphs.empty(): | |
words.append(glyphs) | |
return words | |
func create_line(alignment: int) -> HBoxContainer: | |
var line := HBoxContainer.new() | |
line.add_constant_override("separation", 0) | |
line.alignment = alignment | |
line.add_constant_override("separation", 0) | |
return line | |
func create_label(text: String, is_visible: bool, color: Color) -> Label: | |
var label = Label.new() | |
label.text = text | |
label.self_modulate = color | |
label.visible = is_visible | |
return label | |
func get_color_from_effects(effects: Array) -> Color: | |
for effect in effects: | |
if effect.begins_with("#"): | |
match len(effect): | |
4, 7, 9: | |
return Color(effect) | |
return Color.white | |
func _build_labels(words: Array, character_count: float, rebuild := false): | |
if find_node("Lines") == null: | |
return | |
if is_ready or Engine.editor_hint: | |
effect_labels = [] | |
labels = [] | |
longest_line_length = 0 if rebuild else longest_line_length | |
var glyphs_on_line := 0 | |
var lines := 0 | |
var wave_offset := Vector2(0,0) | |
var shake_offset := Vector2(0,0) | |
var color := Color(1,1,1) | |
var letter_index := 0 | |
var current_line_length := 0 | |
var font = get_font("") | |
var current_line = create_line(alignment) | |
for child in $Lines.get_children(): | |
$Lines.remove_child(child) | |
child.queue_free() | |
for word_index in range(last_word_index, len(words)): | |
var word = words[word_index] | |
# Wrap text by moving down a line, this prevents word breaks | |
# However, a word that is longer than the line length will be broken | |
# ADDITIONALLY, newlines *do not* count towards the number of | |
# characters on a line. | |
if glyphs_on_line + len(word) > glyphs_per_line and len(word) < glyphs_on_line and word[0].character != "\n": | |
longest_line_length = max(current_line_length, longest_line_length) | |
current_line_length = 0 | |
$Lines.add_child(current_line) | |
current_line = create_line(alignment) | |
lines += 1 | |
glyphs_on_line = 0 | |
if lines + 1 > lines_shown: | |
last_word_index = word_index | |
break | |
var number_of_glyphs = len(word) | |
for glyph_index in range(0, number_of_glyphs): | |
letter_index += 1 | |
var current_glyph = word[glyph_index] | |
if current_glyph.character == "\n": | |
longest_line_length = max(current_line_length, longest_line_length) | |
current_line_length = 0 | |
$Lines.add_child(current_line) | |
current_line = create_line(alignment) | |
lines += 1 | |
glyphs_on_line = 0 | |
continue | |
var label = create_label( | |
current_glyph.character, | |
letter_index / character_count <= percent_visible, | |
get_color_from_effects(current_glyph.effects)) | |
labels.append(label) | |
current_line.add_child(label) | |
current_line_length += font.get_string_size(label.text).x | |
if not current_glyph.effects.empty(): | |
effect_labels.append({ | |
label = label, | |
position = Vector2(label.margin_left, | |
label.margin_top), | |
effects = current_glyph.effects}) | |
glyphs_on_line += 1 | |
if glyphs_on_line == glyphs_per_line: | |
longest_line_length = max(current_line_length, longest_line_length) | |
current_line_length = 0 | |
$Lines.add_child(current_line) | |
lines += 1 | |
current_line = create_line(alignment) | |
glyphs_on_line = 0 | |
print(lines) | |
longest_line_length = max(current_line_length, longest_line_length) | |
rect_size.x = 0 | |
rect_size.y = 0 | |
$Lines.rect_size.x = 0 | |
$Lines.rect_size.y = 0 | |
$Lines.rect_min_size.x = longest_line_length | |
minimum_size_changed() | |
$Lines.add_child(current_line) | |
func set_text(new_text: String) -> void: | |
text = new_text | |
words = _parse(text) | |
_build_labels(words, len(text), true) | |
func get_text() -> String: | |
return text | |
func set_alignment(new_alignment): | |
alignment = new_alignment | |
if find_node("Lines") == null: | |
return | |
if is_ready or Engine.editor_hint: | |
for child in $Lines.get_children(): | |
child.alignment = alignment | |
func get_alignment(): | |
return alignment | |
func set_percent_visible(new_percent_visible: float): | |
old_percent_visible = percent_visible | |
percent_visible = new_percent_visible | |
if find_node("Lines") == null: | |
return | |
var characters := 0 | |
if is_ready or Engine.editor_hint: | |
for line in $Lines.get_children(): | |
characters += line.get_child_count() | |
var label_index := 0 | |
for index in range(ceil(characters * percent_visible), characters): | |
labels[index].visible = false | |
for index in range(ceil(characters * old_percent_visible), ceil(characters * percent_visible)): | |
labels[index].visible = true | |
func get_percent_visible(): | |
return percent_visible | |
func get_glyphs_per_line() -> int: | |
return glyphs_per_line | |
func set_glyphs_per_line(new_glyphs_per_line: int): | |
glyphs_per_line = new_glyphs_per_line | |
_build_labels(words, len(text)) | |
func _input(event): | |
if event is InputEventMouseButton: | |
if event.is_pressed(): | |
_build_labels(words, float(len(words))) | |
func _on_script_changed(): | |
print("?") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment