OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_keymap_layouts.gd

100 lines · gd ref:
extends SceneTree
## GATE — keymap step 5: KEYBOARD LAYOUTS (docs/keymap-blender-279.md §6).
##
## Letters bind by CHARACTER (Blender: GHOST_SystemX11 takes the keysym), everything else
## by POSITION (the number row — Blender forces it to digits —, numpad, [ ] - =, the fly
## modal's E / Q). Each event below is shaped as the layout sends it: physical_keycode =
## where the key IS (QWERTY names), keycode = what the layout TYPES there.
##
## Run: HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_keymap_layouts.gd

var ok := true
var checks := 0
var _frame := 0
var _km: Node


func _p(cond: bool, what: String, note: Variant = "") -> void:
	checks += 1
	if not cond:
		ok = false
	print("  %-66s %s  %s" % [what, "OK" if cond else "**FAIL**", str(note)])


## A key event as a layout sends it.
func _ev(physical: int, typed: int, ctrl := false, shift := false) -> InputEventKey:
	var e := InputEventKey.new()
	e.physical_keycode = physical
	e.keycode = typed
	e.pressed = true
	e.ctrl_pressed = ctrl
	e.shift_pressed = shift
	return e


## Every action in the active contexts this event fires (exact, as Keymap.tapped).
func _fires(e: InputEventKey) -> Array[String]:
	var out: Array[String] = []
	for ctx: String in _km.DEFAULTS:
		if not _km.is_context_active(ctx):
			continue
		for a: String in (_km.DEFAULTS[ctx] as Dictionary):
			if _km.tapped(e, a):
				out.append(a)
	return out


func _process(_d: float) -> bool:
	_frame += 1
	if _frame < 3:
		return false
	_km = root.get_node("/root/Keymap")
	var gm: Node = root.get_node("/root/GameMode")
	gm.set_mode("build")
	_km.push_context("builder")

	print("-- AZERTY --")
	var a_at_q := _ev(KEY_Q, KEY_A)
	_p(_km.tapped(a_at_q, "builder.select_all"), "the key that TYPES A (QWERTY's Q spot) selects all")
	_p(not _km.tapped(a_at_q, "builder.q_menu"), "... and is NOT the Q menu")
	_p(_fires(a_at_q) == ["builder.select_all"], "... and fires exactly one action", _fires(a_at_q))
	var z_at_w := _ev(KEY_W, KEY_Z)
	_p(_km.tapped(z_at_w, "builder.wireframe"), "the key that types Z (QWERTY's W spot) is the wireframe")
	var amp_at_1 := _ev(KEY_1, KEY_AMPERSAND)
	_p(_km.tapped(amp_at_1, "build_mode.sel_vertex"), "the number row's 1 (types &) still selects vertex mode")
	var q_at_a := _ev(KEY_A, KEY_Q)
	_p(_km.tapped(q_at_a, "builder.q_menu") and not _km.tapped(q_at_a, "builder.select_all"),
		"the key that types Q (QWERTY's A spot) is the Q menu, not select all")

	print("-- Dvorak --")
	var g_at_u := _ev(KEY_U, KEY_G)
	_p(_km.tapped(g_at_u, "builder.move"), "the key that types G (QWERTY's U spot) moves")
	_p(not _km.tapped(g_at_u, "builder.uv_menu"), "... and is NOT the U menu")
	var dvorak_ctrl_z := _ev(KEY_SLASH, KEY_Z, true)
	_p(_km.tapped(dvorak_ctrl_z, "builder.undo"), "Ctrl + the key that types Z undoes, wherever Z is")

	print("-- German / punctuation by POSITION --")
	var ue_at_bracket := _ev(KEY_BRACKETLEFT, 0x00DC)          # Ü (Godot keycodes are Unicode)
	_p(_km.tapped(ue_at_bracket, "builder.move_in"), "the [ position (types Ü) is still 'move in'")
	_p(_km.key_of(ue_at_bracket) == KEY_BRACKETLEFT, "key_of: [ by position", _km.key_of(ue_at_bracket))
	_p(_km.key_of(a_at_q) == KEY_A, "key_of: a letter by character", _km.key_of(a_at_q))

	print("-- numpad and the fly modal --")
	_p(_km.tapped(_ev(KEY_KP_5, KEY_KP_5), "view.persp_ortho"), "Numpad 5 is perspective / ortho")
	_p(InputMap.event_is_action(_ev(KEY_E, KEY_PERIOD), "fly.up", true),
		"fly up is the E POSITION (Dvorak types . there), like WASD")
	_p(not InputMap.event_is_action(_ev(KEY_PERIOD, KEY_E), "fly.up", true),
		"... not wherever the layout types E")

	print("-- names --")
	_p(String(_km.describe("builder.select_all")) == "A", "a letter command is named by its character",
		_km.describe("builder.select_all"))
	_p(String(_km.describe("build_mode.sel_vertex")) != "", "a positional key has a name", _km.describe("build_mode.sel_vertex"))

	gm.set_mode("play")
	if checks < 16:
		_p(false, "only %d checks ran" % checks)
	print("\nGATE keymap_layouts: %s (%d checks)" % ["PASS" if ok else "FAIL", checks])
	quit(0 if ok else 1)
	return true