Files
ElHombreAlex--FTL-Faster-Th…/scripts/rename_keyboard.gd
T
ElHombreAlex 35cdf71054 V0.1
First publishable version of the mod
2026-10-03 12:59:33 +02:00

145 lines
5.0 KiB
GDScript

extends Node3D
# One rendered surface, with geometric key picking. Text remains authoritative
# in FTL; the keyboard only forwards scoped native text events.
const SIZE := Vector2(0.86, 0.39)
const PIXELS := Vector2(1000, 454)
const UI = preload("res://scripts/ftl_ui_theme.gd")
var entry: Dictionary = {}
var upper := true
var keys: Array = []
var viewport: SubViewport
var canvas: Control
var title: Label
var value_label: Label
var hint: Label
func _ready() -> void:
viewport = SubViewport.new()
viewport.size = Vector2i(PIXELS)
viewport.transparent_bg = true
viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
add_child(viewport)
canvas = Control.new()
canvas.size = PIXELS
canvas.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
viewport.add_child(canvas)
var background := ColorRect.new()
background.size = PIXELS
background.color = UI.DARK
canvas.add_child(background)
title = _label(Vector2(20, 5), 29, "RENAME")
title.add_theme_font_override("font",UI.ASSETS.font("header"))
value_label = _label(Vector2(20, 44), 38, "")
hint = _label(Vector2(20, 417), 28, "Right trigger: type B: cancel Native name length applies")
var rows := ["1234567890", "QWERTYUIOP", "ASDFGHJKL", "ZXCVBNM-_"]
for row in range(rows.size()):
var line: String = rows[row]
var left := (PIXELS.x - line.length() * 94.0) * 0.5
for column in range(line.length()):
_key(Rect2(left + column * 94.0, 100 + row * 61, 88, 55), line[column], "insert", line[column])
_key(Rect2(30, 350, 100, 58), "SHIFT", "shift", "")
_key(Rect2(140, 350, 230, 58), "SPACE", "insert", " ")
_key(Rect2(380, 350, 100, 58), "CLEAR", "clear", "")
_key(Rect2(490, 350, 140, 58), "BACK", "backspace", "")
_key(Rect2(640, 350, 150, 58), "CANCEL", "cancel", "")
_key(Rect2(800, 350, 170, 58), "DONE", "confirm", "")
var mesh := MeshInstance3D.new()
var quad := QuadMesh.new()
quad.size = SIZE
mesh.mesh = quad
var material := StandardMaterial3D.new()
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
material.no_depth_test = true
material.render_priority = 124
material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
material.albedo_texture = viewport.get_texture()
mesh.material_override = material
add_child(mesh)
visible = false
func _label(point: Vector2, font_size: int, text: String) -> Label:
var label := Label.new()
label.position = point
label.add_theme_font_override("font",UI.ASSETS.font("body"))
label.add_theme_font_size_override("font_size", font_size)
label.add_theme_color_override("font_color",UI.INK)
label.text = text
canvas.add_child(label)
return label
func _key(rect: Rect2, text: String, op: String, character: String) -> void:
var plate := Panel.new()
plate.position = rect.position
plate.size = rect.size
var style := StyleBoxFlat.new()
style.bg_color = UI.PANEL if op != "confirm" else UI.SELECTED
style.border_color = UI.EDGE if op != "confirm" else UI.GREEN
style.set_border_width_all(1)
plate.add_theme_stylebox_override("panel",style)
canvas.add_child(plate)
var label := _label(rect.position + Vector2(0,4),32,text)
label.size = rect.size - Vector2(0, 6)
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
keys.append({"rect": rect, "op": op, "text": character, "label": label})
func set_entry(value: Dictionary) -> void:
if value == entry:
return
var first := str(value.get("id", "")) != str(entry.get("id", ""))
entry = value.duplicate(true)
visible = bool(entry.get("active", false))
if first:
upper = true
_update_case()
title.text = "RENAME %s" % str(entry.get("kind", "NAME")).to_upper()
var text := str(entry.get("text", ""))
var cursor := clampi(int(entry.get("cursor", text.length())), 0, text.length())
value_label.text = text.substr(0, cursor) + "|" + text.substr(cursor)
viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
func _update_case() -> void:
for key in keys:
if key.op == "insert" and key.text != " ":
key.label.text = str(key.text).to_upper() if upper else str(key.text).to_lower()
viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
func ray_hit(from: Vector3, direction: Vector3) -> Dictionary:
if not is_visible_in_tree():
return {}
var start := to_local(from)
var ray := to_local(from + direction) - start
if absf(ray.z) < 0.00001:
return {}
var distance := -start.z / ray.z
if distance <= 0:
return {}
var local_point := start + ray * distance
var pixel := Vector2(local_point.x / SIZE.x + 0.5, 0.5 - local_point.y / SIZE.y) * PIXELS
if not Rect2(Vector2.ZERO, PIXELS).has_point(pixel):
return {}
var hit := {"position": to_global(local_point), "op": ""}
for key in keys:
if key.rect.has_point(pixel):
hit.op = key.op
hit.text = str(key.text).to_upper() if upper else str(key.text).to_lower()
break
return hit
func activate(hit: Dictionary) -> Dictionary:
if hit.get("op", "") == "shift":
upper = not upper
_update_case()
return {}
if hit.get("op", "") == "":
return {}
return {"entry_id": entry.get("id", ""), "op": hit.op, "text": hit.get("text", "")}