1 /** diagnostic console */ 2 3 module re.ng.diag.console; 4 5 import std.conv; 6 import std.string; 7 import std.array; 8 import std.range; 9 import std.format; 10 11 import re.input.input; 12 import re.core; 13 import re.math; 14 import re.gfx; 15 import re.ng.diag.default_commands; 16 import re.util.interop; 17 static import raygui; 18 19 /// overlay debug console 20 debug class Console { 21 /// the key that opens the console 22 public Keys key = Keys.KEY_GRAVE; 23 /// the character representation of the console key 24 public char key_char = '`'; 25 /// whether the console is open 26 public bool open = false; 27 /// 64 chars of space 28 private enum blank = "\0 "; 29 30 /// console commands 31 public Command[string] commands; 32 private enum height = 30; 33 private char* console_text; 34 private Appender!(string[]) _history; 35 private int _history_depth = 0; 36 37 /// represents a command for the debug console 38 struct Command { 39 string name; 40 void function(string[]) action; 41 string help; 42 } 43 44 /// create a debug console 45 this() { 46 console_text = blank.c_str(); 47 48 // add default commands 49 add_command(Command("help", &DefaultCommands.c_help, "lists available commands")); 50 add_command(Command("entities", &DefaultCommands.c_entities, "lists scene entities")); 51 add_command(Command("dump", &DefaultCommands.c_dump, "dump a component")); 52 add_command(Command("inspect", &DefaultCommands.c_inspect, 53 "open the inspector on a component")); 54 } 55 56 private void add_command(Command command) { 57 commands[command.name] = command; 58 } 59 60 public void update() { 61 // remove all instances of c from str 62 void sstrip(char* str, char c) { 63 char* pr = str; 64 char* pw = str; 65 while (*pr) { 66 *pw = *pr++; 67 pw += (*pw != c); 68 } 69 *pw = '\0'; 70 } 71 72 // remove console key from textbox 73 sstrip(console_text, '`'); 74 75 void load_history_entry() { 76 // load a history entry 77 auto entry = _history.data[$ - _history_depth]; 78 console_text = entry.c_str(); 79 } 80 81 // arrows can scroll through history 82 if (Input.is_key_pressed(Keys.KEY_UP)) { 83 if (_history_depth < _history.data.length) { 84 _history_depth++; 85 load_history_entry(); 86 } 87 } else if (Input.is_key_pressed(Keys.KEY_DOWN)) { 88 if (_history_depth > 0) { 89 _history_depth--; 90 load_history_entry(); 91 } else { 92 console_text = blank.c_str(); 93 } 94 } 95 } 96 97 public void render() { 98 alias pad = Core.debugger.screen_padding; 99 auto screen_br = Vector2(Core.debugger.ui_bounds.width, Core.debugger.ui_bounds.height); 100 // Core.log.info(format("screen_br: (%s", screen_br)); 101 auto console_bg_bounds = Rectangle(pad, 102 screen_br.y - pad - height, screen_br.x - pad * 2, height); 103 // console background 104 // raylib.DrawRectangleRec(console_bg_bounds, bg_col); 105 auto bg_padding = 4; 106 auto console_bounds = Rectangle(console_bg_bounds.x + bg_padding, console_bg_bounds.y + bg_padding, 107 console_bg_bounds.width - bg_padding * 2, console_bg_bounds.height - bg_padding * 2); 108 // console text 109 if (raygui.GuiTextBox(console_bounds, console_text, 64, true)) { 110 auto console_text_str = to!string(console_text); 111 // strip extra whitespace 112 console_text_str = console_text_str.strip(); 113 if (console_text_str.length > 0) { 114 _history_depth = 0; // we just executed a command, no longer in history 115 _history ~= console_text_str; // add to history 116 // get raw command string 117 auto raw_command = console_text_str.split(' '); 118 process_command(raw_command.front, raw_command.drop(1)); 119 // pass command 120 console_text[0] = '\0'; // clear text 121 } 122 } 123 } 124 125 /// process a command in the console 126 public void process_command(string cmd, string[] args) { 127 if (cmd in commands) { 128 commands[cmd].action(args); 129 } else { 130 Core.log.err(format("unrecognized command: %s", cmd)); 131 } 132 } 133 }