└── Console.gd /Console.gd: -------------------------------------------------------------------------------- 1 | extends WindowDialog 2 | 3 | class ConsoleBBCodeColor extends RichTextEffect: 4 | var bbcode = "bbcode"; 5 | var colors; 6 | func _init(_colors): 7 | colors = _colors; 8 | func _process_custom_fx(fx): 9 | var c = colors.get(fx.env.get("c")); 10 | if c is Color: 11 | fx.color = c; 12 | return true; 13 | 14 | export var command_postfix := "cmd"; 15 | export var input_action := "console"; 16 | export var history_up := "ui_up"; 17 | export var history_down := "ui_down"; 18 | export var convert_arguments := true; 19 | var label := RichTextLabel.new(); 20 | var line := LineEdit.new(); 21 | 22 | var current := 0; 23 | const history := []; 24 | const commands := {}; 25 | const cmd_args_amount := {}; 26 | 27 | const bbcode_colors = { 28 | line = Color(0.8, 0.8, 0.8, 1), 29 | error = Color(1, 0.2, 0.2, 1), 30 | warning = Color(1, 1, 0.2, 1), 31 | }; 32 | 33 | func _init(): 34 | connect_node(self); 35 | window_title = "Console"; 36 | popup_exclusive = true; 37 | resizable = true; 38 | rect_min_size = Vector2(200, 100); 39 | rect_size = Vector2(500, 300); 40 | 41 | var c = VBoxContainer.new(); 42 | add_child(c); 43 | c.margin_bottom = 0; 44 | c.margin_left = 0; 45 | c.margin_top = 0; 46 | c.margin_right = 0; 47 | c.anchor_bottom = 1; 48 | c.anchor_left = 0; 49 | c.anchor_top = 0; 50 | c.anchor_right = 1; 51 | 52 | label.bbcode_enabled = true; 53 | label.size_flags_vertical = SIZE_EXPAND_FILL; 54 | label.scroll_following = true; 55 | label.selection_enabled = true; 56 | label.install_effect(ConsoleBBCodeColor.new(bbcode_colors)); 57 | c.add_child(label); 58 | 59 | # warning-ignore:return_value_discarded 60 | line.connect("text_entered", self, "command"); 61 | line.clear_button_enabled = true; 62 | c.add_child(line); 63 | 64 | func _process(_delta): 65 | if Input.is_action_just_pressed(input_action): 66 | if visible: 67 | hide(); 68 | else: 69 | popup(); 70 | line.clear(); 71 | line.grab_focus(); 72 | if history.size() > 0 && get_focus_owner() == line: 73 | var add = 0; 74 | if Input.is_action_just_pressed(history_up): 75 | add += 1; 76 | if Input.is_action_just_pressed(history_down): 77 | add -= 1; 78 | if add != 0: 79 | current = int(clamp(current-add, 0, history.size()-1)); 80 | line.text = history[current]; 81 | line.caret_position = line.text.length(); 82 | 83 | func connect_node(node): 84 | for method in node.get_method_list(): 85 | var n = method.name; 86 | if n.ends_with("_" + command_postfix): 87 | n = n.trim_suffix("_" + command_postfix); 88 | commands[n] = node; 89 | cmd_args_amount[n] = method.args.size(); 90 | func disconnect_node(node): 91 | for key in commands.keys(): 92 | if commands[key] == node: 93 | # warning-ignore:return_value_discarded 94 | commands.erase(key); 95 | # warning-ignore:return_value_discarded 96 | cmd_args_amount.erase(key); 97 | 98 | func command(cmd): 99 | if cmd == "": 100 | return; 101 | line.clear(); 102 | self.print(str("\n" if label.text.length() != 0 else "", "> ", cmd), "line"); 103 | var args = Array(cmd.split(" ")); 104 | var command = args.pop_front(); 105 | if convert_arguments: 106 | for i in args.size(): 107 | var arg = args[i].to_lower(); 108 | var new_arg = arg; 109 | if arg == "false": 110 | new_arg = false; 111 | elif arg == "true": 112 | new_arg = true; 113 | elif arg.is_valid_float(): 114 | new_arg = float(arg); 115 | elif arg.is_valid_integer(): 116 | new_arg = int(arg); 117 | args[i] = new_arg; 118 | var node = commands.get(command); 119 | if node: 120 | args.resize(cmd_args_amount[command]); 121 | node.callv(command + "_" + command_postfix, args); 122 | else: 123 | cmd_not_found(command); 124 | history.append(cmd); 125 | current = history.size(); 126 | 127 | func print(s, bbcode=null): 128 | write(str("\n" if label.text.length() != 0 else "", s), bbcode); 129 | func write(s, bbcode=null): 130 | # warning-ignore:return_value_discarded 131 | label.append_bbcode(bbcode_wrap(s, bbcode)); 132 | func bbcode_wrap(s, bbcode): 133 | return str(s) if bbcode == null else str("[bbcode c=", bbcode, "]", s, "[/bbcode]"); 134 | 135 | func cmd_not_found(command): 136 | self.print(str("Command '", command, "' not found"), "error"); 137 | 138 | var help_desc = "Use 'help [command]' to get command description"; 139 | var help_help = "Prints all available commands"; 140 | func help_cmd(command): 141 | if command != null && !commands.has(command): 142 | cmd_not_found(command); 143 | return; 144 | var keys = commands.keys(); 145 | keys.sort(); 146 | 147 | var help_table = "[table=2]"; 148 | for cmd in [command] if command != null else keys: 149 | var h = commands[cmd].get(cmd + "_help"); 150 | help_table += str("[cell]", cmd, "\t[/cell][cell]", h if h else "", "[/cell]"); 151 | self.print(str(help_table, "[/table]")); 152 | 153 | if command == null: 154 | self.print(help_desc); 155 | else: 156 | var desc = commands[command].get(command + "_desc"); 157 | if desc != null: 158 | self.print(desc); 159 | 160 | var history_help = "Prints all previously entered commands"; 161 | func history_cmd(): 162 | self.print("History is empty" if history.size() == 0 else PoolStringArray(history).join("\n")); 163 | 164 | var clear_help = "Clears console output"; 165 | func clear_cmd(): 166 | label.clear(); 167 | --------------------------------------------------------------------------------