├── CHANGELOG ├── LICENSE ├── README.md ├── haxelib.json ├── include.xml ├── src └── pgr │ └── dconsole │ ├── DC.hx │ ├── DCCommands.hx │ ├── DCInterp.hx │ ├── DCMonitor.hx │ ├── DCProfiler.hx │ ├── DCThemes.hx │ ├── DCUtil.hx │ ├── DConsole.hx │ ├── input │ ├── DCEmptyInput.hx │ ├── DCInput.hx │ ├── DCKhaInput.hx │ ├── DCLuxeInput.hx │ └── DCOpenflInput.hx │ └── ui │ ├── DCEmtpyInterface.hx │ ├── DCInterface.hx │ ├── DCKhaInterface.hx │ ├── DCLuxeInterface.hx │ └── DCOpenflInterface.hx └── tests ├── .gitignore ├── CoverageReport.bat ├── RunTests.bat ├── TestCommands.hx ├── TestInput.hx ├── TestMonitor.hx ├── TestProfiler.hx ├── TestRegister.hx ├── TestRunner.hx ├── TestUtils.hx ├── project.flow ├── project.xml └── tests.hxproj /CHANGELOG: -------------------------------------------------------------------------------- 1 | ################################################################## 2 | V 5x 3 | ################################################################## 4 | 5 | 5.0.0 6 | * Added Luxe support. 7 | 8 | ################################################################## 9 | V 4x 10 | ################################################################## 11 | 12 | 4.3.1 13 | * Updated to hscript 2.0.3 14 | * Updateds tests 15 | * Added JS/HTML5 log support. 16 | 17 | 4.3 18 | * Refactored Input and Interface to allow handlers. 19 | * Fixed internal logging not going to right console instance. 20 | 21 | 4.2 22 | * Added register class - (allows to register class and access its static fields and methods). 23 | * Added "classes" command to view all registered classes. 24 | 25 | 4.1 26 | * Console supports multiple instances 27 | * Console(s) can be added to other sprites than stage. 28 | * Console resizes with on stage resize event. 29 | * Added custom key combinations to toggle console, monitor and profiler. 30 | * Functions, like commands, also support description. 31 | 32 | 4.0 33 | * Renamed GameConsole/GConsole to TheConsole or DConsole 34 | * Lib classes starting with GC renamed to DC 35 | * Switched to hscript as default interp 36 | * Added command registering 37 | * Small fixes and improvements 38 | * Autocomplete temporarily removed. 39 | 40 | 41 | ################################################################## 42 | V 3x 43 | ################################################################## 44 | 3.1 45 | * Rendering and input refactored 46 | 47 | 3.0 48 | * Reverted main api to GC instead of GameConsole, easier to use. 49 | * Function registering changed, it now registers a pointer to the function instead of the function name + object 50 | * Field registering removed (use object instead). 51 | * Added print command to print field values in runtime. 52 | * Added unit tests. 53 | * Lots of internal fixes and improvements. 54 | Monitor 55 | * Monitor fields are now registered using GC.MonitorField() 56 | * Monitor no longer supports functions, only fields 57 | Profiler 58 | * Added profiler 59 | 60 | 61 | 62 | ################################################################### 63 | V 2x 64 | ################################################################### 65 | * AutoAlias - If no alias is passed, an alias is generated based on the class name. 66 | * Message logging can now be colored. 67 | * Can now register objects and access its methods and functions(eg: set registeredMC.x 20) 68 | * AutoComplete works for one level of depth (does not work yet for object.object.field). 69 | * Arguments autocomplete by A.Pecheney. (toggle arguments key = "F1" atm) 70 | 71 | ################################################################### 72 | V 1.10 73 | ################################################################### 74 | * Gconsole works with flash and neko targets without nme lib. 75 | * Flash, cpp and neko targets have been tested and working fine. 76 | * If you're not using Windows, default font may look bad, use GameConsole.setConsoleFont() in that case. 77 | * The main interface has been renamed from GC to GameConsole. 78 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 TiagoLr ( ~~~ProG4mr~~~ ) 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DConsole 2 | 3 | **DConsole** or **The Console** is a real-time console that allows to: 4 | 5 | * Run scripts. 6 | * Access and modify fields and objects. 7 | * Call registered functions. 8 | * Monitor fields. 9 | * Customize appearence. 10 | * Profile the app in realtime. 11 | * Register new commands that respond to user input. 12 | 13 | 14 | **Latest Changes - 5.0.0** 15 | * Luxe support added 16 | 17 | luxe 18 | 19 | For more changes or other versions, see [CHANGELOG](https://github.com/ProG4mr/dconsole/blob/master/CHANGELOG).

