174 lines
5.0 KiB
GDScript
174 lines
5.0 KiB
GDScript
extends Node
|
|
class_name TerminalControls
|
|
|
|
@onready var historyContainer = $MarginContainer/ScrollContainer/VBoxContainer
|
|
@onready var caret = $MarginContainer/ScrollContainer/VBoxContainer/Label/Caret
|
|
@onready var blink_timer = $MarginContainer/ScrollContainer/VBoxContainer/Label/CaretTimer
|
|
@export var terminalLine: Label
|
|
|
|
var command: String
|
|
var directory: String = "~/"
|
|
var fileSystem: Dictionary = {
|
|
"documents": {
|
|
"special": {
|
|
"notes.txt": "test",
|
|
"secrets": {
|
|
"password.txt": 123
|
|
}
|
|
}
|
|
},
|
|
"bin": {
|
|
"sh": "binary data",
|
|
}
|
|
}
|
|
|
|
func _ready() -> void:
|
|
terminalLine.text = "user@work "+ directory + " "
|
|
UpdateCaretPos()
|
|
blink_timer.start()
|
|
|
|
# --- Caret Blinking Logic ---
|
|
func _on_caret_timer_timeout() -> void:
|
|
caret.visible = !caret.visible
|
|
|
|
func reset_blink() -> void:
|
|
caret.visible = true
|
|
blink_timer.start() # Resets the countdown
|
|
|
|
# --- Input Handling ---
|
|
func InputChar(input) -> void:
|
|
var charPos = terminalLine.get_character_bounds(max(0, terminalLine.text.length() - 1))
|
|
if input == null:
|
|
return
|
|
if charPos.end.x > 1600:
|
|
pass
|
|
else:
|
|
command = input
|
|
terminalLine.text += command
|
|
UpdateCaretPos()
|
|
reset_blink()
|
|
func InputDelChar() -> void:
|
|
if terminalLine.text.length() > ("user@work " + directory).length() + 1:
|
|
terminalLine.text = terminalLine.text.left(-1)
|
|
UpdateCaretPos()
|
|
reset_blink()
|
|
|
|
func EnterCommand() -> void:
|
|
var fullText = terminalLine.text
|
|
var userInput = fullText.trim_prefix("user@work " + directory).strip_edges()
|
|
|
|
if historyContainer.get_child_count() <22:
|
|
CreateHistoryEntry(fullText)
|
|
var parts = userInput.split(" ")
|
|
|
|
match parts[0]:
|
|
"ls":
|
|
var currentDirData
|
|
if parts.size() > 1:
|
|
currentDirData = GetDirAtPath(directory + parts[1])
|
|
else:
|
|
currentDirData = GetDirAtPath(directory)
|
|
if currentDirData is Dictionary:
|
|
var list = ""
|
|
for key in currentDirData.keys():
|
|
list += key + " "
|
|
CreateHistoryEntry(list)
|
|
else:
|
|
CreateHistoryEntry("error: Directory not found")
|
|
"cd":
|
|
if parts.size() > 1:
|
|
var targetPath = parts[1]
|
|
var newDir = ResolvePath(directory, targetPath)
|
|
if GetDirAtPath(newDir) != null:
|
|
directory = newDir
|
|
else:
|
|
CreateHistoryEntry("cd: no such directory: " + targetPath)
|
|
else:
|
|
directory = "~/"
|
|
"cat":
|
|
if parts.size() > 1:
|
|
RetrieveData(parts[1])
|
|
else:
|
|
CreateHistoryEntry("usage: cat [filename]")
|
|
"clear":
|
|
for child in historyContainer.get_children():
|
|
if child != terminalLine:
|
|
child.queue_free()
|
|
"":
|
|
pass
|
|
_:
|
|
CreateHistoryEntry("Command not found: " + userInput)
|
|
|
|
terminalLine.text = "user@work "+ directory + " "
|
|
|
|
# Handle UI scrolling correctly
|
|
|
|
var scrollBox = historyContainer.get_parent() as ScrollContainer
|
|
historyContainer.force_update_transform()
|
|
await get_tree().process_frame
|
|
scrollBox.scroll_vertical = int(scrollBox.get_v_scroll_bar().max_value)
|
|
UpdateCaretPos()
|
|
|
|
# --- History and FileSystem Helpers ---
|
|
func CreateHistoryEntry(content: String) -> void:
|
|
var label = Label.new()
|
|
label.text = content
|
|
label.label_settings = LabelSettings.new()
|
|
label.label_settings.font_size = 42
|
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
label.custom_minimum_size = Vector2(1400, 0)
|
|
|
|
if terminalLine.text.length() > 200:
|
|
for child in historyContainer.get_children():
|
|
if child != terminalLine:
|
|
child.queue_free()
|
|
historyContainer.add_child(label)
|
|
historyContainer.move_child(label, historyContainer.get_child_count() - 2)
|
|
|
|
# Limit history to 15 entries + 1 active line
|
|
if historyContainer.get_child_count() > 22:
|
|
historyContainer.get_child(0).queue_free()
|
|
|
|
func ResolvePath(current: String, target: String) -> String:
|
|
if target.begins_with("~/"):
|
|
return target if target.ends_with("/") else target + "/"
|
|
if target == "..":
|
|
if current == "~/": return "~/"
|
|
var parts = current.trim_suffix("/").rsplit("/", true, 1)
|
|
return parts[0] + "/"
|
|
var joined = current + target
|
|
return joined if joined.ends_with("/") else joined + "/"
|
|
|
|
func GetDirAtPath(path: String):
|
|
if path == "~/": return fileSystem
|
|
var cleanPath = path.trim_prefix("~/").trim_suffix("/")
|
|
var folders = cleanPath.split("/")
|
|
var current = fileSystem
|
|
for folder in folders:
|
|
if current is Dictionary and current.has(folder) and current[folder] is Dictionary:
|
|
current = current[folder]
|
|
else:
|
|
return null
|
|
return current if current is Dictionary else null
|
|
|
|
func RetrieveData(filename: String):
|
|
var currentData = GetDirAtPath(directory)
|
|
if currentData == null:
|
|
CreateHistoryEntry("Error: Current directory invalid.")
|
|
return
|
|
if currentData.has(filename):
|
|
var content = currentData[filename]
|
|
if content is Dictionary:
|
|
CreateHistoryEntry("cat: " + filename + ": is a directory")
|
|
else:
|
|
CreateHistoryEntry(str(content))
|
|
else:
|
|
CreateHistoryEntry("cat: " + filename + ": no such file")
|
|
|
|
func UpdateCaretPos():
|
|
var last_char_index = terminalLine.text.length() - 1
|
|
var char_rect = terminalLine.get_character_bounds(max(0, last_char_index))
|
|
caret.position.x = char_rect.end.x + 1
|
|
caret.position.y = char_rect.position.y
|
|
print(char_rect.end.x)
|