summaryrefslogtreecommitdiff
path: root/addons/gut/gui
diff options
context:
space:
mode:
authorSophia Pearson <codergal89@gmail.com>2022-09-05 20:35:53 +0200
committerSophia Pearson <codergal89@gmail.com>2022-09-05 20:35:53 +0200
commitf20bd89dc4a7bf14a88b1effcaa1887b29314525 (patch)
treed114787f68efd2a7d61d95fa9c84e8e5d69a7c11 /addons/gut/gui
parent1b477b62f8be8c546a35dbd1d2688ebf623c496f (diff)
downloadtexty-f20bd89dc4a7bf14a88b1effcaa1887b29314525.tar.xz
texty-f20bd89dc4a7bf14a88b1effcaa1887b29314525.zip
gui: split GUI into Terminal components
Diffstat (limited to 'addons/gut/gui')
-rw-r--r--addons/gut/gui/GutBottomPanel.gd230
-rw-r--r--addons/gut/gui/GutBottomPanel.tscn193
-rw-r--r--addons/gut/gui/GutRunner.gd9
-rw-r--r--addons/gut/gui/OutputText.gd291
-rw-r--r--addons/gut/gui/OutputText.tscn123
-rw-r--r--addons/gut/gui/RunAtCursor.gd63
-rw-r--r--addons/gut/gui/RunResults.gd509
-rw-r--r--addons/gut/gui/RunResults.tscn165
-rw-r--r--addons/gut/gui/arrow.png4
-rw-r--r--addons/gut/gui/gut_config_gui.gd7
-rw-r--r--addons/gut/gui/play.png4
11 files changed, 1376 insertions, 222 deletions
diff --git a/addons/gut/gui/GutBottomPanel.gd b/addons/gut/gui/GutBottomPanel.gd
index 50a32d8..e7b5037 100644
--- a/addons/gut/gui/GutBottomPanel.gd
+++ b/addons/gut/gui/GutBottomPanel.gd
@@ -8,6 +8,7 @@ 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 ScriptTextEditors = load('res://addons/gut/gui/script_text_editor_controls.gd')
var _interface = null;
var _is_running = false;
@@ -20,12 +21,20 @@ var _last_selected_path = null
onready var _ctrls = {
- output = $layout/RSplit/CResults/Output,
+ output = $layout/RSplit/CResults/Tabs/OutputText.get_rich_text_edit(),
+ output_ctrl = $layout/RSplit/CResults/Tabs/OutputText,
run_button = $layout/ControlBar/RunAll,
+ shortcuts_button = $layout/ControlBar/Shortcuts,
+
+ settings_button = $layout/ControlBar/Settings,
+ run_results_button = $layout/ControlBar/RunResultsBtn,
+ output_button = $layout/ControlBar/OutputBtn,
+
settings = $layout/RSplit/sc/Settings,
shortcut_dialog = $BottomPanelShortcuts,
light = $layout/RSplit/CResults/ControlBar/Light,
results = {
+ bar = $layout/RSplit/CResults/ControlBar,
passing = $layout/RSplit/CResults/ControlBar/Passing/value,
failing = $layout/RSplit/CResults/ControlBar/Failing/value,
pending = $layout/RSplit/CResults/ControlBar/Pending/value,
@@ -33,7 +42,8 @@ onready var _ctrls = {
warnings = $layout/RSplit/CResults/ControlBar/Warnings/value,
orphans = $layout/RSplit/CResults/ControlBar/Orphans/value
},
- run_at_cursor = $layout/ControlBar/RunAtCursor
+ run_at_cursor = $layout/ControlBar/RunAtCursor,
+ run_results = $layout/RSplit/CResults/Tabs/RunResults
}
@@ -42,17 +52,51 @@ func _init():
func _ready():
+ _ctrls.results.bar.connect('draw', self, '_on_results_bar_draw', [_ctrls.results.bar])
+ hide_settings(!_ctrls.settings_button.pressed)
_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)
+
+ _apply_options_to_controls()
+
+ _ctrls.shortcuts_button.icon = get_icon('ShortCut', 'EditorIcons')
+ _ctrls.settings_button.icon = get_icon('Tools', 'EditorIcons')
+ _ctrls.run_results_button.icon = get_icon('AnimationTrackGroup', 'EditorIcons') # Tree
+ _ctrls.output_button.icon = get_icon('Font', 'EditorIcons')
+
+ _ctrls.run_results.set_output_control(_ctrls.output_ctrl)
+ _ctrls.run_results.set_font(
+ _gut_config.options.panel_options.font_name,
+ _gut_config.options.panel_options.font_size)
+
+ var check_import = load('res://addons/gut/images/red.png')
+ if(check_import == null):
+ _ctrls.run_results.add_centered_text("GUT got some new images that are not imported yet. Please restart Godot.")
+ print('GUT got some new images that are not imported yet. Please restart Godot.')
+ else:
+ _ctrls.run_results.add_centered_text("Let's run some tests!")
+
+
+func _apply_options_to_controls():
+ hide_settings(_gut_config.options.panel_options.hide_settings)
+ hide_result_tree(_gut_config.options.panel_options.hide_result_tree)
+ hide_output_text(_gut_config.options.panel_options.hide_output_text)
+
+ _ctrls.output_ctrl.set_use_colors(_gut_config.options.panel_options.use_colors)
+ _ctrls.output_ctrl.set_all_fonts(_gut_config.options.panel_options.font_name)
+ _ctrls.output_ctrl.set_font_size(_gut_config.options.panel_options.font_size)
+
+ _ctrls.run_results.set_font(
+ _gut_config.options.panel_options.font_name,
+ _gut_config.options.panel_options.font_size)
+ _ctrls.run_results.set_show_orphans(!_gut_config.options.hide_orphans)
func _process(delta):
if(_is_running):
if(!_interface.is_playing_scene()):
_is_running = false
- _ctrls.output.add_text("\ndone")
+ _ctrls.output_ctrl.add_text("\ndone")
load_result_output()
_gut_plugin.make_bottom_panel_item_visible(self)
@@ -65,41 +109,6 @@ func load_shortcuts():
_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'):
@@ -108,27 +117,28 @@ func _is_test_script(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"
+ _ctrls.output_ctrl.clear()
+ var text = "Cannot run tests, you have a configuration error:\n"
for e in errs:
text += str('* ', e, "\n")
- text += "[right]Check your settings here ----->[/right]"
- _ctrls.output.bbcode_text = text
+ text += "Check your settings ----->"
+ _ctrls.output_ctrl.add_text(text)
+ hide_output_text(false)
+ hide_settings(false)
+
+
+func _save_config():
+ _gut_config.options = _gut_config_gui.get_options(_gut_config.options)
+ _gut_config.options.panel_options.hide_settings = !_ctrls.settings_button.pressed
+ _gut_config.options.panel_options.hide_result_tree = !_ctrls.run_results_button.pressed
+ _gut_config.options.panel_options.hide_output_text = !_ctrls.output_button.pressed
+ _gut_config.options.panel_options.use_colors = _ctrls.output_ctrl.get_use_colors()
+
+ 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;
func _run_tests():
@@ -138,22 +148,16 @@ func _run_tests():
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)
+ _save_config()
+ _apply_options_to_controls()
- 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()
+ _ctrls.output_ctrl.clear()
+ _ctrls.run_results.clear()
+ _ctrls.run_results.add_centered_text('Running...')
- _update_last_run_label()
_interface.play_custom_scene('res://addons/gut/gui/GutRunner.tscn')
-
_is_running = true
- _ctrls.output.add_text('running...')
+ _ctrls.output_ctrl.add_text('Running...')
func _apply_shortcuts():
@@ -180,27 +184,24 @@ func _run_all():
# ---------------
# Events
# ---------------
+func _on_results_bar_draw(bar):
+ bar.draw_rect(Rect2(Vector2(0, 0), bar.rect_size), Color(0, 0, 0, .2))
+
+
+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_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()
@@ -210,11 +211,6 @@ func _on_BottomPanelShortcuts_popup_hide():
_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
@@ -223,19 +219,64 @@ func _on_RunAtCursor_run_tests(what):
_run_tests()
+func _on_Settings_pressed():
+ hide_settings(!_ctrls.settings_button.pressed)
+ _save_config()
+
+
+func _on_OutputBtn_pressed():
+ hide_output_text(!_ctrls.output_button.pressed)
+ _save_config()
+
+
+func _on_RunResultsBtn_pressed():
+ hide_result_tree(! _ctrls.run_results_button.pressed)
+ _save_config()
+
+
+# Currently not used, but will be when I figure out how to put
+# colors into the text results
+func _on_UseColors_pressed():
+ pass
+
# ---------------
# Public
# ---------------
+func hide_result_tree(should):
+ _ctrls.run_results.visible = !should
+ _ctrls.run_results_button.pressed = !should
+
+
+func hide_settings(should):
+ var s_scroll = _ctrls.settings.get_parent()
+ s_scroll.visible = !should
+
+ # collapse only collapses the first control, so we move
+ # settings around to be the collapsed one
+ if(should):
+ s_scroll.get_parent().move_child(s_scroll, 0)
+ else:
+ s_scroll.get_parent().move_child(s_scroll, 1)
+
+ $layout/RSplit.collapsed = should
+ _ctrls.settings_button.pressed = !should
+
+
+func hide_output_text(should):
+ $layout/RSplit/CResults/Tabs/OutputText.visible = !should
+ _ctrls.output_button.pressed = !should
+
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)
+ _ctrls.output_ctrl.load_file(RESULT_FILE)
var summary = get_file_as_text(RESULT_JSON)
var results = JSON.parse(summary)
if(results.error != OK):
return
+
+ _ctrls.run_results.load_json_results(results.result)
+
var summary_json = results.result['test_scripts']['props']
_ctrls.results.passing.text = str(summary_json.passing)
_ctrls.results.passing.get_parent().visible = true
@@ -263,10 +304,10 @@ func load_result_output():
_light_color = Color(1, 1, 0, .75)
else:
_light_color = Color(0, 1, 0, .75)
+ _ctrls.light.visible = true
_ctrls.light.update()
-
func set_current_script(script):
if(script):
if(_is_test_script(script)):
@@ -278,7 +319,11 @@ func set_current_script(script):
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())
+
+ var ste = ScriptTextEditors.new(_interface.get_script_editor())
+ _ctrls.run_results.set_interface(_interface)
+ _ctrls.run_results.set_script_text_editors(ste)
+ _ctrls.run_at_cursor.set_script_text_editors(ste)
set_current_script(_interface.get_script_editor().get_current_script())
@@ -323,4 +368,3 @@ func nvl(value, if_null):
else:
return value
-
diff --git a/addons/gut/gui/GutBottomPanel.tscn b/addons/gut/gui/GutBottomPanel.tscn
index 0bb7fc3..7ec6649 100644
--- a/addons/gut/gui/GutBottomPanel.tscn
+++ b/addons/gut/gui/GutBottomPanel.tscn
@@ -1,9 +1,11 @@
-[gd_scene load_steps=16 format=2]
+[gd_scene load_steps=11 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]
+[ext_resource path="res://addons/gut/gui/RunResults.tscn" type="PackedScene" id=5]
+[ext_resource path="res://addons/gut/gui/OutputText.tscn" type="PackedScene" id=6]
[sub_resource type="InputEventKey" id=8]
control = true
@@ -12,35 +14,20 @@ 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="Image" id=10]
+data = {
+"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
+"format": "LumAlpha8",
+"height": 16,
+"mipmaps": false,
+"width": 16
+}
-[sub_resource type="DynamicFont" id=17]
-size = 30
-font_data = SubResource( 16 )
+[sub_resource type="ImageTexture" id=2]
+flags = 4
+flags = 4
+image = SubResource( 10 )
+size = Vector2( 16, 16 )
[node name="GutBottomPanel" type="Control"]
anchor_left = -0.0025866
@@ -53,9 +40,6 @@ 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
@@ -89,16 +73,16 @@ 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
+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
+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
+You can assign keyboard shortcuts for these buttons
using the \"shortcuts\" button in the GUT panel."
mouse_filter = 1
text = "Current: "
@@ -107,39 +91,61 @@ text = "Current: "
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 217.0
-margin_right = 456.0
+margin_right = 548.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_left = 552.0
+margin_right = 883.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
+[node name="Sep1" type="ColorRect" parent="layout/ControlBar"]
+margin_left = 887.0
+margin_right = 889.0
margin_bottom = 40.0
-custom_styles/normal = SubResource( 5 )
-__meta__ = {
-"_edit_use_anchors_": false
-}
+rect_min_size = Vector2( 2, 0 )
-[node name="CenterContainer" type="CenterContainer" parent="layout/ControlBar"]
-margin_left = 707.0
-margin_right = 946.0
+[node name="RunResultsBtn" type="ToolButton" parent="layout/ControlBar"]
+margin_left = 893.0
+margin_right = 921.0
margin_bottom = 40.0
-size_flags_horizontal = 3
+hint_tooltip = "Show/Hide Results Tree Panel."
+toggle_mode = true
+pressed = true
+icon = SubResource( 2 )
+
+[node name="OutputBtn" type="ToolButton" parent="layout/ControlBar"]
+margin_left = 925.0
+margin_right = 953.0
+margin_bottom = 40.0
+hint_tooltip = "Show/Hide Output Panel."
+toggle_mode = true
+pressed = true
+icon = SubResource( 2 )
+
+[node name="Settings" type="ToolButton" parent="layout/ControlBar"]
+margin_left = 957.0
+margin_right = 985.0
+margin_bottom = 40.0
+hint_tooltip = "Show/Hide Settings Panel."
+toggle_mode = true
+icon = SubResource( 2 )
-[node name="Shortcuts" type="Button" parent="layout/ControlBar"]
-margin_left = 950.0
-margin_right = 1022.0
+[node name="Sep2" type="ColorRect" parent="layout/ControlBar"]
+margin_left = 989.0
+margin_right = 991.0
+margin_bottom = 40.0
+rect_min_size = Vector2( 2, 0 )
+
+[node name="Shortcuts" type="ToolButton" parent="layout/ControlBar"]
+margin_left = 995.0
+margin_right = 1023.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"
+icon = SubResource( 2 )
[node name="RSplit" type="HSplitContainer" parent="layout"]
margin_top = 44.0
@@ -147,19 +153,36 @@ margin_right = 1023.0
margin_bottom = 599.0
size_flags_horizontal = 3
size_flags_vertical = 3
+collapsed = true
+
+[node name="sc" type="ScrollContainer" parent="layout/RSplit"]
+visible = false
+margin_left = 593.0
+margin_right = 1093.0
+margin_bottom = 555.0
+rect_min_size = Vector2( 500, 0 )
+mouse_filter = 1
+size_flags_vertical = 3
+
+[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"]
+margin_right = 500.0
+margin_bottom = 908.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
[node name="CResults" type="VBoxContainer" parent="layout/RSplit"]
-margin_right = 611.0
+margin_right = 1023.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_right = 1023.0
margin_bottom = 35.0
rect_min_size = Vector2( 0, 35 )
[node name="Light" type="Control" parent="layout/RSplit/CResults/ControlBar"]
+visible = false
margin_right = 30.0
margin_bottom = 35.0
rect_min_size = Vector2( 30, 30 )
@@ -319,52 +342,28 @@ margin_bottom = 24.0
text = "---"
[node name="CenterContainer" type="CenterContainer" parent="layout/RSplit/CResults/ControlBar"]
-margin_left = 34.0
-margin_right = 488.0
+margin_right = 1023.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"]
+[node name="Tabs" type="HSplitContainer" 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_horizontal = 3
size_flags_vertical = 3
-[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"]
-margin_right = 388.0
-margin_bottom = 862.0
+[node name="RunResults" parent="layout/RSplit/CResults/Tabs" instance=ExtResource( 5 )]
+margin_right = 505.0
+margin_bottom = 516.0
size_flags_horizontal = 3
size_flags_vertical = 3
+[node name="OutputText" parent="layout/RSplit/CResults/Tabs" instance=ExtResource( 6 )]
+margin_left = 517.0
+margin_right = 1023.0
+margin_bottom = 516.0
+
[node name="BottomPanelShortcuts" parent="." instance=ExtResource( 2 )]
visible = false
anchor_left = -0.000517324
@@ -378,9 +377,9 @@ 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/RunResultsBtn" to="." method="_on_RunResultsBtn_pressed"]
+[connection signal="pressed" from="layout/ControlBar/OutputBtn" to="." method="_on_OutputBtn_pressed"]
+[connection signal="pressed" from="layout/ControlBar/Settings" to="." method="_on_Settings_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
index b2b16f2..608fc26 100644
--- a/addons/gut/gui/GutRunner.gd
+++ b/addons/gut/gui/GutRunner.gd
@@ -22,8 +22,7 @@ onready var _gut_layer = $GutLayer
func _ready():
if(_gut_config == null):
_gut_config = GutConfig.new()
- _gut_config.load_options(RUNNER_JSON_PATH)
-
+ _gut_config.load_panel_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
@@ -55,9 +54,7 @@ func run_tests():
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 content = _gut.get_logger().get_gui_bbcode()
var f = File.new()
var result = f.open(RESULT_FILE, f.WRITE)
@@ -68,7 +65,7 @@ func _write_results():
print('ERROR Could not save bbcode, result = ', result)
var exporter = ResultExporter.new()
- var f_result = exporter.write_summary_file(_gut, RESULT_JSON)
+ var f_result = exporter.write_json_file(_gut, RESULT_JSON)
_wrote_results = true
diff --git a/addons/gut/gui/OutputText.gd b/addons/gut/gui/OutputText.gd
new file mode 100644
index 0000000..e5cf2b6
--- /dev/null
+++ b/addons/gut/gui/OutputText.gd
@@ -0,0 +1,291 @@
+extends VBoxContainer
+tool
+
+class SearchResults:
+ const L = TextEdit.SEARCH_RESULT_LINE
+ const C = TextEdit.SEARCH_RESULT_COLUMN
+
+ var positions = []
+ var te = null
+ var _last_term = ''
+
+ func _search_te(text, start_position, flags=0):
+ var start_pos = start_position
+ if(start_pos[L] < 0 or start_pos[L] > te.get_line_count()):
+ start_pos[L] = 0
+ if(start_pos[C] < 0):
+ start_pos[L] = 0
+
+ var result = te.search(text, flags, start_pos[L], start_pos[C])
+ if(result.size() == 2 and result[L] == start_position[L] and
+ result[C] == start_position[C] and text == _last_term):
+ if(flags == TextEdit.SEARCH_BACKWARDS):
+ result[C] -= 1
+ else:
+ result[C] += 1
+ result = _search_te(text, result, flags)
+ elif(result.size() == 2):
+ te.scroll_vertical = result[L]
+ te.select(result[L], result[C], result[L], result[C] + text.length())
+ te.cursor_set_column(result[C])
+ te.cursor_set_line(result[L])
+ te.center_viewport_to_cursor()
+
+ _last_term = text
+ te.center_viewport_to_cursor()
+ return result
+
+ func _cursor_to_pos():
+ var to_return = [0, 0]
+ to_return[L] = te.cursor_get_line()
+ to_return[C] = te.cursor_get_column()
+ return to_return
+
+ func find_next(term):
+ return _search_te(term, _cursor_to_pos())
+
+ func find_prev(term):
+ var new_pos = _search_te(term, _cursor_to_pos(), TextEdit.SEARCH_BACKWARDS)
+ return new_pos
+
+ func get_next_pos():
+ pass
+
+ func get_prev_pos():
+ pass
+
+ func clear():
+ pass
+
+ func find_all(text):
+ var c_pos = [0, 0]
+ var found = true
+ var last_pos = [0, 0]
+ positions.clear()
+
+ while(found):
+ c_pos = te.search(text, 0, c_pos[L], c_pos[C])
+
+ if(c_pos.size() > 0 and
+ (c_pos[L] > last_pos[L] or
+ (c_pos[L] == last_pos[L] and c_pos[C] > last_pos[C]))):
+ positions.append([c_pos[L], c_pos[C]])
+ c_pos[C] += 1
+ last_pos = c_pos
+ else:
+ found = false
+
+
+
+onready var _ctrls = {
+ output = $Output,
+
+ copy_button = $Toolbar/CopyButton,
+ use_colors = $Toolbar/UseColors,
+ clear_button = $Toolbar/ClearButton,
+ word_wrap = $Toolbar/WordWrap,
+ show_search = $Toolbar/ShowSearch,
+
+ search_bar = {
+ bar = $Search,
+ search_term = $Search/SearchTerm,
+ }
+}
+var _sr = SearchResults.new()
+
+func _test_running_setup():
+ _ctrls.use_colors.text = 'use colors'
+ _ctrls.show_search.text = 'search'
+ _ctrls.word_wrap.text = 'ww'
+
+ set_all_fonts("CourierPrime")
+ set_font_size(20)
+
+ load_file('user://.gut_editor.bbcode')
+
+
+func _ready():
+ _sr.te = _ctrls.output
+ _ctrls.use_colors.icon = get_icon('RichTextEffect', 'EditorIcons')
+ _ctrls.show_search.icon = get_icon('Search', 'EditorIcons')
+ _ctrls.word_wrap.icon = get_icon('Loop', 'EditorIcons')
+
+ _setup_colors()
+ if(get_parent() == get_tree().root):
+ _test_running_setup()
+
+
+# ------------------
+# Private
+# ------------------
+func _setup_colors():
+ _ctrls.output.clear_colors()
+ var keywords = [
+ ['Failed', Color.red],
+ ['Passed', Color.green],
+ ['Pending', Color.yellow],
+ ['Orphans', Color.yellow],
+ ['WARNING', Color.yellow],
+ ['ERROR', Color.red]
+ ]
+
+ for keyword in keywords:
+ _ctrls.output.add_keyword_color(keyword[0], keyword[1])
+
+ var f_color = _ctrls.output.get_color("font_color")
+ _ctrls.output.add_color_override("font_color_readonly", f_color)
+ _ctrls.output.add_color_override("function_color", f_color)
+ _ctrls.output.add_color_override("member_variable_color", f_color)
+ _ctrls.output.update()
+
+
+func _set_font(font_name, custom_name):
+ var rtl = _ctrls.output
+ 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)
+
+
+# ------------------
+# Events
+# ------------------
+func _on_CopyButton_pressed():
+ copy_to_clipboard()
+
+
+func _on_UseColors_pressed():
+ _ctrls.output.syntax_highlighting = _ctrls.use_colors.pressed
+
+
+func _on_ClearButton_pressed():
+ clear()
+
+
+func _on_ShowSearch_pressed():
+ show_search(_ctrls.show_search.pressed)
+
+
+func _on_SearchTerm_focus_entered():
+ _ctrls.search_bar.search_term.call_deferred('select_all')
+
+func _on_SearchNext_pressed():
+ _sr.find_next(_ctrls.search_bar.search_term.text)
+
+
+func _on_SearchPrev_pressed():
+ _sr.find_prev(_ctrls.search_bar.search_term.text)
+
+
+func _on_SearchTerm_text_changed(new_text):
+ if(new_text == ''):
+ _ctrls.output.deselect()
+ else:
+ _sr.find_next(new_text)
+
+
+func _on_SearchTerm_text_entered(new_text):
+ if(Input.is_physical_key_pressed(KEY_SHIFT)):
+ _sr.find_prev(new_text)
+ else:
+ _sr.find_next(new_text)
+
+
+func _on_SearchTerm_gui_input(event):
+ if(event is InputEventKey and !event.pressed and event.scancode == KEY_ESCAPE):
+ show_search(false)
+
+func _on_WordWrap_pressed():
+ _ctrls.output.wrap_enabled = _ctrls.word_wrap.pressed
+ _ctrls.output.update()
+
+# ------------------
+# Public
+# ------------------
+func show_search(should):
+ _ctrls.search_bar.bar.visible = should
+ if(should):
+ _ctrls.search_bar.search_term.grab_focus()
+ _ctrls.search_bar.search_term.select_all()
+ _ctrls.show_search.pressed = should
+
+
+func search(text, start_pos, highlight=true):
+ return _sr.find_next(text)
+
+
+func copy_to_clipboard():
+ var selected = _ctrls.output.get_selection_text()
+ if(selected != ''):
+ OS.clipboard = selected
+ else:
+ OS.clipboard = _ctrls.output.text
+
+
+func clear():
+ _ctrls.output.text = ''
+
+
+func set_all_fonts(base_name):
+ if(base_name == 'Default'):
+ _set_font(null, 'font')
+# _set_font(null, 'normal_font')
+# _set_font(null, 'bold_font')
+# _set_font(null, 'italics_font')
+# _set_font(null, 'bold_italics_font')
+ else:
+ _set_font(base_name + '-Regular', 'font')
+# _set_font(base_name + '-Regular', 'normal_font')
+# _set_font(base_name + '-Bold', 'bold_font')
+# _set_font(base_name + '-Italic', 'italics_font')
+# _set_font(base_name + '-BoldItalic', 'bold_italics_font')
+
+
+func set_font_size(new_size):
+ var rtl = _ctrls.output
+ if(rtl.get('custom_fonts/font') != null):
+ rtl.get('custom_fonts/font').size = new_size
+# 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 set_use_colors(value):
+ pass
+
+
+func get_use_colors():
+ return false;
+
+
+func get_rich_text_edit():
+ return _ctrls.output
+
+
+func load_file(path):
+ var f = File.new()
+ var result = f.open(path, f.READ)
+ if(result != OK):
+ return
+
+ var t = f.get_as_text()
+ f.close()
+ _ctrls.output.text = t
+ _ctrls.output.scroll_vertical = _ctrls.output.get_line_count()
+ _ctrls.output.set_deferred('scroll_vertical', _ctrls.output.get_line_count())
+
+
+func add_text(text):
+ if(is_inside_tree()):
+ _ctrls.output.text += text
+
+
+func scroll_to_line(line):
+ _ctrls.output.scroll_vertical = line
+ _ctrls.output.cursor_set_line(line)
diff --git a/addons/gut/gui/OutputText.tscn b/addons/gut/gui/OutputText.tscn
new file mode 100644
index 0000000..d7c693a
--- /dev/null
+++ b/addons/gut/gui/OutputText.tscn
@@ -0,0 +1,123 @@
+[gd_scene load_steps=4 format=2]
+
+[ext_resource path="res://addons/gut/gui/OutputText.gd" type="Script" id=1]
+
+[sub_resource type="Image" id=3]
+data = {
+"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
+"format": "LumAlpha8",
+"height": 16,
+"mipmaps": false,
+"width": 16
+}
+
+[sub_resource type="ImageTexture" id=2]
+flags = 4
+flags = 4
+image = SubResource( 3 )
+size = Vector2( 16, 16 )
+
+[node name="OutputText" type="VBoxContainer"]
+margin_right = 862.0
+margin_bottom = 523.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+script = ExtResource( 1 )
+
+[node name="Toolbar" type="HBoxContainer" parent="."]
+margin_right = 862.0
+margin_bottom = 24.0
+size_flags_horizontal = 3
+
+[node name="ShowSearch" type="ToolButton" parent="Toolbar"]
+margin_right = 28.0
+margin_bottom = 24.0
+toggle_mode = true
+icon = SubResource( 2 )
+
+[node name="UseColors" type="ToolButton" parent="Toolbar"]
+margin_left = 32.0
+margin_right = 60.0
+margin_bottom = 24.0
+hint_tooltip = "Colorize output.
+ It's not the same as everywhere else (long story),
+ but it is better than nothing."
+toggle_mode = true
+pressed = true
+icon = SubResource( 2 )
+
+[node name="WordWrap" type="ToolButton" parent="Toolbar"]
+margin_left = 64.0
+margin_right = 92.0
+margin_bottom = 24.0
+hint_tooltip = "Word wrap"
+toggle_mode = true
+icon = SubResource( 2 )
+
+[node name="CenterContainer" type="CenterContainer" parent="Toolbar"]
+margin_left = 96.0
+margin_right = 743.0
+margin_bottom = 24.0
+size_flags_horizontal = 3
+
+[node name="CopyButton" type="Button" parent="Toolbar"]
+margin_left = 747.0
+margin_right = 798.0
+margin_bottom = 24.0
+hint_tooltip = "Copy to clipboard"
+text = " Copy "
+
+[node name="ClearButton" type="Button" parent="Toolbar"]
+margin_left = 802.0
+margin_right = 862.0
+margin_bottom = 24.0
+text = " Clear "
+
+[node name="Output" type="TextEdit" parent="."]
+margin_top = 28.0
+margin_right = 862.0
+margin_bottom = 523.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+readonly = true
+highlight_current_line = true
+syntax_highlighting = true
+show_line_numbers = true
+smooth_scrolling = true
+
+[node name="Search" type="HBoxContainer" parent="."]
+visible = false
+margin_top = 499.0
+margin_right = 862.0
+margin_bottom = 523.0
+
+[node name="SearchTerm" type="LineEdit" parent="Search"]
+margin_right = 804.0
+margin_bottom = 24.0
+size_flags_horizontal = 3
+
+[node name="SearchNext" type="Button" parent="Search"]
+margin_left = 808.0
+margin_right = 862.0
+margin_bottom = 24.0
+hint_tooltip = "Find next (enter)"
+text = "Next"
+
+[node name="SearchPrev" type="Button" parent="Search"]
+margin_left = 808.0
+margin_right = 820.0
+margin_bottom = 20.0
+hint_tooltip = "Find previous (shift + enter)"
+text = "Prev"
+
+[connection signal="pressed" from="Toolbar/ShowSearch" to="." method="_on_ShowSearch_pressed"]
+[connection signal="pressed" from="Toolbar/UseColors" to="." method="_on_UseColors_pressed"]
+[connection signal="pressed" from="Toolbar/WordWrap" to="." method="_on_WordWrap_pressed"]
+[connection signal="pressed" from="Toolbar/CopyButton" to="." method="_on_CopyButton_pressed"]
+[connection signal="pressed" from="Toolbar/ClearButton" to="." method="_on_ClearButton_pressed"]
+[connection signal="focus_entered" from="Search/SearchTerm" to="." method="_on_SearchTerm_focus_entered"]
+[connection signal="gui_input" from="Search/SearchTerm" to="." method="_on_SearchTerm_gui_input"]
+[connection signal="text_changed" from="Search/SearchTerm" to="." method="_on_SearchTerm_text_changed"]
+[connection signal="text_entered" from="Search/SearchTerm" to="." method="_on_SearchTerm_text_entered"]
+[connection signal="pressed" from="Search/SearchNext" to="." method="_on_SearchNext_pressed"]
+[connection signal="pressed" from="Search/SearchPrev" to="." method="_on_SearchPrev_pressed"]
diff --git a/addons/gut/gui/RunAtCursor.gd b/addons/gut/gui/RunAtCursor.gd
index 960414e..dc83c3e 100644
--- a/addons/gut/gui/RunAtCursor.gd
+++ b/addons/gut/gui/RunAtCursor.gd
@@ -14,7 +14,6 @@ onready var _ctrls = {
}
var _editors = null
-var _script_editor = null
var _cur_editor = null
var _last_line = -1
var _cur_script_path = null
@@ -29,7 +28,9 @@ func _ready():
_ctrls.btn_inner.visible = false
_ctrls.btn_method.visible = false
-
+# ----------------
+# Private
+# ----------------
func _set_editor(which):
_last_line = -1
if(_cur_editor != null and _cur_editor.get_ref()):
@@ -44,7 +45,6 @@ func _set_editor(which):
_update_buttons(_last_info)
-
func _update_buttons(info):
_ctrls.lbl_none.visible = _cur_script_path == null
_ctrls.btn_script.visible = _cur_script_path != null
@@ -58,16 +58,19 @@ func _update_buttons(info):
_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.
+
+ # 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
+# ----------------
+# Events
+# ----------------
func _on_cursor_changed(which):
if(which.cursor_get_line() != _last_line):
_last_line = which.cursor_get_line()
@@ -75,20 +78,6 @@ func _on_cursor_changed(which):
_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()
@@ -110,6 +99,22 @@ func _on_BtnRunMethod_pressed():
emit_signal("run_tests", info)
+# ----------------
+# Public
+# ----------------
+func set_script_text_editors(value):
+ _editors = 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 get_script_button():
return _ctrls.btn_script
@@ -121,10 +126,28 @@ func get_inner_button():
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)
+
+
+# Mashed this function in here b/c it has _editors. Probably should be
+# somewhere else (possibly in script_text_editor_controls).
+func search_current_editor_for_text(txt):
+ var te = _editors.get_current_text_edit()
+ var result = te.search(txt, 0, 0, 0)
+ var to_return = -1
+
+ if result.size() > 0:
+ to_return = result[TextEdit.SEARCH_RESULT_LINE]
+
+ return to_return
+
+
+
diff --git a/addons/gut/gui/RunResults.gd b/addons/gut/gui/RunResults.gd
new file mode 100644
index 0000000..a36d10f
--- /dev/null
+++ b/addons/gut/gui/RunResults.gd
@@ -0,0 +1,509 @@
+extends Control
+tool
+
+var _interface = null
+var _utils = load('res://addons/gut/utils.gd').new()
+var _hide_passing = true
+var _font = null
+var _font_size = null
+var _root = null
+var _max_icon_width = 10
+var _editors = null # script_text_editor_controls.gd
+var _show_orphans = true
+var _output_control = null
+
+const _col_1_bg_color = Color(0, 0, 0, .1)
+
+var _icons = {
+ red = load('res://addons/gut/images/red.png'),
+ green = load('res://addons/gut/images/green.png'),
+ yellow = load('res://addons/gut/images/yellow.png'),
+}
+
+signal search_for_text(text)
+
+onready var _ctrls = {
+ tree = $VBox/Output/Scroll/Tree,
+ lbl_overlay = $VBox/Output/OverlayMessage,
+ chk_hide_passing = $VBox/Toolbar/HidePassing,
+ toolbar = {
+ toolbar = $VBox/Toolbar,
+ collapse = $VBox/Toolbar/Collapse,
+ collapse_all = $VBox/Toolbar/CollapseAll,
+ expand = $VBox/Toolbar/Expand,
+ expand_all = $VBox/Toolbar/ExpandAll,
+ hide_passing = $VBox/Toolbar/HidePassing,
+ show_script = $VBox/Toolbar/ShowScript,
+ scroll_output = $VBox/Toolbar/ScrollOutput
+ }
+}
+
+func _test_running_setup():
+ _hide_passing = true
+ _show_orphans = true
+ var _gut_config = load('res://addons/gut/gut_config.gd').new()
+ _gut_config.load_panel_options('res://.gut_editor_config.json')
+ set_font(
+ _gut_config.options.panel_options.font_name,
+ _gut_config.options.panel_options.font_size)
+
+ _ctrls.toolbar.hide_passing.text = '[hp]'
+ load_json_file('user://.gut_editor.json')
+
+
+func _set_toolbutton_icon(btn, icon_name, text):
+ if(Engine.editor_hint):
+ btn.icon = get_icon(icon_name, 'EditorIcons')
+ else:
+ btn.text = str('[', text, ']')
+
+
+func _ready():
+ var f = $FontSampler.get_font("font")
+ var s_size = f.get_string_size("000 of 000 passed")
+ _root = _ctrls.tree.create_item()
+ _ctrls.tree.set_hide_root(true)
+ _ctrls.tree.columns = 2
+ _ctrls.tree.set_column_expand(0, true)
+ _ctrls.tree.set_column_expand(1, false)
+ _ctrls.tree.set_column_min_width(1, s_size.x)
+
+ _set_toolbutton_icon(_ctrls.toolbar.collapse, 'CollapseTree', 'c')
+ _set_toolbutton_icon(_ctrls.toolbar.collapse_all, 'CollapseTree', 'c')
+ _set_toolbutton_icon(_ctrls.toolbar.expand, 'ExpandTree', 'e')
+ _set_toolbutton_icon(_ctrls.toolbar.expand_all, 'ExpandTree', 'e')
+ _set_toolbutton_icon(_ctrls.toolbar.show_script, 'Script', 'ss')
+ _set_toolbutton_icon(_ctrls.toolbar.scroll_output, 'Font', 'so')
+
+ _ctrls.toolbar.hide_passing.set('custom_icons/checked', get_icon('GuiVisibilityHidden', 'EditorIcons'))
+ _ctrls.toolbar.hide_passing.set('custom_icons/unchecked', get_icon('GuiVisibilityVisible', 'EditorIcons'))
+
+ if(get_parent() == get_tree().root):
+ _test_running_setup()
+
+ call_deferred('_update_min_width')
+
+func _update_min_width():
+ rect_min_size.x = _ctrls.toolbar.toolbar.rect_size.x
+
+func _open_file(path, line_number):
+ if(_interface == null):
+ print('Too soon, wait a bit and try again.')
+ return
+
+ var r = load(path)
+ if(line_number != -1):
+ _interface.edit_script(r, line_number)
+ else:
+ _interface.edit_script(r)
+
+ if(_ctrls.toolbar.show_script.pressed):
+ _interface.set_main_screen_editor('Script')
+
+
+func _add_script_tree_item(script_path, script_json):
+ var path_info = _get_path_and_inner_class_name_from_test_path(script_path)
+ # print('* adding script ', path_info)
+ var item_text = script_path
+ var parent = _root
+
+ if(path_info.inner_class != ''):
+ parent = _find_script_item_with_path(path_info.path)
+ item_text = path_info.inner_class
+ if(parent == null):
+ parent = _add_script_tree_item(path_info.path, {})
+
+ var item = _ctrls.tree.create_item(parent)
+ item.set_text(0, item_text)
+ var meta = {
+ "type":"script",
+ "path":path_info.path,
+ "inner_class":path_info.inner_class,
+ "json":script_json}
+ item.set_metadata(0, meta)
+ item.set_custom_bg_color(1, _col_1_bg_color)
+
+ return item
+
+
+func _add_assert_item(text, icon, parent_item):
+ # print(' * adding assert')
+ var assert_item = _ctrls.tree.create_item(parent_item)
+ assert_item.set_icon_max_width(0, _max_icon_width)
+ assert_item.set_text(0, text)
+ assert_item.set_metadata(0, {"type":"assert"})
+ assert_item.set_icon(0, icon)
+ assert_item.set_custom_bg_color(1, _col_1_bg_color)
+
+ return assert_item
+
+
+func _add_test_tree_item(test_name, test_json, script_item):
+ # print(' * adding test ', test_name)
+ var no_orphans_to_show = !_show_orphans or (_show_orphans and test_json.orphans == 0)
+ if(_hide_passing and test_json['status'] == 'pass' and no_orphans_to_show):
+ return
+
+ var item = _ctrls.tree.create_item(script_item)
+ var status = test_json['status']
+ var meta = {"type":"test", "json":test_json}
+
+ item.set_text(0, test_name)
+ item.set_text(1, status)
+ item.set_text_align(1, TreeItem.ALIGN_RIGHT)
+ item.set_custom_bg_color(1, _col_1_bg_color)
+
+ item.set_metadata(0, meta)
+ item.set_icon_max_width(0, _max_icon_width)
+
+ var orphan_text = 'orphans'
+ if(test_json.orphans == 1):
+ orphan_text = 'orphan'
+ orphan_text = str(test_json.orphans, ' ', orphan_text)
+
+
+ if(status == 'pass' and no_orphans_to_show):
+ item.set_icon(0, _icons.green)
+ elif(status == 'pass' and !no_orphans_to_show):
+ item.set_icon(0, _icons.yellow)
+ item.set_text(1, orphan_text)
+ elif(status == 'fail'):
+ item.set_icon(0, _icons.red)
+ else:
+ item.set_icon(0, _icons.yellow)
+
+ if(!_hide_passing):
+ for passing in test_json.passing:
+ _add_assert_item('pass: ' + passing, _icons.green, item)
+
+ for failure in test_json.failing:
+ _add_assert_item("fail: " + failure.replace("\n", ''), _icons.red, item)
+
+ for pending in test_json.pending:
+ _add_assert_item("pending: " + pending.replace("\n", ''), _icons.yellow, item)
+
+ if(status != 'pass' and !no_orphans_to_show):
+ _add_assert_item(orphan_text, _icons.yellow, item)
+
+ return item
+
+
+func _load_result_tree(j):
+ var scripts = j['test_scripts']['scripts']
+ var script_keys = scripts.keys()
+ # if we made it here, the json is valid and we did something, otherwise the
+ # 'nothing to see here' should be visible.
+ clear_centered_text()
+
+ var _last_script_item = null
+ for key in script_keys:
+ var tests = scripts[key]['tests']
+ var test_keys = tests.keys()
+ var s_item = _add_script_tree_item(key, scripts[key])
+ var bad_count = 0
+
+ for test_key in test_keys:
+ var t_item = _add_test_tree_item(test_key, tests[test_key], s_item)
+ if(tests[test_key].status != 'pass'):
+ bad_count += 1
+ elif(t_item != null):
+ t_item.collapsed = true
+
+ # get_children returns the first child or null. its a dumb name.
+ if(s_item.get_children() == null):
+ # var m = s_item.get_metadata(0)
+ # print('!! Deleting ', m.path, ' ', m.inner_class)
+ s_item.free()
+ else:
+ var total_text = str(test_keys.size(), ' passed')
+ s_item.set_text_align(1, s_item.ALIGN_LEFT)
+ if(bad_count == 0):
+ s_item.collapsed = true
+ else:
+ total_text = str(test_keys.size() - bad_count, ' of ', test_keys.size(), ' passed')
+ s_item.set_text(1, total_text)
+
+ _free_childless_scripts()
+ _show_all_passed()
+
+
+func _free_childless_scripts():
+ var item = _root.get_children()
+ while(item != null):
+ var next_item = item.get_next()
+ if(item.get_children() == null):
+ item.free()
+ item = next_item
+
+
+func _find_script_item_with_path(path):
+ var item = _root.get_children()
+ var to_return = null
+
+ while(item != null and to_return == null):
+ if(item.get_metadata(0).path == path):
+ to_return = item
+ else:
+ item = item.get_next()
+
+ return to_return
+
+
+func _get_line_number_from_assert_msg(msg):
+ var line = -1
+ if(msg.find('at line') > 0):
+ line = int(msg.split("at line")[-1].split(" ")[-1])
+ return line
+
+
+func _get_path_and_inner_class_name_from_test_path(path):
+ var to_return = {
+ path = '',
+ inner_class = ''
+ }
+
+ to_return.path = path
+ if !path.ends_with('.gd'):
+ var loc = path.find('.gd')
+ to_return.inner_class = path.split('.')[-1]
+ to_return.path = path.substr(0, loc + 3)
+ return to_return
+
+
+func _handle_tree_item_select(item, force_scroll):
+ var item_type = item.get_metadata(0).type
+
+ var path = '';
+ var line = -1;
+ var method_name = ''
+ var inner_class = ''
+
+ if(item_type == 'test'):
+ var s_item = item.get_parent()
+ path = s_item.get_metadata(0)['path']
+ inner_class = s_item.get_metadata(0)['inner_class']
+ line = -1
+ method_name = item.get_text(0)
+ elif(item_type == 'assert'):
+ var s_item = item.get_parent().get_parent()
+ path = s_item.get_metadata(0)['path']
+ inner_class = s_item.get_metadata(0)['inner_class']
+
+ line = _get_line_number_from_assert_msg(item.get_text(0))
+ method_name = item.get_parent().get_text(0)
+ elif(item_type == 'script'):
+ path = item.get_metadata(0)['path']
+ if(item.get_parent() != _root):
+ inner_class = item.get_text(0)
+ line = -1
+ method_name = ''
+ else:
+ return
+
+ var path_info = _get_path_and_inner_class_name_from_test_path(path)
+ if(force_scroll or _ctrls.toolbar.show_script.pressed):
+ _goto_code(path, line, method_name, inner_class)
+ if(force_scroll or _ctrls.toolbar.scroll_output.pressed):
+ _goto_output(path, method_name, inner_class)
+
+
+# starts at beginning of text edit and searches for each search term, moving
+# through the text as it goes; ensuring that, when done, it found the first
+# occurance of the last srting that happend after the first occurance of
+# each string before it. (Generic way of searching for a method name in an
+# inner class that may have be a duplicate of a method name in a different
+# inner class)
+func _get_line_number_for_seq_search(search_strings, te):
+# var te = _editors.get_current_text_edit()
+ var result = null
+ var to_return = -1
+ var start_line = 0
+ var start_col = 0
+ var s_flags = 0
+
+ var i = 0
+ var string_found = true
+ while(i < search_strings.size() and string_found):
+ result = te.search(search_strings[i], s_flags, start_line, start_col)
+ if(result.size() > 0):
+ start_line = result[TextEdit.SEARCH_RESULT_LINE]
+ start_col = result[TextEdit.SEARCH_RESULT_COLUMN]
+ to_return = start_line
+ else:
+ string_found = false
+ i += 1
+
+ return to_return
+
+
+func _goto_code(path, line, method_name='', inner_class =''):
+ if(_interface == null):
+ print('going to ', [path, line, method_name, inner_class])
+ return
+
+ _open_file(path, line)
+ if(line == -1):
+ var search_strings = []
+ if(inner_class != ''):
+ search_strings.append(inner_class)
+
+ if(method_name != ''):
+ search_strings.append(method_name)
+
+ line = _get_line_number_for_seq_search(search_strings, _editors.get_current_text_edit())
+ if(line != -1):
+ _interface.get_script_editor().goto_line(line)
+
+
+func _goto_output(path, method_name, inner_class):
+ if(_output_control == null):
+ return
+
+ var search_strings = [path]
+
+ if(inner_class != ''):
+ search_strings.append(inner_class)
+
+ if(method_name != ''):
+ search_strings.append(method_name)
+
+ var line = _get_line_number_for_seq_search(search_strings, _output_control.get_rich_text_edit())
+ if(line != -1):
+ _output_control.scroll_to_line(line)
+
+
+func _show_all_passed():
+ if(_root.get_children() == null):
+ add_centered_text('Everything passed!')
+
+
+func _set_collapsed_on_all(item, value):
+ if(item == _root):
+ var node = _root.get_children()
+ while(node != null):
+ node.call_recursive('set_collapsed', value)
+ node = node.get_next()
+ else:
+ item.call_recursive('set_collapsed', value)
+
+# --------------
+# Events
+# --------------
+func _on_Tree_item_selected():
+ # do not force scroll
+ var item = _ctrls.tree.get_selected()
+ _handle_tree_item_select(item, false)
+ # it just looks better if the left is always selected.
+ if(item.is_selected(1)):
+ item.deselect(1)
+ item.select(0)
+
+
+func _on_Tree_item_activated():
+ # force scroll
+ print('double clicked')
+ _handle_tree_item_select(_ctrls.tree.get_selected(), true)
+
+func _on_Collapse_pressed():
+ collapse_selected()
+
+
+func _on_Expand_pressed():
+ expand_selected()
+
+
+func _on_CollapseAll_pressed():
+ collapse_all()
+
+
+func _on_ExpandAll_pressed():
+ expand_all()
+
+
+func _on_Hide_Passing_pressed():
+ _hide_passing = _ctrls.toolbar.hide_passing.pressed
+
+# --------------
+# Public
+# --------------
+func load_json_file(path):
+ var text = _utils.get_file_as_text(path)
+ if(text != ''):
+ var result = JSON.parse(text)
+ if(result.error != OK):
+ add_centered_text(str(path, " has invalid json in it \n",
+ 'Error ', result.error, "@", result.error_line, "\n",
+ result.error_string))
+ return
+
+ load_json_results(result.result)
+ else:
+ add_centered_text(str(path, ' was empty or does not exist.'))
+
+
+func load_json_results(j):
+ clear()
+ add_centered_text('Nothing Here')
+ _load_result_tree(j)
+
+
+func add_centered_text(t):
+ _ctrls.lbl_overlay.text = t
+
+
+func clear_centered_text():
+ _ctrls.lbl_overlay.text = ''
+
+
+func clear():
+ _ctrls.tree.clear()
+ _root = _ctrls.tree.create_item()
+ clear_centered_text()
+
+
+func set_interface(which):
+ _interface = which
+
+
+func set_script_text_editors(value):
+ _editors = value
+
+
+func collapse_all():
+ _set_collapsed_on_all(_root, true)
+
+
+func expand_all():
+ _set_collapsed_on_all(_root, false)
+
+
+func collapse_selected():
+ var item = _ctrls.tree.get_selected()
+ if(item != null):
+ _set_collapsed_on_all(item, true)
+
+func expand_selected():
+ var item = _ctrls.tree.get_selected()
+ if(item != null):
+ _set_collapsed_on_all(item, false)
+
+
+func set_show_orphans(should):
+ _show_orphans = should
+
+
+func set_font(font_name, size):
+ pass
+# var dyn_font = DynamicFont.new()
+# var font_data = DynamicFontData.new()
+# font_data.font_path = 'res://addons/gut/fonts/' + font_name + '-Regular.ttf'
+# font_data.antialiased = true
+# dyn_font.font_data = font_data
+#
+# _font = dyn_font
+# _font.size = size
+# _font_size = size
+
+
+func set_output_control(value):
+ _output_control = value
diff --git a/addons/gut/gui/RunResults.tscn b/addons/gut/gui/RunResults.tscn
new file mode 100644
index 0000000..be4c1d5
--- /dev/null
+++ b/addons/gut/gui/RunResults.tscn
@@ -0,0 +1,165 @@
+[gd_scene load_steps=4 format=2]
+
+[ext_resource path="res://addons/gut/gui/RunResults.gd" type="Script" id=1]
+
+[sub_resource type="Image" id=3]
+data = {
+"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
+"format": "LumAlpha8",
+"height": 16,
+"mipmaps": false,
+"width": 16
+}
+
+[sub_resource type="ImageTexture" id=2]
+flags = 4
+flags = 4
+image = SubResource( 3 )
+size = Vector2( 16, 16 )
+
+[node name="RunResults" type="Control"]
+margin_right = 595.0
+margin_bottom = 459.0
+rect_min_size = Vector2( 302, 0 )
+script = ExtResource( 1 )
+
+[node name="VBox" type="VBoxContainer" parent="."]
+anchor_right = 1.0
+anchor_bottom = 1.0
+
+[node name="Toolbar" type="HBoxContainer" parent="VBox"]
+margin_right = 296.0
+margin_bottom = 24.0
+size_flags_horizontal = 0
+
+[node name="Expand" type="ToolButton" parent="VBox/Toolbar"]
+margin_right = 28.0
+margin_bottom = 24.0
+hint_tooltip = "Expand selected item and all children."
+icon = SubResource( 2 )
+
+[node name="Collapse" type="ToolButton" parent="VBox/Toolbar"]
+margin_left = 32.0
+margin_right = 60.0
+margin_bottom = 24.0
+hint_tooltip = "Collapse selected item and all children."
+icon = SubResource( 2 )
+
+[node name="Sep" type="ColorRect" parent="VBox/Toolbar"]
+margin_left = 64.0
+margin_right = 66.0
+margin_bottom = 24.0
+rect_min_size = Vector2( 2, 0 )
+
+[node name="LblAll" type="Label" parent="VBox/Toolbar"]
+margin_left = 70.0
+margin_top = 5.0
+margin_right = 91.0
+margin_bottom = 19.0
+text = "All:"
+align = 1
+
+[node name="ExpandAll" type="ToolButton" parent="VBox/Toolbar"]
+margin_left = 95.0
+margin_right = 123.0
+margin_bottom = 24.0
+hint_tooltip = "Expand All."
+icon = SubResource( 2 )
+
+[node name="CollapseAll" type="ToolButton" parent="VBox/Toolbar"]
+margin_left = 127.0
+margin_right = 155.0
+margin_bottom = 24.0
+hint_tooltip = "Collapse all."
+icon = SubResource( 2 )
+
+[node name="Sep2" type="ColorRect" parent="VBox/Toolbar"]
+margin_left = 159.0
+margin_right = 161.0
+margin_bottom = 24.0
+rect_min_size = Vector2( 2, 0 )
+
+[node name="HidePassing" type="CheckBox" parent="VBox/Toolbar"]
+margin_left = 165.0
+margin_right = 189.0
+margin_bottom = 24.0
+hint_tooltip = "Show/Hide passing tests. Takes effect on next run."
+size_flags_horizontal = 4
+custom_icons/checked = SubResource( 2 )
+custom_icons/unchecked = SubResource( 2 )
+pressed = true
+__meta__ = {
+"_editor_description_": ""
+}
+
+[node name="Sep3" type="ColorRect" parent="VBox/Toolbar"]
+margin_left = 193.0
+margin_right = 195.0
+margin_bottom = 24.0
+rect_min_size = Vector2( 2, 0 )
+
+[node name="LblSync" type="Label" parent="VBox/Toolbar"]
+margin_left = 199.0
+margin_top = 5.0
+margin_right = 232.0
+margin_bottom = 19.0
+text = "Sync:"
+align = 1
+
+[node name="ShowScript" type="ToolButton" parent="VBox/Toolbar"]
+margin_left = 236.0
+margin_right = 264.0
+margin_bottom = 24.0
+hint_tooltip = "Open script and scroll to line when a tree item is clicked."
+toggle_mode = true
+pressed = true
+icon = SubResource( 2 )
+
+[node name="ScrollOutput" type="ToolButton" parent="VBox/Toolbar"]
+margin_left = 268.0
+margin_right = 296.0
+margin_bottom = 24.0
+hint_tooltip = "Scroll to related line in the output panel when tree item clicked."
+toggle_mode = true
+pressed = true
+icon = SubResource( 2 )
+
+[node name="Output" type="Panel" parent="VBox"]
+self_modulate = Color( 1, 1, 1, 0.541176 )
+margin_top = 28.0
+margin_right = 595.0
+margin_bottom = 459.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="Scroll" type="ScrollContainer" parent="VBox/Output"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+
+[node name="Tree" type="Tree" parent="VBox/Output/Scroll"]
+margin_right = 595.0
+margin_bottom = 431.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+columns = 2
+hide_root = true
+
+[node name="OverlayMessage" type="Label" parent="VBox/Output"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+align = 1
+valign = 1
+
+[node name="FontSampler" type="Label" parent="."]
+visible = false
+margin_right = 40.0
+margin_bottom = 14.0
+text = "000 of 000 passed"
+
+[connection signal="pressed" from="VBox/Toolbar/Expand" to="." method="_on_Expand_pressed"]
+[connection signal="pressed" from="VBox/Toolbar/Collapse" to="." method="_on_Collapse_pressed"]
+[connection signal="pressed" from="VBox/Toolbar/ExpandAll" to="." method="_on_ExpandAll_pressed"]
+[connection signal="pressed" from="VBox/Toolbar/CollapseAll" to="." method="_on_CollapseAll_pressed"]
+[connection signal="pressed" from="VBox/Toolbar/HidePassing" to="." method="_on_Hide_Passing_pressed"]
+[connection signal="item_activated" from="VBox/Output/Scroll/Tree" to="." method="_on_Tree_item_activated"]
+[connection signal="item_selected" from="VBox/Output/Scroll/Tree" to="." method="_on_Tree_item_selected"]
diff --git a/addons/gut/gui/arrow.png b/addons/gut/gui/arrow.png
index 09d8323..26a7dc4 100644
--- a/addons/gut/gui/arrow.png
+++ b/addons/gut/gui/arrow.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f9918e125593427685252d339f9c49d2ce8ce0b0fff9d28089164d78fccd80e2
-size 645
+oid sha256:7f5a2f25d0cc68b8281bda24cdcd1d816e718d11fa0d050ede6cbad9033d17ed
+size 122
diff --git a/addons/gut/gui/gut_config_gui.gd b/addons/gut/gui/gut_config_gui.gd
index dc58be4..4f056ff 100644
--- a/addons/gut/gui/gut_config_gui.gd
+++ b/addons/gut/gui/gut_config_gui.gd
@@ -272,8 +272,8 @@ func get_config_issues():
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")
+ if(!_cfg_ctrls['suffix'].text.ends_with('.gd')):
+ to_return.append("Script suffix must end in '.gd'")
return to_return
@@ -354,6 +354,8 @@ func set_options(options):
_add_title('Misc')
_add_value('prefix', options.prefix, 'Script Prefix',
"The filename prefix for all test scripts.")
+ _add_value('suffix', options.suffix, 'Script Suffix',
+ "Script suffix, including .gd extension. For example '_foo.gd'.")
func get_options(base_opts):
@@ -404,5 +406,6 @@ func get_options(base_opts):
# Misc
to_return.prefix = _cfg_ctrls.prefix.text
+ to_return.suffix = _cfg_ctrls.suffix.text
return to_return
diff --git a/addons/gut/gui/play.png b/addons/gut/gui/play.png
index 697f319..2be1d4d 100644
--- a/addons/gut/gui/play.png
+++ b/addons/gut/gui/play.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:90041b369445e15dfaa4ec011cb4f1c0536a8d63295a574e0f8743f772dc418e
-size 676
+oid sha256:d4280462a7940ebaf184e2b92bd01a69cf09ae1c97b175ac92e1410d16ecf278
+size 143