diff options
Diffstat (limited to 'addons/gut/gui')
| -rw-r--r-- | addons/gut/gui/BottomPanelShortcuts.gd | 82 | ||||
| -rw-r--r-- | addons/gut/gui/BottomPanelShortcuts.tscn | 232 | ||||
| -rw-r--r-- | addons/gut/gui/GutBottomPanel.gd | 326 | ||||
| -rw-r--r-- | addons/gut/gui/GutBottomPanel.tscn | 386 | ||||
| -rw-r--r-- | addons/gut/gui/GutRunner.gd | 98 | ||||
| -rw-r--r-- | addons/gut/gui/GutRunner.tscn | 9 | ||||
| -rw-r--r-- | addons/gut/gui/GutSceneTheme.tres | 11 | ||||
| -rw-r--r-- | addons/gut/gui/RunAtCursor.gd | 130 | ||||
| -rw-r--r-- | addons/gut/gui/RunAtCursor.tscn | 80 | ||||
| -rw-r--r-- | addons/gut/gui/Settings.tscn | 7 | ||||
| -rw-r--r-- | addons/gut/gui/ShortcutButton.gd | 164 | ||||
| -rw-r--r-- | addons/gut/gui/ShortcutButton.tscn | 80 | ||||
| -rw-r--r-- | addons/gut/gui/arrow.png | 3 | ||||
| -rw-r--r-- | addons/gut/gui/arrow.png.import | 35 | ||||
| -rw-r--r-- | addons/gut/gui/gut_config_gui.gd | 408 | ||||
| -rw-r--r-- | addons/gut/gui/play.png | 3 | ||||
| -rw-r--r-- | addons/gut/gui/play.png.import | 35 | ||||
| -rw-r--r-- | addons/gut/gui/script_text_editor_controls.gd | 230 |
18 files changed, 2319 insertions, 0 deletions
diff --git a/addons/gut/gui/BottomPanelShortcuts.gd b/addons/gut/gui/BottomPanelShortcuts.gd new file mode 100644 index 0000000..86fbf8d --- /dev/null +++ b/addons/gut/gui/BottomPanelShortcuts.gd @@ -0,0 +1,82 @@ +tool +extends WindowDialog + +onready var _ctrls = { + run_all = $Layout/CRunAll/ShortcutButton, + run_current_script = $Layout/CRunCurrentScript/ShortcutButton, + run_current_inner = $Layout/CRunCurrentInner/ShortcutButton, + run_current_test = $Layout/CRunCurrentTest/ShortcutButton, + panel_button = $Layout/CPanelButton/ShortcutButton, +} + +func _ready(): + for key in _ctrls: + var sc_button = _ctrls[key] + sc_button.connect('start_edit', self, '_on_edit_start', [sc_button]) + sc_button.connect('end_edit', self, '_on_edit_end') + + + # show dialog when running scene from editor. + if(get_parent() == get_tree().root): + popup_centered() + +# ------------ +# Events +# ------------ +func _on_Hide_pressed(): + hide() + +func _on_edit_start(which): + for key in _ctrls: + var sc_button = _ctrls[key] + if(sc_button != which): + sc_button.disable_set(true) + sc_button.disable_clear(true) + +func _on_edit_end(): + for key in _ctrls: + var sc_button = _ctrls[key] + sc_button.disable_set(false) + sc_button.disable_clear(false) + +# ------------ +# Public +# ------------ +func get_run_all(): + return _ctrls.run_all.get_shortcut() + +func get_run_current_script(): + return _ctrls.run_current_script.get_shortcut() + +func get_run_current_inner(): + return _ctrls.run_current_inner.get_shortcut() + +func get_run_current_test(): + return _ctrls.run_current_test.get_shortcut() + +func get_panel_button(): + return _ctrls.panel_button.get_shortcut() + + +func save_shortcuts(path): + var f = ConfigFile.new() + + f.set_value('main', 'run_all', _ctrls.run_all.get_shortcut()) + f.set_value('main', 'run_current_script', _ctrls.run_current_script.get_shortcut()) + f.set_value('main', 'run_current_inner', _ctrls.run_current_inner.get_shortcut()) + f.set_value('main', 'run_current_test', _ctrls.run_current_test.get_shortcut()) + f.set_value('main', 'panel_button', _ctrls.panel_button.get_shortcut()) + + f.save(path) + + +func load_shortcuts(path): + var emptyShortcut = ShortCut.new() + var f = ConfigFile.new() + f.load(path) + + _ctrls.run_all.set_shortcut(f.get_value('main', 'run_all', emptyShortcut)) + _ctrls.run_current_script.set_shortcut(f.get_value('main', 'run_current_script', emptyShortcut)) + _ctrls.run_current_inner.set_shortcut(f.get_value('main', 'run_current_inner', emptyShortcut)) + _ctrls.run_current_test.set_shortcut(f.get_value('main', 'run_current_test', emptyShortcut)) + _ctrls.panel_button.set_shortcut(f.get_value('main', 'panel_button', emptyShortcut)) diff --git a/addons/gut/gui/BottomPanelShortcuts.tscn b/addons/gut/gui/BottomPanelShortcuts.tscn new file mode 100644 index 0000000..5e2e5d9 --- /dev/null +++ b/addons/gut/gui/BottomPanelShortcuts.tscn @@ -0,0 +1,232 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://addons/gut/gui/ShortcutButton.tscn" type="PackedScene" id=1] +[ext_resource path="res://addons/gut/gui/BottomPanelShortcuts.gd" type="Script" id=2] + +[node name="BottomPanelShortcuts" type="WindowDialog"] +visible = true +anchor_right = 0.234 +anchor_bottom = 0.328 +margin_right = 195.384 +margin_bottom = 62.2 +rect_min_size = Vector2( 435, 305 ) +popup_exclusive = true +window_title = "GUT Shortcuts" +resizable = true +script = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Layout" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 5.0 +margin_right = -5.0 +margin_bottom = 2.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TopPad" type="CenterContainer" parent="Layout"] +margin_right = 425.0 +margin_bottom = 5.0 +rect_min_size = Vector2( 0, 5 ) + +[node name="Label2" type="Label" parent="Layout"] +margin_top = 9.0 +margin_right = 425.0 +margin_bottom = 29.0 +rect_min_size = Vector2( 0, 20 ) +text = "Always Active" +align = 1 +valign = 1 +autowrap = true + +[node name="ColorRect" type="ColorRect" parent="Layout/Label2"] +show_behind_parent = true +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color( 0, 0, 0, 0.196078 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CPanelButton" type="HBoxContainer" parent="Layout"] +margin_top = 33.0 +margin_right = 425.0 +margin_bottom = 58.0 + +[node name="Label" type="Label" parent="Layout/CPanelButton"] +margin_right = 138.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Show/Hide GUT Panel" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CPanelButton" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 142.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="GutPanelPad" type="CenterContainer" parent="Layout"] +margin_top = 62.0 +margin_right = 425.0 +margin_bottom = 67.0 +rect_min_size = Vector2( 0, 5 ) + +[node name="Label" type="Label" parent="Layout"] +margin_top = 71.0 +margin_right = 425.0 +margin_bottom = 91.0 +rect_min_size = Vector2( 0, 20 ) +text = "Only Active When GUT Panel Shown" +align = 1 +valign = 1 +autowrap = true + +[node name="ColorRect2" type="ColorRect" parent="Layout/Label"] +show_behind_parent = true +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color( 0, 0, 0, 0.196078 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="TopPad2" type="CenterContainer" parent="Layout"] +margin_top = 95.0 +margin_right = 425.0 +margin_bottom = 100.0 +rect_min_size = Vector2( 0, 5 ) + +[node name="CRunAll" type="HBoxContainer" parent="Layout"] +margin_top = 104.0 +margin_right = 425.0 +margin_bottom = 129.0 + +[node name="Label" type="Label" parent="Layout/CRunAll"] +margin_right = 50.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Run All" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CRunAll" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 54.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="CRunCurrentScript" type="HBoxContainer" parent="Layout"] +margin_top = 133.0 +margin_right = 425.0 +margin_bottom = 158.0 + +[node name="Label" type="Label" parent="Layout/CRunCurrentScript"] +margin_right = 115.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Run Current Script" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CRunCurrentScript" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 119.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="CRunCurrentInner" type="HBoxContainer" parent="Layout"] +margin_top = 162.0 +margin_right = 425.0 +margin_bottom = 187.0 + +[node name="Label" type="Label" parent="Layout/CRunCurrentInner"] +margin_right = 150.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Run Current Inner Class" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CRunCurrentInner" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 154.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="CRunCurrentTest" type="HBoxContainer" parent="Layout"] +margin_top = 191.0 +margin_right = 425.0 +margin_bottom = 216.0 + +[node name="Label" type="Label" parent="Layout/CRunCurrentTest"] +margin_right = 106.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 50, 0 ) +size_flags_vertical = 7 +text = "Run Current Test" +valign = 1 + +[node name="ShortcutButton" parent="Layout/CRunCurrentTest" instance=ExtResource( 1 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 110.0 +margin_right = 425.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 + +[node name="CenterContainer2" type="CenterContainer" parent="Layout"] +margin_top = 220.0 +margin_right = 425.0 +margin_bottom = 241.0 +rect_min_size = Vector2( 0, 5 ) +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ShiftDisclaimer" type="Label" parent="Layout"] +margin_top = 245.0 +margin_right = 425.0 +margin_bottom = 259.0 +text = "\"Shift\" cannot be the only modifier for a shortcut." +align = 2 +autowrap = true + +[node name="HBoxContainer" type="HBoxContainer" parent="Layout"] +margin_top = 263.0 +margin_right = 425.0 +margin_bottom = 293.0 + +[node name="CenterContainer" type="CenterContainer" parent="Layout/HBoxContainer"] +margin_right = 361.0 +margin_bottom = 30.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Hide" type="Button" parent="Layout/HBoxContainer"] +margin_left = 365.0 +margin_right = 425.0 +margin_bottom = 30.0 +rect_min_size = Vector2( 60, 30 ) +text = "Close" + +[node name="BottomPad" type="CenterContainer" parent="Layout"] +margin_top = 297.0 +margin_right = 425.0 +margin_bottom = 307.0 +rect_min_size = Vector2( 0, 10 ) +size_flags_horizontal = 3 + +[connection signal="pressed" from="Layout/HBoxContainer/Hide" to="." method="_on_Hide_pressed"] diff --git a/addons/gut/gui/GutBottomPanel.gd b/addons/gut/gui/GutBottomPanel.gd new file mode 100644 index 0000000..50a32d8 --- /dev/null +++ b/addons/gut/gui/GutBottomPanel.gd @@ -0,0 +1,326 @@ +tool +extends Control + +const RUNNER_JSON_PATH = 'res://.gut_editor_config.json' +const RESULT_FILE = 'user://.gut_editor.bbcode' +const RESULT_JSON = 'user://.gut_editor.json' +const SHORTCUTS_PATH = 'res://.gut_editor_shortcuts.cfg' + +var TestScript = load('res://addons/gut/test.gd') +var GutConfigGui = load('res://addons/gut/gui/gut_config_gui.gd') + +var _interface = null; +var _is_running = false; +var _gut_config = load('res://addons/gut/gut_config.gd').new() +var _gut_config_gui = null +var _gut_plugin = null +var _light_color = Color(0, 0, 0, .5) +var _panel_button = null +var _last_selected_path = null + + +onready var _ctrls = { + output = $layout/RSplit/CResults/Output, + run_button = $layout/ControlBar/RunAll, + settings = $layout/RSplit/sc/Settings, + shortcut_dialog = $BottomPanelShortcuts, + light = $layout/RSplit/CResults/ControlBar/Light, + results = { + passing = $layout/RSplit/CResults/ControlBar/Passing/value, + failing = $layout/RSplit/CResults/ControlBar/Failing/value, + pending = $layout/RSplit/CResults/ControlBar/Pending/value, + errors = $layout/RSplit/CResults/ControlBar/Errors/value, + warnings = $layout/RSplit/CResults/ControlBar/Warnings/value, + orphans = $layout/RSplit/CResults/ControlBar/Orphans/value + }, + run_at_cursor = $layout/ControlBar/RunAtCursor +} + + +func _init(): + _gut_config.load_panel_options(RUNNER_JSON_PATH) + + +func _ready(): + _gut_config_gui = GutConfigGui.new(_ctrls.settings) + _gut_config_gui.set_options(_gut_config.options) + _set_all_fonts_in_ftl(_ctrls.output, _gut_config.options.panel_options.font_name) + _set_font_size_for_rtl(_ctrls.output, _gut_config.options.panel_options.font_size) + + +func _process(delta): + if(_is_running): + if(!_interface.is_playing_scene()): + _is_running = false + _ctrls.output.add_text("\ndone") + load_result_output() + _gut_plugin.make_bottom_panel_item_visible(self) + +# --------------- +# Private +# --------------- + +func load_shortcuts(): + _ctrls.shortcut_dialog.load_shortcuts(SHORTCUTS_PATH) + _apply_shortcuts() + + +# ----------------------------------- +func _set_font(rtl, font_name, custom_name): + if(font_name == null): + rtl.set('custom_fonts/' + custom_name, null) + else: + var dyn_font = DynamicFont.new() + var font_data = DynamicFontData.new() + font_data.font_path = 'res://addons/gut/fonts/' + font_name + '.ttf' + font_data.antialiased = true + dyn_font.font_data = font_data + rtl.set('custom_fonts/' + custom_name, dyn_font) + + +func _set_all_fonts_in_ftl(ftl, base_name): + if(base_name == 'Default'): + _set_font(ftl, null, 'normal_font') + _set_font(ftl, null, 'bold_font') + _set_font(ftl, null, 'italics_font') + _set_font(ftl, null, 'bold_italics_font') + else: + _set_font(ftl, base_name + '-Regular', 'normal_font') + _set_font(ftl, base_name + '-Bold', 'bold_font') + _set_font(ftl, base_name + '-Italic', 'italics_font') + _set_font(ftl, base_name + '-BoldItalic', 'bold_italics_font') + + +func _set_font_size_for_rtl(rtl, new_size): + if(rtl.get('custom_fonts/normal_font') != null): + rtl.get('custom_fonts/bold_italics_font').size = new_size + rtl.get('custom_fonts/bold_font').size = new_size + rtl.get('custom_fonts/italics_font').size = new_size + rtl.get('custom_fonts/normal_font').size = new_size +# ----------------------------------- + + +func _is_test_script(script): + var from = script.get_base_script() + while(from and from.resource_path != 'res://addons/gut/test.gd'): + from = from.get_base_script() + + return from != null + + +func _update_last_run_label(): + var text = '' + + if( _gut_config.options.selected == null and + _gut_config.options.inner_class == null and + _gut_config.options.unit_test_name == null): + text = 'All' + else: + text = nvl(_gut_config.options.selected, '') + ' ' + text += nvl(_gut_config.options.inner_class, '') + ' ' + text += nvl(_gut_config.options.unit_test_name, '') + + + +func _show_errors(errs): + _ctrls.output.clear() + var text = "Cannot run tests, you have a conrfiguration error:\n" + for e in errs: + text += str('* ', e, "\n") + text += "[right]Check your settings here ----->[/right]" + _ctrls.output.bbcode_text = text + + +func _run_tests(): + var issues = _gut_config_gui.get_config_issues() + if(issues.size() > 0): + _show_errors(issues) + return + + write_file(RESULT_FILE, 'Run in progress') + _gut_config.options = _gut_config_gui.get_options(_gut_config.options) + _set_all_fonts_in_ftl(_ctrls.output, _gut_config.options.panel_options.font_name) + _set_font_size_for_rtl(_ctrls.output, _gut_config.options.panel_options.font_size) + + var w_result = _gut_config.write_options(RUNNER_JSON_PATH) + if(w_result != OK): + push_error(str('Could not write options to ', RUNNER_JSON_PATH, ': ', w_result)) + return; + + _ctrls.output.clear() + + _update_last_run_label() + _interface.play_custom_scene('res://addons/gut/gui/GutRunner.tscn') + + _is_running = true + _ctrls.output.add_text('running...') + + +func _apply_shortcuts(): + _ctrls.run_button.shortcut = _ctrls.shortcut_dialog.get_run_all() + + _ctrls.run_at_cursor.get_script_button().shortcut = \ + _ctrls.shortcut_dialog.get_run_current_script() + _ctrls.run_at_cursor.get_inner_button().shortcut = \ + _ctrls.shortcut_dialog.get_run_current_inner() + _ctrls.run_at_cursor.get_test_button().shortcut = \ + _ctrls.shortcut_dialog.get_run_current_test() + + _panel_button.shortcut = _ctrls.shortcut_dialog.get_panel_button() + + +func _run_all(): + _gut_config.options.selected = null + _gut_config.options.inner_class = null + _gut_config.options.unit_test_name = null + + _run_tests() + + +# --------------- +# Events +# --------------- +func _on_editor_script_changed(script): + if(script): + set_current_script(script) + + +func _on_RunAll_pressed(): + _on_RunTests_pressed() + + +func _on_RunTests_pressed(): + _run_all() + + +func _on_CopyButton_pressed(): + OS.clipboard = _ctrls.output.text + + +func _on_ClearButton_pressed(): + _ctrls.output.clear() + + +func _on_Shortcuts_pressed(): + _ctrls.shortcut_dialog.popup_centered() + + +func _on_BottomPanelShortcuts_popup_hide(): + _apply_shortcuts() + _ctrls.shortcut_dialog.save_shortcuts(SHORTCUTS_PATH) + + +func _on_Light_draw(): + var l = _ctrls.light + l.draw_circle(Vector2(l.rect_size.x / 2, l.rect_size.y / 2), l.rect_size.x / 2, _light_color) + + +func _on_RunAtCursor_run_tests(what): + _gut_config.options.selected = what.script + _gut_config.options.inner_class = what.inner_class + _gut_config.options.unit_test_name = what.test_method + + _run_tests() + + +# --------------- +# Public +# --------------- + +func load_result_output(): + _ctrls.output.bbcode_text = get_file_as_text(RESULT_FILE) + _ctrls.output.grab_focus() + _ctrls.output.scroll_to_line(_ctrls.output.get_line_count() -1) + + var summary = get_file_as_text(RESULT_JSON) + var results = JSON.parse(summary) + if(results.error != OK): + return + var summary_json = results.result['test_scripts']['props'] + _ctrls.results.passing.text = str(summary_json.passing) + _ctrls.results.passing.get_parent().visible = true + + _ctrls.results.failing.text = str(summary_json.failures) + _ctrls.results.failing.get_parent().visible = true + + _ctrls.results.pending.text = str(summary_json.pending) + _ctrls.results.pending.get_parent().visible = _ctrls.results.pending.text != '0' + + _ctrls.results.errors.text = str(summary_json.errors) + _ctrls.results.errors.get_parent().visible = _ctrls.results.errors.text != '0' + + _ctrls.results.warnings.text = str(summary_json.warnings) + _ctrls.results.warnings.get_parent().visible = _ctrls.results.warnings.text != '0' + + _ctrls.results.orphans.text = str(summary_json.orphans) + _ctrls.results.orphans.get_parent().visible = _ctrls.results.orphans.text != '0' and !_gut_config.options.hide_orphans + + if(summary_json.tests == 0): + _light_color = Color(1, 0, 0, .75) + elif(summary_json.failures != 0): + _light_color = Color(1, 0, 0, .75) + elif(summary_json.pending != 0): + _light_color = Color(1, 1, 0, .75) + else: + _light_color = Color(0, 1, 0, .75) + _ctrls.light.update() + + + +func set_current_script(script): + if(script): + if(_is_test_script(script)): + var file = script.resource_path.get_file() + _last_selected_path = script.resource_path.get_file() + _ctrls.run_at_cursor.activate_for_script(script.resource_path) + + +func set_interface(value): + _interface = value + _interface.get_script_editor().connect("editor_script_changed", self, '_on_editor_script_changed') + _ctrls.run_at_cursor.set_script_editor(_interface.get_script_editor()) + set_current_script(_interface.get_script_editor().get_current_script()) + + +func set_plugin(value): + _gut_plugin = value + + +func set_panel_button(value): + _panel_button = value + +# ------------------------------------------------------------------------------ +# Write a file. +# ------------------------------------------------------------------------------ +func write_file(path, content): + var f = File.new() + var result = f.open(path, f.WRITE) + if(result == OK): + f.store_string(content) + f.close() + return result + + +# ------------------------------------------------------------------------------ +# Returns the text of a file or an empty string if the file could not be opened. +# ------------------------------------------------------------------------------ +func get_file_as_text(path): + var to_return = '' + var f = File.new() + var result = f.open(path, f.READ) + if(result == OK): + to_return = f.get_as_text() + f.close() + return to_return + + +# ------------------------------------------------------------------------------ +# return if_null if value is null otherwise return value +# ------------------------------------------------------------------------------ +func nvl(value, if_null): + if(value == null): + return if_null + else: + return value + + diff --git a/addons/gut/gui/GutBottomPanel.tscn b/addons/gut/gui/GutBottomPanel.tscn new file mode 100644 index 0000000..0bb7fc3 --- /dev/null +++ b/addons/gut/gui/GutBottomPanel.tscn @@ -0,0 +1,386 @@ +[gd_scene load_steps=16 format=2] + +[ext_resource path="res://addons/gut/gui/GutBottomPanel.gd" type="Script" id=1] +[ext_resource path="res://addons/gut/gui/BottomPanelShortcuts.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/gut/gui/RunAtCursor.tscn" type="PackedScene" id=3] +[ext_resource path="res://addons/gut/gui/play.png" type="Texture" id=4] + +[sub_resource type="InputEventKey" id=8] +control = true +scancode = 49 + +[sub_resource type="ShortCut" id=9] +shortcut = SubResource( 8 ) + +[sub_resource type="StyleBoxEmpty" id=5] + +[sub_resource type="DynamicFontData" id=10] +font_path = "res://addons/gut/fonts/CourierPrime-BoldItalic.ttf" + +[sub_resource type="DynamicFont" id=11] +size = 30 +font_data = SubResource( 10 ) + +[sub_resource type="DynamicFontData" id=12] +font_path = "res://addons/gut/fonts/CourierPrime-Italic.ttf" + +[sub_resource type="DynamicFont" id=13] +size = 30 +font_data = SubResource( 12 ) + +[sub_resource type="DynamicFontData" id=14] +font_path = "res://addons/gut/fonts/CourierPrime-Bold.ttf" + +[sub_resource type="DynamicFont" id=15] +size = 30 +font_data = SubResource( 14 ) + +[sub_resource type="DynamicFontData" id=16] +font_path = "res://addons/gut/fonts/CourierPrime-Regular.ttf" + +[sub_resource type="DynamicFont" id=17] +size = 30 +font_data = SubResource( 16 ) + +[node name="GutBottomPanel" type="Control"] +anchor_left = -0.0025866 +anchor_top = -0.00176575 +anchor_right = 0.997413 +anchor_bottom = 0.998234 +margin_left = 2.64868 +margin_top = 1.05945 +margin_right = 2.64862 +margin_bottom = 1.05945 +rect_min_size = Vector2( 0, 300 ) +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="layout" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ControlBar" type="HBoxContainer" parent="layout"] +margin_right = 1023.0 +margin_bottom = 40.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="RunAll" type="Button" parent="layout/ControlBar"] +margin_right = 150.0 +margin_bottom = 40.0 +rect_min_size = Vector2( 150, 0 ) +hint_tooltip = "Run all test scripts in the suite." +size_flags_vertical = 11 +shortcut = SubResource( 9 ) +text = "Run All" +icon = ExtResource( 4 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="layout/ControlBar"] +margin_left = 154.0 +margin_top = 13.0 +margin_right = 213.0 +margin_bottom = 27.0 +hint_tooltip = "When a test script is edited, buttons are displayed to +run the opened script or an Inner-Test-Class or a +single test. The buttons change based on the location +of the cursor in the file. + +These buttons will remain active when editing other +items so that you can run tests without having to switch +back to the test script. + +You can assign keyboard shortcuts for these buttons +using the \"shortcuts\" button in the GUT panel." +mouse_filter = 1 +text = "Current: " + +[node name="RunAtCursor" parent="layout/ControlBar" instance=ExtResource( 3 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 217.0 +margin_right = 456.0 +margin_bottom = 40.0 +rect_min_size = Vector2( 0, 40 ) + +[node name="CenterContainer2" type="CenterContainer" parent="layout/ControlBar"] +margin_left = 460.0 +margin_right = 699.0 +margin_bottom = 40.0 +size_flags_horizontal = 3 + +[node name="FocusButton" type="Button" parent="layout/ControlBar"] +show_behind_parent = true +margin_left = 703.0 +margin_right = 703.0 +margin_bottom = 40.0 +custom_styles/normal = SubResource( 5 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CenterContainer" type="CenterContainer" parent="layout/ControlBar"] +margin_left = 707.0 +margin_right = 946.0 +margin_bottom = 40.0 +size_flags_horizontal = 3 + +[node name="Shortcuts" type="Button" parent="layout/ControlBar"] +margin_left = 950.0 +margin_right = 1022.0 +margin_bottom = 40.0 +hint_tooltip = "Set shortcuts for GUT buttons. Shortcuts do not work when the GUT panel is not visible." +size_flags_vertical = 11 +text = "Shortcuts" + +[node name="RSplit" type="HSplitContainer" parent="layout"] +margin_top = 44.0 +margin_right = 1023.0 +margin_bottom = 599.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="CResults" type="VBoxContainer" parent="layout/RSplit"] +margin_right = 611.0 +margin_bottom = 555.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ControlBar" type="HBoxContainer" parent="layout/RSplit/CResults"] +margin_right = 611.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 0, 35 ) + +[node name="Light" type="Control" parent="layout/RSplit/CResults/ControlBar"] +margin_right = 30.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 30, 30 ) + +[node name="Passing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 107.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Passing"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 54.0 +margin_bottom = 24.0 +text = "Passing" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Passing"] +margin_left = 58.0 +margin_top = 10.0 +margin_right = 73.0 +margin_bottom = 24.0 +text = "---" + +[node name="Failing" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 100.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Failing"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 47.0 +margin_bottom = 24.0 +text = "Failing" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Failing"] +margin_left = 51.0 +margin_top = 10.0 +margin_right = 66.0 +margin_bottom = 24.0 +text = "---" + +[node name="Pending" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 110.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Pending"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 57.0 +margin_bottom = 24.0 +text = "Pending" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Pending"] +margin_left = 61.0 +margin_top = 10.0 +margin_right = 76.0 +margin_bottom = 24.0 +text = "---" + +[node name="Orphans" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 110.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Orphans"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 57.0 +margin_bottom = 24.0 +text = "Orphans" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Orphans"] +margin_left = 61.0 +margin_top = 10.0 +margin_right = 76.0 +margin_bottom = 24.0 +text = "---" + +[node name="Errors" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 96.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Errors"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 43.0 +margin_bottom = 24.0 +hint_tooltip = "The number of GUT errors generated. This does not include engine errors." +text = "Errors" + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Errors"] +margin_left = 47.0 +margin_top = 10.0 +margin_right = 62.0 +margin_bottom = 24.0 +text = "---" + +[node name="Warnings" type="HBoxContainer" parent="layout/RSplit/CResults/ControlBar"] +visible = false +margin_left = 34.0 +margin_right = 118.0 +margin_bottom = 35.0 + +[node name="Sep" type="ColorRect" parent="layout/RSplit/CResults/ControlBar/Warnings"] +margin_right = 2.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 2, 0 ) + +[node name="label" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"] +margin_left = 6.0 +margin_top = 10.0 +margin_right = 65.0 +margin_bottom = 24.0 +text = "Warnings" +__meta__ = { +"_editor_description_": "The number of GUT Warnings generated. This does not include engine warnings." +} + +[node name="value" type="Label" parent="layout/RSplit/CResults/ControlBar/Warnings"] +margin_left = 69.0 +margin_top = 10.0 +margin_right = 84.0 +margin_bottom = 24.0 +text = "---" + +[node name="CenterContainer" type="CenterContainer" parent="layout/RSplit/CResults/ControlBar"] +margin_left = 34.0 +margin_right = 488.0 +margin_bottom = 35.0 +size_flags_horizontal = 3 + +[node name="CopyButton" type="Button" parent="layout/RSplit/CResults/ControlBar"] +margin_left = 492.0 +margin_right = 547.0 +margin_bottom = 35.0 +text = " Copy " + +[node name="ClearButton" type="Button" parent="layout/RSplit/CResults/ControlBar"] +margin_left = 551.0 +margin_right = 611.0 +margin_bottom = 35.0 +text = " Clear " + +[node name="Output" type="RichTextLabel" parent="layout/RSplit/CResults"] +margin_top = 39.0 +margin_right = 611.0 +margin_bottom = 555.0 +focus_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +custom_fonts/bold_italics_font = SubResource( 11 ) +custom_fonts/italics_font = SubResource( 13 ) +custom_fonts/bold_font = SubResource( 15 ) +custom_fonts/normal_font = SubResource( 17 ) +bbcode_enabled = true +scroll_following = true +selection_enabled = true + +[node name="sc" type="ScrollContainer" parent="layout/RSplit"] +margin_left = 623.0 +margin_right = 1023.0 +margin_bottom = 555.0 +rect_min_size = Vector2( 400, 0 ) +mouse_filter = 1 +size_flags_vertical = 3 + +[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"] +margin_right = 388.0 +margin_bottom = 862.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="BottomPanelShortcuts" parent="." instance=ExtResource( 2 )] +visible = false +anchor_left = -0.000517324 +anchor_top = 0.000882874 +anchor_right = 0.233483 +anchor_bottom = 0.328883 +margin_left = 10.0649 +margin_top = -173.752 +margin_right = 31.6969 +margin_bottom = -125.552 + +[connection signal="pressed" from="layout/ControlBar/RunAll" to="." method="_on_RunAll_pressed"] +[connection signal="run_tests" from="layout/ControlBar/RunAtCursor" to="." method="_on_RunAtCursor_run_tests"] +[connection signal="pressed" from="layout/ControlBar/FocusButton" to="." method="_on_FocusButton_pressed"] +[connection signal="pressed" from="layout/ControlBar/Shortcuts" to="." method="_on_Shortcuts_pressed"] +[connection signal="draw" from="layout/RSplit/CResults/ControlBar/Light" to="." method="_on_Light_draw"] +[connection signal="pressed" from="layout/RSplit/CResults/ControlBar/CopyButton" to="." method="_on_CopyButton_pressed"] +[connection signal="pressed" from="layout/RSplit/CResults/ControlBar/ClearButton" to="." method="_on_ClearButton_pressed"] +[connection signal="popup_hide" from="BottomPanelShortcuts" to="." method="_on_BottomPanelShortcuts_popup_hide"] diff --git a/addons/gut/gui/GutRunner.gd b/addons/gut/gui/GutRunner.gd new file mode 100644 index 0000000..b2b16f2 --- /dev/null +++ b/addons/gut/gui/GutRunner.gd @@ -0,0 +1,98 @@ +extends Node2D + +var Gut = load('res://addons/gut/gut.gd') +var ResultExporter = load('res://addons/gut/result_exporter.gd') +var GutConfig = load('res://addons/gut/gut_config.gd') + +const RUNNER_JSON_PATH = 'res://.gut_editor_config.json' +const RESULT_FILE = 'user://.gut_editor.bbcode' +const RESULT_JSON = 'user://.gut_editor.json' + +var _gut_config = null +var _gut = null; +var _wrote_results = false +# Flag for when this is being used at the command line. Otherwise it is +# assumed this is being used by the panel and being launched with +# play_custom_scene +var _cmdln_mode = false + +onready var _gut_layer = $GutLayer + + +func _ready(): + if(_gut_config == null): + _gut_config = GutConfig.new() + _gut_config.load_options(RUNNER_JSON_PATH) + + + # The command line will call run_tests on its own. When used from the panel + # we have to kick off the tests ourselves b/c there's no way I know of to + # interact with the scene that was run via play_custom_scene. + if(!_cmdln_mode): + call_deferred('run_tests') + + +func run_tests(): + if(_gut == null): + _gut = Gut.new() + + _gut.set_add_children_to(self) + if(_gut_config.options.gut_on_top): + _gut_layer.add_child(_gut) + else: + add_child(_gut) + + if(!_cmdln_mode): + _gut.connect('tests_finished', self, '_on_tests_finished', + [_gut_config.options.should_exit, _gut_config.options.should_exit_on_success]) + + _gut_config.config_gut(_gut) + if(_gut_config.options.gut_on_top): + _gut.get_gui().goto_bottom_right_corner() + + var run_rest_of_scripts = _gut_config.options.unit_test_name == '' + _gut.test_scripts(run_rest_of_scripts) + + +func _write_results(): + # bbcode_text appears to be empty. I'm not 100% sure why. Until that is + # figured out we have to just get the text which stinks. + var content = _gut.get_gui().get_text_box().text + + var f = File.new() + var result = f.open(RESULT_FILE, f.WRITE) + if(result == OK): + f.store_string(content) + f.close() + else: + print('ERROR Could not save bbcode, result = ', result) + + var exporter = ResultExporter.new() + var f_result = exporter.write_summary_file(_gut, RESULT_JSON) + _wrote_results = true + + +func _exit_tree(): + if(!_wrote_results and !_cmdln_mode): + _write_results() + + +func _on_tests_finished(should_exit, should_exit_on_success): + _write_results() + + if(should_exit): + get_tree().quit() + elif(should_exit_on_success and _gut.get_fail_count() == 0): + get_tree().quit() + + +func get_gut(): + if(_gut == null): + _gut = Gut.new() + return _gut + +func set_gut_config(which): + _gut_config = which + +func set_cmdln_mode(is_it): + _cmdln_mode = is_it diff --git a/addons/gut/gui/GutRunner.tscn b/addons/gut/gui/GutRunner.tscn new file mode 100644 index 0000000..077e411 --- /dev/null +++ b/addons/gut/gui/GutRunner.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/gut/gui/GutRunner.gd" type="Script" id=1] + +[node name="GutRunner" type="Node2D"] +script = ExtResource( 1 ) + +[node name="GutLayer" type="CanvasLayer" parent="."] +layer = 128 diff --git a/addons/gut/gui/GutSceneTheme.tres b/addons/gut/gui/GutSceneTheme.tres new file mode 100644 index 0000000..565a6af --- /dev/null +++ b/addons/gut/gui/GutSceneTheme.tres @@ -0,0 +1,11 @@ +[gd_resource type="Theme" load_steps=3 format=2] + +[sub_resource type="DynamicFontData" id=9] +font_path = "res://addons/gut/fonts/AnonymousPro-Regular.ttf" + +[sub_resource type="DynamicFont" id=10] +size = 14 +font_data = SubResource( 9 ) + +[resource] +default_font = SubResource( 10 ) diff --git a/addons/gut/gui/RunAtCursor.gd b/addons/gut/gui/RunAtCursor.gd new file mode 100644 index 0000000..960414e --- /dev/null +++ b/addons/gut/gui/RunAtCursor.gd @@ -0,0 +1,130 @@ +tool +extends Control + + +var ScriptTextEditors = load('res://addons/gut/gui/script_text_editor_controls.gd') + +onready var _ctrls = { + btn_script = $HBox/BtnRunScript, + btn_inner = $HBox/BtnRunInnerClass, + btn_method = $HBox/BtnRunMethod, + lbl_none = $HBox/LblNoneSelected, + arrow_1 = $HBox/Arrow1, + arrow_2 = $HBox/Arrow2 +} + +var _editors = null +var _script_editor = null +var _cur_editor = null +var _last_line = -1 +var _cur_script_path = null +var _last_info = null + +signal run_tests(what) + + +func _ready(): + _ctrls.lbl_none.visible = true + _ctrls.btn_script.visible = false + _ctrls.btn_inner.visible = false + _ctrls.btn_method.visible = false + + +func _set_editor(which): + _last_line = -1 + if(_cur_editor != null and _cur_editor.get_ref()): + _cur_editor.get_ref().disconnect('cursor_changed', self, '_on_cursor_changed') + + if(which != null): + _cur_editor = weakref(which) + which.connect('cursor_changed', self, '_on_cursor_changed', [which]) + + _last_line = which.cursor_get_line() + _last_info = _editors.get_line_info() + _update_buttons(_last_info) + + + +func _update_buttons(info): + _ctrls.lbl_none.visible = _cur_script_path == null + _ctrls.btn_script.visible = _cur_script_path != null + + _ctrls.btn_inner.visible = info.inner_class != null + _ctrls.arrow_1.visible = info.inner_class != null + _ctrls.btn_inner.text = str(info.inner_class) + _ctrls.btn_inner.hint_tooltip = str("Run all tests in Inner-Test-Class ", info.inner_class) + + _ctrls.btn_method.visible = info.test_method != null + _ctrls.arrow_2.visible = info.test_method != null + _ctrls.btn_method.text = str(info.test_method) + _ctrls.btn_method.hint_tooltip = str("Run test ", info.test_method) + + # The button's new size won't take effect until the next frame. + # This appears to be what was causing the button to not be clickable the + # first time. + call_deferred("_update_rect_size") + + +func _update_rect_size(): + rect_min_size.x = _ctrls.btn_method.rect_size.x + _ctrls.btn_method.rect_position.x + +func _on_cursor_changed(which): + if(which.cursor_get_line() != _last_line): + _last_line = which.cursor_get_line() + _last_info = _editors.get_line_info() + _update_buttons(_last_info) + + +func set_script_editor(value): + _script_editor = value + _editors = ScriptTextEditors.new(value) + + +func activate_for_script(path): + _ctrls.btn_script.visible = true + _ctrls.btn_script.text = path.get_file() + _ctrls.btn_script.hint_tooltip = str("Run all tests in script ", path) + _cur_script_path = path + _editors.refresh() + _set_editor(_editors.get_current_text_edit()) + + +func _on_BtnRunScript_pressed(): + var info = _last_info.duplicate() + info.script = _cur_script_path.get_file() + info.inner_class = null + info.test_method = null + emit_signal("run_tests", info) + + +func _on_BtnRunInnerClass_pressed(): + var info = _last_info.duplicate() + info.script = _cur_script_path.get_file() + info.test_method = null + emit_signal("run_tests", info) + + +func _on_BtnRunMethod_pressed(): + var info = _last_info.duplicate() + info.script = _cur_script_path.get_file() + emit_signal("run_tests", info) + + +func get_script_button(): + return _ctrls.btn_script + + +func get_inner_button(): + return _ctrls.btn_inner + + +func get_test_button(): + return _ctrls.btn_method + +# not used, thought was configurable but it's just the script prefix +func set_method_prefix(value): + _editors.set_method_prefix(value) + +# not used, thought was configurable but it's just the script prefix +func set_inner_class_prefix(value): + _editors.set_inner_class_prefix(value) diff --git a/addons/gut/gui/RunAtCursor.tscn b/addons/gut/gui/RunAtCursor.tscn new file mode 100644 index 0000000..b4662df --- /dev/null +++ b/addons/gut/gui/RunAtCursor.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/gut/gui/RunAtCursor.gd" type="Script" id=1] +[ext_resource path="res://addons/gut/gui/play.png" type="Texture" id=2] +[ext_resource path="res://addons/gut/gui/arrow.png" type="Texture" id=3] + +[node name="RunAtCursor" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_right = 1.0 +margin_bottom = -527.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HBox" type="HBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="LblNoneSelected" type="Label" parent="HBox"] +margin_top = 29.0 +margin_right = 50.0 +margin_bottom = 43.0 +text = "<None>" + +[node name="BtnRunScript" type="Button" parent="HBox"] +visible = false +margin_left = 54.0 +margin_right = 140.0 +margin_bottom = 73.0 +text = "<script>" +icon = ExtResource( 2 ) + +[node name="Arrow1" type="TextureButton" parent="HBox"] +visible = false +margin_left = 54.0 +margin_right = 78.0 +margin_bottom = 73.0 +rect_min_size = Vector2( 24, 0 ) +texture_normal = ExtResource( 3 ) +expand = true +stretch_mode = 3 + +[node name="BtnRunInnerClass" type="Button" parent="HBox"] +visible = false +margin_left = 134.0 +margin_right = 243.0 +margin_bottom = 73.0 +text = "<inner class>" +icon = ExtResource( 2 ) + +[node name="Arrow2" type="TextureButton" parent="HBox"] +visible = false +margin_left = 54.0 +margin_right = 78.0 +margin_bottom = 73.0 +rect_min_size = Vector2( 24, 0 ) +texture_normal = ExtResource( 3 ) +expand = true +stretch_mode = 3 + +[node name="BtnRunMethod" type="Button" parent="HBox"] +visible = false +margin_left = 247.0 +margin_right = 337.0 +margin_bottom = 73.0 +text = "<method>" +icon = ExtResource( 2 ) + +[connection signal="pressed" from="HBox/BtnRunScript" to="." method="_on_BtnRunScript_pressed"] +[connection signal="pressed" from="HBox/BtnRunInnerClass" to="." method="_on_BtnRunInnerClass_pressed"] +[connection signal="pressed" from="HBox/BtnRunMethod" to="." method="_on_BtnRunMethod_pressed"] diff --git a/addons/gut/gui/Settings.tscn b/addons/gut/gui/Settings.tscn new file mode 100644 index 0000000..819a56e --- /dev/null +++ b/addons/gut/gui/Settings.tscn @@ -0,0 +1,7 @@ +[gd_scene format=2] + +[node name="Settings" type="VBoxContainer"] +margin_right = 388.0 +margin_bottom = 586.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 diff --git a/addons/gut/gui/ShortcutButton.gd b/addons/gut/gui/ShortcutButton.gd new file mode 100644 index 0000000..116659d --- /dev/null +++ b/addons/gut/gui/ShortcutButton.gd @@ -0,0 +1,164 @@ +tool +extends Control + + +onready var _ctrls = { + shortcut_label = $Layout/lblShortcut, + set_button = $Layout/SetButton, + save_button = $Layout/SaveButton, + cancel_button = $Layout/CancelButton, + clear_button = $Layout/ClearButton +} + +signal changed +signal start_edit +signal end_edit + +const NO_SHORTCUT = '<None>' + +var _source_event = InputEventKey.new() +var _pre_edit_event = null +var _key_disp = NO_SHORTCUT + +var _modifier_keys = [KEY_ALT, KEY_CONTROL, KEY_META, KEY_SHIFT] + +# Called when the node enters the scene tree for the first time. +func _ready(): + set_process_unhandled_key_input(false) + + +func _display_shortcut(): + _ctrls.shortcut_label.text = to_s() + + +func _is_shift_only_modifier(): + return _source_event.shift and \ + !(_source_event.meta or _source_event.control or _source_event.alt) + + +func _has_modifier(): + return _source_event.alt or _source_event.control or _source_event.meta or _source_event.shift + + +func _is_modifier(scancode): + return _modifier_keys.has(scancode) + + +func _edit_mode(should): + set_process_unhandled_key_input(should) + _ctrls.set_button.visible = !should + _ctrls.save_button.visible = should + _ctrls.save_button.disabled = should + _ctrls.cancel_button.visible = should + _ctrls.clear_button.visible = !should + + if(should and to_s() == ''): + _ctrls.shortcut_label.text = 'press buttons' + else: + _ctrls.shortcut_label.text = to_s() + + if(should): + emit_signal("start_edit") + else: + emit_signal("end_edit") + +# --------------- +# Events +# --------------- +func _unhandled_key_input(event): + if(event is InputEventKey): + if(event.pressed): + _source_event.alt = event.alt or event.scancode == KEY_ALT + _source_event.control = event.control or event.scancode == KEY_CONTROL + _source_event.meta = event.meta or event.scancode == KEY_META + _source_event.shift = event.shift or event.scancode == KEY_SHIFT + + if(_has_modifier() and !_is_modifier(event.scancode)): + _source_event.scancode = event.scancode + _key_disp = OS.get_scancode_string(event.scancode) + else: +# _source_event.set_scancode = null + _key_disp = NO_SHORTCUT + _display_shortcut() + _ctrls.save_button.disabled = !is_valid() + + +func _on_SetButton_pressed(): + _pre_edit_event = _source_event.duplicate(true) + _edit_mode(true) + + +func _on_SaveButton_pressed(): + _edit_mode(false) + _pre_edit_event = null + emit_signal('changed') + + +func _on_CancelButton_pressed(): + _edit_mode(false) + _source_event = _pre_edit_event + _key_disp = OS.get_scancode_string(_source_event.scancode) + if(_key_disp == ''): + _key_disp = NO_SHORTCUT + _display_shortcut() + + +func _on_ClearButton_pressed(): + clear_shortcut() + +# --------------- +# Public +# --------------- +func to_s(): + var modifiers = [] + if(_source_event.alt): + modifiers.append('alt') + if(_source_event.control): + modifiers.append('ctrl') + if(_source_event.meta): + modifiers.append('meta') + if(_source_event.shift): + modifiers.append('shift') + + if(_source_event.scancode != null): + modifiers.append(_key_disp) + + var mod_text = '' + for i in range(modifiers.size()): + mod_text += modifiers[i] + if(i != modifiers.size() - 1): + mod_text += ' + ' + + return mod_text + + +func is_valid(): + return _has_modifier() and _key_disp != NO_SHORTCUT and !_is_shift_only_modifier() + + +func get_shortcut(): + var to_return = ShortCut.new() + to_return.shortcut = _source_event + return to_return + + +func set_shortcut(sc): + if(sc == null or sc.shortcut == null): + clear_shortcut() + else: + _source_event = sc.shortcut + _key_disp = OS.get_scancode_string(_source_event.scancode) + _display_shortcut() + + +func clear_shortcut(): + _source_event = InputEventKey.new() + _key_disp = NO_SHORTCUT + _display_shortcut() + + +func disable_set(should): + _ctrls.set_button.disabled = should + +func disable_clear(should): + _ctrls.clear_button.disabled = should diff --git a/addons/gut/gui/ShortcutButton.tscn b/addons/gut/gui/ShortcutButton.tscn new file mode 100644 index 0000000..3c94317 --- /dev/null +++ b/addons/gut/gui/ShortcutButton.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/gut/gui/ShortcutButton.gd" type="Script" id=1] + +[node name="ShortcutButton" type="Control"] +anchor_right = 0.123 +anchor_bottom = 0.04 +margin_right = 33.048 +margin_bottom = 1.0 +rect_min_size = Vector2( 125, 25 ) +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Layout" type="HBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="lblShortcut" type="Label" parent="Layout"] +margin_right = 50.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 +size_flags_vertical = 7 +text = "<None>" +align = 2 +valign = 1 + +[node name="CenterContainer" type="CenterContainer" parent="Layout"] +margin_left = 54.0 +margin_right = 64.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 10, 0 ) + +[node name="SetButton" type="Button" parent="Layout"] +margin_left = 68.0 +margin_right = 128.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 60, 0 ) +text = "Set" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="SaveButton" type="Button" parent="Layout"] +visible = false +margin_left = 82.0 +margin_right = 142.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 60, 0 ) +text = "Save" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CancelButton" type="Button" parent="Layout"] +visible = false +margin_left = 82.0 +margin_right = 142.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 60, 0 ) +text = "Cancel" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ClearButton" type="Button" parent="Layout"] +margin_left = 132.0 +margin_right = 192.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 60, 0 ) +text = "Clear" + +[connection signal="pressed" from="Layout/SetButton" to="." method="_on_SetButton_pressed"] +[connection signal="pressed" from="Layout/SaveButton" to="." method="_on_SaveButton_pressed"] +[connection signal="pressed" from="Layout/CancelButton" to="." method="_on_CancelButton_pressed"] +[connection signal="pressed" from="Layout/ClearButton" to="." method="_on_ClearButton_pressed"] diff --git a/addons/gut/gui/arrow.png b/addons/gut/gui/arrow.png new file mode 100644 index 0000000..09d8323 --- /dev/null +++ b/addons/gut/gui/arrow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9918e125593427685252d339f9c49d2ce8ce0b0fff9d28089164d78fccd80e2 +size 645 diff --git a/addons/gut/gui/arrow.png.import b/addons/gut/gui/arrow.png.import new file mode 100644 index 0000000..3336a6b --- /dev/null +++ b/addons/gut/gui/arrow.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/arrow.png-2b5b2d838b5b3467cf300ac2da1630d9.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/gut/gui/arrow.png" +dest_files=[ "res://.import/arrow.png-2b5b2d838b5b3467cf300ac2da1630d9.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/addons/gut/gui/gut_config_gui.gd b/addons/gut/gui/gut_config_gui.gd new file mode 100644 index 0000000..dc58be4 --- /dev/null +++ b/addons/gut/gui/gut_config_gui.gd @@ -0,0 +1,408 @@ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +class DirectoryCtrl: + extends HBoxContainer + + var text = '' setget set_text, get_text + var _txt_path = LineEdit.new() + var _btn_dir = Button.new() + var _dialog = FileDialog.new() + + func _init(): + _btn_dir.text = '...' + _btn_dir.connect('pressed', self, '_on_dir_button_pressed') + + _txt_path.size_flags_horizontal = _txt_path.SIZE_EXPAND_FILL + + _dialog.mode = _dialog.MODE_OPEN_DIR + _dialog.resizable = true + _dialog.connect("dir_selected", self, '_on_selected') + _dialog.connect("file_selected", self, '_on_selected') + _dialog.rect_size = Vector2(1000, 700) + + func _on_selected(path): + set_text(path) + + + func _on_dir_button_pressed(): + _dialog.current_dir = _txt_path.text + _dialog.popup_centered() + + + func _ready(): + add_child(_txt_path) + add_child(_btn_dir) + add_child(_dialog) + + func get_text(): + return _txt_path.text + + func set_text(t): + text = t + _txt_path.text = text + + func get_line_edit(): + return _txt_path + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +class FileCtrl: + extends DirectoryCtrl + + func _init(): + _dialog.mode = _dialog.MODE_OPEN_FILE + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +class Vector2Ctrl: + extends VBoxContainer + + var value = Vector2(-1, -1) setget set_value, get_value + var disabled = false setget set_disabled, get_disabled + var x_spin = SpinBox.new() + var y_spin = SpinBox.new() + + func _init(): + add_child(_make_one('x: ', x_spin)) + add_child(_make_one('y: ', y_spin)) + + func _make_one(txt, spinner): + var hbox = HBoxContainer.new() + var lbl = Label.new() + lbl.text = txt + hbox.add_child(lbl) + hbox.add_child(spinner) + spinner.min_value = -1 + spinner.max_value = 10000 + spinner.size_flags_horizontal = spinner.SIZE_EXPAND_FILL + return hbox + + func set_value(v): + if(v != null): + x_spin.value = v[0] + y_spin.value = v[1] + + # Returns array instead of vector2 b/c that is what is stored in + # in the dictionary and what is expected everywhere else. + func get_value(): + return [x_spin.value, y_spin.value] + + func set_disabled(should): + get_parent().visible = !should + x_spin.visible = !should + y_spin.visible = !should + + func get_disabled(): + pass + + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +var _base_container = null +var _base_control = null +const DIRS_TO_LIST = 6 +var _cfg_ctrls = {} +var _avail_fonts = ['AnonymousPro', 'CourierPrime', 'LobsterTwo', 'Default'] + + +func _init(cont): + _base_container = cont + + _base_control = HBoxContainer.new() + _base_control.size_flags_horizontal = _base_control.SIZE_EXPAND_FILL + _base_control.mouse_filter = _base_control.MOUSE_FILTER_PASS + + # I don't remember what this is all about at all. Could be + # garbage. Decided to spend more time typing this message + # than figuring it out. + var lbl = Label.new() + lbl.size_flags_horizontal = lbl.SIZE_EXPAND_FILL + lbl.mouse_filter = lbl.MOUSE_FILTER_STOP + _base_control.add_child(lbl) + + +# ------------------ +# Private +# ------------------ +func _new_row(key, disp_text, value_ctrl, hint): + var ctrl = _base_control.duplicate() + var lbl = ctrl.get_node("Label") + + lbl.hint_tooltip = hint + lbl.text = disp_text + _base_container.add_child(ctrl) + + _cfg_ctrls[key] = value_ctrl + ctrl.add_child(value_ctrl) + + var rpad = CenterContainer.new() + rpad.rect_min_size.x = 5 + ctrl.add_child(rpad) + + return ctrl + + +func _add_title(text): + var row = _base_control.duplicate() + var lbl = row.get_node('Label') + + lbl.text = text + lbl.align = Label.ALIGN_CENTER + _base_container.add_child(row) + + row.connect('draw', self, '_on_title_cell_draw', [row]) + + +func _add_number(key, value, disp_text, v_min, v_max, hint=''): + var value_ctrl = SpinBox.new() + value_ctrl.value = value + value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL + value_ctrl.min_value = v_min + value_ctrl.max_value = v_max + _wire_select_on_focus(value_ctrl.get_line_edit()) + + _new_row(key, disp_text, value_ctrl, hint) + + +func _add_select(key, value, values, disp_text, hint=''): + var value_ctrl = OptionButton.new() + var select_idx = 0 + for i in range(values.size()): + value_ctrl.add_item(values[i]) + if(value == values[i]): + select_idx = i + value_ctrl.selected = select_idx + value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL + + _new_row(key, disp_text, value_ctrl, hint) + + +func _add_value(key, value, disp_text, hint=''): + var value_ctrl = LineEdit.new() + value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL + value_ctrl.text = value + _wire_select_on_focus(value_ctrl) + + _new_row(key, disp_text, value_ctrl, hint) + + +func _add_boolean(key, value, disp_text, hint=''): + var value_ctrl = CheckBox.new() + value_ctrl.pressed = value + + _new_row(key, disp_text, value_ctrl, hint) + + +func _add_directory(key, value, disp_text, hint=''): + var value_ctrl = DirectoryCtrl.new() + value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL + value_ctrl.text = value + _wire_select_on_focus(value_ctrl.get_line_edit()) + + _new_row(key, disp_text, value_ctrl, hint) + + +func _add_file(key, value, disp_text, hint=''): + var value_ctrl = FileCtrl.new() + value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL + value_ctrl.text = value + _wire_select_on_focus(value_ctrl.get_line_edit()) + + _new_row(key, disp_text, value_ctrl, hint) + + +func _add_color(key, value, disp_text, hint=''): + var value_ctrl = ColorPickerButton.new() + value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL + value_ctrl.color = value + + _new_row(key, disp_text, value_ctrl, hint) + + +func _add_vector2(key, value, disp_text, hint=''): + var value_ctrl = Vector2Ctrl.new() + value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL + value_ctrl.value = value + _wire_select_on_focus(value_ctrl.x_spin.get_line_edit()) + _wire_select_on_focus(value_ctrl.y_spin.get_line_edit()) + + _new_row(key, disp_text, value_ctrl, hint) +# ----------------------------- + + +# ------------------ +# Events +# ------------------ +func _wire_select_on_focus(which): + which.connect('focus_entered', self, '_on_ctrl_focus_highlight', [which]) + which.connect('focus_exited', self, '_on_ctrl_focus_unhighlight', [which]) + + +func _on_ctrl_focus_highlight(which): + if(which.has_method('select_all')): + which.call_deferred('select_all') + + +func _on_ctrl_focus_unhighlight(which): + if(which.has_method('select')): + which.select(0, 0) + + +func _on_title_cell_draw(which): + which.draw_rect(Rect2(Vector2(0, 0), which.rect_size), Color(0, 0, 0, .15)) + + +# ------------------ +# Public +# ------------------ +func get_config_issues(): + var to_return = [] + var has_directory = false + var dir = Directory.new() + + for i in range(DIRS_TO_LIST): + var key = str('directory_', i) + var path = _cfg_ctrls[key].text + if(path != null and path != ''): + has_directory = true + if(!dir.dir_exists(path)): + to_return.append(str('Test directory ', path, ' does not exist.')) + + if(!has_directory): + to_return.append('You do not have any directories set.') + + if(_cfg_ctrls['prefix'].text == ''): + to_return.append("You must set a Script prefix or GUT won't find any scripts") + + return to_return + + +func set_options(options): + _add_title("Settings") + _add_number("log_level", options.log_level, "Log Level", 0, 3, + "Detail level for log messages.\n" + \ + "\t0: Errors and failures only.\n" + \ + "\t1: Adds all test names + warnings + info\n" + \ + "\t2: Shows all asserts\n" + \ + "\t3: Adds more stuff probably, maybe not.") + _add_boolean('ignore_pause', options.ignore_pause, 'Ignore Pause', + "Ignore calls to pause_before_teardown") + _add_boolean('hide_orphans', options.hide_orphans, 'Hide Orphans', + 'Do not display orphan counts in output.') + _add_boolean('should_exit', options.should_exit, 'Exit on Finish', + "Exit when tests finished.") + _add_boolean('should_exit_on_success', options.should_exit_on_success, 'Exit on Success', + "Exit if there are no failures. Does nothing if 'Exit on Finish' is enabled.") + + + _add_title("Panel Output") + _add_select('output_font_name', options.panel_options.font_name, _avail_fonts, 'Font', + "The name of the font to use when running tests and in the output panel to the left.") + _add_number('output_font_size', options.panel_options.font_size, 'Font Size', 5, 100, + "The font size to use when running tests and in the output panel to the left.") + + + _add_title('Runner Window') + _add_boolean("gut_on_top", options.gut_on_top, "On Top", + "The GUT Runner appears above children added during tests.") + _add_number('opacity', options.opacity, 'Opacity', 0, 100, + "The opacity of GUT when tests are running.") + _add_boolean('should_maximize', options.should_maximize, 'Maximize', + "Maximize GUT when tests are being run.") + _add_boolean('compact_mode', options.compact_mode, 'Compact Mode', + 'The runner will be in compact mode. This overrides Maximize.') + + _add_title('Runner Appearance') + _add_select('font_name', options.font_name, _avail_fonts, 'Font', + "The font to use for text output in the Gut Runner.") + _add_number('font_size', options.font_size, 'Font Size', 5, 100, + "The font size for text output in the Gut Runner.") + _add_color('font_color', options.font_color, 'Font Color', + "The font color for text output in the Gut Runner.") + _add_color('background_color', options.background_color, 'Background Color', + "The background color for text output in the Gut Runner.") + _add_boolean('disable_colors', options.disable_colors, 'Disable Formatting', + 'Disable formatting and colors used in the Runner. Does not affect panel output.') + + _add_title('Test Directories') + _add_boolean('include_subdirs', options.include_subdirs, 'Include Subdirs', + "Include subdirectories of the directories configured below.") + for i in range(DIRS_TO_LIST): + var value = '' + if(options.dirs.size() > i): + value = options.dirs[i] + + _add_directory(str('directory_', i), value, str('Directory ', i)) + + _add_title("XML Output") + _add_value("junit_xml_file", options.junit_xml_file, "Output Path", + "Path and filename where GUT should create a JUnit compliant XML file. " + + "This file will contain the results of the last test run. To avoid " + + "overriding the file use Include Timestamp.") + _add_boolean("junit_xml_timestamp", options.junit_xml_timestamp, "Include Timestamp", + "Include a timestamp in the filename so that each run gets its own xml file.") + + + _add_title('Hooks') + _add_file('pre_run_script', options.pre_run_script, 'Pre-Run Hook', + 'This script will be run by GUT before any tests are run.') + _add_file('post_run_script', options.post_run_script, 'Post-Run Hook', + 'This script will be run by GUT after all tests are run.') + + + _add_title('Misc') + _add_value('prefix', options.prefix, 'Script Prefix', + "The filename prefix for all test scripts.") + + +func get_options(base_opts): + var to_return = base_opts.duplicate() + + # Settings + to_return.log_level = _cfg_ctrls.log_level.value + to_return.ignore_pause = _cfg_ctrls.ignore_pause.pressed + to_return.hide_orphans = _cfg_ctrls.hide_orphans.pressed + to_return.should_exit = _cfg_ctrls.should_exit.pressed + to_return.should_exit_on_success = _cfg_ctrls.should_exit_on_success.pressed + + #Output + to_return.panel_options.font_name = _cfg_ctrls.output_font_name.get_item_text( + _cfg_ctrls.output_font_name.selected) + to_return.panel_options.font_size = _cfg_ctrls.output_font_size.value + + # Runner Appearance + to_return.font_name = _cfg_ctrls.font_name.get_item_text( + _cfg_ctrls.font_name.selected) + to_return.font_size = _cfg_ctrls.font_size.value + to_return.should_maximize = _cfg_ctrls.should_maximize.pressed + to_return.compact_mode = _cfg_ctrls.compact_mode.pressed + to_return.opacity = _cfg_ctrls.opacity.value + to_return.background_color = _cfg_ctrls.background_color.color.to_html() + to_return.font_color = _cfg_ctrls.font_color.color.to_html() + to_return.disable_colors = _cfg_ctrls.disable_colors.pressed + to_return.gut_on_top = _cfg_ctrls.gut_on_top.pressed + + + # Directories + to_return.include_subdirs = _cfg_ctrls.include_subdirs.pressed + var dirs = [] + for i in range(DIRS_TO_LIST): + var key = str('directory_', i) + var val = _cfg_ctrls[key].text + if(val != '' and val != null): + dirs.append(val) + to_return.dirs = dirs + + # XML Output + to_return.junit_xml_file = _cfg_ctrls.junit_xml_file.text + to_return.junit_xml_timestamp = _cfg_ctrls.junit_xml_timestamp.pressed + + # Hooks + to_return.pre_run_script = _cfg_ctrls.pre_run_script.text + to_return.post_run_script = _cfg_ctrls.post_run_script.text + + # Misc + to_return.prefix = _cfg_ctrls.prefix.text + + return to_return diff --git a/addons/gut/gui/play.png b/addons/gut/gui/play.png new file mode 100644 index 0000000..697f319 --- /dev/null +++ b/addons/gut/gui/play.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90041b369445e15dfaa4ec011cb4f1c0536a8d63295a574e0f8743f772dc418e +size 676 diff --git a/addons/gut/gui/play.png.import b/addons/gut/gui/play.png.import new file mode 100644 index 0000000..047fdc4 --- /dev/null +++ b/addons/gut/gui/play.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/play.png-5c90e88e8136487a183a099d67a7de24.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/gut/gui/play.png" +dest_files=[ "res://.import/play.png-5c90e88e8136487a183a099d67a7de24.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/addons/gut/gui/script_text_editor_controls.gd b/addons/gut/gui/script_text_editor_controls.gd new file mode 100644 index 0000000..98741e9 --- /dev/null +++ b/addons/gut/gui/script_text_editor_controls.gd @@ -0,0 +1,230 @@ +# Holds weakrefs to a ScriptTextEditor and related children nodes +# that might be useful. Though the TextEdit is really the only one, but +# since the tree may change, the first TextEdit under a CodeTextEditor is +# the one we use...so we hold a ref to the CodeTextEditor too. +class ScriptEditorControlRef: + var _text_edit = null + var _script_editor = null + var _code_editor = null + + func _init(script_edit): + _script_editor = weakref(script_edit) + _populate_controls() + + + func _populate_controls(): + # who knows if the tree will change so get the first instance of each + # type of control we care about. Chances are there won't be more than + # one of these in the future, but their position in the tree may change. + _code_editor = weakref(_get_first_child_named('CodeTextEditor', _script_editor.get_ref())) + _text_edit = weakref(_get_first_child_named("TextEdit", _code_editor.get_ref())) + + + func _get_first_child_named(obj_name, parent_obj): + if(parent_obj == null): + return null + + var kids = parent_obj.get_children() + var index = 0 + var to_return = null + + while(index < kids.size() and to_return == null): + if(str(kids[index]).find(str("[", obj_name)) != -1): + to_return = kids[index] + else: + to_return = _get_first_child_named(obj_name, kids[index]) + if(to_return == null): + index += 1 + + return to_return + + + func get_script_text_edit(): + return _script_editor.get_ref() + + + func get_text_edit(): + # ScriptTextEditors that are loaded when the project is opened + # do not have their children populated yet. So if we may have to + # _populate_controls again if we don't have one. + if(_text_edit == null): + _populate_controls() + return _text_edit.get_ref() + + + func get_script_editor(): + return _script_editor + + + func is_visible(): + var to_return = false + if(_script_editor.get_ref()): + to_return = _script_editor.get_ref().visible + return to_return + +# ############################################################################## +# +# ############################################################################## + +# Used to make searching for the controls easier and faster. +var _script_editors_parent = null +# reference the ScriptEditor instance +var _script_editor = null +# Array of ScriptEditorControlRef containing all the opened ScriptTextEditors +# and related controls at the time of the last refresh. +var _script_editor_controls = [] + +var _method_prefix = 'test_' +var _inner_class_prefix = 'Test' + +func _init(script_edit): + _script_editor = script_edit + refresh() + + +func _is_script_editor(obj): + return str(obj).find('[ScriptTextEditor') != -1 + + +# Find the first ScriptTextEditor and then get its parent. Done this way +# because who knows if the parent object will change. This is somewhat +# future proofed. +func _find_script_editors_parent(): + var _first_editor = _get_first_child_of_type_name("ScriptTextEditor", _script_editor) + if(_first_editor != null): + _script_editors_parent = _first_editor.get_parent() + + +func _populate_editors(): + if(_script_editors_parent == null): + return + + _script_editor_controls.clear() + for child in _script_editors_parent.get_children(): + if(_is_script_editor(child)): + var ref = ScriptEditorControlRef.new(child) + _script_editor_controls.append(ref) + +# Yes, this is the same as the one above but with a different name. This was +# easier than trying to find a place where it could be used by both. +func _get_first_child_of_type_name(obj_name, parent_obj): + if(parent_obj == null): + return null + + var kids = parent_obj.get_children() + var index = 0 + var to_return = null + + while(index < kids.size() and to_return == null): + if(str(kids[index]).find(str("[", obj_name)) != -1): + to_return = kids[index] + else: + to_return = _get_first_child_of_type_name(obj_name, kids[index]) + if(to_return == null): + index += 1 + + return to_return + + +func _get_func_name_from_line(text): + text = text.strip_edges() + var left = text.split("(")[0] + var func_name = left.split(" ")[1] + return func_name + + +func _get_class_name_from_line(text): + text = text.strip_edges() + var right = text.split(" ")[1] + var the_name = right.rstrip(":") + return the_name + +func refresh(): + if(_script_editors_parent == null): + _find_script_editors_parent() + + if(_script_editors_parent != null): + _populate_editors() + + +func get_current_text_edit(): + var cur_script_editor = null + var idx = 0 + + while(idx < _script_editor_controls.size() and cur_script_editor == null): + if(_script_editor_controls[idx].is_visible()): + cur_script_editor = _script_editor_controls[idx] + else: + idx += 1 + + var to_return = null + if(cur_script_editor != null): + to_return = cur_script_editor.get_text_edit() + + return to_return + + +func get_script_editor_controls(): + var to_return = [] + for ctrl_ref in _script_editor_controls: + to_return.append(ctrl_ref.get_script_text_edit()) + + return to_return + + +func get_line_info(): + var editor = get_current_text_edit() + if(editor == null): + return + + var info = { + script = null, + inner_class = null, + test_method = null + } + + var line = editor.cursor_get_line() + var done_func = false + var done_inner = false + while(line > 0 and (!done_func or !done_inner)): + if(editor.can_fold(line)): + var text = editor.get_line(line) + var strip_text = text.strip_edges(true, false) # only left + + if(!done_func and strip_text.begins_with("func ")): + var func_name = _get_func_name_from_line(text) + if(func_name.begins_with(_method_prefix)): + info.test_method = func_name + done_func = true + # If the func line is left justified then there won't be any + # inner classes above it. + if(strip_text == text): + done_inner = true + + if(!done_inner and strip_text.begins_with("class")): + var inner_name = _get_class_name_from_line(text) + if(inner_name.begins_with(_inner_class_prefix)): + info.inner_class = inner_name + done_inner = true + # if we found an inner class then we are already past + # any test the cursor could be in. + done_func = true + line -= 1 + + return info + + +func get_method_prefix(): + return _method_prefix + + +func set_method_prefix(method_prefix): + _method_prefix = method_prefix + + +func get_inner_class_prefix(): + return _inner_class_prefix + + +func set_inner_class_prefix(inner_class_prefix): + _inner_class_prefix = inner_class_prefix |