20 | Currently supported targets: 21 | * Openfl - flash 22 | * Openfl - cpp (working with -Dlegacy flag) 23 | * Openfl - neko (working with -Dlegacy flag) 24 | * Luxe - Web 25 | * Luxe - Windows 26 | 27 | ####Donsole-online 28 | You can try dconsole [on this repo github pages](http://tiagolr.github.io/dconsole/). 29 | ####Install 30 | ``` 31 | haxelib install dconsole 32 | ``` 33 | ####Getting Started 34 | 35 | Using dconsole is straightforward: 36 | 37 | ```actionscript 38 | import pgr.dconsole.DC; 39 | 40 | DC.init(); 41 | DC.log("This text will be logged."); 42 | DC.registerFunction(this.testFunction, "myfunction"); 43 | DC.registerObject(this, "myobject"); 44 | DC.registerClass(Math, "Math"); 45 | ``` 46 | 47 | To show the console, press **TAB**, then type **"help"** or **"commands"** 48 | to see what commands are available, also **"objects"** and **"functions"** are useful commands to show registered objects and functions. 49 | 50 | help 51 | 52 | The console can be used to control your application, accessing registered objects and functions in realtime 53 | without having to re-compile. Its also possible to evaluate complex expressions and scripts using haxe sintax. 54 | 55 | example 56 | 57 | ####Monitor
58 | 59 | The monitor allows you to register fields and monitor their value in real time. 60 | For example, to monitor a display object x position: 61 | ```js 62 | DG.monitorField(player, "x", "playerX"); 63 | ``` 64 | 65 | Pressing **CTRL + TAB** brings up the monitor that shows the variable updated in real time 66 | monitor 67 |
The screenshot shows monitor being used in Adam Atomic's Mode demo. 68 | 69 | ####Profiler
70 | 71 | The profiler is lightweight and portable tool that shows: 72 | 73 | * What code is eating more cpu. 74 | * How many times is some code executed inside other code block. 75 | * How much time code takes to execute (benchmark) 76 | * Other statistics not shown by default like absolute elapsed, min, max, totalInstances etc.. 77 | 78 | To sample a code block do: 79 | ```js 80 | DC.beginProfile("SampleName"); 81 | // Code goes here 82 | DC.endProfile("SampleName"); 83 | ``` 84 | Toggling the profiler with **SHIFT + TAB** shows real-time statistics that are updated according to refresh rate.
85 | 86 | profiler 87 |
The screenshot shows the profiler using multiple nested samples, idents are used to vizualize the samples hierarchy.
88 | 89 | * *EL* elapsed milliseconds 90 | * *AVG* average elapsed milliseconds 91 | * *EL(%)* elapsed percentage 92 | * *AVG(%)* average elapsed percentage 93 | * *#* Occurrences of sample inside root sample 94 | * *Name* Sample name 95 |
96 | 97 | ###HTML5 / JS (Experimental) 98 | 99 | DConsole can also run on html5 / javascript using jquery-terminal to process input and log console output (who needs web-kit console right?) 100 | 101 | 1. Add [jquery-terminal](http://terminal.jcubic.pl/) to your html page. 102 | 2. Add the tag ```
``` inside `````` 103 | 4. Add the following script ``` 107 | 108 | 131 | ``` 132 | 133 | Notes: dead code elimination must be off ```-dce no```, otherwise problems may occur. You can also refer to this repo github pages [github pages](http://tiagolr.github.io/dconsole/) to see how to use dconsole with html5. 134 | 135 | ####Tips and Tricks
136 | 137 | * *DC.init(100)* will start the console with 100% height. 138 | * *DC.setVerboseErrors(true)* prints stack information when erros occur. 139 | * Use *DC.registerCommand(...)* to add custom commands. 140 | * Use *DC.registerClass(...)* to enable classes to be used from the console. 141 | 142 | ####Suggestions / Comments / Bugs 143 | 144 | [Email me](mailto:prog4mr@gmail.com) suggestions, comments, bug reports etc..
145 | Or create a new issue (even better). 146 | 147 | -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dconsole", 3 | "url" : "https://github.com/ProG4mr/dconsole", 4 | "license": "MIT", 5 | "tags": ["cross", "console", "logger", "monitor", "profiler", "utility", "game"], 6 | "description": "A real-time console with multiple utilities like scripting, logging, monitoring, profiling etc.", 7 | "version": "4.3.2", 8 | "classPath": "src", 9 | "releasenote": "Updated to hscript 2.0.4", 10 | "contributors": ["prog4mr"], 11 | "dependencies": { "hscript": "2.0.4" } 12 | } 13 | -------------------------------------------------------------------------------- /include.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/pgr/dconsole/DC.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole; 2 | import pgr.dconsole.input.DCInput; 3 | import pgr.dconsole.input.DCEmptyInput; 4 | import pgr.dconsole.ui.DCInterface; 5 | import pgr.dconsole.ui.DCEmtpyInterface; 6 | #if openfl 7 | import pgr.dconsole.input.DCOpenflInput; 8 | import pgr.dconsole.ui.DCOpenflInterface; 9 | #end 10 | #if luxe 11 | import pgr.dconsole.input.DCLuxeInput; 12 | import pgr.dconsole.ui.DCLuxeInterface; 13 | #end 14 | #if kha 15 | import pgr.dconsole.input.DCKhaInput; 16 | import pgr.dconsole.ui.DCKhaInterface; 17 | #end 18 | 19 | /** 20 | * DC class provides user API to The Console. 21 | * It creates a console instance that is added on top of other stage sprites (default). 22 | * 23 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 24 | */ 25 | @:expose 26 | class DC 27 | { 28 | inline static public var VERSION = "5.0.0"; 29 | /** Aligns console to bottom */ 30 | static public var ALIGN_DOWN:String = "DOWN"; 31 | /** Aligns console to top */ 32 | static public var ALIGN_UP:String = "UP"; 33 | 34 | static public var instance:DConsole; 35 | 36 | /** 37 | * Inits TheConsole. 38 | * @param heightPt Pertentage height of the console 39 | * @param align Aligns console using "UP" or "DOWN". 40 | * @param theme Select the console theme from GCThemes. 41 | * @param monitorRate The number of frames elapsed for each monitor refresh. 42 | */ 43 | public static function init(?heightPt:Float = 33, ?align:String = "DOWN", ?theme:DCThemes.Theme = null, input:DCInput = null, interfc:DCInterface = null) { 44 | if (instance != null) { 45 | return; // DConsole has been initialized already. 46 | } 47 | 48 | if (input == null) { 49 | #if (openfl && !js) 50 | input = new DCOpenflInput(); 51 | #elseif luxe 52 | input = new DCLuxeInput(); 53 | #elseif kha 54 | input = new DCKhaInput(); 55 | #else 56 | input = new DCEmptyInput(); 57 | #end 58 | } 59 | 60 | if (interfc == null) { 61 | #if (openfl && !js) 62 | interfc = new DCOpenflInterface(heightPt, align); 63 | #elseif luxe 64 | interfc = new DCLuxeInterface(heightPt, align); 65 | Luxe.next(function() { 66 | DC.registerClass(luxe.Vector, 'Vector'); 67 | }); 68 | #elseif kha 69 | interfc = new DCKhaInterface(heightPt, align); 70 | #else 71 | interfc = new DCEmtpyInterface(); 72 | #end 73 | } 74 | 75 | instance = new DConsole(input, interfc, theme); 76 | DC.logInfo("~~~~~~~~~~ DCONSOLE ~~~~~~~~~~ (v" + VERSION + ")"); 77 | } 78 | /** 79 | * Sets the console font. 80 | * To change font color see GCThemes. 81 | * @param font 82 | * @param embed 83 | * @param size 84 | * @param bold 85 | * @param italic 86 | * @param underline 87 | */ 88 | public static function setConsoleFont(font:String = null, embed:Bool = true, size:Int = 14, bold:Bool = false, italic:Bool = false, underline:Bool = false ){ 89 | checkInstance(); 90 | instance.interfc.setConsoleFont(font, embed, size, bold, italic, underline); 91 | } 92 | /** 93 | * Sets the prompt font. 94 | * To change font color see GCThemes. 95 | * @param font 96 | * @param size 97 | * @param bold 98 | * @param italic 99 | * @param underline 100 | */ 101 | public static function setPromptFont(font:String = null, embed:Bool = true, size:Int = 16, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ){ 102 | checkInstance(); 103 | instance.interfc.setPromptFont(font, embed, size, bold, italic, underline); 104 | } 105 | /** 106 | * Sets the monitor font. 107 | * To change font color see GCThemes. 108 | * @param font 109 | * @param size 110 | * @param yOffset Use this to align the font with the prompt graphic field. 111 | * @param bold 112 | * @param italic 113 | * @param underline 114 | */ 115 | public static function setMonitorFont(font:String = null, embed:Bool = true, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ){ 116 | checkInstance(); 117 | instance.interfc.setConsoleFont(font, embed, size, bold, italic, underline); 118 | } 119 | 120 | /** 121 | * Sets the monitor, console, and prompt fonts in one go. 122 | * Sizes and offsets use the default values. 123 | * 124 | * To change the font color, see GCThemes 125 | * @param font The path of the desired font file 126 | * @param embed ? 127 | * @param bold True if the font should be bold 128 | * @param italic True if the font should be italicized 129 | * @param underline True if the font should be underlined 130 | */ 131 | public static function setFont(font:String = null, embed:Bool = true, size:Int = 16, bold:Bool = false, ?italic:Bool = false, underline:Bool = false){ 132 | checkInstance(); 133 | instance.interfc.setConsoleFont(font, embed, size, bold, italic, underline); 134 | instance.interfc.setPromptFont(font, embed, size, bold, italic, underline); 135 | instance.interfc.setConsoleFont(font, embed, size, bold, italic, underline); 136 | instance.interfc.setProfilerFont(font, embed, size, bold, italic, underline); 137 | } 138 | 139 | /** 140 | * Shows console. 141 | */ 142 | public static function showConsole() { 143 | checkInstance(); 144 | instance.showConsole(); 145 | } 146 | /** 147 | * Hides console. 148 | */ 149 | public static function hideConsole() { 150 | checkInstance(); 151 | instance.hideConsole(); 152 | } 153 | /** 154 | * Shows monitor and refreshes displayed info according to refreshRate. 155 | */ 156 | public static function showMonitor() { 157 | checkInstance(); 158 | instance.showMonitor(); 159 | } 160 | /** 161 | * Stops monitoring. 162 | */ 163 | public static function hideMonitor() 164 | { 165 | checkInstance(); 166 | instance.hideMonitor(); 167 | } 168 | /** 169 | * Shows profiler and refreshes statistics according to refreshRate. 170 | */ 171 | public static function showProfiler() { 172 | checkInstance(); 173 | instance.showProfiler(); 174 | } 175 | /** 176 | * Stops showing and refreshing profiler. 177 | */ 178 | public static function hideProfiler() 179 | { 180 | checkInstance(); 181 | instance.hideProfiler(); 182 | } 183 | /** 184 | * Enables console and its listeners. 185 | */ 186 | public static function enable() { 187 | checkInstance(); 188 | instance.enable(); 189 | } 190 | /** 191 | * Disable console and its listeners. 192 | */ 193 | public static function disable() { 194 | checkInstance(); 195 | instance.disable(); 196 | } 197 | /** 198 | * Sets console toggle key combination. 199 | * @param keyCode The key code. 200 | * @param ctrlKey If control key is pressed. 201 | * @param shiftKey If shift key is pressed. 202 | * @param altKey If alt key is pressed. 203 | */ 204 | public static function setConsoleKey(keyCode:Int, ctrlKey:Bool = false, shiftKey:Bool = false, altKey:Bool = false) { 205 | checkInstance(); 206 | instance.setConsoleKey(keyCode, ctrlKey, shiftKey, altKey); 207 | } 208 | /** 209 | * Sets monitor toggle key combination. 210 | * @param keyCode The key code. 211 | * @param ctrlKey If control key is pressed. 212 | * @param shiftKey If shift key is pressed. 213 | * @param altKey If alt key is pressed. 214 | */ 215 | public static function setMonitorKey(keyCode:Int, ctrlKey:Bool = false, shiftKey:Bool = false, altKey:Bool = false) { 216 | checkInstance(); 217 | instance.setMonitorKey(keyCode, ctrlKey, shiftKey, altKey); 218 | } 219 | /** 220 | * Sets console toggle key combination. 221 | * @param keyCode The key code. 222 | * @param ctrlKey If control key is pressed. 223 | * @param shiftKey If shift key is pressed. 224 | * @param altKey If alt key is pressed. 225 | */ 226 | public static function setProfilerKey(keyCode:Int, ctrlKey:Bool = false, shiftKey:Bool = false, altKey:Bool = false) { 227 | checkInstance(); 228 | instance.setProfilerKey(keyCode, ctrlKey, shiftKey, altKey); 229 | } 230 | /** 231 | * Logs a message to the console. 232 | * @param data The message to log. 233 | * @param color The color of text. (-1 uses default color) 234 | */ 235 | public static function log(data:Dynamic, color:Int = -1) { 236 | checkInstance(); 237 | instance.log(data, color); 238 | } 239 | /** 240 | * Logs a warning message to the console. 241 | * @param data The message to log. 242 | */ 243 | static public function logWarning(data:Dynamic) { 244 | checkInstance(); 245 | instance.logWarning(data); 246 | } 247 | /** 248 | * Logs a error message to the console. 249 | * @param data The message to log. 250 | */ 251 | static public function logError(data:Dynamic) { 252 | checkInstance(); 253 | instance.log(data, DCThemes.current.LOG_ERR); 254 | } 255 | /** 256 | * Logs a confirmation message to the console. 257 | * @param data The message to log. 258 | */ 259 | static public function logConfirmation(data:Dynamic) { 260 | checkInstance(); 261 | instance.logConfirmation(data); 262 | } 263 | /** 264 | * Logs a info message to the console. 265 | * @param data The message to log. 266 | */ 267 | static public function logInfo(data:Dynamic) { 268 | checkInstance(); 269 | instance.logInfo(data); 270 | } 271 | 272 | /** 273 | * Adds this field to be monitored. 274 | * When monitor is visibile, the value will be visible and updated in realtime. 275 | * Private fields or fields with getter/setter are also supported. 276 | * 277 | * @param object object containing the field 278 | * @param fieldName field name, eg: "x" or "rotation" 279 | * @param alias name to be displayed 280 | */ 281 | static public function monitorField(object:Dynamic, fieldName:String, alias:String) { 282 | checkInstance(); 283 | instance.monitorField(object, fieldName, alias); 284 | } 285 | 286 | /** 287 | * Sets the refresh rate of the monitor. 288 | * @param refreshRate Time (in milliseconds) between monitor values update. 289 | */ 290 | static public function setMonitorRefreshRate(refreshRate:Int = 100) { 291 | checkInstance(); 292 | instance.monitor.setRefreshRate(refreshRate); 293 | } 294 | 295 | /** 296 | * Registers a command to be invoked from the console. 297 | * For examples check GCCommands, all runtime commands are registered during console init(). 298 | * 299 | * @param Function The method called when the command is invoked. 300 | * @param alias The command name used to invoke it from the console. 301 | * @param shortcut Alternative name used to invoke it. 302 | * @param description Short description shown in commands list. 303 | * @param help Long description shown in help. 304 | */ 305 | static public function registerCommand(Function:Array->Void, 306 | alias:String, 307 | shortcut:String = "", 308 | description:String = "", 309 | help:String = "") 310 | { 311 | checkInstance(); 312 | instance.commands.registerCommand(Function, alias, shortcut, description, help); 313 | } 314 | 315 | /** 316 | * Registers an object to be used in the console. 317 | * @param object The object to register. 318 | * @param alias The alias displayed in the console. (optional) - if no alias is given, an automatic alias will be created. 319 | */ 320 | public static function registerObject(object:Dynamic, alias:String = "") { 321 | checkInstance(); 322 | instance.commands.registerObject(object, alias); 323 | } 324 | 325 | /** 326 | * Allows a class static methods and properties to be used from the console. 327 | * @param alias The variable name that invokes this class. 328 | * @param cls The class to be exposed to the console. 329 | */ 330 | public static function registerClass(cls:Class, alias:String) { 331 | checkInstance(); 332 | instance.commands.registerClass(cls, alias); 333 | } 334 | /** 335 | * Registers a function to be called from the console. 336 | * If monitor argument is set, this function will be displayed on monitor window. 337 | * 338 | * @param Function The function to be registered. 339 | * @param alias The alias displayed in the console. 340 | * @param description Short description shown in commands list. 341 | */ 342 | public static function registerFunction(Function:Dynamic, alias:String, description:String = "") { 343 | checkInstance(); 344 | instance.commands.registerFunction(Function, alias, description); 345 | } 346 | /** 347 | * Deletes field from registry. 348 | * @param alias 349 | */ 350 | public static function unregisterFunction(alias:String) { 351 | checkInstance(); 352 | instance.commands.unregisterFunction(alias); 353 | } 354 | 355 | public static function unregisterObject(alias:String) { 356 | checkInstance(); 357 | instance.commands.unregisterObject(alias); 358 | } 359 | /** 360 | * Clears console logs. 361 | */ 362 | public static function clearConsole() { 363 | checkInstance(); 364 | instance.clearConsole(); 365 | } 366 | /** 367 | * Removes all entrys from registry. 368 | */ 369 | public static function clearRegistry() { 370 | checkInstance(); 371 | instance.commands.clearRegistry(); 372 | } 373 | 374 | /** 375 | * Resets profiler history and samples. 376 | * If any samples are running, clearProfiler will fail. 377 | */ 378 | static public function clearProfiler() { 379 | checkInstance(); 380 | instance.profiler.clear(); 381 | } 382 | 383 | /** 384 | * Removes all registered fields from monitor 385 | */ 386 | public static function clearMonitor() { 387 | checkInstance(); 388 | instance.monitor.clear(); 389 | } 390 | 391 | /** 392 | * Brings console to front in stage. 393 | * Useful when other ojects are added directly to stage, hiding the console. 394 | */ 395 | public static function toFront() { 396 | checkInstance(); 397 | instance.interfc.toFront(); 398 | } 399 | 400 | /** 401 | * Begins profiling sample, use endProfile(sampleName) to display 402 | * time elapsed statistics between the two calls inside console profiler. 403 | */ 404 | public static function beginProfile(sampleName:String) { 405 | checkInstance(); 406 | instance.profiler.begin(sampleName); 407 | } 408 | /** 409 | * Ends the sample and dumps output to the profiler if this sample has no 410 | * other parent samples. 411 | */ 412 | public static function endProfile(sampleName:String) { 413 | checkInstance(); 414 | instance.profiler.end(sampleName); 415 | } 416 | 417 | /** 418 | * Set weather to print stack information when errors occur 419 | * @param b 420 | */ 421 | public static function setVerboseErrors(b:Bool) { 422 | checkInstance(); 423 | instance.commands.printErrorStack = b; 424 | } 425 | 426 | /** 427 | * Makes console interp evaluate expression 428 | * @param expr 429 | */ 430 | public static function eval(expr:String) { 431 | checkInstance(); 432 | instance.commands.evaluate(expr); 433 | } 434 | 435 | //--------------------------------------------------------------------------------- 436 | // PRIVATE / AUX 437 | //--------------------------------------------------------------------------------- 438 | private static function checkInstance() { 439 | if (instance == null) { 440 | init(); 441 | } 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /src/pgr/dconsole/DCCommands.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole; 2 | 3 | import haxe.CallStack; 4 | import hscript.Expr.Error; 5 | import hscript.Interp; 6 | import hscript.Parser; 7 | import pgr.dconsole.DCUtil.ALIAS_TYPE; 8 | 9 | 10 | typedef Command = { 11 | callback:Array->Void, // the command callback receiving the list of arguments as a parameter 12 | alias:String, // the command name (must be unique) 13 | shortcut:String, // (optional) another key input to call this command (must also be unique) 14 | description:String, // short description of what the command does 15 | help:String, // extended description on how to use the command 16 | } 17 | 18 | typedef Func = { 19 | callback:Dynamic, 20 | description:String, 21 | } 22 | 23 | /** 24 | * DCCommands contains the logic used by GC to execute the commands 25 | * given by the user. 26 | * 27 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 28 | */ 29 | @:access(hscript.Interp) 30 | class DCCommands 31 | { 32 | public var functionsMap:Map = new Map(); 33 | public var objectsMap:Map = new Map(); 34 | public var commandsMap:Map < String, Command > = new Map < String, Command > (); 35 | public var classMap:Map < String, Class> = new Map < String, Class>(); 36 | public var hScriptParser:Parser; 37 | public var hScriptInterp:DCInterp; 38 | public var printErrorStack:Bool = false; 39 | 40 | private var _console:DConsole; 41 | 42 | public function new(console:DConsole) { 43 | 44 | _console = console; 45 | 46 | hScriptParser = new Parser(); 47 | hScriptParser.allowTypes = true; 48 | hScriptParser.allowJSON = true; 49 | hScriptInterp = new DCInterp(); 50 | hScriptInterp.variables.set("objectsMap", objectsMap); 51 | registerClass(Math, "Math"); 52 | } 53 | 54 | public function registerClass(cls:Class, alias:String) { 55 | if (!hScriptInterp.variables.exists(alias) && cls != null) { 56 | hScriptInterp.variables.set(alias, cls); 57 | classMap.set(alias, cls); 58 | } 59 | } 60 | 61 | /** 62 | * Evaluates input: 63 | * If the first word is a registered command, 64 | * call that command and send rest of input as arguments (tokens) 65 | * Otherwise let the interpreter handle the input. 66 | */ 67 | public function evaluate(input:String) { 68 | 69 | var args:Array = input.split(' '); 70 | var commandName = args[0].toLowerCase(); 71 | if (commandName != null && commandName != '') { 72 | for (command in commandsMap.iterator()) { 73 | if (commandName == command.alias || commandName == command.shortcut) { 74 | 75 | // Command found, execute command instead of using hscrit interp 76 | args.shift(); 77 | command.callback(args); 78 | return; 79 | } 80 | } 81 | } 82 | 83 | // hscript interp handle input 84 | try { 85 | if (StringTools.endsWith(StringTools.trim(input), ";") == false) { 86 | input = StringTools.trim(input) + ";"; 87 | } 88 | var program = hScriptParser.parseString(input); 89 | // using exprReturn instead of execute to skip interp internal state reset. 90 | var result = hScriptInterp.exprReturn(program); 91 | if (Std.is(result, Float) || Std.is(result, Bool) || result != null) { 92 | _console.logConfirmation(result); 93 | } 94 | 95 | } 96 | catch (e:Dynamic) { 97 | if (printErrorStack) { 98 | var stack = CallStack.exceptionStack(); 99 | for (entry in stack) { 100 | _console.log(entry); 101 | } 102 | } 103 | _console.logError(Std.string(e)); 104 | } 105 | 106 | } 107 | 108 | public function registerCommand(Function:Array->Void, 109 | alias:String, 110 | shortcut:String = "", 111 | description:String = "", 112 | help:String = "") 113 | { 114 | if (!Reflect.isFunction(Function)) { 115 | _console.logError("Command function " + Std.string(Function) + " is not valid."); 116 | return; 117 | } 118 | 119 | alias = DCUtil.formatAlias(this, alias, ALIAS_TYPE.COMMAND); 120 | if (alias == null) { 121 | _console.log("Failed to register command, make sure alias or shortcut is correct"); 122 | return; 123 | } 124 | 125 | if (shortcut != "") { 126 | shortcut = DCUtil.formatAlias(this, shortcut, ALIAS_TYPE.COMMAND); 127 | // failed to validade this 128 | if (shortcut == null) { 129 | shortcut = ""; // no shortcut 130 | } 131 | } 132 | 133 | var command:Command = { 134 | callback:Function, 135 | alias:alias, 136 | shortcut:shortcut, 137 | description:description, 138 | help:help 139 | } 140 | 141 | commandsMap.set(alias, command); 142 | } 143 | 144 | 145 | public function registerFunction(Function:Dynamic, alias:String, description:String) { 146 | 147 | if (!Reflect.isFunction(Function)) { 148 | _console.logError("Function " + Std.string(Function) + " is not valid."); 149 | return; 150 | } 151 | 152 | alias = DCUtil.formatAlias(this, alias, ALIAS_TYPE.FUNCTION); 153 | if (alias == null) { 154 | _console.logError("Function " + Std.string(Function) + " alias not valid"); 155 | return; 156 | } 157 | 158 | functionsMap.set(alias, { callback:Function, description:description }); 159 | hScriptInterp.variables.set(alias, Function); // registers var in hscript 160 | } 161 | 162 | 163 | public function registerObject(object:Dynamic, alias:String) { 164 | 165 | if (!Reflect.isObject(object)) { 166 | _console.logError("dynamic passed is not an object."); 167 | return; 168 | } 169 | 170 | if (alias == "") { 171 | alias = DCUtil.formatAlias(this, StringTools.replace(Type.getClassName(Type.getClass(object)).toLowerCase(), '.', '_'), ALIAS_TYPE.OBJECT); 172 | } else { 173 | alias = DCUtil.formatAlias(this, alias, ALIAS_TYPE.OBJECT); 174 | } 175 | 176 | if (alias == null) { 177 | _console.logError("failed to register object " + Type.getClassName(Type.getClass(object)) + " make sure object alias is correct"); 178 | return; 179 | } 180 | 181 | objectsMap.set(alias, object); 182 | hScriptInterp.variables.set(alias, object); // registers var in hscript 183 | 184 | } 185 | 186 | 187 | public function unregisterFunction(alias:String) { 188 | 189 | if (functionsMap.exists(alias)) { 190 | functionsMap.remove(alias); 191 | hScriptInterp.variables.remove(alias); 192 | _console.logInfo(alias + " unregistered."); 193 | } 194 | _console.logError(alias + " not found."); 195 | } 196 | 197 | 198 | public function unregisterObject(alias:String) { 199 | 200 | if (objectsMap.exists(alias)) { 201 | objectsMap.remove(alias); 202 | hScriptInterp.variables.remove(alias); // registers var in hscript 203 | _console.logInfo(alias + " unregistered."); 204 | } 205 | _console.logError(alias + " not found."); 206 | } 207 | 208 | 209 | public function clearRegistry() { 210 | functionsMap = new Map(); 211 | objectsMap = new Map(); 212 | } 213 | 214 | 215 | //------------------------------------------------------------------------------- 216 | // CONSOLE RUNTIME COMMANDS 217 | //------------------------------------------------------------------------------- 218 | public function showHelp(args:Array) { 219 | var output :String = "\n"; 220 | 221 | if (args.length > 0) { 222 | 223 | // print command help 224 | var commandName = args[0].toLowerCase(); 225 | if (commandName != null && commandName != '') { 226 | for (command in commandsMap.iterator()) { 227 | if (commandName == command.alias || commandName == command.shortcut) { 228 | output += "command: " + command.alias.toUpperCase() + '\n'; 229 | if (command.shortcut != "") 230 | output += "shortcut: " + '' + command.shortcut.toUpperCase() + '\n'; 231 | output += command.description + '\n\n'; 232 | output += command.help + '\n'; 233 | _console.logInfo(output); 234 | return; 235 | } 236 | } 237 | } else { 238 | _console.logWarning("Command name not found"); 239 | return; 240 | } 241 | 242 | } else { 243 | // print normal help 244 | output += "Type COMMANDS to view available commands\n"; 245 | output += "'PAGEUP' or 'PAGEDOWN' keys to scroll text\n"; 246 | output += "'UP' or 'DOWN' keys to navigate history\n"; 247 | _console.logInfo(output); 248 | } 249 | } 250 | 251 | 252 | public function showCommands(args:Array) { 253 | var output:String = ""; 254 | 255 | for (command in commandsMap.iterator()) { 256 | var line:String = command.alias.toUpperCase(); 257 | if (command.shortcut != "") 258 | line += ' ' + '(' + command.shortcut.toUpperCase() + ')'; 259 | line = StringTools.rpad(line, ' ', 20); 260 | line += command.description + '\n'; 261 | output += line; 262 | } 263 | 264 | _console.logInfo(output); 265 | } 266 | 267 | 268 | public function listFunctions(args:Array) { 269 | var list = ""; 270 | for (key in functionsMap.keys()) { 271 | var line:String = ""; 272 | line += key; 273 | line = StringTools.rpad(line, ' ', 20); 274 | line += functionsMap[key].description + '\n'; 275 | list += line; 276 | } 277 | 278 | if (list.toString() == "") { 279 | _console.logInfo("no functions registered."); 280 | return; 281 | } 282 | 283 | _console.logConfirmation(list); 284 | } 285 | 286 | 287 | public function listObjects(args:Array) { 288 | var list = ""; 289 | for (key in objectsMap.keys()) { 290 | list += key + '\n'; 291 | } 292 | 293 | if (list.toString() == '') { 294 | _console.logInfo("no objects registered."); 295 | return; 296 | } 297 | 298 | _console.logConfirmation(list); 299 | } 300 | 301 | 302 | public function listClasses(args:Array) { 303 | var list = ""; 304 | for (key in classMap.keys()) { 305 | list += key + '\n'; 306 | } 307 | 308 | if (list.toString() == '') { 309 | _console.logInfo("no classes registered."); 310 | return; 311 | } 312 | 313 | _console.logConfirmation(list); 314 | } 315 | 316 | //--------------------------------------------------------------------------------- 317 | // AUX 318 | //--------------------------------------------------------------------------------- 319 | public function getFunction(alias:String):Dynamic { 320 | if (functionsMap.exists(alias)) { 321 | return functionsMap[alias].callback; 322 | } 323 | return null; 324 | } 325 | 326 | 327 | public function getObject(alias:String) { 328 | return objectsMap[alias]; 329 | } 330 | 331 | public function getCommand(alias:String):Command { 332 | alias = alias.toLowerCase(); 333 | for (command in commandsMap.iterator()) { 334 | if (command.alias == alias || command.shortcut == alias) { 335 | return command; 336 | } 337 | } 338 | return null; 339 | } 340 | 341 | public function getClass(alias:String):Class { 342 | return classMap[alias]; 343 | } 344 | 345 | } 346 | -------------------------------------------------------------------------------- /src/pgr/dconsole/DCInterp.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole; 2 | import hscript.Interp; 3 | 4 | /** 5 | * Overrides hscript interp with small changes. 6 | * @author TiagoLr 7 | */ 8 | class DCInterp extends Interp { 9 | 10 | public function new () { 11 | super(); 12 | declared = new Array(); 13 | depth = 0; 14 | } 15 | 16 | override function get( o : Dynamic, f : String ) : Dynamic { 17 | if( o == null ) throw hscript.Expr.Error.EInvalidAccess(f); 18 | return Reflect.getProperty(o,f); 19 | } 20 | 21 | override function set( o : Dynamic, f : String, v : Dynamic ) : Dynamic { 22 | if( o == null ) throw hscript.Expr.Error.EInvalidAccess(f); 23 | Reflect.setProperty(o,f,v); 24 | return v; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/pgr/dconsole/DCMonitor.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole; 2 | import haxe.Timer; 3 | #if kha 4 | import kha.Scheduler; 5 | #end 6 | 7 | typedef MonitorField = { 8 | object:Dynamic, 9 | field:String, 10 | alias:String, 11 | } 12 | 13 | /** 14 | * ... 15 | * @author TiagoLr 16 | */ 17 | class DCMonitor { 18 | 19 | public var startTime(default, null):Int; 20 | public var visible(default, null):Bool; 21 | public var refreshRate(default, null):Int = 100; 22 | public var fields:Array; 23 | 24 | var refreshTimer:Timer; 25 | var hidden:Bool; 26 | var console:DConsole; 27 | 28 | public function new(console:DConsole) { 29 | this.console = console; 30 | fields = new Array(); 31 | setRefreshRate(); 32 | } 33 | //--------------------------------------------------------------------------------- 34 | // LOGIC 35 | //--------------------------------------------------------------------------------- 36 | @:allow(pgr.dconsole.DConsole) 37 | function show() { 38 | visible = true; 39 | writeOutput(); 40 | startTimer(); 41 | } 42 | 43 | @:allow(pgr.dconsole.DConsole) 44 | function hide() { 45 | visible = false; 46 | } 47 | 48 | public function addField(object:Dynamic, fieldName:String, alias:String) { 49 | var mfield:MonitorField = { object:object, field:fieldName, alias:alias }; 50 | fields.push(mfield); 51 | } 52 | 53 | public function clear() { 54 | fields = new Array(); 55 | } 56 | 57 | 58 | public function setRefreshRate(refreshRate:Int = 100) { 59 | this.refreshRate = refreshRate; 60 | } 61 | 62 | public function writeOutput() { 63 | var output = new Array(); 64 | 65 | for (v in fields) { 66 | output.push(v.alias + ': ' + Reflect.getProperty(v.object, v.field) + '\n'); 67 | } 68 | 69 | console.interfc.writeMonitorOutput(output); 70 | } 71 | 72 | function startTimer() { 73 | if (!this.visible) { 74 | return; 75 | } 76 | 77 | function onTimer() { 78 | writeOutput(); 79 | #if !kha 80 | startTimer(); 81 | #end 82 | } 83 | 84 | #if openfl 85 | Timer.delay(onTimer, refreshRate); 86 | #elseif luxe 87 | Luxe.timer.schedule(refreshRate / 1000, onTimer); 88 | #elseif kha 89 | Scheduler.addTimeTask(onTimer, 0, 1 / refreshRate); 90 | #end 91 | } 92 | 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/pgr/dconsole/DCProfiler.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole; 2 | import haxe.Timer; 3 | import pgr.dconsole.DCProfiler.PFSample; 4 | import pgr.dconsole.DCProfiler.SampleHistory; 5 | #if kha 6 | import kha.Scheduler; 7 | #end 8 | 9 | typedef PFSample = { 10 | name:String, 11 | startTime:Int, 12 | elapsed:Int, 13 | instances:Int, // number of begin() 14 | openInstances:Int, // number of begin() without end() 15 | numParents:Int, // number of parents 16 | parentName:String, 17 | childrenElapsed:Int // time elapsed for all children 18 | } 19 | 20 | /** 21 | * Mesures time elapsed between two points inside an application code. 22 | * Writes the last elapsed time and average to monitor output. 23 | * 24 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 25 | */ 26 | class DCProfiler { 27 | 28 | /** Number of spaces between each display field (Elapsed, average, etc..) */ 29 | static public inline var NUM_SPACES:Int = 8; 30 | 31 | public var refreshRate(default, null):Int = 100; 32 | public var visible(default, null):Bool; 33 | public var samples:Array; 34 | public var history:Array; 35 | 36 | var console:DConsole; 37 | var refreshTimer:Timer; 38 | 39 | public function new(console:DConsole) { 40 | this.console = console; 41 | history = new Array(); 42 | samples = new Array(); 43 | setRefreshRate(); 44 | } 45 | 46 | public function clear() { 47 | for (sample in samples) { 48 | if (sample.openInstances > 0) { 49 | DC.logWarning("cannot clear profiler while samples are open"); 50 | } 51 | } 52 | 53 | history = new Array(); 54 | samples = new Array(); 55 | } 56 | 57 | //--------------------------------------------------------------------------------- 58 | // LOGIC 59 | //--------------------------------------------------------------------------------- 60 | @:allow(pgr.dconsole.DConsole) 61 | function show() { 62 | visible = true; 63 | startTimer(); 64 | writeOutput(); // renders first frame. 65 | } 66 | 67 | @:allow(pgr.dconsole.DConsole) 68 | function hide() { 69 | visible = false; 70 | } 71 | 72 | 73 | public function begin(sampleName:String) { 74 | 75 | var sample:PFSample = getSample(sampleName); 76 | 77 | if (sample != null) { 78 | 79 | // reset some stats and relaunch sample. 80 | sample.openInstances++; 81 | sample.instances++; 82 | sample.startTime = getTimeMS(); 83 | sample.parentName = ""; 84 | 85 | if (sample.openInstances > 1) { 86 | throw sampleName + " already started."; 87 | } 88 | 89 | } else { 90 | // create new sample. 91 | sample = 92 | { 93 | name:sampleName, 94 | startTime: getTimeMS(), 95 | elapsed:0, 96 | instances:1, 97 | openInstances:1, 98 | numParents:0, 99 | parentName:"", 100 | childrenElapsed:0 101 | }; 102 | samples.push(sample); 103 | } 104 | setSampleParent(sample); 105 | } 106 | 107 | public function end(sampleName:String) { 108 | var sample:PFSample = getSample(sampleName); 109 | 110 | if (sample == null) { 111 | throw sampleName + "not found"; 112 | } 113 | var endTime = getTimeMS(); 114 | var elapsed = endTime - sample.startTime; 115 | 116 | if (sample.openInstances < 1) { 117 | throw sampleName + " is not started"; 118 | } 119 | 120 | sample.openInstances--; 121 | 122 | // accumulate elapsed time 123 | sample.elapsed += elapsed; 124 | 125 | // accumulate parent.childrenElapsed with sample elapsed time 126 | if (sample.parentName != "") { 127 | getSample(sample.parentName).childrenElapsed += elapsed; 128 | } 129 | 130 | // if this sample is not nested, create (or update) output for sample and its children 131 | if (sample.numParents == 0) { 132 | createHistory(sample); 133 | } 134 | } 135 | 136 | 137 | private function createHistory(sample:PFSample) { 138 | var entry:SampleHistory = getHistory(sample.name); 139 | 140 | // updates history entry 141 | if (entry != null) { 142 | entry.clearBranchSamples(); 143 | entry.update(sample); 144 | } else { 145 | // creates new entry 146 | entry = new SampleHistory(sample); 147 | history.push(entry); 148 | } 149 | 150 | for (s in samples) { 151 | // 152 | // updates children entries for the sample. 153 | if (s.numParents > 0 && s.name != sample.name) { 154 | entry.addChildEntry(s); 155 | } 156 | 157 | if (s.openInstances > 0) { 158 | throw "cross sampling detected: " + s.name + " is still open inside " + sample.name; 159 | } 160 | 161 | // clears all samples after top tree sample has finished. 162 | s.numParents = 0; 163 | s.parentName = ""; 164 | s.elapsed = 0; 165 | s.openInstances = 0; 166 | s.instances = 0; 167 | s.childrenElapsed = 0; 168 | } 169 | } 170 | 171 | 172 | public function setRefreshRate(refreshRate:Int = 100) { 173 | this.refreshRate = refreshRate; 174 | } 175 | 176 | 177 | function getFormatedDisplay(data:String, addSeparator = true):String { 178 | var formatted:String = ""; 179 | 180 | formatted += StringTools.lpad(data, " ", NUM_SPACES); 181 | formatted += " "; 182 | if (addSeparator) { 183 | formatted += "|"; 184 | } 185 | 186 | return formatted; 187 | } 188 | 189 | 190 | public function writeOutput() { 191 | 192 | var output:String = ""; 193 | 194 | output += getFormatedDisplay("EL"); 195 | output += getFormatedDisplay("AVG"); 196 | output += getFormatedDisplay("EL(%)"); 197 | output += getFormatedDisplay("AVG(%)"); 198 | output += getFormatedDisplay("#"); 199 | output += getFormatedDisplay("NAME", false); 200 | output += "\n"; 201 | output += StringTools.rpad("-", "-", (NUM_SPACES + 1) * 7); 202 | output += "\n"; 203 | 204 | for (entry in history) { 205 | 206 | output += getFormatedDisplay(entry.getRelElapsed()); 207 | output += getFormatedDisplay(entry.getRelAverage()); 208 | output += getFormatedDisplay(entry.getPercentElapsed(entry.elapsed)); 209 | output += getFormatedDisplay(entry.getPercentAverage(entry.totalElapsed)); 210 | output += getFormatedDisplay(Std.string(entry.branchInstances)); 211 | output += " " + entry.getFormattedName(); 212 | output += "\n"; 213 | 214 | for (child in entry.childHistory) { 215 | output += getFormatedDisplay(child.getRelElapsed()); 216 | output += getFormatedDisplay(child.getRelAverage()); 217 | output += getFormatedDisplay(child.getPercentElapsed(entry.elapsed)); 218 | output += getFormatedDisplay(child.getPercentAverage(entry.totalElapsed)); 219 | output += getFormatedDisplay(Std.string(child.branchInstances)); 220 | output += " " + child.getFormattedName(); 221 | output += "\n"; 222 | } 223 | } 224 | 225 | console.interfc.writeProfilerOutput(output); 226 | 227 | } 228 | 229 | public function setSampleParent(sample:PFSample) { 230 | sample.numParents = 0; 231 | 232 | for (s in samples) { 233 | if (s.openInstances > 0 && s.name != sample.name) { // any other opened samples are parents. 234 | 235 | sample.numParents++; 236 | // set newly found parent. 237 | if (sample.parentName == "") { 238 | sample.parentName = s.name; 239 | } else { 240 | // set open sample with most parents as this sample parent 241 | if (s.numParents > getSample(sample.parentName).numParents) { 242 | sample.parentName = s.name; 243 | } 244 | } 245 | } 246 | } 247 | } 248 | 249 | public function getSample(sampleName):PFSample { 250 | for (sample in samples) { 251 | if (sample.name == sampleName) { 252 | return sample; 253 | } 254 | } 255 | return null; 256 | } 257 | 258 | public function getHistory(entryName):SampleHistory { 259 | for (entry in history) { 260 | if (entry.name == entryName) { 261 | return entry; 262 | } 263 | } 264 | return null; 265 | } 266 | 267 | 268 | function startTimer() { 269 | if (!visible) { 270 | return; 271 | } 272 | 273 | function onTimer() { 274 | writeOutput(); 275 | #if !kha 276 | startTimer(); 277 | #end 278 | } 279 | 280 | #if openfl 281 | Timer.delay(onTimer, refreshRate); 282 | #elseif luxe 283 | Luxe.timer.schedule(refreshRate / 1000, onTimer); 284 | #elseif kha 285 | Scheduler.addTimeTask(onTimer, 0, 1 / refreshRate); 286 | #end 287 | } 288 | 289 | function getTimeMS():Int { 290 | return Std.int(Timer.stamp() * 1000); 291 | } 292 | } 293 | 294 | class SampleHistory { 295 | 296 | public var name:String = ""; 297 | public var elapsed:Int = 0; 298 | public var totalElapsed:Int = 0; 299 | public var minElapsed:Int = 0; 300 | public var maxElapsed:Int = 0; 301 | public var avgElapsed:Int = 0; 302 | public var childrenElapsed:Int = 0; 303 | public var totalChildrenElapsed:Int = 0; 304 | public var branchInstances:Int = 0; 305 | public var instances:Int = 0; 306 | public var numParents:Int = 0; 307 | public var startTime:Int; // used to sort history arrays 308 | 309 | public var nLogs:Int = 0; 310 | 311 | public var childHistory:Array; 312 | 313 | public function new (s:PFSample) { 314 | childHistory = new Array(); 315 | 316 | this.name = s.name; 317 | this.elapsed = s.elapsed; 318 | minElapsed = elapsed; 319 | maxElapsed = elapsed; 320 | 321 | update(s); 322 | } 323 | 324 | public function clearBranchSamples() { 325 | branchInstances = 0; 326 | for (child in childHistory) { 327 | child.branchInstances = 0; 328 | } 329 | } 330 | 331 | public function update(s:PFSample) { 332 | if (s.name != name) { 333 | throw "updating history from different sample."; 334 | } 335 | 336 | this.childrenElapsed = s.childrenElapsed; 337 | this.elapsed = s.elapsed; 338 | 339 | if (elapsed > maxElapsed) { 340 | maxElapsed = elapsed; 341 | } 342 | 343 | if (elapsed < minElapsed) { 344 | minElapsed = elapsed; 345 | } 346 | this.startTime = s.startTime; 347 | this.instances += s.instances; 348 | this.branchInstances += s.instances; 349 | this.numParents = s.numParents; 350 | 351 | this.totalElapsed += elapsed; 352 | this.totalChildrenElapsed += childrenElapsed; 353 | 354 | nLogs++; 355 | } 356 | /** 357 | * Adds child history sample. 358 | */ 359 | public function addChildEntry(s:PFSample) { 360 | if (s.name == name) { 361 | throw "adding " + s.name + " to " + name + " as child sample."; 362 | } 363 | 364 | var child:SampleHistory = getChild(s.name); 365 | 366 | if (child == null) { 367 | child = new SampleHistory(s); 368 | childHistory.push(child); 369 | } else { 370 | child.update(s); 371 | } 372 | } 373 | /** 374 | * Returns elapsed time. 375 | */ 376 | public function getElapsed():String { 377 | return Std.string(elapsed); 378 | } 379 | /** 380 | * Returns average elapsed time. 381 | */ 382 | public function getAverage():String { 383 | return Std.string(totalElapsed / nLogs); 384 | } 385 | /** 386 | * 387 | */ 388 | public function getMinElapsed():String { 389 | return Std.string(minElapsed); 390 | } 391 | /** 392 | * 393 | */ 394 | public function getMaxElapsed():String { 395 | return Std.string(maxElapsed); 396 | } 397 | /** 398 | * Returns elapsed time relative to children. 399 | */ 400 | public function getRelElapsed():String { 401 | return Std.string(elapsed - childrenElapsed); 402 | } 403 | /** 404 | * Returns average elapsed time relative to children. 405 | */ 406 | public function getRelAverage():String { 407 | return Std.string(Std.int((totalElapsed - totalChildrenElapsed) / nLogs)); 408 | } 409 | /** 410 | * Gets relative time usage percentage 411 | */ 412 | public function getPercentElapsed(parentElapsed:Int):String { 413 | return Std.string(Std.int((elapsed - childrenElapsed) * 100 / parentElapsed * 10) / 10); 414 | } 415 | /** 416 | * 417 | */ 418 | public function getPercentAverage(totalTime:Int):String { 419 | return Std.string(Std.int((totalElapsed - totalChildrenElapsed) * 100 / totalTime * 10) / 10); 420 | } 421 | /** 422 | * Returns idented name. 423 | */ 424 | public function getFormattedName():String { 425 | var s = ""; 426 | for (i in 0...this.numParents) { 427 | s += " "; 428 | } 429 | s += name; 430 | return s; 431 | } 432 | 433 | public function getChild(childName):SampleHistory { 434 | for (child in childHistory) { 435 | if (child.name == childName) { 436 | return child; 437 | } 438 | } 439 | return null; 440 | } 441 | 442 | } 443 | 444 | -------------------------------------------------------------------------------- /src/pgr/dconsole/DCThemes.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole; 2 | import pgr.dconsole.DCThemes.Theme; 3 | 4 | 5 | // alpha values are shared by console and prompt 6 | typedef Theme = { 7 | var CON_C : Int; // Console color 8 | var CON_TXT_C : Int; // Console text color 9 | var CON_A : Float; // Console alpha 10 | var CON_TXT_A : Float; // Console text alpha 11 | 12 | var PRM_TXT_C : Int; // Prompt text color 13 | var PRM_C : Int; // Prompt background color 14 | 15 | var MON_C : Int; // Monitor background color 16 | var MON_TXT_C : Int; // Monitor text color 17 | var MON_A : Float; // Monitor background alpha 18 | var MON_TXT_A : Float; // Monitor text alpha 19 | 20 | var LOG_WAR : Int; // Log warning color; 21 | var LOG_ERR : Int; // Log error color; 22 | var LOG_INF : Int; // Log info color; 23 | var LOG_CON : Int; // log confirmation color; 24 | } 25 | 26 | 27 | /** 28 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 29 | * 30 | * Static class that provides the themes for the console. 31 | * Create your own themes here. 32 | */ 33 | class DCThemes 34 | { 35 | static public var current:Theme; 36 | 37 | static public var LIGHT:Theme = { 38 | CON_C : 0xc5c5c5, 39 | CON_TXT_C : 0x0, 40 | CON_A : .7, 41 | CON_TXT_A : 1, 42 | 43 | PRM_C : 0xc5c5c5, 44 | PRM_TXT_C : 0x0, 45 | 46 | MON_C : 0x000000, 47 | MON_TXT_C : 0xFFFFFF, 48 | MON_A : .7, 49 | MON_TXT_A : .7, 50 | 51 | LOG_WAR : 0x666600, // Warning messages color; 52 | LOG_ERR : 0x770000, // Error message color; 53 | LOG_INF : 0x006666, // Info messages color; 54 | LOG_CON : 0x007700, // Confirmation messages color; 55 | } 56 | 57 | static public var DARK:Theme = { 58 | CON_C : 0x353535, 59 | CON_TXT_C : 0xFFFFFF, 60 | CON_A : .7, 61 | CON_TXT_A : 1, 62 | 63 | PRM_C : 0x454545, 64 | PRM_TXT_C : 0xFFFFFF, 65 | 66 | MON_C : 0x000000, 67 | MON_TXT_C : 0xFFFFFF, 68 | MON_A : .7, 69 | MON_TXT_A : .7, 70 | 71 | LOG_WAR : 0xFFFF00, // Warning messages color; 72 | LOG_ERR : 0xFF0000, // Error message color; 73 | LOG_INF : 0x00FFFF, // Info messages color; 74 | LOG_CON : 0x00FF00, // Confirmation messages color; 75 | } 76 | 77 | public function new() {} 78 | } -------------------------------------------------------------------------------- /src/pgr/dconsole/DCUtil.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole; 2 | 3 | 4 | enum ALIAS_TYPE { 5 | COMMAND; 6 | OBJECT; 7 | FUNCTION; 8 | } 9 | 10 | class DCUtil 11 | { 12 | static public function formatAlias(commands:DCCommands, alias:String, type:ALIAS_TYPE):String { 13 | var i:Int = 1; 14 | 15 | // make sure alias is valid 16 | if (alias == null || alias == "") { 17 | return null; 18 | } 19 | 20 | //Variable names are case sensitive in Haxe. A valid variable name starts with a letter or underscore, 21 | //followed by any number of letters, numbers, or underscores. 22 | var r = ~/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/; 23 | if (!r.match(alias)) { 24 | return null; 25 | } 26 | 27 | if (type == ALIAS_TYPE.COMMAND) { 28 | alias = alias.toLowerCase(); 29 | } 30 | 31 | var aux = alias; 32 | 33 | // make alias unique 34 | // while alias exists in any of the arrays, modify alias adding prefixes or suffixes. 35 | while (commands.getCommand(alias) != null 36 | || commands.getObject(alias) != null 37 | || commands.getFunction(alias) != null 38 | || commands.getClass(alias) != null) 39 | { 40 | switch (type) { 41 | case COMMAND: 42 | //concatenate c 43 | alias = 'c' + alias; 44 | case FUNCTION: 45 | // concatenate f 46 | alias = 'f' + alias; 47 | case OBJECT: 48 | // append i 49 | alias = aux + Std.string(i); 50 | i++; 51 | } 52 | } 53 | 54 | return alias; 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /src/pgr/dconsole/DConsole.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole; 2 | 3 | #if js 4 | import js.Lib; 5 | #end 6 | import pgr.dconsole.DCThemes.Theme; 7 | import pgr.dconsole.input.DCInput; 8 | import pgr.dconsole.input.DCEmptyInput; 9 | import pgr.dconsole.ui.DCInterface; 10 | import pgr.dconsole.ui.DCEmtpyInterface; 11 | 12 | 13 | #if openfl 14 | import pgr.dconsole.ui.DCOpenflInterface; 15 | import pgr.dconsole.input.DCOpenflInput; 16 | #end 17 | #if kha 18 | import pgr.dconsole.ui.DCKhaInterface; 19 | import pgr.dconsole.input.DCKhaInput; 20 | #end 21 | 22 | typedef SCKey = { 23 | altKey:Bool, 24 | ctrlKey:Bool, 25 | shiftKey:Bool, 26 | keycode:Int, 27 | } 28 | 29 | /** 30 | * DConsole is the main class of this lib, it should be instantiated only once 31 | * and then use its instance to control the console. 32 | * 33 | * Its recomended to use DC class as API for this lib. 34 | * 35 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 36 | */ 37 | class DConsole { 38 | 39 | /** Aligns console to bottom */ 40 | static public var ALIGN_DOWN:String = "DOWN"; 41 | /** Aligns console to top */ 42 | static public var ALIGN_UP:String = "UP"; 43 | 44 | private var _historyArray:Array; 45 | private var _historyIndex:Int; 46 | public var input:DCInput; 47 | public var interfc:DCInterface; 48 | public var monitor:DCMonitor; 49 | public var profiler:DCProfiler; 50 | public var commands:DCCommands; 51 | 52 | /** console toggle key */ 53 | public var consoleKey:SCKey; 54 | /** monitor toggle key */ 55 | public var monitorKey:SCKey; 56 | /** profiler toggle key */ 57 | public var profilerKey:SCKey; 58 | 59 | public var enabled(default, null):Bool; 60 | public var visible(default, null):Bool; 61 | 62 | 63 | 64 | public function new(input:DCInput = null, interfc:DCInterface = null, theme:DCThemes.Theme = null) { 65 | 66 | if (input == null) { 67 | #if (openfl && !js) 68 | input = new DCOpenflInput(); 69 | #else 70 | input = new DCEmptyInput(); 71 | #end 72 | } 73 | 74 | if (interfc == null) { 75 | #if (openfl && !js) 76 | interfc = new DCOpenflInterface(33, "DOWN"); 77 | #else 78 | interfc = new DCEmtpyInterface(); 79 | #end 80 | } 81 | 82 | if (theme == null) { 83 | DCThemes.current = DCThemes.DARK; 84 | } else { 85 | DCThemes.current = theme; 86 | } 87 | 88 | // default key is tab 89 | setConsoleKey(9); 90 | // default key is ctrl + tab 91 | setMonitorKey(9, true); 92 | // default key is shift + tab 93 | setProfilerKey(9, false, true); 94 | 95 | // create monitor 96 | monitor = new DCMonitor(this); 97 | 98 | // create profiler 99 | profiler = new DCProfiler(this); 100 | 101 | // create input 102 | this.input = input; 103 | input.console = this; 104 | input.init(); 105 | 106 | // create console interface 107 | this.interfc = interfc; 108 | interfc.console = this; 109 | interfc.init(); 110 | 111 | commands = new DCCommands(this); 112 | 113 | clearHistory(); 114 | 115 | enable(); 116 | hideConsole(); 117 | hideMonitor(); 118 | hideProfiler(); 119 | 120 | commands.registerCommand(commands.showHelp, "help", "", "Type HELP [command-name] for more info"); 121 | commands.registerCommand(commands.showCommands, "commands", "", "Shows available commands", "Type HELP [command-name] for more info"); 122 | commands.registerCommand(commands.listFunctions, "functions", "funcs", "Lists registered functions", "To call a function type functionName( args ), make sure the args type and number are correct"); 123 | commands.registerCommand(commands.listObjects, "objects", "objs", "Lists registered objects", "To print an object field type object.field\nTo set and object field type object.field = value"); 124 | commands.registerCommand(commands.listClasses, "classes", "", "Lists registered classes", "Registered classes can access their static fields and methods, eg: Math.abs(value), or Math.PI"); 125 | commands.registerCommand(clearConsole, "clear", "", "Clears console view"); 126 | commands.registerCommand(toggleMonitor, "monitor", "", "Toggles monitor on and off", "Monitor is used to track variable values in runtime\nCONTROL + CONSOLE_KEY (default TAB) also toggles monitor"); 127 | commands.registerCommand(toggleProfiler, "profiler", "", "Toggles profiler on and off", "Profiler is used to profile app and view statistics like time elapsed and percentage in runtime\nSHIFT + CONSOLE_KEY (default TAB) also toggles profiler"); 128 | } 129 | 130 | 131 | public function showConsole() { 132 | visible = true; 133 | if (!enabled) { 134 | return; 135 | } 136 | interfc.showConsole(); 137 | } 138 | 139 | public function hideConsole() { 140 | visible = false; 141 | if (!enabled) { 142 | return; 143 | } 144 | interfc.hideConsole(); 145 | } 146 | 147 | 148 | public function enable() { 149 | enabled = true; 150 | if (visible) { 151 | interfc.showConsole(); 152 | } 153 | if (monitor.visible) { 154 | interfc.showMonitor(); 155 | } 156 | if (profiler.visible) { 157 | interfc.showProfiler(); 158 | } 159 | input.enable(); 160 | } 161 | 162 | 163 | public function disable() { 164 | enabled = false; 165 | interfc.hideConsole(); 166 | interfc.hideMonitor(); 167 | interfc.hideProfiler(); 168 | input.disable(); 169 | } 170 | 171 | public function setConsoleKey(keyCode:Int, ctrlKey:Bool = false, shiftKey:Bool = false, altKey:Bool = false) { 172 | consoleKey = makeShorcutKey(keyCode, ctrlKey, shiftKey, altKey); 173 | } 174 | 175 | public function setMonitorKey(keyCode:Int, ctrlKey:Bool = false, shiftKey:Bool = false, altKey:Bool = false) { 176 | monitorKey = makeShorcutKey(keyCode, ctrlKey, shiftKey, altKey); 177 | } 178 | 179 | public function setProfilerKey(keyCode:Int, ctrlKey:Bool = false, shiftKey:Bool = false, altKey:Bool = false) { 180 | profilerKey = makeShorcutKey(keyCode, ctrlKey, shiftKey, altKey); 181 | } 182 | 183 | function makeShorcutKey(keyCode:Int, ctrlKey:Bool = false, shiftKey:Bool = false, altKey:Bool = false):SCKey { 184 | return { 185 | altKey:altKey, 186 | ctrlKey:ctrlKey, 187 | shiftKey:shiftKey, 188 | keycode:keyCode, 189 | } 190 | } 191 | 192 | 193 | public function log(data:Dynamic, color:Int = -1) { 194 | 195 | if (!Std.is(data, Float) && !Std.is(data, Bool) && data == "") { 196 | return; 197 | } 198 | 199 | interfc.log(data, color); 200 | 201 | #if js 202 | // dispatches log inside a js event 203 | var scolor = StringTools.hex(color, 6); 204 | var s = Std.string(data); 205 | 206 | // js Lib.eval does not support \n in strings, so split the string and log each line. 207 | s = StringTools.replace(s, "\n", "\\n"); 208 | Lib.eval ( 209 | 'var event = new CustomEvent("console_log", { detail: { data:"' + s + '", color:"' + scolor + '" }}); ' + 210 | 'document.dispatchEvent(event);' 211 | ); 212 | #end 213 | } 214 | 215 | public function logConfirmation(data:Dynamic) { 216 | log(data, DCThemes.current.LOG_CON); 217 | } 218 | 219 | public function logInfo(data:Dynamic) { 220 | log(data, DCThemes.current.LOG_INF); 221 | } 222 | 223 | public function logError(data:Dynamic) { 224 | log(data, DCThemes.current.LOG_ERR); 225 | } 226 | 227 | public function logWarning(data:Dynamic) { 228 | log(data, DCThemes.current.LOG_WAR); 229 | } 230 | 231 | /** 232 | * Clears input text; 233 | */ 234 | public function clearConsole(args:Array = null) { 235 | interfc.clearConsole(); 236 | } 237 | 238 | public function clearHistory() { 239 | _historyArray = new Array(); 240 | _historyIndex = -1; 241 | } 242 | 243 | 244 | public function monitorField(object:Dynamic, fieldName:String, alias:String) { 245 | 246 | if (fieldName == null || fieldName == "") { 247 | logError("invalid fieldName"); 248 | return; 249 | } 250 | 251 | if (alias == null || alias == "") { 252 | logError("invalid alias"); 253 | return; 254 | } 255 | 256 | if (object == null || !Reflect.isObject(object)) { 257 | logError("invalid object."); 258 | return; 259 | } 260 | 261 | try { 262 | Reflect.getProperty(object, fieldName); 263 | } catch (e:Dynamic) { 264 | logError("could not find field: " + fieldName); 265 | return; 266 | } 267 | 268 | monitor.addField(object, fieldName, alias); 269 | } 270 | 271 | 272 | public function toggleMonitor(args:Array = null) { 273 | if (monitor.visible) { 274 | hideMonitor(); 275 | } else { 276 | showMonitor(); 277 | } 278 | } 279 | 280 | public function showMonitor() { 281 | hideProfiler(); 282 | monitor.show(); 283 | interfc.showMonitor(); 284 | } 285 | 286 | public function hideMonitor() { 287 | monitor.hide(); 288 | interfc.hideMonitor(); 289 | } 290 | 291 | 292 | public function toggleProfiler(args:Array = null) { 293 | if (profiler.visible) { 294 | hideProfiler(); 295 | } else { 296 | showProfiler(); 297 | } 298 | } 299 | 300 | public function showProfiler() { 301 | hideMonitor(); 302 | profiler.show(); 303 | interfc.showProfiler(); 304 | } 305 | 306 | public function hideProfiler() { 307 | profiler.hide(); 308 | interfc.hideProfiler(); 309 | } 310 | 311 | public function prevHistory() { 312 | _historyIndex--; 313 | 314 | if (_historyIndex < 0) { 315 | _historyIndex = 0; 316 | } 317 | 318 | if (_historyIndex > _historyArray.length - 1) { 319 | return; 320 | } 321 | 322 | interfc.setInputTxt(_historyArray[_historyIndex]); 323 | interfc.moveCarretToEnd(); 324 | } 325 | 326 | public function nextHistory() { 327 | 328 | if (_historyIndex + 1 > _historyArray.length - 1) { 329 | return; 330 | } 331 | 332 | _historyIndex++; 333 | 334 | interfc.setInputTxt(_historyArray[_historyIndex]); 335 | interfc.moveCarretToEnd(); 336 | } 337 | 338 | 339 | public function processInputLine() { 340 | 341 | var currText = interfc.getInputTxt(); 342 | // no input to process 343 | if (currText == '' || currText == null) { 344 | return; 345 | } 346 | 347 | // HISTORY 348 | _historyArray.insert(0, currText); 349 | resetHistoryIndex(); 350 | 351 | // LOG AND CLEAN PROMPT 352 | log("> " + currText); 353 | interfc.clearInput(); 354 | 355 | commands.evaluate(currText); 356 | } 357 | 358 | // returns history index to beggining. 359 | public function resetHistoryIndex() { 360 | _historyIndex = -1; 361 | } 362 | 363 | public function scrollDown() { 364 | interfc.scrollConsoleDown(); 365 | } 366 | 367 | public function scrollUp() { 368 | interfc.scrollConsoleUp(); 369 | } 370 | 371 | } 372 | -------------------------------------------------------------------------------- /src/pgr/dconsole/input/DCEmptyInput.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole.input; 2 | import pgr.dconsole.DConsole; 3 | 4 | /** 5 | * ... 6 | * @author TiagoLr 7 | */ 8 | class DCEmptyInput implements DCInput { 9 | 10 | public var console:DConsole; 11 | 12 | public function new() {} 13 | public function init() {} 14 | public function enable() {} 15 | public function disable() {} 16 | 17 | } -------------------------------------------------------------------------------- /src/pgr/dconsole/input/DCInput.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole.input; 2 | import pgr.dconsole.DConsole; 3 | 4 | /** 5 | * Handles input 6 | * @author TiagoLr 7 | */ 8 | interface DCInput { 9 | 10 | var console:DConsole; 11 | 12 | public function init():Void; 13 | public function enable():Void; 14 | public function disable():Void; 15 | 16 | } -------------------------------------------------------------------------------- /src/pgr/dconsole/input/DCKhaInput.hx: -------------------------------------------------------------------------------- 1 | #if kha 2 | package pgr.dconsole.input; 3 | 4 | import kha.input.Keyboard; 5 | import kha.input.KeyCode; 6 | import pgr.dconsole.DConsole; 7 | 8 | class DCKhaInput implements DCInput { 9 | 10 | public var console:DConsole; 11 | var altDown:Bool = false; 12 | var ctrlDown:Bool = false; 13 | var shiftDown:Bool = false; 14 | 15 | public function new() {} 16 | 17 | public function init():Void { 18 | enable(); 19 | } 20 | public function enable():Void { 21 | if (Keyboard.get() != null) Keyboard.get().remove(onKeyDown, onKeyUp, null); 22 | if (Keyboard.get() != null) Keyboard.get().notify(onKeyDown, onKeyUp, null); 23 | } 24 | public function disable():Void { 25 | if (Keyboard.get() != null) Keyboard.get().remove(onKeyDown, onKeyUp, null); 26 | } 27 | 28 | public function onKeyDown(k: KeyCode) { 29 | switch (k) { 30 | case KeyCode.Alt: altDown = true; 31 | case KeyCode.Control: ctrlDown = true; 32 | case KeyCode.Shift: shiftDown = true; 33 | default: return; 34 | } 35 | } 36 | 37 | public function onKeyUp(k: KeyCode) { 38 | switch (k) { 39 | case KeyCode.Alt: altDown = false; 40 | case KeyCode.Control: ctrlDown = false; 41 | case KeyCode.Shift: shiftDown = false; 42 | default: k; 43 | } 44 | 45 | if (matchesKey(console.monitorKey, k)) { 46 | console.toggleMonitor(); 47 | } 48 | else if (matchesKey(console.profilerKey, k)) { 49 | console.toggleProfiler(); 50 | } 51 | else if (matchesKey(console.consoleKey, k)) { 52 | if (console.visible) console.hideConsole(); 53 | else console.showConsole(); 54 | } 55 | 56 | else if (console.visible) { 57 | switch (k) { 58 | case KeyCode.Return: console.processInputLine(); 59 | case KeyCode.PageDown: console.scrollDown(); 60 | case KeyCode.PageUp: console.scrollUp(); 61 | case KeyCode.Down: console.prevHistory(); 62 | case KeyCode.Up: console.nextHistory(); 63 | default: return; 64 | } 65 | } 66 | } 67 | 68 | function matchesKey(key: SCKey, k: KeyCode) { 69 | return key.keycode == cast(k, Int) 70 | && key.altKey == altDown 71 | && key.ctrlKey == ctrlDown 72 | && key.shiftKey == shiftDown; 73 | } 74 | } 75 | 76 | #end 77 | -------------------------------------------------------------------------------- /src/pgr/dconsole/input/DCLuxeInput.hx: -------------------------------------------------------------------------------- 1 | #if luxe 2 | package pgr.dconsole.input; 3 | import luxe.Entity; 4 | import luxe.Input.Key; 5 | import luxe.Input.KeyEvent; 6 | import pgr.dconsole.DConsole; 7 | 8 | /** 9 | * Handles input 10 | * @author TiagoLr 11 | */ 12 | class DCLuxeInput implements DCInput { 13 | 14 | public var console:DConsole; 15 | public var inputListener:InputListener; 16 | var enabled:Bool; 17 | 18 | public function new() {} 19 | 20 | public function init() { 21 | enable(); 22 | 23 | inputListener = new InputListener(); 24 | inputListener.console = this.console; 25 | } 26 | 27 | 28 | public function enable() { enabled = true;} 29 | public function disable() { enabled = false;} 30 | } 31 | 32 | 33 | private class InputListener extends Entity { 34 | public var console:DConsole; 35 | 36 | public function new () { 37 | super({name:'dc_input_entity'}); 38 | } 39 | 40 | override public function onkeyup(event:KeyEvent) { 41 | if (!console.enabled) { 42 | return; 43 | } 44 | 45 | // TOGGLE MONITOR 46 | if (matchesKey(event.keycode, console.monitorKey)) { 47 | console.toggleMonitor(); 48 | } 49 | 50 | // TOGGLE PROFILER 51 | else if (matchesKey(event.keycode, console.profilerKey)) { 52 | console.toggleProfiler(); 53 | } 54 | 55 | // SHOW / HIDE CONSOLE 56 | else if (matchesKey(event.keycode, console.consoleKey)) { 57 | 58 | console.visible ? 59 | console.hideConsole(): 60 | console.showConsole(); 61 | 62 | } else if (console.visible) { 63 | 64 | // CONSOLE INPUT HANDLING 65 | switch (event.keycode) { 66 | case Key.enter: console.processInputLine(); 67 | case Key.pagedown: console.scrollDown(); 68 | case Key.pageup: console.scrollUp(); 69 | case Key.down: console.prevHistory(); 70 | case Key.up: console.nextHistory(); 71 | } 72 | } 73 | 74 | return super.onkeydown(event); 75 | } 76 | 77 | private function matchesKey(pressedKey:Int, key:SCKey) { 78 | return pressedKey == key.keycode 79 | && (Luxe.input.keydown(Key.lshift) == key.shiftKey || Luxe.input.keydown(Key.rshift) == key.shiftKey) 80 | && (Luxe.input.keydown(Key.lctrl) == key.ctrlKey || Luxe.input.keydown(Key.rctrl) == key.ctrlKey) 81 | && (Luxe.input.keydown(Key.lalt) == key.altKey || Luxe.input.keydown(Key.ralt) == key.altKey); 82 | } 83 | } 84 | #end -------------------------------------------------------------------------------- /src/pgr/dconsole/input/DCOpenflInput.hx: -------------------------------------------------------------------------------- 1 | #if openfl 2 | package pgr.dconsole.input; 3 | import flash.events.KeyboardEvent; 4 | import flash.Lib; 5 | import flash.ui.Keyboard; 6 | import pgr.dconsole.DConsole; 7 | 8 | /** 9 | * Handles input 10 | * @author TiagoLr 11 | */ 12 | class DCOpenflInput implements DCInput { 13 | 14 | public var console:DConsole; 15 | 16 | public function new() {} 17 | 18 | public function init() { 19 | enable(); 20 | } 21 | 22 | public function enable() { 23 | // make sure events are removed first. 24 | Lib.current.stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp, false); 25 | Lib.current.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false); 26 | Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp, false); 27 | Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false); 28 | } 29 | 30 | 31 | public function disable() { 32 | Lib.current.stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp, false); 33 | Lib.current.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false); 34 | } 35 | 36 | 37 | private function onKeyDown(e:KeyboardEvent):Void { 38 | #if !(cpp || neko) // BUGFIX 39 | if (console.enabled && console.visible) { 40 | e.stopImmediatePropagation(); 41 | } 42 | #end 43 | } 44 | 45 | 46 | private function onKeyUp(e:KeyboardEvent):Void { 47 | // TOGGLE MONITOR 48 | if (matchesKey(console.monitorKey, e)) { 49 | console.toggleMonitor(); 50 | return; 51 | } 52 | 53 | // TOGGLE PROFILER 54 | else 55 | if (matchesKey(console.profilerKey, e)) { 56 | console.toggleProfiler(); 57 | return; 58 | } 59 | 60 | // SHOW/HIDE CONSOLE 61 | else 62 | if (matchesKey(console.consoleKey, e)) { 63 | if (console.visible) { 64 | console.hideConsole(); 65 | } else { 66 | console.showConsole(); 67 | } 68 | return; 69 | } 70 | 71 | // IGNORE INPUT IF CONSOLE HIDDEN 72 | else 73 | if (!console.visible) { 74 | return; 75 | } 76 | 77 | switch (e.keyCode) { 78 | case Keyboard.ENTER: console.processInputLine(); 79 | case Keyboard.PAGE_DOWN: console.scrollDown(); 80 | case Keyboard.PAGE_UP: console.scrollUp(); 81 | case Keyboard.UP: console.nextHistory(); 82 | case Keyboard.DOWN: console.prevHistory(); 83 | default: console.resetHistoryIndex(); // 84 | } 85 | 86 | #if !(cpp || neko) // BUGFIX 87 | e.stopImmediatePropagation(); // BUG - cpp issues. 88 | #end 89 | } 90 | 91 | function matchesKey(key:SCKey, e:KeyboardEvent) { 92 | return (key.keycode == cast(e.keyCode, Int) 93 | && key.altKey == e.altKey 94 | && key.ctrlKey == e.ctrlKey 95 | && key.shiftKey == e.shiftKey); 96 | } 97 | 98 | } 99 | #end -------------------------------------------------------------------------------- /src/pgr/dconsole/ui/DCEmtpyInterface.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole.ui; 2 | 3 | /** 4 | * ... 5 | * @author TiagoLr 6 | */ 7 | class DCEmtpyInterface implements DCInterface { 8 | public var console:DConsole; 9 | 10 | public function new() {} 11 | 12 | public function init() {} 13 | 14 | public function showConsole() {} 15 | 16 | public function hideConsole() {} 17 | 18 | public function writeMonitorOutput(output:Array) {} 19 | 20 | public function showMonitor() {} 21 | 22 | public function hideMonitor() {} 23 | 24 | public function writeProfilerOutput(output:String) {} 25 | 26 | public function showProfiler() {} 27 | 28 | public function hideProfiler() {} 29 | 30 | //--------------------------------------------------------------------------------- 31 | // PUBLIC METHODS 32 | //--------------------------------------------------------------------------------- 33 | public function log(data:Dynamic, color:Int) {} 34 | 35 | public function moveCarretToEnd() {} 36 | 37 | public function scrollConsoleUp() {} 38 | 39 | public function scrollConsoleDown() {} 40 | 41 | /** 42 | * Brings this display object to the front of display list. 43 | */ 44 | public function toFront() {} 45 | 46 | public function setConsoleFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, italic:Bool = false, underline:Bool = false ) {} 47 | 48 | 49 | public function setPromptFont(font:String = null, embed:Bool = false, size:Int = 16, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) {} 50 | 51 | public function setProfilerFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) {} 52 | 53 | public function setMonitorFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) {} 54 | 55 | /** 56 | * Removes last input char 57 | */ 58 | public function inputRemoveLastChar() {} 59 | 60 | public function getInputTxt():String { return ""; } 61 | 62 | public function setInputTxt(string:String) { } 63 | 64 | public function getConsoleText() { return ""; } 65 | 66 | public function getMonitorText() { return { col1:"", col2:"" } } 67 | 68 | public function clearInput() {} 69 | 70 | 71 | public function clearConsole() {} 72 | } -------------------------------------------------------------------------------- /src/pgr/dconsole/ui/DCInterface.hx: -------------------------------------------------------------------------------- 1 | package pgr.dconsole.ui; 2 | 3 | /** 4 | * 5 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 6 | */ 7 | 8 | interface DCInterface 9 | { 10 | var console:DConsole; 11 | 12 | public function init() : Void; 13 | 14 | public function showConsole() : Void; 15 | 16 | public function hideConsole() : Void; 17 | 18 | public function writeMonitorOutput(output:Array) : Void; 19 | 20 | public function showMonitor() : Void; 21 | 22 | public function hideMonitor() : Void; 23 | 24 | public function writeProfilerOutput(output:String) : Void; 25 | 26 | public function showProfiler() : Void; 27 | 28 | public function hideProfiler() : Void; 29 | 30 | //--------------------------------------------------------------------------------- 31 | // PUBLIC METHODS 32 | //--------------------------------------------------------------------------------- 33 | public function log(data:Dynamic, color:Int) : Void; 34 | 35 | public function moveCarretToEnd() : Void; 36 | 37 | public function scrollConsoleUp() : Void; 38 | 39 | public function scrollConsoleDown() : Void; 40 | 41 | /** 42 | * Brings this display object to the front of display list. 43 | */ 44 | public function toFront() : Void; 45 | 46 | public function setConsoleFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, italic:Bool = false, underline:Bool = false ) : Void; 47 | 48 | public function setPromptFont(font:String = null, embed:Bool = false, size:Int = 16, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) : Void; 49 | 50 | public function setProfilerFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) : Void; 51 | 52 | public function setMonitorFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) : Void; 53 | 54 | /** 55 | * Removes last input char 56 | */ 57 | public function inputRemoveLastChar() : Void; 58 | 59 | public function getInputTxt():String; 60 | 61 | public function setInputTxt(string:String) : Void; 62 | 63 | public function getConsoleText() : String; 64 | 65 | public function getMonitorText() : { col1:String, col2:String }; 66 | 67 | public function clearInput() : Void; 68 | 69 | public function clearConsole() : Void; 70 | 71 | 72 | } -------------------------------------------------------------------------------- /src/pgr/dconsole/ui/DCKhaInterface.hx: -------------------------------------------------------------------------------- 1 | #if kha 2 | package pgr.dconsole.ui; 3 | 4 | import Std; 5 | import haxe.io.Bytes; 6 | 7 | import kha.System; 8 | import kha.Scheduler; 9 | import kha.Image; 10 | import kha.Color; 11 | import kha.Assets; 12 | import kha.Framebuffer; 13 | import kha.math.Vector2; 14 | import kha.Font; 15 | import kha.input.KeyCode; 16 | import kha.input.Keyboard; 17 | import Math; 18 | using kha.graphics2.GraphicsExtension; 19 | 20 | import kha2d.Sprite; 21 | 22 | class KhaText { 23 | public var text: String = ""; 24 | public var visible: Bool = true; 25 | public var color: Int; 26 | public var font: Font; 27 | public var fontSize: Int; 28 | public var x: Float; 29 | public var y: Float; 30 | public var maxLines:Int; 31 | public var startFromLine = 0; 32 | 33 | public function new(color: Int, font: Font, fontSize: Int, x: Float, y: Float, maxLines: Int=1) { 34 | this.color = color; 35 | this.font = font; 36 | this.fontSize = fontSize; 37 | this.x = x; 38 | this.y = y; 39 | this.maxLines = maxLines; 40 | } 41 | 42 | public function render(fb: Framebuffer) { 43 | fb.g2.color = color; 44 | fb.g2.font = font; 45 | fb.g2.fontSize = fontSize; 46 | var lines = getLines(); 47 | for (i in startFromLine...Std.int(Math.min(lines.length, startFromLine + maxLines))) { 48 | fb.g2.drawString(lines[i], x, y + fontSize * (i - startFromLine)); 49 | } 50 | } 51 | 52 | function getLines(): Array { 53 | return text.split("\n"); 54 | } 55 | 56 | public function scrollToBottom() { 57 | var totalLines = getLines().length; 58 | if (totalLines > maxLines) { 59 | startFromLine = totalLines - maxLines; 60 | } 61 | } 62 | 63 | public function scrollUp() { 64 | startFromLine = Std.int(Math.max(0, startFromLine - 1)); 65 | } 66 | 67 | public function scrollDown() { 68 | startFromLine = Std.int(Math.min(startFromLine + 1, getLines().length - maxLines)); 69 | } 70 | } 71 | 72 | class KhaPromptCursor { 73 | public var color: Int; 74 | public var p0: Vector2; 75 | public var p1: Vector2; 76 | public var visible = true; 77 | public var thickness = 1; 78 | 79 | public function new(color: Int, p0: Vector2, p1: Vector2) { 80 | this.color = color; 81 | this.p0 = p0; 82 | this.p1 = p1; 83 | } 84 | } 85 | 86 | class KhaPromptText extends KhaText { 87 | public var cursor: KhaPromptCursor; 88 | var index(default, set):Int = 0; 89 | var console:DConsole; 90 | 91 | public function new(color: Int, font: Font, fontSize: Int, x: Float, y: Float, 92 | console:DConsole, cursor: KhaPromptCursor) { 93 | super(color, font, fontSize, x, y); 94 | 95 | this.console = console; 96 | this.cursor = cursor; 97 | 98 | if (Keyboard.get() != null) Keyboard.get().notify(onKeyDown, null, onTextInput); 99 | Scheduler.addTimeTask(blinkCursor, 0, .5); 100 | 101 | } 102 | 103 | public function onTextInput(char: String) { 104 | if (this.visible == false) { 105 | return; 106 | } 107 | this.text = text.substr(0, index) + char + text.substr(index, text.length); 108 | index += char.length; 109 | console.resetHistoryIndex(); 110 | } 111 | 112 | public function onKeyDown(k: KeyCode) { 113 | if (this.visible == false) { 114 | return; 115 | } 116 | switch(k) { 117 | case KeyCode.Backspace: 118 | index--; 119 | text = text.substr(0, index) + text.substr(index + 1, text.length); 120 | case KeyCode.Delete: 121 | text = text.substr(0, index) + text.substr(index + 1, text.length); 122 | case KeyCode.Left: 123 | index--; 124 | case KeyCode.Right: 125 | index++; 126 | default: return; 127 | } 128 | } 129 | 130 | public function moveCarretToEnd() { 131 | index = text.length; 132 | } 133 | 134 | inline function set_index(i:Int) { 135 | index = Std.int(Math.min(text.length, Math.max(0, i))); 136 | 137 | //update cursor position 138 | var width = font.width(fontSize, text.substr(0, index)); 139 | if (width == 0) { 140 | width = 1; // fix cursor not visible 141 | } 142 | cursor.p0 = new Vector2(width, cursor.p0.y); 143 | cursor.p1 = new Vector2(width, cursor.p1.y); 144 | 145 | return index; 146 | } 147 | 148 | public override function render(fb: Framebuffer) { 149 | super.render(fb); 150 | if (cursor.visible == true) { 151 | fb.g2.color = cursor.color; 152 | fb.g2.drawLine(cursor.p0.x, cursor.p0.y, cursor.p1.x, cursor.p1.y, cursor.thickness); 153 | } 154 | } 155 | 156 | function blinkCursor() { 157 | if (!this.visible) { 158 | return; 159 | } 160 | cursor.visible = !cursor.visible; 161 | } 162 | } 163 | 164 | class DCKhaInterface implements DCInterface { 165 | public var console:DConsole; 166 | 167 | var align:String; 168 | var heightPt:Float; // percentage height 169 | 170 | var consoleHeight: Int; 171 | 172 | var consoleDisplay:Sprite; 173 | var promptDisplay: Sprite; 174 | 175 | var txtConsole: KhaText; 176 | var txtPrompt: KhaPromptText; 177 | var promptCursor: KhaPromptCursor; 178 | 179 | var font:Font = null; 180 | var fontSize: Int = 15; 181 | 182 | var PROMPT_HEIGHT = 20; 183 | 184 | var monitorDisplay: Sprite; 185 | var txtMonitorLeft: KhaText; 186 | var txtMonitorRight: KhaText; 187 | 188 | var profilerDisplay: Sprite; 189 | var txtProfiler: KhaText; 190 | 191 | public function new(heightPt: Float, align:String) { 192 | if (heightPt <= 0 || heightPt > 100) heightPt = 100; // clamp to >0..1 193 | if (heightPt > 1) heightPt = Std.int(heightPt) / 100; // turn >0..100 into percentage from 1..0 194 | 195 | this.heightPt = heightPt; 196 | this.align = align; 197 | } 198 | 199 | public function init() { 200 | createConsoleDisplay(); 201 | createMonitorDisplay(); 202 | createProfilerDisplay(); 203 | 204 | Assets.loadFont("Consolas", function(font:Font) { 205 | this.font = font; 206 | //TODO: 207 | txtPrompt.font = font; 208 | txtConsole.font = font; 209 | txtMonitorLeft.font = font; 210 | txtMonitorRight.font = font; 211 | txtProfiler.font = font; 212 | System.notifyOnFrames(function (framebuffers) { render(framebuffers[0]); }); 213 | }); 214 | } 215 | 216 | function packColorBytes(color: Int, alpha: Float = 1.): Int { 217 | return (Std.int(alpha * 255) << 24) | color; 218 | } 219 | 220 | function createImageBytes(color: Color, width: Int, height: Int): Bytes { 221 | var bytes = Bytes.alloc(width * height * 4); 222 | var i = 0; 223 | while (i < width * height * 4) { 224 | bytes.set(i, color.Rb); 225 | bytes.set(i + 1, color.Gb); 226 | bytes.set(i + 2, color.Bb); 227 | bytes.set(i + 3, color.Ab); 228 | i += 4; 229 | } 230 | return bytes; 231 | } 232 | 233 | function createConsoleDisplay() { 234 | //consoleDisplay 235 | consoleHeight = Std.int(System.windowHeight() * heightPt) - PROMPT_HEIGHT; 236 | 237 | var color = Color.fromValue(packColorBytes(DCThemes.current.CON_C, DCThemes.current.CON_A)); 238 | var bytes = createImageBytes(color, System.windowWidth(), consoleHeight); 239 | var image = Image.fromBytes(bytes, System.windowWidth(), consoleHeight); 240 | 241 | consoleDisplay = new Sprite(image); 242 | consoleDisplay.setPosition(new Vector2(0, System.windowHeight() * (1 - heightPt))); 243 | ////////////// 244 | 245 | //promptDisplay 246 | var color = Color.fromValue(packColorBytes(DCThemes.current.PRM_C)); 247 | var bytes = createImageBytes(color, System.windowWidth(), PROMPT_HEIGHT); 248 | var image = Image.fromBytes(bytes, System.windowWidth(), PROMPT_HEIGHT); 249 | 250 | promptDisplay = new Sprite(image); 251 | promptDisplay.setPosition(new Vector2(0, System.windowHeight() - PROMPT_HEIGHT)); 252 | ////////////// 253 | 254 | txtConsole = new KhaText( 255 | packColorBytes(DCThemes.current.PRM_TXT_C, DCThemes.current.CON_TXT_A), 256 | font, fontSize, consoleDisplay.x, consoleDisplay.y + 3, Math.ceil(consoleHeight / fontSize)); 257 | 258 | promptCursor = new KhaPromptCursor( 259 | packColorBytes(DCThemes.current.PRM_TXT_C), 260 | new Vector2(1, System.windowHeight() - PROMPT_HEIGHT + 3), 261 | new Vector2(1, System.windowHeight() - PROMPT_HEIGHT + 3 + fontSize)); 262 | 263 | //TODO: align 264 | txtPrompt = new KhaPromptText( 265 | packColorBytes(DCThemes.current.PRM_TXT_C), 266 | font, fontSize, promptDisplay.x, promptDisplay.y + 3, 267 | console, promptCursor); 268 | } 269 | 270 | public function showConsole() { 271 | consoleDisplay.visible = true; 272 | txtConsole.visible = true; 273 | promptDisplay.visible = true; 274 | txtPrompt.visible = true; 275 | promptCursor.visible = true; 276 | 277 | monitorDisplay.height = System.windowHeight() - consoleHeight - PROMPT_HEIGHT; 278 | txtMonitorLeft.maxLines = Math.ceil(monitorDisplay.height / fontSize); 279 | txtMonitorRight.maxLines = Math.ceil(monitorDisplay.height / fontSize); 280 | 281 | profilerDisplay.height = System.windowHeight() - consoleHeight - PROMPT_HEIGHT; 282 | txtProfiler.maxLines = Math.ceil(profilerDisplay.height / fontSize); 283 | } 284 | 285 | public function hideConsole() { 286 | consoleDisplay.visible = false; 287 | txtConsole.visible = false; 288 | promptDisplay.visible = false; 289 | txtPrompt.visible = false; 290 | promptCursor.visible = false; 291 | 292 | monitorDisplay.height = System.windowHeight(); 293 | txtMonitorLeft.maxLines = Math.ceil(monitorDisplay.height / fontSize); 294 | txtMonitorRight.maxLines = Math.ceil(monitorDisplay.height / fontSize); 295 | 296 | profilerDisplay.height = System.windowHeight(); 297 | txtProfiler.maxLines = Math.ceil(profilerDisplay.height / fontSize); 298 | } 299 | 300 | function createMonitorDisplay() { 301 | var color = Color.fromValue(packColorBytes(DCThemes.current.MON_C, DCThemes.current.MON_A)); 302 | var bytes = createImageBytes(color, System.windowWidth(), System.windowHeight()); 303 | var image = Image.fromBytes(bytes, System.windowWidth(), System.windowHeight()); 304 | 305 | monitorDisplay = new Sprite(image); 306 | monitorDisplay.setPosition(new Vector2(0, 0)); 307 | 308 | //TODO: maxWidth 309 | txtMonitorLeft = new KhaText( 310 | packColorBytes(DCThemes.current.MON_TXT_C, DCThemes.current.MON_TXT_A), 311 | font, fontSize, monitorDisplay.x, monitorDisplay.y + 3, 312 | Math.ceil(System.windowHeight() / fontSize)); 313 | 314 | txtMonitorRight = new KhaText( 315 | packColorBytes(DCThemes.current.MON_TXT_C, DCThemes.current.MON_TXT_A), 316 | font, fontSize, System.windowWidth() / 2, monitorDisplay.y + 3, 317 | Math.ceil(System.windowHeight() / fontSize)); 318 | 319 | } 320 | 321 | // Splits output into left and right monitor text fields 322 | public function writeMonitorOutput(output:Array) { 323 | txtMonitorLeft.text = ""; 324 | txtMonitorRight.text = ""; 325 | 326 | txtMonitorLeft.text += "DC Monitor\n\n"; 327 | txtMonitorRight.text += "\n\n"; 328 | 329 | var i = 0; 330 | while (output.length > 0) { 331 | 332 | if (i % 2 == 0) { 333 | txtMonitorLeft.text += output.shift(); 334 | } else { 335 | txtMonitorRight.text += output.shift(); 336 | } 337 | i++; 338 | } 339 | } 340 | 341 | public function showMonitor() { 342 | monitorDisplay.visible = true; 343 | txtMonitorLeft.visible = true; 344 | txtMonitorRight.visible = true; 345 | } 346 | 347 | public function hideMonitor() { 348 | monitorDisplay.visible = false; 349 | txtMonitorLeft.visible = false; 350 | txtMonitorRight.visible = false; 351 | } 352 | 353 | function createProfilerDisplay() { 354 | var color = Color.fromValue(packColorBytes(DCThemes.current.MON_C, DCThemes.current.MON_A)); 355 | var bytes = createImageBytes(color, System.windowWidth(), System.windowHeight()); 356 | var image = Image.fromBytes(bytes, System.windowWidth(), System.windowHeight()); 357 | 358 | profilerDisplay = new Sprite(image); 359 | profilerDisplay.setPosition(new Vector2(0, 0)); 360 | 361 | txtProfiler = new KhaText( 362 | packColorBytes(DCThemes.current.MON_TXT_C, DCThemes.current.MON_TXT_A), 363 | font, fontSize, profilerDisplay.x, profilerDisplay.y + 3, 364 | Math.ceil(System.windowHeight() / fontSize)); 365 | } 366 | 367 | 368 | public function writeProfilerOutput(output:String) { 369 | txtProfiler.text = "DC Profiler\n\n"; 370 | txtProfiler.text += output; 371 | } 372 | 373 | public function showProfiler() { 374 | profilerDisplay.visible = true; 375 | txtProfiler.visible = true; 376 | } 377 | 378 | public function hideProfiler() { 379 | profilerDisplay.visible = false; 380 | txtProfiler.visible = false; 381 | } 382 | 383 | public function render(fb: Framebuffer): Void { 384 | fb.g2.begin(false); 385 | var oldColor = fb.g2.color; 386 | if (consoleDisplay.visible == true) {consoleDisplay.render(fb.g2);} 387 | if (txtConsole.visible == true) {txtConsole.render(fb);} 388 | if (promptDisplay.visible == true) {promptDisplay.render(fb.g2);} 389 | if (txtPrompt.visible == true) {txtPrompt.render(fb);} 390 | 391 | if (monitorDisplay.visible == true) {monitorDisplay.render(fb.g2);} 392 | if (txtMonitorLeft.visible == true) {txtMonitorLeft.render(fb);} 393 | if (txtMonitorRight.visible == true) {txtMonitorRight.render(fb);} 394 | 395 | if (profilerDisplay.visible == true) {profilerDisplay.render(fb.g2);} 396 | if (txtProfiler.visible == true) {txtProfiler.render(fb);} 397 | //TODO: this prevents setting incorrect alpha for subsequent render calls 398 | fb.g2.color = oldColor; 399 | fb.g2.end(); 400 | } 401 | 402 | //--------------------------------------------------------------------------------- 403 | // PUBLIC METHODS 404 | //--------------------------------------------------------------------------------- 405 | public function log(data:Dynamic, color:Int) : Void { 406 | var str:String = txtConsole.text + Std.string(data) + '\n'; 407 | 408 | if (str.length > 2000) { 409 | str = str.substr(str.length - 2000); 410 | } 411 | 412 | txtConsole.text = str; 413 | txtConsole.scrollToBottom(); 414 | } 415 | 416 | public function moveCarretToEnd() { 417 | txtPrompt.moveCarretToEnd(); 418 | } 419 | 420 | public function scrollConsoleUp() : Void { 421 | txtConsole.scrollUp(); 422 | } 423 | 424 | public function scrollConsoleDown() : Void { 425 | txtConsole.scrollDown(); 426 | } 427 | 428 | /** 429 | * Brings this display object to the front of display list. 430 | */ 431 | public function toFront() : Void {} 432 | 433 | public function setConsoleFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, italic:Bool = false, underline:Bool = false ) : Void { 434 | //TODO: 435 | } 436 | 437 | public function setPromptFont(font:String = null, embed:Bool = false, size:Int = 16, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) : Void { 438 | //TODO: 439 | } 440 | 441 | public function setProfilerFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) : Void { 442 | // TODO: 443 | } 444 | 445 | public function setMonitorFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) : Void { 446 | //TODO: 447 | } 448 | 449 | /** 450 | * Removes last input char 451 | */ 452 | public function inputRemoveLastChar() { 453 | if (txtPrompt.text.length > 0) { 454 | txtPrompt.text = txtPrompt.text.substr(0, txtPrompt.text.length - 1); 455 | txtPrompt.moveCarretToEnd(); 456 | } 457 | } 458 | 459 | 460 | public function getInputTxt():String { 461 | return txtPrompt.text; 462 | } 463 | 464 | 465 | public function setInputTxt(string:String) { 466 | txtPrompt.text = string; 467 | txtPrompt.moveCarretToEnd(); 468 | } 469 | 470 | public function getConsoleText() : String { 471 | return txtConsole.text; 472 | } 473 | 474 | public function getMonitorText() { 475 | return { 476 | col1:txtMonitorLeft.text, 477 | col2:txtMonitorRight.text, 478 | } 479 | } 480 | 481 | public function clearInput() { 482 | txtPrompt.text = ""; 483 | txtPrompt.moveCarretToEnd(); 484 | } 485 | 486 | public function clearConsole() : Void { 487 | txtConsole.text = ""; 488 | } 489 | } 490 | #end 491 | -------------------------------------------------------------------------------- /src/pgr/dconsole/ui/DCLuxeInterface.hx: -------------------------------------------------------------------------------- 1 | #if luxe 2 | package pgr.dconsole.ui; 3 | import pgr.dconsole.DConsole; 4 | import luxe.Input.Key; 5 | import luxe.Input.KeyEvent; 6 | import luxe.Input.TextEvent; 7 | import luxe.options.TextOptions; 8 | import luxe.Rectangle; 9 | import luxe.Sprite; 10 | import luxe.Text; 11 | import luxe.utils.Maths; 12 | import luxe.Vector; 13 | import phoenix.Batcher; 14 | import phoenix.Camera; 15 | import phoenix.Color; 16 | import phoenix.geometry.LineGeometry; 17 | 18 | /** 19 | * 20 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 21 | */ 22 | class LuxePromptText extends Text { 23 | public var cursor:LineGeometry; 24 | var index(default, set):Int = 0; 25 | var console:DConsole; 26 | 27 | public function new(options:TextOptions, console:DConsole) { 28 | super(options); 29 | this.console = console; 30 | Luxe.timer.schedule(.5, blinkCursor, true); 31 | } 32 | 33 | override public function ontextinput(event:TextEvent) { 34 | if (this.visible == false) { 35 | return; 36 | } 37 | 38 | this.text = text.substr(0, index) + event.text + text.substr(index, text.length); 39 | index++; 40 | console.resetHistoryIndex(); 41 | } 42 | 43 | override function onkeydown( event:KeyEvent ) { 44 | if (this.visible == false) { 45 | return; 46 | } 47 | 48 | switch(event.keycode) { 49 | case Key.backspace: 50 | index--; 51 | text = text.substr(0, index) + text.substr(index + 1, text.length); 52 | case Key.delete: 53 | text = text.substr(0, index) + text.substr(index + 1, text.length); 54 | case Key.left: 55 | index--; 56 | case Key.right: 57 | index++; 58 | } 59 | } 60 | 61 | public function moveCarretToEnd() { 62 | index = text.length; 63 | } 64 | 65 | inline function set_index(i:Int) { 66 | i = Maths.clampi(i, 0, text.length); 67 | 68 | index = i; 69 | 70 | // update cursor position 71 | var width = font.width_of(text.substr(0, index), point_size, letter_spacing); 72 | if (width == 0) { 73 | width = 1; // fix cursor not visible 74 | } 75 | cursor.p0 = new Vector(width, cursor.p0.y); 76 | cursor.p1 = new Vector(width, cursor.p1.y); 77 | 78 | return index; 79 | } 80 | 81 | function blinkCursor() { 82 | if (!this.visible) { 83 | return; 84 | } 85 | cursor.visible = !cursor.visible; 86 | } 87 | 88 | } 89 | 90 | class DCLuxeInterface implements DCInterface 91 | { 92 | public var batcher:Batcher; 93 | public var camera:Camera; 94 | 95 | var monitorDisplay:Sprite; 96 | var txtMonitorLeft:Text; 97 | var txtMonitorRight:Text; 98 | 99 | var profilerDisplay:Sprite; 100 | var txtProfiler:Text; 101 | 102 | var consoleDisplay:Sprite; 103 | var promptDisplay:Sprite; 104 | var txtConsole:Text; 105 | var txtPrompt:LuxePromptText; 106 | var promptCursor:LineGeometry; 107 | 108 | public var console:DConsole; 109 | 110 | var heightPt:Float; 111 | var align:String; 112 | 113 | public function new(heightPt:Float, align:String) { 114 | if (heightPt <= 0 || heightPt > 100) heightPt = 100; // clamp to >0..1 115 | if (heightPt > 1) heightPt = Std.int(heightPt) / 100; // turn >0..100 into percentage from 1..0 116 | 117 | this.heightPt = heightPt; 118 | this.align = align; 119 | } 120 | 121 | public function init() { 122 | batcher = new Batcher(Luxe.renderer, 'dc_batcher'); 123 | camera = new Camera(); 124 | batcher.view = camera; 125 | batcher.layer = 100; 126 | Luxe.renderer.add_batch(batcher); 127 | 128 | createMonitorDisplay(); 129 | createProfilerDisplay(); 130 | createConsoleDisplay(); 131 | 132 | //onResize(); 133 | //Lib.current.stage.addEventListener(Event.RESIZE, onResize); 134 | 135 | } 136 | 137 | function onResize() { 138 | // TODO on resize 139 | 140 | /*if (Std.is(this.parent, Stage)) { 141 | var stg:Stage = cast this.parent; 142 | maxWidth = stg.stageWidth; 143 | maxHeight = stg.stageHeight; 144 | } else { 145 | maxWidth = this.parent.width; 146 | maxHeight = this.parent.height; 147 | } 148 | 149 | drawConsole(); // redraws console. 150 | drawMonitor(); 151 | drawProfiler();*/ 152 | } 153 | 154 | 155 | function createConsoleDisplay() { 156 | var PROMPT_HEIGHT = 20; 157 | 158 | consoleDisplay = new Sprite( { 159 | color:new Color().rgb(DCThemes.current.CON_C), 160 | size: new Vector(Luxe.screen.width, Luxe.screen.height * heightPt - PROMPT_HEIGHT), 161 | centered:false, 162 | pos: new Vector(0, Luxe.screen.height - Luxe.screen.height * heightPt), 163 | batcher:batcher, 164 | }); 165 | consoleDisplay.color.a = DCThemes.current.CON_A; 166 | 167 | promptDisplay = new Sprite( { 168 | color:new Color().rgb(DCThemes.current.PRM_C), 169 | size: new Vector(Luxe.screen.width, PROMPT_HEIGHT), 170 | centered:false, 171 | depth:1, // fixes bug where prompt display is not visible 172 | pos: new Vector(0, Luxe.screen.height - PROMPT_HEIGHT), 173 | batcher:batcher, 174 | }); 175 | 176 | txtConsole = new Text( { 177 | parent:consoleDisplay, 178 | color: new Color().rgb(DCThemes.current.PRM_TXT_C), 179 | bounds: new Rectangle(0, 0, consoleDisplay.size.x, 0), 180 | bounds_wrap: true, 181 | point_size: 14, 182 | batcher:batcher, 183 | }); 184 | txtConsole.color.a = DCThemes.current.CON_TXT_A; 185 | 186 | txtConsole.geometry.clip_rect = new Rectangle( 187 | consoleDisplay.pos.x, 188 | consoleDisplay.pos.y, 189 | consoleDisplay.size.x, 190 | consoleDisplay.size.y 191 | ); 192 | 193 | txtPrompt = new LuxePromptText( { 194 | parent:promptDisplay, 195 | color: new Color().rgb(DCThemes.current.PRM_TXT_C), 196 | bounds: new Rectangle(0, 0, Luxe.screen.width, promptDisplay.size.y), 197 | point_size: 15, 198 | depth:1.1, 199 | align_vertical: TextAlign.center, 200 | batcher:batcher, 201 | }, console); 202 | 203 | promptCursor = Luxe.draw.line( { 204 | color: new Color().rgb(DCThemes.current.PRM_TXT_C), 205 | depth: 10, 206 | batcher: batcher, 207 | p0: new Vector(1, Luxe.screen.height - PROMPT_HEIGHT + 3), 208 | p1: new Vector(1, Luxe.screen.height - PROMPT_HEIGHT + 3 + 15) 209 | }); 210 | txtPrompt.cursor = promptCursor; 211 | 212 | // TODO enable mouse wheel over console 213 | } 214 | 215 | public function showConsole() { 216 | consoleDisplay.visible = true; 217 | txtConsole.visible = true; 218 | promptDisplay.visible = true; 219 | txtPrompt.visible = true; 220 | promptCursor.visible = true; 221 | } 222 | 223 | public function hideConsole() { 224 | consoleDisplay.visible = false; 225 | txtConsole.visible = false; 226 | promptDisplay.visible = false; 227 | txtPrompt.visible = false; 228 | promptCursor.visible = false; 229 | } 230 | 231 | //--------------------------------------------------------------------------------- 232 | // MONITOR 233 | //--------------------------------------------------------------------------------- 234 | function createMonitorDisplay() { 235 | 236 | monitorDisplay = new Sprite( { 237 | size: new Vector(Luxe.screen.width, Luxe.screen.height), 238 | color: new Color().rgb(DCThemes.current.MON_C), 239 | centered:false, 240 | batcher:batcher, 241 | }); 242 | monitorDisplay.color.a = DCThemes.current.MON_A; 243 | 244 | txtMonitorLeft = new Text( { 245 | color: new Color().rgb(DCThemes.current.MON_TXT_C), 246 | bounds: new Rectangle(0, 0, Luxe.screen.width / 2, Luxe.screen.height), 247 | point_size:14, 248 | bounds_wrap:true, 249 | batcher:batcher, 250 | }); 251 | 252 | txtMonitorRight = new Text( { 253 | color: new Color().rgb(DCThemes.current.MON_TXT_C), 254 | bounds: new Rectangle(0, 0, Luxe.screen.width / 2, Luxe.screen.height), 255 | point_size:14, 256 | bounds_wrap:true, 257 | pos: new Vector(Luxe.screen.width / 2, 0), 258 | batcher:batcher, 259 | }); 260 | 261 | txtMonitorLeft.color.a = DCThemes.current.MON_TXT_A; 262 | txtMonitorRight.color.a = DCThemes.current.MON_TXT_A; 263 | 264 | } 265 | 266 | // Splits output into left and right monitor text fields 267 | public function writeMonitorOutput(output:Array) { 268 | txtMonitorLeft.text = ""; 269 | txtMonitorRight.text = ""; 270 | 271 | txtMonitorLeft.text += "DC Monitor\n\n"; 272 | txtMonitorRight.text += "\n\n"; 273 | 274 | var i = 0; 275 | while (output.length > 0) { 276 | 277 | if (i % 2 == 0) { 278 | txtMonitorLeft.text += output.shift(); 279 | } else { 280 | txtMonitorRight.text += output.shift(); 281 | } 282 | i++; 283 | } 284 | } 285 | 286 | public function showMonitor() { 287 | monitorDisplay.visible = true; 288 | txtMonitorLeft.visible = true; 289 | txtMonitorRight.visible = true; 290 | } 291 | 292 | public function hideMonitor() { 293 | monitorDisplay.visible = false; 294 | txtMonitorLeft.visible = false; 295 | txtMonitorRight.visible = false; 296 | } 297 | 298 | //--------------------------------------------------------------------------------- 299 | // PROFILER 300 | //--------------------------------------------------------------------------------- 301 | function createProfilerDisplay() { 302 | profilerDisplay = new Sprite( { 303 | size: new Vector(Luxe.screen.width, Luxe.screen.height), 304 | color: new Color().rgb(DCThemes.current.MON_C), 305 | centered: false, 306 | batcher:batcher, 307 | }); 308 | profilerDisplay.color.a = DCThemes.current.MON_A; 309 | 310 | txtProfiler = new Text( { 311 | color: new Color().rgb(DCThemes.current.MON_TXT_C), 312 | bounds: new Rectangle(0, 0, Luxe.screen.width, Luxe.screen.height), 313 | point_size:14, 314 | bounds_wrap:true, 315 | batcher:batcher, 316 | }); 317 | txtProfiler.color.a = DCThemes.current.MON_TXT_A; 318 | } 319 | 320 | public function writeProfilerOutput(output:String) { 321 | txtProfiler.text = "DC Profiler\n\n"; 322 | txtProfiler.text += output; 323 | } 324 | 325 | public function showProfiler() { 326 | profilerDisplay.visible = true; 327 | txtProfiler.visible = true; 328 | } 329 | 330 | public function hideProfiler() { 331 | profilerDisplay.visible = false; 332 | txtProfiler.visible = false; 333 | } 334 | 335 | //--------------------------------------------------------------------------------- 336 | // PUBLIC METHODS 337 | //--------------------------------------------------------------------------------- 338 | public function log(data:Dynamic, color:Int) { 339 | var str:String = txtConsole.text + Std.string(data) + '\n'; 340 | 341 | // FIX - Luxe (version 1.0.0 alpha 1) creates a polygon and 6 vertexes for each text character. 342 | // Not only a single geometry is limited to 64k vertexes, long textfields cause big performance lost. 343 | // For now the console characters are limited to 2000. 344 | if (str.length > 2000) { 345 | str = str.substr(str.length - 2000); 346 | } 347 | 348 | txtConsole.text = str; 349 | scrollToBottom(); 350 | } 351 | 352 | public function moveCarretToEnd() { 353 | txtPrompt.moveCarretToEnd(); 354 | } 355 | 356 | public function scrollConsoleUp() { 357 | txtConsole.pos.y += (consoleDisplay.size.y - txtConsole.geom.point_size); 358 | if (txtConsole.pos.y > 0) 359 | txtConsole.pos.y = 0; 360 | } 361 | 362 | public function scrollConsoleDown() { 363 | txtConsole.pos.y -= (consoleDisplay.size.y - txtConsole.geom.point_size); 364 | 365 | var diff = txtConsole.geom.text_height - consoleDisplay.size.y - txtConsole.geom.point_size; 366 | if (diff <= 0) { 367 | txtConsole.pos.y = 0; 368 | } 369 | 370 | if (txtConsole.pos.y < -diff) { 371 | txtConsole.pos.y = -diff; 372 | } 373 | } 374 | 375 | function scrollToBottom() { 376 | var diff = txtConsole.geom.text_height - consoleDisplay.size.y - txtConsole.geom.point_size; 377 | if (diff > 0) { 378 | txtConsole.pos.y = -diff; 379 | } else { 380 | txtConsole.pos.y = 0; 381 | } 382 | } 383 | 384 | /** 385 | * Brings this display object to the front of display list. 386 | */ 387 | public function toFront() {} 388 | 389 | public function setConsoleFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, italic:Bool = false, underline:Bool = false ) { 390 | // TODO 391 | } 392 | 393 | public function setPromptFont(font:String = null, embed:Bool = false, size:Int = 16, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) { 394 | // TODO 395 | } 396 | 397 | public function setProfilerFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ) { 398 | // TODO 399 | } 400 | 401 | public function setMonitorFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ){ 402 | // TODO 403 | } 404 | 405 | /** 406 | * Removes last input char 407 | */ 408 | public function inputRemoveLastChar() { 409 | if (txtPrompt.text.length > 0) { 410 | txtPrompt.text = txtPrompt.text.substr(0, txtPrompt.text.length - 1); 411 | txtPrompt.moveCarretToEnd(); 412 | } 413 | } 414 | 415 | 416 | public function getInputTxt():String { 417 | return txtPrompt.text; 418 | } 419 | 420 | 421 | public function setInputTxt(string:String) { 422 | txtPrompt.text = string; 423 | txtPrompt.moveCarretToEnd(); 424 | } 425 | 426 | 427 | public function getConsoleText():String { 428 | return txtConsole.text; 429 | } 430 | 431 | public function getMonitorText() { 432 | return { 433 | col1:txtMonitorLeft.text, 434 | col2:txtMonitorRight.text, 435 | } 436 | } 437 | 438 | public function clearInput() { 439 | txtPrompt.text = ""; 440 | txtPrompt.moveCarretToEnd(); 441 | } 442 | 443 | 444 | public function clearConsole() { 445 | txtConsole.text = ""; 446 | } 447 | 448 | 449 | } 450 | #end 451 | -------------------------------------------------------------------------------- /src/pgr/dconsole/ui/DCOpenflInterface.hx: -------------------------------------------------------------------------------- 1 | #if openfl 2 | package pgr.dconsole.ui; 3 | 4 | import flash.display.MovieClip; 5 | import flash.display.Sprite; 6 | import flash.Lib; 7 | import flash.text.TextField; 8 | import flash.text.TextFieldAutoSize; 9 | import flash.text.TextFieldType; 10 | import flash.text.TextFormat; 11 | import flash.text.TextFormatAlign; 12 | import openfl.display.Stage; 13 | import openfl.events.Event; 14 | import pgr.dconsole.DCThemes.Theme; 15 | 16 | /** 17 | * 18 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 19 | */ 20 | 21 | class DCOpenflInterface extends Sprite implements DCInterface 22 | { 23 | public var console:DConsole; 24 | 25 | var _promptFontYOffset:Int; 26 | var yAlign:String; 27 | var heightPt:Float; // percentage height 28 | var widthPt:Float; // percentage width 29 | var maxWidth:Float; // width in pixels 30 | var maxHeight:Float; // height in pixels 31 | var margin:Int = 0; 32 | 33 | var monitorDisplay:Sprite; 34 | var txtMonitorLeft:TextField; 35 | var txtMonitorRight:TextField; 36 | 37 | var profilerDisplay:Sprite; 38 | var txtProfiler:TextField; 39 | 40 | var consoleDisplay:Sprite; 41 | var promptDisplay:Sprite; 42 | var txtConsole:TextField; 43 | var txtPrompt:TextField; 44 | 45 | public function new(heightPt:Float, align:String) { 46 | super(); 47 | Lib.current.stage.addChild(this); // by default the interface adds itself to the stage. 48 | 49 | if (heightPt <= 0 || heightPt > 100) heightPt = 100; // clamp to >0..1 50 | if (heightPt > 1) heightPt = Std.int(heightPt) / 100; // turn >0..100 into percentage from 1..0 51 | 52 | this.heightPt = heightPt; 53 | yAlign = align; 54 | } 55 | 56 | public function init() { 57 | createMonitorDisplay(); 58 | createProfilerDisplay(); 59 | createConsoleDisplay(); 60 | 61 | setConsoleFont(); 62 | setMonitorFont(); 63 | setProfilerFont(); 64 | setPromptFont(); 65 | 66 | onResize(); 67 | Lib.current.stage.addEventListener(Event.RESIZE, onResize); 68 | } 69 | 70 | function onResize(e:Event = null) { 71 | 72 | //if (Std.is(this.parent, openfl.display.Stage)) { 73 | //var stg:Stage = cast this.parent; 74 | maxWidth = this.stage.stageWidth; 75 | maxHeight = this.stage.stageHeight; 76 | //} else { 77 | //maxWidth = this.parent.width; 78 | //maxHeight = this.parent.height; 79 | //} 80 | 81 | drawConsole(); // redraws console. 82 | drawMonitor(); 83 | drawProfiler(); 84 | } 85 | 86 | 87 | function createConsoleDisplay() { 88 | consoleDisplay = new Sprite(); 89 | consoleDisplay.alpha = DCThemes.current.CON_TXT_A; 90 | consoleDisplay.mouseEnabled = false; 91 | addChild(consoleDisplay); 92 | 93 | promptDisplay = new Sprite(); 94 | addChild(promptDisplay); 95 | 96 | txtPrompt = new TextField(); 97 | txtPrompt.type = TextFieldType.INPUT; 98 | txtPrompt.selectable = true; 99 | txtPrompt.multiline = false; 100 | promptDisplay.addChild(txtPrompt); 101 | 102 | txtConsole = new TextField(); 103 | txtConsole.selectable = false; 104 | txtConsole.mouseEnabled = false; 105 | txtConsole.multiline = true; 106 | txtConsole.wordWrap = true; 107 | txtConsole.alpha = DCThemes.current.CON_TXT_A; 108 | consoleDisplay.addChild(txtConsole); 109 | #if flash 110 | txtConsole.mouseWheelEnabled = true; 111 | #end 112 | 113 | } 114 | 115 | /** 116 | * Draws console fields after changes to console appearence 117 | */ 118 | function drawConsole() { 119 | 120 | var _yOffset = (yAlign == DC.ALIGN_DOWN) ? maxHeight - maxHeight * heightPt: 0; 121 | 122 | // draw console background. 123 | consoleDisplay.graphics.clear(); 124 | consoleDisplay.graphics.beginFill(DCThemes.current.CON_C, 1); 125 | consoleDisplay.graphics.drawRect(0, 0, maxWidth, maxHeight * heightPt); 126 | consoleDisplay.graphics.endFill(); 127 | consoleDisplay.y = _yOffset; 128 | consoleDisplay.alpha = DCThemes.current.CON_A; 129 | 130 | // draw text input field. 131 | promptDisplay.graphics.clear(); 132 | promptDisplay.graphics.beginFill(DCThemes.current.PRM_C); 133 | promptDisplay.graphics.drawRect(0, 0, maxWidth, txtPrompt.textHeight); 134 | promptDisplay.graphics.endFill(); 135 | promptDisplay.y = consoleDisplay.y + maxHeight * heightPt - txtPrompt.textHeight; 136 | 137 | // Resize textfields 138 | txtConsole.width = maxWidth; 139 | txtConsole.x = 0; 140 | txtPrompt.x = 0; 141 | txtConsole.height = maxHeight * heightPt - txtPrompt.textHeight + 2; 142 | 143 | txtPrompt.y = - 2; // -2 just looks better. 144 | txtPrompt.width = maxWidth; 145 | txtPrompt.height = 32; 146 | 147 | #if (cpp || neko) // BUGFIX 148 | // fix margins bug. 149 | txtConsole.x += 10; 150 | txtConsole.width -= 10; 151 | txtPrompt.x += 10; 152 | txtPrompt.width -= 10; 153 | // fix bad starting font bug. 154 | txtPrompt.text = ''; 155 | #end 156 | } 157 | 158 | public function showConsole() { 159 | consoleDisplay.visible = true; 160 | promptDisplay.visible = true; 161 | toFront(); 162 | Lib.current.stage.focus = txtPrompt; 163 | } 164 | 165 | public function hideConsole() { 166 | consoleDisplay.visible = false; 167 | promptDisplay.visible = false; 168 | Lib.current.stage.focus = null; 169 | } 170 | 171 | //--------------------------------------------------------------------------------- 172 | // MONITOR 173 | //--------------------------------------------------------------------------------- 174 | function createMonitorDisplay() { 175 | 176 | monitorDisplay = new Sprite(); 177 | monitorDisplay.mouseEnabled = false; 178 | monitorDisplay.mouseChildren = false; 179 | addChild(monitorDisplay); 180 | 181 | txtMonitorLeft = new TextField(); 182 | txtMonitorLeft.selectable = false; 183 | txtMonitorLeft.multiline = true; 184 | txtMonitorLeft.wordWrap = true; 185 | monitorDisplay.addChild(txtMonitorLeft); 186 | 187 | txtMonitorRight = new TextField(); 188 | txtMonitorRight.selectable = false; 189 | txtMonitorRight.multiline = true; 190 | txtMonitorRight.wordWrap = true; 191 | monitorDisplay.addChild(txtMonitorRight); 192 | monitorDisplay.visible = false; 193 | } 194 | 195 | function drawMonitor() { 196 | 197 | // draws background 198 | monitorDisplay.graphics.clear(); 199 | monitorDisplay.graphics.beginFill(DCThemes.current.MON_C, DCThemes.current.MON_A); 200 | monitorDisplay.graphics.drawRect(0, 0, maxWidth, maxHeight); 201 | monitorDisplay.graphics.endFill(); 202 | // draws decoration line 203 | var s = txtMonitorLeft.text; 204 | txtMonitorLeft.text = " "; 205 | var h = txtMonitorLeft.textHeight; 206 | monitorDisplay.alpha = DCThemes.current.MON_TXT_A; 207 | monitorDisplay.graphics.lineStyle(1, DCThemes.current.MON_TXT_C); 208 | monitorDisplay.graphics.moveTo(0, h); 209 | monitorDisplay.graphics.lineTo(maxWidth, h); 210 | txtMonitorLeft.text = s; 211 | // position and scales left text 212 | txtMonitorLeft.x = 0; 213 | txtMonitorLeft.width = maxWidth / 2; 214 | txtMonitorLeft.height = maxHeight; 215 | // position and scale right text 216 | txtMonitorRight.x = maxWidth / 2; 217 | txtMonitorRight.width = maxWidth / 2; 218 | txtMonitorRight.height = maxHeight; 219 | } 220 | 221 | // Splits output into left and right monitor text fields 222 | public function writeMonitorOutput(output:Array) { 223 | txtMonitorLeft.text = ""; 224 | txtMonitorRight.text = ""; 225 | 226 | txtMonitorLeft.text += "DC Monitor\n\n"; 227 | txtMonitorRight.text += "\n\n"; 228 | 229 | var i = 0; 230 | while (output.length > 0) { 231 | 232 | if (i % 2 == 0) { 233 | txtMonitorLeft.text += output.shift(); 234 | } else { 235 | txtMonitorRight.text += output.shift(); 236 | } 237 | i++; 238 | } 239 | } 240 | 241 | 242 | public function showMonitor() { 243 | monitorDisplay.visible = true; 244 | } 245 | public function hideMonitor() { 246 | monitorDisplay.visible = false; 247 | } 248 | 249 | //--------------------------------------------------------------------------------- 250 | // PROFILER 251 | //--------------------------------------------------------------------------------- 252 | function createProfilerDisplay() { 253 | 254 | profilerDisplay = new Sprite(); 255 | profilerDisplay.mouseEnabled = false; 256 | profilerDisplay.mouseChildren = false; 257 | addChild(profilerDisplay); 258 | 259 | txtProfiler = new TextField(); 260 | txtProfiler.selectable = false; 261 | txtProfiler.multiline = true; 262 | txtProfiler.wordWrap = true; 263 | profilerDisplay.addChild(txtProfiler); 264 | profilerDisplay.visible = false; 265 | } 266 | 267 | function drawProfiler() { 268 | 269 | // draw background 270 | profilerDisplay.graphics.clear(); 271 | profilerDisplay.graphics.beginFill(DCThemes.current.MON_C, DCThemes.current.MON_A); 272 | profilerDisplay.graphics.drawRect(0, 0, maxWidth, maxHeight); 273 | profilerDisplay.graphics.endFill(); 274 | // draw decoration line 275 | var s = txtProfiler.text; 276 | txtProfiler.text = " "; 277 | var h = txtProfiler.textHeight; 278 | profilerDisplay.graphics.lineStyle(1, DCThemes.current.MON_TXT_C); 279 | profilerDisplay.graphics.moveTo(0, h); 280 | profilerDisplay.graphics.lineTo(maxWidth, h); 281 | txtProfiler.text = s; 282 | // position and scale monitor text 283 | txtProfiler.alpha = DCThemes.current.MON_TXT_A; 284 | txtProfiler.width = maxWidth; 285 | txtProfiler.height = maxHeight; 286 | } 287 | 288 | public function writeProfilerOutput(output:String) { 289 | txtProfiler.text = "DC Profiler\n\n"; 290 | txtProfiler.text += output; 291 | } 292 | 293 | public function showProfiler() { 294 | profilerDisplay.visible = true; 295 | } 296 | 297 | public function hideProfiler() { 298 | profilerDisplay.visible = false; 299 | } 300 | 301 | //--------------------------------------------------------------------------------- 302 | // PUBLIC METHODS 303 | //--------------------------------------------------------------------------------- 304 | public function log(data:Dynamic, color:Int) { 305 | // Adds text to console interface 306 | var tf = txtConsole; 307 | tf.appendText(Std.string(data) + '\n'); 308 | tf.scrollV = tf.maxScrollV; 309 | 310 | // Applies color - is always applied to avoid bug. 311 | if (color == -1) { 312 | color = DCThemes.current.CON_TXT_C; 313 | } 314 | 315 | // Applies text formatting 316 | var format:TextFormat = new TextFormat(); 317 | format.color = color; 318 | var l = Std.string(data).length; 319 | tf.setTextFormat(format, tf.text.length - l - 1, tf.text.length - 1); 320 | scrollToBottom(); 321 | } 322 | 323 | public function moveCarretToEnd() { 324 | #if !(cpp || neko) 325 | txtPrompt.setSelection(txtPrompt.length, txtPrompt.length); 326 | #end 327 | } 328 | 329 | public function scrollConsoleUp() { 330 | txtConsole.scrollV -= txtConsole.bottomScrollV - txtConsole.scrollV +1; 331 | if (txtConsole.scrollV < 0) 332 | txtConsole.scrollV = 0; 333 | } 334 | 335 | public function scrollConsoleDown() { 336 | txtConsole.scrollV += txtConsole.bottomScrollV - txtConsole.scrollV +1; 337 | if (txtConsole.scrollV > txtConsole.maxScrollV) 338 | txtConsole.scrollV = txtConsole.maxScrollV; 339 | } 340 | 341 | function scrollToBottom() { 342 | txtConsole.scrollV = txtConsole.maxScrollV; 343 | } 344 | 345 | /** 346 | * Brings this display object to the front of display list. 347 | */ 348 | public function toFront() { 349 | parent.setChildIndex(this, parent.numChildren - 1); 350 | } 351 | 352 | public function setConsoleFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, italic:Bool = false, underline:Bool = false ){ 353 | #if (flash || html5) 354 | if (font == null) { 355 | #else 356 | if (font == null && Sys.systemName() == "Windows") { 357 | #end 358 | font = "Consolas"; 359 | } 360 | embed ? txtConsole.embedFonts = true : txtConsole.embedFonts = false; 361 | txtConsole.defaultTextFormat = new TextFormat(font, size, DCThemes.current.CON_TXT_C, bold, italic, underline, '', '', TextFormatAlign.LEFT, margin, margin); 362 | // TODO - redraw console here? 363 | } 364 | 365 | 366 | public function setPromptFont(font:String = null, embed:Bool = false, size:Int = 16, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ){ 367 | #if (flash || html5) 368 | if (font == null) { 369 | #else 370 | if (font == null && Sys.systemName() == "Windows") { 371 | #end 372 | font = "Consolas"; 373 | } 374 | embed ? txtPrompt.embedFonts = true : txtPrompt.embedFonts = false; 375 | txtPrompt.defaultTextFormat = new TextFormat(font, size, DCThemes.current.PRM_TXT_C, bold, italic, underline, '', '' , TextFormatAlign.LEFT); 376 | // TODO - redraw console here? 377 | } 378 | 379 | public function setProfilerFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ){ 380 | #if (flash || html5) 381 | if (font == null) { 382 | #else 383 | if (font == null && Sys.systemName() == "Windows") { 384 | #end 385 | font = "Consolas"; 386 | } 387 | 388 | embed ? txtProfiler.embedFonts = true : txtProfiler.embedFonts = false; 389 | txtProfiler.defaultTextFormat = new TextFormat(font, size, DCThemes.current.MON_TXT_C, bold, italic, underline, '', '', TextFormatAlign.LEFT, 10,10); 390 | } 391 | 392 | public function setMonitorFont(font:String = null, embed:Bool = false, size:Int = 14, bold:Bool = false, ?italic:Bool = false, underline:Bool = false ){ 393 | #if (flash || html5) 394 | if (font == null) { 395 | #else 396 | if (font == null && Sys.systemName() == "Windows") { 397 | #end 398 | font = "Consolas"; 399 | } 400 | 401 | embed ? txtMonitorLeft.embedFonts = true : txtMonitorLeft.embedFonts = false; 402 | embed ? txtMonitorRight.embedFonts = true : txtMonitorRight.embedFonts = false; 403 | txtMonitorLeft.defaultTextFormat = new TextFormat(font, size, DCThemes.current.MON_TXT_C, bold, italic, underline, '', '', TextFormatAlign.LEFT, 10,10); 404 | txtMonitorRight.defaultTextFormat = new TextFormat(font, size, DCThemes.current.MON_TXT_C, bold, italic, underline, '', '', TextFormatAlign.LEFT, 10,10); 405 | } 406 | 407 | /** 408 | * Removes last input char 409 | */ 410 | public function inputRemoveLastChar() { 411 | if (txtPrompt.text.length > 0) { 412 | txtPrompt.text = txtPrompt.text.substr(0, txtPrompt.text.length - 1); 413 | } 414 | } 415 | 416 | 417 | public function getInputTxt():String { 418 | return txtPrompt.text; 419 | } 420 | 421 | 422 | public function setInputTxt(string:String) { 423 | txtPrompt.text = string; 424 | } 425 | 426 | 427 | public function getConsoleText():String { 428 | return txtConsole.text; 429 | } 430 | 431 | public function getMonitorText() { 432 | return { 433 | col1:txtMonitorLeft.text, 434 | col2:txtMonitorRight.text, 435 | } 436 | } 437 | 438 | 439 | public function clearInput() { 440 | txtPrompt.text = ""; 441 | } 442 | 443 | 444 | public function clearConsole() { 445 | txtConsole.text = ""; 446 | } 447 | 448 | 449 | } 450 | #end 451 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | .temp 3 | report.txt -------------------------------------------------------------------------------- /tests/CoverageReport.bat: -------------------------------------------------------------------------------- 1 | :: Creates coverage report and saves it to report.txt file. 2 | :: MCoverage lib must be installed 3 | @echo off 4 | CALL lime test neko -debug -DCOVERAGE > report.txt -------------------------------------------------------------------------------- /tests/RunTests.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call lime test flash -debug 3 | pause 4 | call lime test neko -debug -Dlegacy 5 | pause 6 | call lime test cpp -debug -Dlegacy 7 | pause 8 | call haxelib run flow run web 9 | pause 10 | call haxelib run flow run windows -------------------------------------------------------------------------------- /tests/TestCommands.hx: -------------------------------------------------------------------------------- 1 | package; 2 | #if openfl 3 | import flash.events.KeyboardEvent; 4 | import flash.ui.Keyboard; 5 | #end 6 | import haxe.unit.TestCase; 7 | import pgr.dconsole.DC; 8 | import pgr.dconsole.DConsole; 9 | import pgr.dconsole.ui.DCInterface; 10 | 11 | /** 12 | * Tests console runtime commands. 13 | * 14 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 15 | */ 16 | class TestCommands extends TestCase 17 | { 18 | var interfc:DCInterface; 19 | var console:DConsole; 20 | 21 | var i:Int; 22 | var f:Float; 23 | var s:String; 24 | var b:Bool; 25 | var setter(get, set):Int; 26 | var testObject:TestObject; 27 | 28 | private function get_setter():Int { 29 | return 12345; 30 | } 31 | 32 | private function set_setter(value:Int):Int { 33 | i = value; 34 | return i; 35 | } 36 | 37 | override public function setup() { 38 | if (console == null) { 39 | DC.init(); 40 | console = DC.instance; 41 | interfc = cast console.interfc; 42 | testObject = new TestObject(); 43 | } 44 | 45 | i = 0; 46 | f = 0; 47 | s = ""; 48 | b = false; 49 | testObject.b = false; 50 | testObject.i = 0; 51 | testObject.s = ""; 52 | 53 | DC.clearRegistry(); 54 | interfc.clearInput(); 55 | interfc.clearConsole(); 56 | DC.showConsole(); 57 | DC.enable(); 58 | } 59 | 60 | /** */ 61 | public function testSet() { 62 | DC.registerObject(this, "o1"); 63 | 64 | // set this object string 65 | consoleDo("o1.s = 'haha'"); 66 | assertTrue(s == "haha"); 67 | 68 | // set this object int 69 | consoleDo("o1.i = 32"); 70 | assertTrue(i == 32); 71 | 72 | // set this object bool 73 | b = false; 74 | consoleDo("o1.b = true"); 75 | assertTrue(b == true); 76 | 77 | // set this object float 78 | consoleDo("o1.f = 0.0001"); 79 | assertTrue(f == 0.0001); 80 | 81 | // set nested object int 82 | consoleDo("o1.testObject.i = 32"); 83 | assertTrue(testObject.i == 32); 84 | 85 | // set multiple values 86 | consoleDo("o1.i = -1 -2 -3"); 87 | assertTrue(i == -6); 88 | 89 | // set object setter 90 | consoleDo("o1.setter = 99"); 91 | assertTrue(i == 99); 92 | 93 | 94 | // special incorrect sets (see if program does not crash) 95 | consoleDo("_____________ ="); 96 | consoleDo("_____________ = ____________"); 97 | consoleDo("= null"); 98 | consoleDo("null = null"); 99 | consoleDo(". = . = . = . = . = . = ."); 100 | consoleDo(". . . . "); 101 | consoleDo("o1. = null"); 102 | consoleDo("o1.null = null"); 103 | consoleDo("o1 = "); 104 | consoleDo("o1.null"); 105 | consoleDo("o1.200000"); 106 | consoleDo("o1.______ = 1000"); 107 | consoleDo("o1.testObject. = 'string'"); 108 | consoleDo("o1.testObject.. = 'string'"); 109 | consoleDo("o1.testObject.null = 'string'"); 110 | consoleDo("o1.testObject.string = 'string'"); 111 | consoleDo("o1.i = 'string'"); 112 | consoleDo("o1.b = 'string'"); 113 | } 114 | 115 | public function testCall() { 116 | DC.registerFunction(F1, "F1"); 117 | DC.registerFunction(TestCommands.F2, "F2"); 118 | DC.registerFunction(testObject.F, "F3"); 119 | DC.registerFunction(F4, "F4"); 120 | DC.registerObject(this, "o1"); 121 | 122 | // call this object function 123 | consoleDo("F1()"); 124 | assertTrue(consoleHasText("F1 LOGGED")); 125 | consoleDo("o1.F1()"); 126 | assertTrue(consoleHasText("F1 LOGGED")); 127 | 128 | // call static function 129 | consoleDo("F2()"); 130 | assertTrue(consoleHasText("F2 LOGGED")); 131 | 132 | // call nested object function 133 | DC.clearConsole(); 134 | assertFalse(consoleHasText("OF LOGGED")); 135 | consoleDo("o1.testObject.F()"); 136 | assertTrue(consoleHasText("OF LOGGED")); 137 | 138 | // call function with arguments 139 | DC.clearConsole(); 140 | consoleDo("F4('test',0, true)"); 141 | assertTrue(consoleHasText("testF4 LOGGED")); 142 | assertTrue(consoleHasText("1")); 143 | assertTrue(consoleHasText("true")); 144 | 145 | // call nested object function with arguments 146 | DC.clearConsole(); 147 | consoleDo("o1.testObject.F2('test',0,true)"); 148 | assertTrue(consoleHasText("testOF4 LOGGED")); 149 | assertTrue(consoleHasText("2")); 150 | assertTrue(consoleHasText("true")); 151 | 152 | 153 | // special incorrect calls (see if program does not crash) 154 | consoleDo("F4(1.0,true,null)"); // (incorrect data types) 155 | consoleDo("F4(1.0,'str',1.3)"); // (incorrect data types) 156 | consoleDo("(_____________)"); 157 | consoleDo("(null)"); 158 | consoleDo("(.............)"); 159 | consoleDo("(.............)"); 160 | consoleDo("o1.()"); 161 | consoleDo("o1()"); 162 | consoleDo("o1.(null)"); 163 | consoleDo("o1.(nothing)"); 164 | consoleDo("F1(1000000)"); // too much arguments 165 | consoleDo("F4(1000)"); // few arguments 166 | consoleDo("o1.testObject.F(1234)"); // nested function, too much arguments 167 | consoleDo("o1.testObject.()"); 168 | consoleDo("o1.testObject.(.)"); 169 | consoleDo("o1.testObject.(null)"); 170 | consoleDo("o1.testObject.('string')"); 171 | } 172 | 173 | 174 | public function testPrint() { 175 | DC.registerObject(this, "o1"); 176 | 177 | // test print this object int 178 | this.i = 100; 179 | consoleDo("o1.i"); 180 | assertTrue(consoleHasText("100")); 181 | 182 | // test print getter 183 | consoleDo("o1.setter"); 184 | assertTrue(consoleHasText("12345")); 185 | 186 | // test print nested object value 187 | testObject.i = 11111; 188 | consoleDo("o1.testObject.i"); 189 | assertTrue(consoleHasText("11111")); 190 | 191 | // special incorrect calls (see if program does not crash) 192 | consoleDo("_____________"); 193 | consoleDo("null"); 194 | consoleDo("............."); 195 | consoleDo("............."); 196 | consoleDo("o1."); 197 | consoleDo("o1"); 198 | consoleDo("o1.null"); 199 | consoleDo("o1.nothing"); 200 | consoleDo("o1 1000000"); // too much arguments 201 | consoleDo("o1 1000"); // few arguments 202 | consoleDo("o1.testObject.i 12345"); // nested function, too much arguments 203 | consoleDo("o1.testObject."); 204 | consoleDo("o1.testObject.."); 205 | consoleDo("o1.testObject.null"); 206 | consoleDo("o1.testObject.string"); 207 | } 208 | 209 | // TODO - Test register class. 210 | 211 | //--------------------------------------------------------------------------------- 212 | // AUX 213 | //--------------------------------------------------------------------------------- 214 | function consoleDo(command:String) { 215 | interfc.clearConsole(); 216 | interfc.clearInput(); 217 | interfc.setInputTxt(command); 218 | #if openfl 219 | cast(interfc, pgr.dconsole.ui.DCOpenflInterface).stage.dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, Keyboard.ENTER)); 220 | #elseif luxe 221 | cast(console.input, pgr.dconsole.input.DCLuxeInput).inputListener.onkeyup( { 222 | scancode : 0, 223 | keycode : luxe.Input.Key.enter, 224 | state : null, 225 | mod : null, 226 | repeat : false, 227 | timestamp : 0, 228 | window_id : 0, 229 | }); 230 | #end 231 | } 232 | 233 | function consoleHasText(txt:String):Bool { 234 | return interfc.getConsoleText().lastIndexOf(txt) != -1; 235 | } 236 | 237 | function F1() { 238 | DC.log("F1 LOGGED"); 239 | } 240 | 241 | public static function F2() { 242 | DC.log("F2 LOGGED"); 243 | } 244 | 245 | function F4(s:String, i:Int, b:Bool) { 246 | DC.log(s + "F4 LOGGED"); 247 | DC.log(i + 1); 248 | DC.log(!b); 249 | } 250 | 251 | } 252 | 253 | 254 | private class TestObject { 255 | public var i:Int; 256 | public var s:String; 257 | public var b:Bool; 258 | 259 | public function new() {} 260 | 261 | public function F() { 262 | DC.log("OF LOGGED"); 263 | } 264 | 265 | public function F2(s:String, i:Int, b:Bool) { 266 | DC.log(s + "OF4 LOGGED"); 267 | DC.log(i + 2); 268 | DC.log(!b); 269 | } 270 | } -------------------------------------------------------------------------------- /tests/TestInput.hx: -------------------------------------------------------------------------------- 1 | package; 2 | #if openfl 3 | import flash.text.TextField; 4 | import flash.ui.Keyboard; 5 | #end 6 | import haxe.unit.TestCase; 7 | import pgr.dconsole.DC; 8 | import pgr.dconsole.DConsole; 9 | import pgr.dconsole.input.DCInput; 10 | import pgr.dconsole.ui.DCInterface; 11 | /** 12 | * Tests console reaction to keystrokes. 13 | * 14 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 15 | */ 16 | class TestInput extends TestCase 17 | { 18 | var input:DCInput; 19 | var interfc:DCInterface; 20 | var console:DConsole; 21 | var i:Int; 22 | var f:Float; 23 | var s:String; 24 | 25 | var key_up:Int; 26 | var key_down:Int; 27 | var key_enter:Int; 28 | var key_pageup:Int; 29 | var key_pagedown:Int; 30 | 31 | override public function setup() { 32 | if (console == null) { 33 | DC.init(); 34 | console = DC.instance; 35 | interfc = console.interfc; 36 | input = console.input; 37 | } 38 | 39 | #if openfl 40 | console.setConsoleKey(Keyboard.TAB); 41 | console.setMonitorKey(Keyboard.TAB, true); 42 | console.setProfilerKey(Keyboard.TAB, false, true); 43 | key_up = Keyboard.UP; 44 | key_down = Keyboard.DOWN; 45 | key_pageup = Keyboard.PAGE_UP; 46 | key_pagedown = Keyboard.PAGE_DOWN; 47 | key_enter = Keyboard.ENTER; 48 | #elseif luxe 49 | console.setConsoleKey(luxe.Input.Key.key_a); 50 | console.setMonitorKey(luxe.Input.Key.key_b); 51 | console.setProfilerKey(luxe.Input.Key.key_c); 52 | key_up = luxe.Input.Key.up; 53 | key_down = luxe.Input.Key.down; 54 | key_pageup = luxe.Input.Key.pageup; 55 | key_pagedown = luxe.Input.Key.pagedown; 56 | key_enter = luxe.Input.Key.enter; 57 | #end 58 | interfc.clearInput(); 59 | interfc.clearConsole(); 60 | console.enable(); 61 | console.showConsole(); 62 | } 63 | 64 | /** 65 | * Tests show/hide commands. 66 | */ 67 | public function testVisibility() { 68 | console.hideConsole(); 69 | assertFalse(console.visible); 70 | console.showConsole(); 71 | assertTrue(console.visible); 72 | } 73 | 74 | /** 75 | * Tests opening/closing console with shortcut key 76 | */ 77 | public function testConsoleToggleKey() { 78 | console.hideConsole(); 79 | assertFalse(console.visible); 80 | TestRunner.pressKey(console.consoleKey.keycode); 81 | assertTrue(console.visible); 82 | TestRunner.pressKey(console.consoleKey.keycode); 83 | assertFalse(console.visible); 84 | } 85 | 86 | /** 87 | * Tests disabled console behaviour to keystrokes. 88 | */ 89 | @:access(pgr.dconsole.ui.DCOpenflInterface.consoleDisplay) 90 | @:access(pgr.dconsole.ui.DCLuxeInterface.consoleDisplay) 91 | public function testDisable() { 92 | 93 | #if openfl 94 | var consoleDisplay = cast(interfc, pgr.dconsole.ui.DCOpenflInterface).consoleDisplay; 95 | #elseif luxe 96 | var consoleDisplay = cast(interfc, pgr.dconsole.ui.DCLuxeInterface).consoleDisplay; 97 | #end 98 | 99 | console.disable(); 100 | TestRunner.pressKey(console.consoleKey.keycode); 101 | assertFalse(consoleDisplay.visible); 102 | console.showConsole(); 103 | assertFalse(consoleDisplay.visible); 104 | console.enable(); 105 | assertTrue(consoleDisplay.visible); 106 | } 107 | 108 | /** 109 | * Tests hidden console behaviour to keystrokes. 110 | */ 111 | public function testHiddenConsole() { 112 | console.hideConsole(); 113 | 114 | interfc.setInputTxt("SomeText"); 115 | TestRunner.pressKey(key_enter); 116 | assertTrue(interfc.getConsoleText() == ""); 117 | 118 | TestRunner.pressKey(console.consoleKey.keycode); 119 | assertTrue(console.visible); 120 | TestRunner.pressKey(key_enter); 121 | assertFalse(interfc.getConsoleText() == ""); 122 | } 123 | 124 | 125 | public function testLogging() { 126 | 127 | // test clearconsole() 128 | assertTrue(consoleIsEmpty()); 129 | 130 | // test simple text logging 131 | console.log("testlog"); 132 | assertTrue(consoleHasText("testlog")); 133 | 134 | // test clearconsole() 135 | console.clearConsole(); 136 | assertTrue(consoleIsEmpty()); 137 | 138 | // test different data types logging - object, function, float, bool. 139 | // checks results and detects if console crashes 140 | console.log(null); 141 | assertTrue(consoleHasText("null")); 142 | 143 | console.clearConsole(); 144 | console.log([ "1" => 1, "2" => 2, "3" => 3 ]); 145 | assertFalse(consoleIsEmpty()); 146 | 147 | console.log(this); 148 | assertFalse(consoleIsEmpty()); 149 | 150 | console.log(1234); 151 | assertTrue(consoleHasText("1234")); 152 | 153 | console.log(1234.1234); 154 | assertTrue(consoleHasText("1234.1234")); 155 | 156 | console.log(12 * 12); 157 | assertTrue(consoleHasText("144")); 158 | 159 | console.log(testLogging); 160 | assertTrue(consoleHasText("function")); 161 | 162 | console.log(true); 163 | assertTrue(consoleHasText("true")); 164 | 165 | } 166 | 167 | /** 168 | * Test history 169 | */ 170 | public function testHistory() { 171 | 172 | // no history, try next history, previous history, prompt text remains the same. 173 | console.clearHistory(); 174 | assertTrue(inputIsEmpty()); 175 | TestRunner.pressKey(key_up); 176 | assertTrue(inputIsEmpty()); 177 | TestRunner.pressKey(key_down); 178 | assertTrue(inputIsEmpty()); 179 | 180 | // enter some commands. 181 | interfc.setInputTxt("1"); 182 | TestRunner.pressKey(key_enter); 183 | interfc.setInputTxt("2"); 184 | TestRunner.pressKey(key_enter); 185 | interfc.setInputTxt("3"); 186 | TestRunner.pressKey(key_enter); 187 | assertTrue(inputIsEmpty()); 188 | 189 | // test previousHistory 190 | TestRunner.pressKey(key_up); 191 | assertTrue(inputHasText("3")); 192 | TestRunner.pressKey(key_up); 193 | assertTrue(inputHasText("2")); 194 | TestRunner.pressKey(key_up); 195 | assertTrue(inputHasText("1")); 196 | TestRunner.pressKey(key_up); 197 | assertTrue(inputHasText("1")); 198 | // test nextHistory 199 | TestRunner.pressKey(key_down); 200 | assertTrue(inputHasText("2")); 201 | TestRunner.pressKey(key_down); 202 | assertTrue(inputHasText("3")); 203 | TestRunner.pressKey(key_down); 204 | assertTrue(inputHasText("3")); 205 | 206 | TestRunner.pressKey(key_enter); 207 | TestRunner.pressKey(key_up); 208 | TestRunner.pressKey(key_up); 209 | assertTrue(inputHasText("3")); 210 | 211 | } 212 | 213 | @:access(pgr.dconsole.ui.DCOpenflInterface.txtConsole) 214 | @:access(pgr.dconsole.ui.DCLuxeInterface.txtConsole) 215 | public function testScroll() { 216 | 217 | // enter a lot of text. 218 | // test pageUp, see if scroll changes. 219 | // test pageDown, see if scroll returns back. 220 | 221 | for (i in 0...30) { 222 | console.log("..."); 223 | } 224 | 225 | #if openfl 226 | var txtConsole:TextField = cast(interfc, pgr.dconsole.ui.DCOpenflInterface).txtConsole; 227 | 228 | assertTrue(txtConsole.scrollV == txtConsole.maxScrollV); 229 | TestRunner.pressKey(key_pageup); 230 | assertTrue(txtConsole.scrollV < txtConsole.maxScrollV); 231 | TestRunner.pressKey(key_pagedown); 232 | assertTrue(txtConsole.scrollV == txtConsole.maxScrollV); 233 | 234 | #elseif luxe 235 | var txt = cast(interfc, pgr.dconsole.ui.DCLuxeInterface).txtConsole; 236 | var startY = txt.pos.y; 237 | 238 | TestRunner.pressKey(key_pageup); 239 | assertTrue(txt.pos.y > startY); 240 | TestRunner.pressKey(key_pagedown); 241 | assertEquals(txt.pos.y, startY); 242 | #end 243 | 244 | } 245 | 246 | //--------------------------------------------------------------------------------- 247 | // AUX 248 | //--------------------------------------------------------------------------------- 249 | private function consoleHasText(txt:String):Bool { 250 | return interfc.getConsoleText().lastIndexOf(txt) != -1; 251 | } 252 | 253 | private function consoleIsEmpty():Bool { 254 | return interfc.getConsoleText() == ""; 255 | } 256 | 257 | private function inputHasText(txt:String):Bool { 258 | return interfc.getInputTxt().lastIndexOf(txt) != -1; 259 | } 260 | 261 | private function inputIsEmpty():Bool { 262 | return interfc.getInputTxt() == ""; 263 | } 264 | 265 | } -------------------------------------------------------------------------------- /tests/TestMonitor.hx: -------------------------------------------------------------------------------- 1 | package; 2 | #if openfl 3 | import flash.text.TextField; 4 | import flash.ui.Keyboard; 5 | #end 6 | import haxe.unit.TestCase; 7 | import pgr.dconsole.DC; 8 | import pgr.dconsole.DCMonitor; 9 | import pgr.dconsole.DConsole; 10 | import pgr.dconsole.DCProfiler; 11 | import pgr.dconsole.ui.DCInterface; 12 | 13 | /** 14 | * Tests monitor. 15 | * 16 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 17 | */ 18 | class TestMonitor extends TestCase 19 | { 20 | var interfc:DCInterface; 21 | var console:DConsole; 22 | var monitor:DCMonitor; 23 | var profiler:DCProfiler; 24 | 25 | var i:Int; 26 | var b:Bool; 27 | var f:Float; 28 | var setter(get, null):String; 29 | 30 | 31 | private function get_setter():String { 32 | return setter + "_string"; 33 | } 34 | 35 | 36 | override public function setup() { 37 | if (console == null) { 38 | DC.init(); 39 | console = DC.instance; 40 | interfc = console.interfc; 41 | monitor = console.monitor; 42 | profiler = console.profiler; 43 | } 44 | 45 | #if openfl 46 | console.setConsoleKey(Keyboard.TAB); 47 | console.setMonitorKey(Keyboard.TAB, true); 48 | console.setProfilerKey(Keyboard.TAB, false, true); 49 | #elseif luxe 50 | console.setConsoleKey(luxe.Input.Key.key_a); 51 | console.setMonitorKey(luxe.Input.Key.key_b); 52 | console.setProfilerKey(luxe.Input.Key.key_c); 53 | #end 54 | 55 | interfc.clearInput(); 56 | interfc.clearConsole(); 57 | console.enable(); 58 | console.showConsole(); 59 | console.hideMonitor(); 60 | console.hideProfiler(); 61 | DC.clearMonitor(); 62 | refreshMonitor(); 63 | i = 0; 64 | b = false; 65 | f = 0; 66 | } 67 | 68 | 69 | override public function tearDown():Void { 70 | console.hideProfiler(); 71 | console.hideMonitor(); 72 | DC.clearMonitor(); 73 | refreshMonitor(); 74 | } 75 | 76 | public function testClearMonitor() { 77 | i = 999; 78 | 79 | DC.monitorField(this, "i", "i"); 80 | refreshMonitor(); 81 | 82 | assertTrue(monitorHasField("i")); 83 | assertTrue(monitorHasText("999")); 84 | 85 | DC.clearMonitor(); 86 | refreshMonitor(); 87 | 88 | assertFalse(monitorHasField("i")); 89 | assertFalse(monitorHasText("999")); 90 | } 91 | 92 | 93 | // test response with console disabled 94 | public function testDisable() { 95 | console.disable(); 96 | // CTRL + console key 97 | TestRunner.pressKey(console.monitorKey.keycode, console.monitorKey.ctrlKey, console.monitorKey.shiftKey); 98 | assertFalse(monitor.visible); 99 | console.showMonitor(); 100 | //assertFalse(monitor.visible); 101 | //console.enable(); 102 | assertTrue(monitor.visible); 103 | 104 | // TODO - monitor.disable 105 | // TODO - console.disable 106 | 107 | } 108 | 109 | 110 | public function testVisibility() { 111 | console.hideMonitor(); 112 | assertFalse(monitor.visible); 113 | 114 | console.showMonitor(); 115 | assertTrue(monitor.visible); 116 | 117 | TestRunner.pressKey(console.monitorKey.keycode, console.monitorKey.ctrlKey, console.monitorKey.shiftKey); 118 | assertFalse(monitor.visible); 119 | 120 | TestRunner.pressKey(console.monitorKey.keycode, console.monitorKey.ctrlKey, console.monitorKey.shiftKey); 121 | assertTrue(monitor.visible); 122 | 123 | // tests profiler and monitor not be visible at the same time. 124 | console.showMonitor(); 125 | console.showProfiler(); 126 | assertTrue(profiler.visible); 127 | assertFalse(monitor.visible); 128 | 129 | console.showProfiler(); 130 | console.showMonitor(); 131 | assertTrue(monitor.visible); 132 | assertFalse(profiler.visible); 133 | } 134 | 135 | 136 | public function testAddField() { 137 | // test adding valid fields 138 | DC.monitorField(this, "i", "i"); 139 | DC.monitorField(this, "b", "b"); 140 | DC.monitorField(this, "f", "f"); 141 | 142 | assertTrue(monitor.fields.length == 3); 143 | assertTrue(monitorHasField("i")); 144 | assertTrue(monitorHasField("b")); 145 | assertTrue(monitorHasField("f")); 146 | 147 | DC.clearMonitor(); 148 | 149 | // test adding invalid fields 150 | DC.monitorField(null, null, null); 151 | DC.monitorField(this, null, null); 152 | DC.monitorField(this, "i", null); 153 | DC.monitorField(this, "", "i"); 154 | DC.monitorField(this, "i", ""); 155 | DC.monitorField(this, null, "i"); 156 | DC.monitorField(this, null, "i"); 157 | 158 | assertTrue(monitor.fields.length == 0); 159 | } 160 | 161 | 162 | public function testOutput() { 163 | i = 999; 164 | b = true; 165 | f = 0.001; 166 | 167 | DC.monitorField(this, "i", "vari"); 168 | DC.monitorField(this, "b", "varb"); 169 | DC.monitorField(this, "f", "varf"); 170 | 171 | refreshMonitor(); 172 | 173 | assertTrue(monitorHasText("vari")); 174 | assertTrue(monitorHasText("varb")); 175 | assertTrue(monitorHasText("varf")); 176 | 177 | assertTrue(monitorHasText("999")); 178 | assertTrue(monitorHasText("true")); 179 | assertTrue(monitorHasText("0.001")); 180 | 181 | i = 111; 182 | b = false; 183 | f = 0.1; 184 | 185 | refreshMonitor(); 186 | 187 | assertFalse(monitorHasText("999")); 188 | assertFalse(monitorHasText("true")); 189 | assertFalse(monitorHasText("0.01")); 190 | 191 | assertTrue(monitorHasText("111")); 192 | assertTrue(monitorHasText("false")); 193 | assertTrue(monitorHasText("0.1")); 194 | } 195 | 196 | 197 | 198 | function refreshMonitor() { 199 | monitor.writeOutput(); 200 | } 201 | 202 | 203 | function monitorHasText(txt:String):Bool { 204 | var montxt = interfc.getMonitorText(); 205 | return (montxt.col1.lastIndexOf(txt) != -1 || montxt.col2.lastIndexOf(txt) != -1); 206 | } 207 | 208 | 209 | function monitorHasField(id:String):Bool { 210 | for (field in monitor.fields) { 211 | if (field.alias == id) { 212 | return true; 213 | } 214 | } 215 | return false; 216 | } 217 | 218 | } -------------------------------------------------------------------------------- /tests/TestProfiler.hx: -------------------------------------------------------------------------------- 1 | package; 2 | #if openfl 3 | import flash.Lib; 4 | import flash.ui.Keyboard; 5 | #end 6 | import haxe.Timer; 7 | import haxe.unit.TestCase; 8 | import pgr.dconsole.DC; 9 | import pgr.dconsole.DCMonitor; 10 | import pgr.dconsole.DConsole; 11 | import pgr.dconsole.DCProfiler; 12 | import pgr.dconsole.ui.DCInterface; 13 | 14 | /** 15 | * Tests profiler. 16 | * 17 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 18 | */ 19 | class TestProfiler extends TestCase 20 | { 21 | var monitor:DCMonitor; 22 | var profiler:DCProfiler; 23 | var interfc:DCInterface; 24 | var console:DConsole; 25 | 26 | override public function setup() { 27 | if (console == null) { 28 | DC.init(); 29 | console = DC.instance; 30 | interfc = cast console.interfc; 31 | monitor = console.monitor; 32 | profiler = console.profiler; 33 | } 34 | 35 | #if openfl 36 | console.setConsoleKey(Keyboard.TAB); 37 | console.setMonitorKey(Keyboard.TAB, true); 38 | console.setProfilerKey(Keyboard.TAB, false, true); 39 | #elseif luxe 40 | console.setConsoleKey(luxe.Input.Key.key_a); 41 | console.setMonitorKey(luxe.Input.Key.key_b); 42 | console.setProfilerKey(luxe.Input.Key.key_c); 43 | #end 44 | 45 | interfc.clearInput(); 46 | interfc.clearConsole(); 47 | console.enable(); 48 | console.showConsole(); 49 | console.hideMonitor(); 50 | console.hideProfiler(); 51 | DC.clearProfiler(); 52 | } 53 | 54 | override public function tearDown():Void { 55 | // clear profiler 56 | DC.clearProfiler(); 57 | console.hideProfiler(); 58 | console.hideMonitor(); 59 | } 60 | 61 | // test response with console disabled 62 | public function testDisable() { 63 | console.disable(); 64 | TestRunner.pressKey(console.consoleKey.keycode, false, true); // toggle profiler 65 | assertFalse(profiler.visible); 66 | console.showProfiler(); 67 | //assertFalse(monitor.visible); 68 | //console.enable(); 69 | assertTrue(profiler.visible); 70 | 71 | // TODO - monitor.disable 72 | // TODO - console.disable 73 | } 74 | 75 | public function testVisibility() { 76 | console.hideProfiler(); 77 | assertFalse(profiler.visible); 78 | 79 | console.showProfiler(); 80 | assertTrue(profiler.visible); 81 | 82 | TestRunner.pressKey(console.profilerKey.keycode, console.profilerKey.ctrlKey, console.profilerKey.shiftKey); 83 | assertFalse(profiler.visible); 84 | 85 | TestRunner.pressKey(console.profilerKey.keycode, console.profilerKey.ctrlKey, console.profilerKey.shiftKey); 86 | assertTrue(profiler.visible); 87 | 88 | // tests profiler and monitor not be visible at the same time. 89 | console.showMonitor(); 90 | console.showProfiler(); 91 | assertTrue(profiler.visible); 92 | assertFalse(monitor.visible); 93 | 94 | console.showProfiler(); 95 | console.showMonitor(); 96 | assertTrue(monitor.visible); 97 | assertFalse(profiler.visible); 98 | } 99 | 100 | public function testBeginProfile() { 101 | // test opening same sample twice. 102 | try { 103 | DC.beginProfile("start1"); 104 | DC.beginProfile("start1"); 105 | assertTrue(false); // this should not be reached 106 | } catch (s:String) {} 107 | 108 | DC.clearProfiler(); 109 | assertFalse(existsSample("start1")); 110 | 111 | // test samples status after begin 112 | DC.beginProfile("start1"); 113 | assertTrue(existsSample("start1")); 114 | assertTrue(getSample("start1").openInstances == 1); 115 | assertTrue(getSample("start1").instances == 1); 116 | 117 | DC.endProfile("start1"); 118 | assertTrue(existsSample("start1")); // sample now exists but has 0 open instances 119 | assertTrue(getSample("start1").openInstances == 0); 120 | 121 | try { 122 | DC.beginProfile("start1"); 123 | DC.beginProfile("start2"); 124 | DC.beginProfile("start1"); // test opening same sample as a nested nested child 125 | assertTrue(false); 126 | } catch (s:String) {} 127 | 128 | } 129 | 130 | 131 | public function testEndProfile() { 132 | try { 133 | DC.endProfile("start1"); // ending non existing sample 134 | assertTrue(false); // this line should not be reached. 135 | } catch (s:String) {} 136 | 137 | try { 138 | DC.beginProfile("start1"); 139 | DC.endProfile("start1"); 140 | DC.endProfile("start1"); // ending same sample twice 141 | assertTrue(false); // this line should not be reached. 142 | } catch (s:String) { } 143 | 144 | assertTrue(true); 145 | } 146 | 147 | 148 | public function testCrossProfile() { 149 | try { 150 | DC.beginProfile("s1"); 151 | DC.beginProfile("s2"); 152 | DC.endProfile("s1"); 153 | assertTrue(false); // code not supposed to be reached. 154 | } catch (s:Dynamic) {} 155 | 156 | assertTrue(true); // cross profile exception catched. 157 | } 158 | 159 | // test samples statistics 160 | public function testSample() { 161 | 162 | var startTime; 163 | var delta; 164 | var h1, h2:SampleHistory; 165 | var lastElapsed; 166 | 167 | 168 | // Run sample, verify statistics 169 | DC.beginProfile("start1"); 170 | startTime = Timer.stamp() * 1000; 171 | delaySample("start1", 100); 172 | DC.endProfile("start1"); 173 | delta = Timer.stamp() * 1000 - startTime; 174 | 175 | h1 = getHistory("start1"); 176 | assertTrue(h1.elapsed >= 100 && h1.elapsed <= 100 + delta); 177 | assertTrue(h1.numParents == 0); 178 | assertTrue(h1.nLogs == 1); 179 | assertTrue(h1.totalElapsed == h1.elapsed); 180 | assertTrue(h1.childrenElapsed == 0); 181 | assertTrue(h1.instances == 1); 182 | lastElapsed = h1.elapsed; 183 | 184 | // Re-Run sample, verify updated statistics 185 | DC.beginProfile("start1"); 186 | startTime = Timer.stamp() * 1000; 187 | delaySample("start1", 200); 188 | DC.endProfile("start1"); 189 | delta = Timer.stamp() * 1000 - startTime; 190 | 191 | assertTrue(h1.elapsed >= 200 && h1.elapsed <= 200 + delta); 192 | assertTrue(h1.numParents == 0); 193 | assertTrue(h1.nLogs == 2); 194 | assertTrue(h1.totalElapsed == lastElapsed + h1.elapsed); 195 | assertTrue(h1.childrenElapsed == 0); 196 | assertTrue(h1.instances == 2); 197 | lastElapsed = h1.elapsed; 198 | 199 | } 200 | 201 | // test samples history/statistics with nested samples 202 | public function testNestedSample() { 203 | 204 | var startTime; 205 | var delta; 206 | var h1, h2:SampleHistory; 207 | var lastElapsed; 208 | 209 | // run samples 1 and 2, sample 2 multiple times inside 1 210 | DC.beginProfile("start1"); 211 | startTime = Timer.stamp() * 1000; 212 | delaySample("start1", 100); 213 | DC.beginProfile("start2"); 214 | delaySample("start2", 100); 215 | DC.endProfile("start2"); 216 | DC.endProfile("start1"); 217 | delta = Timer.stamp() * 1000 - startTime; 218 | 219 | h1 = getHistory("start1"); 220 | h2 = getChild("start1", "start2"); 221 | 222 | assertFalse(h1 == null); 223 | assertFalse(h2 == null); 224 | assertFalse(existsHistory("start2")); // sample 2 exists only as a child of sample1 225 | 226 | assertTrue(h1.elapsed >= 200 && h1.elapsed <= 200 + delta); 227 | assertTrue(h1.numParents == 0); 228 | assertTrue(h1.nLogs == 1); 229 | assertTrue(h1.totalElapsed == h1.elapsed); 230 | assertTrue(h1.childrenElapsed == h2.elapsed); 231 | assertTrue(h1.instances == 1); 232 | 233 | // children stats 234 | assertTrue(h2.elapsed >= 100 && h2.elapsed <= 100 + delta); 235 | assertTrue(h2.numParents == 1); 236 | assertTrue(h2.nLogs == 1); 237 | assertTrue(h2.totalElapsed == h2.elapsed); 238 | assertTrue(h2.childrenElapsed == 0); 239 | assertTrue(h2.instances == 1); 240 | 241 | lastElapsed = h1.elapsed; 242 | 243 | // Run samples 1 and 2, sample 2 multiple times inside 1 244 | 245 | // Run sample 2 alone 246 | 247 | // Run sample 1 alone 248 | } 249 | 250 | //--------------------------------------------------------------------------------- 251 | // AUX 252 | //--------------------------------------------------------------------------------- 253 | function existsSample(s:String):Bool { 254 | return (profiler.getSample(s) != null); 255 | } 256 | 257 | function getSample(s:String):PFSample { 258 | return profiler.getSample(s); 259 | } 260 | 261 | function existsHistory(s:String):Bool { 262 | return (profiler.getHistory(s) != null); 263 | } 264 | 265 | function getHistory(s:String):SampleHistory { 266 | return profiler.getHistory(s); 267 | } 268 | 269 | function delaySample(sampleName:String, delay:Int) { 270 | var sample = getSample(sampleName); 271 | 272 | sample.startTime -= delay; 273 | 274 | for (s in profiler.samples) { 275 | if (s.openInstances > 0 && s.name != sample.name) { 276 | // also apply the delay to sample parents 277 | s.startTime -= delay; 278 | } 279 | } 280 | } 281 | 282 | function getChild(sampleName:String, childName:String):SampleHistory { 283 | return getHistory(sampleName).getChild(childName); 284 | } 285 | 286 | } -------------------------------------------------------------------------------- /tests/TestRegister.hx: -------------------------------------------------------------------------------- 1 | package; 2 | import haxe.unit.TestCase; 3 | import pgr.dconsole.DC; 4 | import pgr.dconsole.DConsole; 5 | import pgr.dconsole.ui.DCInterface; 6 | 7 | /** 8 | * Tests console object and function register. 9 | * 10 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 11 | */ 12 | class TestRegister extends TestCase 13 | { 14 | var interfc:DCInterface; 15 | var console:DConsole; 16 | var i:Int; 17 | var f:Float; 18 | var s:String; 19 | var args:Array; 20 | 21 | override public function setup() { 22 | if (console == null) { 23 | DC.init(); 24 | console = DC.instance; 25 | interfc = console.interfc; 26 | } 27 | 28 | DC.clearRegistry(); 29 | } 30 | 31 | function commandDummy(Args:Array) { 32 | args = Args; 33 | } 34 | 35 | public function testRegisterCommand() { 36 | 37 | var numcms = commandsCount(); 38 | // tests registering non functions 39 | DC.registerCommand(null, null); 40 | DC.registerCommand(commandDummy, null); 41 | DC.registerCommand(commandDummy, "!.@.()"); 42 | DC.registerCommand(commandDummy, ""); 43 | assertTrue(commandsCount() == numcms); // no commands were added 44 | 45 | // tests if command is called and receives arguments 46 | DC.registerCommand(commandDummy, "commandDummy"); 47 | console.commands.evaluate("commandDummy 123"); 48 | assertTrue(args.length == 1); 49 | console.commands.evaluate("commandDummy"); // no args 50 | assertTrue(args.length == 0); 51 | console.commands.evaluate("CoMmanDDumMy test"); // case sensitivity test 52 | assertTrue(args.length == 1); 53 | } 54 | 55 | public function testRegisterMethods() { 56 | 57 | // test registering non functions 58 | DC.registerFunction(null, " "); 59 | DC.registerFunction(this, " "); 60 | DC.registerFunction("", " "); 61 | assertTrue(functionsCount() == 0); 62 | 63 | // test registering methods with same alias 64 | DC.registerFunction(testF2, "f1"); 65 | DC.registerFunction(testF1, "f1"); 66 | DC.registerFunction(TestRegister.testF1(), ""); 67 | assertTrue(functionsCount() == 2); 68 | 69 | var numfs = functionsCount(); 70 | // test unregister 71 | DC.unregisterFunction("invalid function name"); 72 | DC.unregisterFunction("f1"); 73 | assertTrue(functionsCount() == numfs - 1); 74 | } 75 | 76 | public function testRegisterObjects() { 77 | 78 | // test register non objects 79 | DC.registerObject(null); 80 | DC.registerObject(testF1); 81 | DC.registerObject(1234); 82 | DC.registerObject(true); 83 | assertTrue(objectsCount() == 0); 84 | 85 | // duplicate register. 86 | DC.registerObject(this, "this"); 87 | DC.registerObject(this, "this"); 88 | assertTrue(objectsCount() == 2); // two objects added 89 | 90 | // test unique alias generation 91 | DC.registerObject(this); 92 | DC.registerObject(this); 93 | DC.registerObject(this, ""); 94 | DC.registerObject(this, null); 95 | assertTrue(objectsCount() == 6); // four objects added 96 | 97 | // test unregister object 98 | DC.unregisterObject("test alias"); 99 | DC.unregisterObject("this"); 100 | assertTrue(objectsCount() == 5); // one object removed 101 | } 102 | 103 | public function testClear() { 104 | 105 | DC.registerFunction(testF1, "testF1"); 106 | DC.registerObject(this, "this"); 107 | 108 | assertTrue(functionsCount() == 1); 109 | assertTrue(objectsCount() == 1); 110 | 111 | DC.clearRegistry(); 112 | 113 | assertTrue(functionsCount() == 0); 114 | assertTrue(objectsCount() == 0); 115 | } 116 | 117 | //--------------------------------------------------------------------------------- 118 | // AUX 119 | //--------------------------------------------------------------------------------- 120 | function objectsCount():Int { 121 | return Lambda.array(console.commands.objectsMap).length; 122 | } 123 | 124 | function functionsCount():Int { 125 | return Lambda.array(console.commands.functionsMap).length; 126 | } 127 | 128 | function commandsCount():Int { 129 | return Lambda.array(console.commands.commandsMap).length; 130 | } 131 | 132 | public static function testF1():String { 133 | return "F1"; 134 | } 135 | 136 | public static function testF2():String { 137 | return "F2"; 138 | } 139 | } -------------------------------------------------------------------------------- /tests/TestRunner.hx: -------------------------------------------------------------------------------- 1 | package; 2 | import haxe.unit.TestRunner; 3 | import pgr.dconsole.DC; 4 | class TestRunner 5 | #if luxe 6 | extends luxe.Game 7 | #end 8 | { 9 | #if openfl 10 | static function main() { 11 | #elseif luxe 12 | override function ready() { 13 | #end 14 | 15 | var r = new haxe.unit.TestRunner(); 16 | 17 | r.add(new TestInput()); 18 | r.add(new TestRegister()); 19 | r.add(new TestCommands()); 20 | r.add(new TestUtils()); 21 | r.add(new TestMonitor()); 22 | r.add(new TestProfiler()); 23 | 24 | r.run(); 25 | 26 | #if COVERAGE 27 | var logger = mcover.coverage.MCoverage.getLogger(); 28 | logger.report(); 29 | #end 30 | 31 | #if (cpp || neko) 32 | Sys.exit(0); 33 | #end 34 | } 35 | 36 | public static function pressKey(key:Int, ctrl:Bool = false, shift:Bool = false) { 37 | var interfc = DC.instance.interfc; 38 | var input = DC.instance.input; 39 | 40 | #if (openfl && cpp && legacy) 41 | cast(interfc, pgr.dconsole.ui.DCOpenflInterface).stage.dispatchEvent(new flash.events.KeyboardEvent(flash.events.KeyboardEvent.KEY_UP, true, false, 0, key, 0, ctrl, false, shift)); 42 | #elseif openfl 43 | cast(interfc, pgr.dconsole.ui.DCOpenflInterface).stage.dispatchEvent(new flash.events.KeyboardEvent(flash.events.KeyboardEvent.KEY_UP, true, false, 0, key, null, ctrl, false, shift)); 44 | #elseif luxe 45 | cast(input, pgr.dconsole.input.DCLuxeInput).inputListener.onkeyup( { 46 | scancode : 0, 47 | keycode : key, 48 | state : null, 49 | mod : null, 50 | repeat : false, 51 | timestamp : 0, 52 | window_id : 0, 53 | }); 54 | #end 55 | } 56 | } -------------------------------------------------------------------------------- /tests/TestUtils.hx: -------------------------------------------------------------------------------- 1 | package; 2 | #if openfl 3 | import flash.ui.Keyboard; 4 | #elseif luxe 5 | import luxe.Input.Key; 6 | #end 7 | import haxe.unit.TestCase; 8 | import pgr.dconsole.DC; 9 | import pgr.dconsole.DConsole; 10 | import pgr.dconsole.DCUtil; 11 | import pgr.dconsole.ui.DCInterface; 12 | 13 | /** 14 | * Tests console runtime commands. 15 | * 16 | * @author TiagoLr ( ~~~ProG4mr~~~ ) 17 | */ 18 | class TestUtils extends TestCase 19 | { 20 | var interfc:DCInterface; 21 | var console:DConsole; 22 | var i:Int; 23 | var f:Float; 24 | var s:String; 25 | 26 | override public function setup() { 27 | if (console == null) { 28 | DC.init(); 29 | console = DC.instance; 30 | interfc = console.interfc; 31 | } 32 | 33 | var key = #if openfl Keyboard.TAB #elseif luxe Key.tab #end; 34 | 35 | console.setConsoleKey(key); 36 | console.setMonitorKey(key, true); 37 | console.setProfilerKey(key, false, true); 38 | interfc.clearInput(); 39 | interfc.clearConsole(); 40 | console.enable(); 41 | console.showConsole(); 42 | DC.clearRegistry(); 43 | } 44 | 45 | public function testAutoAlias() { 46 | var alias:String; 47 | 48 | alias = DCUtil.formatAlias(console.commands, "", ALIAS_TYPE.OBJECT); 49 | assertTrue(alias == null); 50 | 51 | alias = DCUtil.formatAlias(console.commands, null, ALIAS_TYPE.OBJECT); 52 | assertTrue(alias == null); 53 | 54 | alias = DCUtil.formatAlias(console.commands, ".invalidName", ALIAS_TYPE.OBJECT); 55 | assertTrue(alias == null); 56 | 57 | alias = DCUtil.formatAlias(console.commands, "(invalidName)", ALIAS_TYPE.OBJECT); 58 | assertTrue(alias == null); 59 | 60 | alias = DCUtil.formatAlias(console.commands, " invalidName)", ALIAS_TYPE.OBJECT); 61 | assertTrue(alias == null); 62 | 63 | alias = DCUtil.formatAlias(console.commands, "!invalidName)", ALIAS_TYPE.OBJECT); 64 | assertTrue(alias == null); 65 | 66 | DC.registerCommand(commandDummy, "c", "cs"); 67 | DC.registerCommand(commandDummy, "c", "cs"); 68 | DC.registerCommand(commandDummy, "c", "cs"); 69 | assertTrue(existsCommand("c")); 70 | assertTrue(existsCommand("cc")); 71 | assertTrue(existsCommand("ccc")); 72 | assertTrue(existsCommand("C")); // test case insesitivity 73 | assertTrue(existsCommand("cs")); // test shortcut 74 | assertTrue(existsCommand("ccs")); 75 | assertTrue(existsCommand("cccs")); 76 | assertTrue(existsCommand("CS")); // test shortcut case insensivity 77 | 78 | DC.registerObject(this, "o"); 79 | DC.registerObject(this, "o"); 80 | DC.registerObject(this, "o"); 81 | DC.registerObject(this, "c"); 82 | assertTrue(existsObject("o")); 83 | assertTrue(existsObject("o1")); 84 | assertTrue(existsObject("o2")); 85 | assertTrue(existsObject("c1")); 86 | 87 | DC.registerFunction(fDummy, "f"); 88 | DC.registerFunction(fDummy, "f"); 89 | DC.registerFunction(fDummy, "f"); 90 | 91 | assertTrue(existsFunction("f")); 92 | assertTrue(existsFunction("ff")); 93 | assertTrue(existsFunction("fff")); 94 | } 95 | 96 | function commandDummy(args:Array) { } 97 | 98 | function fDummy() {}; 99 | 100 | function existsCommand(c:String) { 101 | return console.commands.getCommand(c) != null; 102 | } 103 | 104 | function existsFunction(f:String) { 105 | return console.commands.getFunction(f) != null; 106 | } 107 | 108 | function existsObject(o:String) { 109 | return console.commands.getObject(o) != null; 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /tests/project.flow: -------------------------------------------------------------------------------- 1 | { 2 | project : { 3 | name : 'empty', 4 | version : '1.0.0', 5 | author : 'luxeengine', 6 | 7 | app : { 8 | name : 'luxe_empty', 9 | package : 'com.luxeengine.empty', 10 | main : 'TestRunner' 11 | }, 12 | 13 | build : { 14 | dependencies : { 15 | luxe : '*', 16 | dconsole: '*' 17 | } 18 | }, 19 | 20 | files : { 21 | config : 'config.json', 22 | assets : 'assets/' 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/tests.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | "$(CompilerPath)/haxelib" run lime build "$(OutputFile)" $(TargetBuild) -$(BuildConfig) -Dfdb 55 | 56 | 57 | 58 | 59 | 63 | 64 | 65 | --------------------------------------------------------------------------------