fix: 修复聊天系统编译错误
- 修复 WebSocketManager/SocketIOClient 函数缩进错误 - 重命名 is_connected() 避免与 Object 基类冲突 - 修复 tscn 文件多余前导空格 - 修复测试文件 GUT 断言函数调用 - 添加 GUT 测试框架
This commit is contained in:
13
addons/gut/gui/EditorRadioButton.tres
Normal file
13
addons/gut/gui/EditorRadioButton.tres
Normal file
@@ -0,0 +1,13 @@
|
||||
[gd_resource type="Theme" load_steps=3 format=3 uid="uid://dssgvu257o1si"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_u716c"]
|
||||
bg_color = Color(0.43137255, 0.8784314, 0.6156863, 0.5254902)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ht2pf"]
|
||||
bg_color = Color(0, 0.44705883, 0.23921569, 1)
|
||||
|
||||
[resource]
|
||||
Button/colors/font_hover_pressed_color = Color(1, 1, 1, 1)
|
||||
Button/colors/font_pressed_color = Color(1, 1, 1, 1)
|
||||
Button/styles/hover = SubResource("StyleBoxFlat_u716c")
|
||||
Button/styles/pressed = SubResource("StyleBoxFlat_ht2pf")
|
||||
543
addons/gut/gui/GutBottomPanel.gd
Normal file
543
addons/gut/gui/GutBottomPanel.gd
Normal file
@@ -0,0 +1,543 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
var GutConfigGui = load('res://addons/gut/gui/gut_config_gui.gd')
|
||||
var AboutWindow = load("res://addons/gut/gui/about.tscn")
|
||||
|
||||
var _interface = null;
|
||||
var _is_running = false :
|
||||
set(val):
|
||||
_is_running = val
|
||||
_disable_run_buttons(_is_running)
|
||||
|
||||
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) :
|
||||
set(val):
|
||||
_light_color = val
|
||||
if(is_inside_tree()):
|
||||
_ctrls.light.queue_redraw()
|
||||
var _panel_button = null
|
||||
var _user_prefs = null
|
||||
var _shell_out_panel = null
|
||||
|
||||
|
||||
var menu_manager = null :
|
||||
set(val):
|
||||
menu_manager = val
|
||||
if(val != null):
|
||||
_apply_shortcuts()
|
||||
menu_manager.toggle_windowed.connect(_on_toggle_windowed)
|
||||
menu_manager.about.connect(show_about)
|
||||
menu_manager.run_all.connect(_run_all)
|
||||
menu_manager.show_gut.connect(_on_show_gut)
|
||||
|
||||
|
||||
@onready var _ctrls = {
|
||||
about = %ExtraButtons/About,
|
||||
light = %StatusIndicator,
|
||||
output_button = %ExtraButtons/OutputBtn,
|
||||
run_button = $layout/ControlBar/RunAll,
|
||||
run_externally_dialog = $ShellOutOptions,
|
||||
run_mode = %ExtraButtons/RunMode,
|
||||
run_at_cursor = $layout/ControlBar/RunAtCursor,
|
||||
run_results_button = %ExtraButtons/RunResultsBtn,
|
||||
settings = $layout/RSplit/sc/Settings,
|
||||
settings_button = %ExtraButtons/Settings,
|
||||
shortcut_dialog = $ShortcutDialog,
|
||||
shortcuts_button = %ExtraButtons/Shortcuts,
|
||||
|
||||
results = {
|
||||
bar = $layout/ControlBar2,
|
||||
errors = %errors_value,
|
||||
failing = %failing_value,
|
||||
orphans = %orphans_value,
|
||||
passing = %passing_value,
|
||||
pending = %pending_value,
|
||||
warnings = %warnings_value,
|
||||
},
|
||||
}
|
||||
|
||||
@onready var results_v_split = %VSplitResults
|
||||
@onready var results_h_split = %HSplitResults
|
||||
@onready var results_tree = %RunResults
|
||||
@onready var results_text = %OutputText
|
||||
@onready var make_floating_btn = %MakeFloating
|
||||
|
||||
|
||||
func _ready():
|
||||
if(get_parent() is SubViewport):
|
||||
return
|
||||
|
||||
GutEditorGlobals.create_temp_directory()
|
||||
|
||||
_user_prefs = GutEditorGlobals.user_prefs
|
||||
_gut_config_gui = GutConfigGui.new(_ctrls.settings)
|
||||
|
||||
_ctrls.results.bar.connect('draw', _on_results_bar_draw.bind(_ctrls.results.bar))
|
||||
hide_settings(!_ctrls.settings_button.button_pressed)
|
||||
|
||||
_gut_config.load_options(GutEditorGlobals.editor_run_gut_config_path)
|
||||
_gut_config_gui.set_options(_gut_config.options)
|
||||
|
||||
_ctrls.shortcuts_button.icon = get_theme_icon('Shortcut', 'EditorIcons')
|
||||
_ctrls.settings_button.icon = get_theme_icon('Tools', 'EditorIcons')
|
||||
_ctrls.run_results_button.icon = get_theme_icon('AnimationTrackGroup', 'EditorIcons') # Tree
|
||||
_ctrls.output_button.icon = get_theme_icon('Font', 'EditorIcons')
|
||||
make_floating_btn.icon = get_theme_icon("MakeFloating", 'EditorIcons')
|
||||
make_floating_btn.text = ''
|
||||
_ctrls.about.icon = get_theme_icon('Info', 'EditorIcons')
|
||||
_ctrls.about.text = ''
|
||||
_ctrls.run_mode.icon = get_theme_icon("ViewportSpeed", 'EditorIcons')
|
||||
|
||||
results_tree.set_output_control(results_text)
|
||||
|
||||
var check_import = load('res://addons/gut/images/HSplitContainer.svg')
|
||||
if(check_import == null):
|
||||
results_tree.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:
|
||||
results_tree.add_centered_text("Let's run some tests!")
|
||||
|
||||
_ctrls.run_externally_dialog.load_from_file()
|
||||
_apply_options_to_controls()
|
||||
|
||||
results_vert_layout()
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
if(_is_running):
|
||||
if(_ctrls.run_externally_dialog.should_run_externally()):
|
||||
if(!is_instance_valid(_shell_out_panel)):
|
||||
_is_running = false
|
||||
show_me()
|
||||
elif(!_interface.is_playing_scene()):
|
||||
_is_running = false
|
||||
results_text.add_text("\ndone")
|
||||
load_result_output()
|
||||
show_me()
|
||||
|
||||
|
||||
# ---------------
|
||||
# Private
|
||||
# ---------------
|
||||
func _apply_options_to_controls():
|
||||
hide_settings(_user_prefs.hide_settings.value)
|
||||
hide_result_tree(_user_prefs.hide_result_tree.value)
|
||||
hide_output_text(_user_prefs.hide_output_text.value)
|
||||
results_tree.set_show_orphans(!_gut_config.options.hide_orphans)
|
||||
var shell_dialog_size = _user_prefs.run_externally_options_dialog_size.value
|
||||
|
||||
if(shell_dialog_size != Vector2i(-1, -1)):
|
||||
_ctrls.run_externally_dialog.size = Vector2i(shell_dialog_size)
|
||||
|
||||
if(_user_prefs.shortcuts_dialog_size.value != Vector2i(-1, -1)):
|
||||
_ctrls.shortcut_dialog.size = _user_prefs.shortcuts_dialog_size.value
|
||||
|
||||
var mode_ind = 'Ed'
|
||||
if(_ctrls.run_externally_dialog.run_mode == _ctrls.run_externally_dialog.RUN_MODE_BLOCKING):
|
||||
mode_ind = 'ExB'
|
||||
elif(_ctrls.run_externally_dialog.run_mode == _ctrls.run_externally_dialog.RUN_MODE_NON_BLOCKING):
|
||||
mode_ind = 'ExN'
|
||||
_ctrls.run_mode.text = mode_ind
|
||||
|
||||
_ctrls.run_at_cursor.apply_gut_config(_gut_config)
|
||||
|
||||
|
||||
|
||||
func _disable_run_buttons(should):
|
||||
_ctrls.run_button.disabled = should
|
||||
_ctrls.run_at_cursor.disabled = should
|
||||
|
||||
|
||||
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 _show_errors(errs):
|
||||
results_text.clear()
|
||||
var text = "Cannot run tests, you have a configuration error:\n"
|
||||
for e in errs:
|
||||
text += str('* ', e, "\n")
|
||||
text += "Check your settings ----->"
|
||||
results_text.add_text(text)
|
||||
hide_output_text(false)
|
||||
hide_settings(false)
|
||||
|
||||
|
||||
func _save_user_prefs():
|
||||
_user_prefs.hide_settings.value = !_ctrls.settings_button.button_pressed
|
||||
_user_prefs.hide_result_tree.value = !_ctrls.run_results_button.button_pressed
|
||||
_user_prefs.hide_output_text.value = !_ctrls.output_button.button_pressed
|
||||
_user_prefs.shortcuts_dialog_size.value = _ctrls.shortcut_dialog.size
|
||||
|
||||
_user_prefs.run_externally.value = _ctrls.run_externally_dialog.run_mode != _ctrls.run_externally_dialog.RUN_MODE_EDITOR
|
||||
_user_prefs.run_externally_options_dialog_size.value = _ctrls.run_externally_dialog.size
|
||||
|
||||
_user_prefs.save_it()
|
||||
|
||||
|
||||
func _save_config():
|
||||
_save_user_prefs()
|
||||
|
||||
_gut_config.options = _gut_config_gui.get_options(_gut_config.options)
|
||||
var w_result = _gut_config.write_options(GutEditorGlobals.editor_run_gut_config_path)
|
||||
if(w_result != OK):
|
||||
push_error(str('Could not write options to ', GutEditorGlobals.editor_run_gut_config_path, ': ', w_result))
|
||||
else:
|
||||
_gut_config_gui.mark_saved()
|
||||
|
||||
|
||||
func _run_externally():
|
||||
_shell_out_panel = GutUtils.RunExternallyScene.instantiate()
|
||||
_shell_out_panel.bottom_panel = self
|
||||
_shell_out_panel.blocking_mode = _ctrls.run_externally_dialog.run_mode
|
||||
_shell_out_panel.additional_arguments = _ctrls.run_externally_dialog.get_additional_arguments_array()
|
||||
|
||||
add_child(_shell_out_panel)
|
||||
_shell_out_panel.run_tests()
|
||||
|
||||
|
||||
func _run_tests():
|
||||
show_me()
|
||||
if(_is_running):
|
||||
push_error("GUT: Cannot run tests, tests are already running.")
|
||||
return
|
||||
|
||||
clear_results()
|
||||
GutEditorGlobals.create_temp_directory()
|
||||
_light_color = Color.BLUE
|
||||
|
||||
var issues = _gut_config_gui.get_config_issues()
|
||||
if(issues.size() > 0):
|
||||
_show_errors(issues)
|
||||
return
|
||||
|
||||
write_file(GutEditorGlobals.editor_run_bbcode_results_path, 'Run in progress')
|
||||
write_file(GutEditorGlobals.editor_run_json_results_path, '')
|
||||
_save_config()
|
||||
_apply_options_to_controls()
|
||||
|
||||
results_text.clear()
|
||||
results_tree.clear()
|
||||
results_tree.add_centered_text('Running...')
|
||||
|
||||
_is_running = true
|
||||
results_text.add_text('Running...')
|
||||
|
||||
if(_ctrls.run_externally_dialog.should_run_externally()):
|
||||
_gut_plugin.make_bottom_panel_item_visible(self)
|
||||
_run_externally()
|
||||
else:
|
||||
_interface.play_custom_scene('res://addons/gut/gui/run_from_editor.tscn')
|
||||
|
||||
|
||||
func _apply_shortcuts():
|
||||
if(menu_manager != null):
|
||||
menu_manager.apply_gut_shortcuts(_ctrls.shortcut_dialog)
|
||||
|
||||
_ctrls.run_button.shortcut = \
|
||||
_ctrls.shortcut_dialog.scbtn_run_all.get_shortcut()
|
||||
_ctrls.run_at_cursor.get_script_button().shortcut = \
|
||||
_ctrls.shortcut_dialog.scbtn_run_current_script.get_shortcut()
|
||||
_ctrls.run_at_cursor.get_inner_button().shortcut = \
|
||||
_ctrls.shortcut_dialog.scbtn_run_current_inner.get_shortcut()
|
||||
_ctrls.run_at_cursor.get_test_button().shortcut = \
|
||||
_ctrls.shortcut_dialog.scbtn_run_current_test.get_shortcut()
|
||||
# Took this out because it seems to break using the shortcut when docked.
|
||||
# Though it does allow the shortcut to work when windowed. Shortcuts
|
||||
# are weird.
|
||||
# make_floating_btn.shortcut = \
|
||||
# _ctrls.shortcut_dialog.scbtn_windowed.get_shortcut()
|
||||
|
||||
|
||||
if(_panel_button != null):
|
||||
_panel_button.shortcut = _ctrls.shortcut_dialog.scbtn_panel.get_shortcut()
|
||||
|
||||
|
||||
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_results_bar_draw(bar):
|
||||
bar.draw_rect(Rect2(Vector2(0, 0), bar.size), Color(0, 0, 0, .2))
|
||||
|
||||
|
||||
func _on_Light_draw():
|
||||
var l = _ctrls.light
|
||||
l.draw_circle(Vector2(l.size.x / 2, l.size.y / 2), l.size.x / 2, _light_color)
|
||||
|
||||
|
||||
func _on_RunAll_pressed():
|
||||
_run_all()
|
||||
|
||||
|
||||
func _on_Shortcuts_pressed():
|
||||
_ctrls.shortcut_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_sortcut_dialog_confirmed() -> void:
|
||||
_apply_shortcuts()
|
||||
_ctrls.shortcut_dialog.save_shortcuts()
|
||||
_save_user_prefs()
|
||||
|
||||
|
||||
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.method
|
||||
|
||||
_run_tests()
|
||||
|
||||
|
||||
func _on_Settings_pressed():
|
||||
hide_settings(!_ctrls.settings_button.button_pressed)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _on_OutputBtn_pressed():
|
||||
hide_output_text(!_ctrls.output_button.button_pressed)
|
||||
_save_config()
|
||||
|
||||
|
||||
func _on_RunResultsBtn_pressed():
|
||||
hide_result_tree(! _ctrls.run_results_button.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
|
||||
|
||||
|
||||
func _on_shell_out_options_confirmed() -> void:
|
||||
_ctrls.run_externally_dialog.save_to_file()
|
||||
_save_user_prefs()
|
||||
_apply_options_to_controls()
|
||||
|
||||
|
||||
func _on_run_mode_pressed() -> void:
|
||||
_ctrls.run_externally_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_toggle_windowed():
|
||||
_gut_plugin.toggle_windowed()
|
||||
|
||||
|
||||
func _on_to_window_pressed() -> void:
|
||||
_gut_plugin.toggle_windowed()
|
||||
|
||||
|
||||
func _on_show_gut() -> void:
|
||||
show_hide()
|
||||
|
||||
|
||||
func _on_about_pressed() -> void:
|
||||
show_about()
|
||||
|
||||
# ---------------
|
||||
# Public
|
||||
# ---------------
|
||||
func load_shortcuts():
|
||||
_ctrls.shortcut_dialog.load_shortcuts()
|
||||
_apply_shortcuts()
|
||||
|
||||
|
||||
func hide_result_tree(should):
|
||||
results_tree.visible = !should
|
||||
_ctrls.run_results_button.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.button_pressed = !should
|
||||
|
||||
|
||||
func hide_output_text(should):
|
||||
results_text.visible = !should
|
||||
_ctrls.output_button.button_pressed = !should
|
||||
|
||||
|
||||
func clear_results():
|
||||
_light_color = Color(0, 0, 0, .5)
|
||||
|
||||
_ctrls.results.passing.text = "0"
|
||||
_ctrls.results.passing.get_parent().visible = false
|
||||
|
||||
_ctrls.results.failing.text = "0"
|
||||
_ctrls.results.failing.get_parent().visible = false
|
||||
|
||||
_ctrls.results.pending.text = "0"
|
||||
_ctrls.results.pending.get_parent().visible = false
|
||||
|
||||
_ctrls.results.errors.text = "0"
|
||||
_ctrls.results.errors.get_parent().visible = false
|
||||
|
||||
_ctrls.results.warnings.text = "0"
|
||||
_ctrls.results.warnings.get_parent().visible = false
|
||||
|
||||
_ctrls.results.orphans.text = "0"
|
||||
_ctrls.results.orphans.get_parent().visible = false
|
||||
|
||||
|
||||
func load_result_json():
|
||||
var summary = get_file_as_text(GutEditorGlobals.editor_run_json_results_path)
|
||||
var test_json_conv = JSON.new()
|
||||
if (test_json_conv.parse(summary) != OK):
|
||||
return
|
||||
var results = test_json_conv.get_data()
|
||||
|
||||
results_tree.load_json_results(results)
|
||||
|
||||
var summary_json = results['test_scripts']['props']
|
||||
_ctrls.results.passing.text = str(int(summary_json.passing))
|
||||
_ctrls.results.passing.get_parent().visible = true
|
||||
|
||||
_ctrls.results.failing.text = str(int(summary_json.failures))
|
||||
_ctrls.results.failing.get_parent().visible = true
|
||||
|
||||
_ctrls.results.pending.text = str(int(summary_json.pending) + int(summary_json.risky))
|
||||
_ctrls.results.pending.get_parent().visible = _ctrls.results.pending.text != '0'
|
||||
|
||||
_ctrls.results.errors.text = str(int(summary_json.errors))
|
||||
_ctrls.results.errors.get_parent().visible = _ctrls.results.errors.text != '0'
|
||||
|
||||
_ctrls.results.warnings.text = str(int(summary_json.warnings))
|
||||
_ctrls.results.warnings.get_parent().visible = _ctrls.results.warnings.text != '0'
|
||||
|
||||
_ctrls.results.orphans.text = str(int(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 or summary_json.risky != 0):
|
||||
_light_color = Color(1, 1, 0, .75)
|
||||
else:
|
||||
_light_color = Color(0, 1, 0, .75)
|
||||
|
||||
_ctrls.light.visible = true
|
||||
|
||||
|
||||
func load_result_text():
|
||||
results_text.load_file(GutEditorGlobals.editor_run_bbcode_results_path)
|
||||
|
||||
|
||||
func load_result_output():
|
||||
load_result_text()
|
||||
load_result_json()
|
||||
|
||||
|
||||
func set_interface(value):
|
||||
_interface = value
|
||||
results_tree.set_interface(_interface)
|
||||
|
||||
|
||||
func set_plugin(value):
|
||||
_gut_plugin = value
|
||||
|
||||
|
||||
func set_panel_button(value):
|
||||
_panel_button = value
|
||||
|
||||
|
||||
func write_file(path, content):
|
||||
var f = FileAccess.open(path, FileAccess.WRITE)
|
||||
if(f != null):
|
||||
f.store_string(content)
|
||||
f = null;
|
||||
|
||||
return FileAccess.get_open_error()
|
||||
|
||||
|
||||
func get_file_as_text(path):
|
||||
var to_return = ''
|
||||
var f = FileAccess.open(path, FileAccess.READ)
|
||||
if(f != null):
|
||||
to_return = f.get_as_text()
|
||||
f = null
|
||||
return to_return
|
||||
|
||||
|
||||
func get_text_output_control():
|
||||
return results_text
|
||||
|
||||
|
||||
func add_output_text(text):
|
||||
results_text.add_text(text)
|
||||
|
||||
|
||||
func show_about():
|
||||
var about = AboutWindow.instantiate()
|
||||
add_child(about)
|
||||
about.popup_centered()
|
||||
about.confirmed.connect(about.queue_free)
|
||||
|
||||
|
||||
func show_me():
|
||||
if(owner is Window):
|
||||
owner.grab_focus()
|
||||
else:
|
||||
_gut_plugin.make_bottom_panel_item_visible(self)
|
||||
|
||||
|
||||
func show_hide():
|
||||
if(owner is Window):
|
||||
if(owner.has_focus()):
|
||||
var win_to_focus_on = EditorInterface.get_editor_main_screen().get_parent()
|
||||
while(win_to_focus_on != null and win_to_focus_on is not Window):
|
||||
win_to_focus_on = win_to_focus_on.get_parent()
|
||||
if(win_to_focus_on != null):
|
||||
win_to_focus_on.grab_focus()
|
||||
else:
|
||||
owner.grab_focus()
|
||||
else:
|
||||
pass
|
||||
# We don't have to do anything when we are docked because the GUT
|
||||
# bottom panel has the shortcut and it does the toggling all on its
|
||||
# own.
|
||||
|
||||
|
||||
func get_shortcut_dialog():
|
||||
return _ctrls.shortcut_dialog
|
||||
|
||||
|
||||
func results_vert_layout():
|
||||
if(results_tree.get_parent() != results_v_split):
|
||||
results_tree.reparent(results_v_split)
|
||||
results_text.reparent(results_v_split)
|
||||
results_v_split.visible = true
|
||||
results_h_split.visible = false
|
||||
|
||||
|
||||
func results_horiz_layout():
|
||||
if(results_tree.get_parent() != results_h_split):
|
||||
results_tree.reparent(results_h_split)
|
||||
results_text.reparent(results_h_split)
|
||||
results_v_split.visible = false
|
||||
results_h_split.visible = true
|
||||
1
addons/gut/gui/GutBottomPanel.gd.uid
Normal file
1
addons/gut/gui/GutBottomPanel.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dtvnb0xatk0my
|
||||
295
addons/gut/gui/GutBottomPanel.tscn
Normal file
295
addons/gut/gui/GutBottomPanel.tscn
Normal file
@@ -0,0 +1,295 @@
|
||||
[gd_scene load_steps=10 format=3 uid="uid://b3bostcslstem"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dtvnb0xatk0my" path="res://addons/gut/gui/GutBottomPanel.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://0yunjxtaa8iw" path="res://addons/gut/gui/RunAtCursor.tscn" id="3"]
|
||||
[ext_resource type="Texture2D" uid="uid://cr6tvdv0ve6cv" path="res://addons/gut/gui/play.png" id="4"]
|
||||
[ext_resource type="Texture2D" uid="uid://bvo0uao7deu0q" path="res://addons/gut/icon.png" id="4_xv2r3"]
|
||||
[ext_resource type="PackedScene" uid="uid://4gyyn12um08h" path="res://addons/gut/gui/RunResults.tscn" id="5"]
|
||||
[ext_resource type="PackedScene" uid="uid://bqmo4dj64c7yl" path="res://addons/gut/gui/OutputText.tscn" id="6"]
|
||||
[ext_resource type="PackedScene" uid="uid://dj5ve0bq7xa5j" path="res://addons/gut/gui/ShortcutDialog.tscn" id="7_srqj5"]
|
||||
[ext_resource type="PackedScene" uid="uid://ckv5eh8xyrwbk" path="res://addons/gut/gui/ShellOutOptions.tscn" id="7_xv2r3"]
|
||||
|
||||
[sub_resource type="Shortcut" id="9"]
|
||||
|
||||
[node name="GutBottomPanel" type="Control"]
|
||||
custom_minimum_size = Vector2(250, 250)
|
||||
layout_mode = 3
|
||||
anchor_left = -0.0025866
|
||||
anchor_top = -0.00176575
|
||||
anchor_right = 0.997413
|
||||
anchor_bottom = 0.998234
|
||||
offset_left = 2.64868
|
||||
offset_top = 1.05945
|
||||
offset_right = 2.64862
|
||||
offset_bottom = 1.05945
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="layout" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="ControlBar" type="HBoxContainer" parent="layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RunAll" type="Button" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 11
|
||||
shortcut = SubResource("9")
|
||||
text = "Run All"
|
||||
icon = ExtResource("4")
|
||||
|
||||
[node name="Sep3" type="ColorRect" parent="layout/ControlBar"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RunAtCursor" parent="layout/ControlBar" instance=ExtResource("3")]
|
||||
custom_minimum_size = Vector2(648, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CenterContainer2" type="CenterContainer" parent="layout/ControlBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="MakeFloating" type="Button" parent="layout/ControlBar"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Move the GUT panel to a window."
|
||||
icon = ExtResource("4_xv2r3")
|
||||
flat = true
|
||||
|
||||
[node name="ControlBar2" type="HBoxContainer" parent="layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep2" type="ColorRect" parent="layout/ControlBar2"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
color = Color(1, 1, 1, 0)
|
||||
|
||||
[node name="StatusIndicator" type="Control" parent="layout/ControlBar2"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(30, 30)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Passing" type="HBoxContainer" parent="layout/ControlBar2"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/ControlBar2/Passing"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/ControlBar2/Passing"]
|
||||
layout_mode = 2
|
||||
text = "Pass"
|
||||
|
||||
[node name="passing_value" type="Label" parent="layout/ControlBar2/Passing"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Failing" type="HBoxContainer" parent="layout/ControlBar2"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/ControlBar2/Failing"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/ControlBar2/Failing"]
|
||||
layout_mode = 2
|
||||
text = "Fail"
|
||||
|
||||
[node name="failing_value" type="Label" parent="layout/ControlBar2/Failing"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Pending" type="HBoxContainer" parent="layout/ControlBar2"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/ControlBar2/Pending"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/ControlBar2/Pending"]
|
||||
layout_mode = 2
|
||||
text = "Risky"
|
||||
|
||||
[node name="pending_value" type="Label" parent="layout/ControlBar2/Pending"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Orphans" type="HBoxContainer" parent="layout/ControlBar2"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/ControlBar2/Orphans"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/ControlBar2/Orphans"]
|
||||
layout_mode = 2
|
||||
text = "Orphans"
|
||||
|
||||
[node name="orphans_value" type="Label" parent="layout/ControlBar2/Orphans"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Errors" type="HBoxContainer" parent="layout/ControlBar2"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/ControlBar2/Errors"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/ControlBar2/Errors"]
|
||||
layout_mode = 2
|
||||
text = "Errors"
|
||||
|
||||
[node name="errors_value" type="Label" parent="layout/ControlBar2/Errors"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="Warnings" type="HBoxContainer" parent="layout/ControlBar2"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="layout/ControlBar2/Warnings"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="label" type="Label" parent="layout/ControlBar2/Warnings"]
|
||||
layout_mode = 2
|
||||
text = "Warnings"
|
||||
|
||||
[node name="warnings_value" type="Label" parent="layout/ControlBar2/Warnings"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "---"
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="layout/ControlBar2"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ExtraButtons" type="HBoxContainer" parent="layout/ControlBar2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sep1" type="ColorRect" parent="layout/ControlBar2/ExtraButtons"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RunMode" type="Button" parent="layout/ControlBar2/ExtraButtons"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Run Mode. Run tests through the editor or externally in a different process."
|
||||
text = "ExN"
|
||||
icon = ExtResource("4_xv2r3")
|
||||
|
||||
[node name="Sep2" type="ColorRect" parent="layout/ControlBar2/ExtraButtons"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RunResultsBtn" type="Button" parent="layout/ControlBar2/ExtraButtons"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Show/Hide Result Tree"
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
icon = ExtResource("4_xv2r3")
|
||||
|
||||
[node name="OutputBtn" type="Button" parent="layout/ControlBar2/ExtraButtons"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Show/Hide Text Output"
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
icon = ExtResource("4_xv2r3")
|
||||
|
||||
[node name="Settings" type="Button" parent="layout/ControlBar2/ExtraButtons"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "GUT Settings"
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
icon = ExtResource("4_xv2r3")
|
||||
|
||||
[node name="Sep3" type="ColorRect" parent="layout/ControlBar2/ExtraButtons"]
|
||||
custom_minimum_size = Vector2(1, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Shortcuts" type="Button" parent="layout/ControlBar2/ExtraButtons"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 11
|
||||
tooltip_text = "GUT Shortcuts"
|
||||
icon = ExtResource("4_xv2r3")
|
||||
|
||||
[node name="About" type="Button" parent="layout/ControlBar2/ExtraButtons"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "About"
|
||||
text = "A"
|
||||
|
||||
[node name="RSplit" type="HSplitContainer" parent="layout"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="CResults" type="VBoxContainer" parent="layout/RSplit"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="HSplitResults" type="HSplitContainer" parent="layout/RSplit/CResults"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="RunResults" parent="layout/RSplit/CResults/HSplitResults" instance=ExtResource("5")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(255, 255)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="OutputText" parent="layout/RSplit/CResults/HSplitResults" instance=ExtResource("6")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VSplitResults" type="VSplitContainer" parent="layout/RSplit/CResults"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="sc" type="ScrollContainer" parent="layout/RSplit"]
|
||||
custom_minimum_size = Vector2(500, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Settings" type="VBoxContainer" parent="layout/RSplit/sc"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ShortcutDialog" parent="." instance=ExtResource("7_srqj5")]
|
||||
visible = false
|
||||
|
||||
[node name="ShellOutOptions" parent="." instance=ExtResource("7_xv2r3")]
|
||||
size = Vector2i(1300, 1336)
|
||||
visible = false
|
||||
|
||||
[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/MakeFloating" to="." method="_on_to_window_pressed"]
|
||||
[connection signal="draw" from="layout/ControlBar2/StatusIndicator" to="." method="_on_Light_draw"]
|
||||
[connection signal="pressed" from="layout/ControlBar2/ExtraButtons/RunMode" to="." method="_on_run_mode_pressed"]
|
||||
[connection signal="pressed" from="layout/ControlBar2/ExtraButtons/RunResultsBtn" to="." method="_on_RunResultsBtn_pressed"]
|
||||
[connection signal="pressed" from="layout/ControlBar2/ExtraButtons/OutputBtn" to="." method="_on_OutputBtn_pressed"]
|
||||
[connection signal="pressed" from="layout/ControlBar2/ExtraButtons/Settings" to="." method="_on_Settings_pressed"]
|
||||
[connection signal="pressed" from="layout/ControlBar2/ExtraButtons/Shortcuts" to="." method="_on_Shortcuts_pressed"]
|
||||
[connection signal="pressed" from="layout/ControlBar2/ExtraButtons/About" to="." method="_on_about_pressed"]
|
||||
[connection signal="confirmed" from="ShortcutDialog" to="." method="_on_sortcut_dialog_confirmed"]
|
||||
[connection signal="confirmed" from="ShellOutOptions" to="." method="_on_shell_out_options_confirmed"]
|
||||
326
addons/gut/gui/GutControl.gd
Normal file
326
addons/gut/gui/GutControl.gd
Normal file
@@ -0,0 +1,326 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
const RUNNER_JSON_PATH = 'res://.gut_editor_config.json'
|
||||
|
||||
var GutConfig = load('res://addons/gut/gut_config.gd')
|
||||
var GutRunnerScene = load('res://addons/gut/gui/GutRunner.tscn')
|
||||
var GutConfigGui = load('res://addons/gut/gui/gut_config_gui.gd')
|
||||
|
||||
var _config = GutConfig.new()
|
||||
var _config_gui = null
|
||||
var _gut_runner = null
|
||||
var _tree_root : TreeItem = null
|
||||
|
||||
var _script_icon = load('res://addons/gut/images/Script.svg')
|
||||
var _folder_icon = load('res://addons/gut/images/Folder.svg')
|
||||
|
||||
var _tree_scripts = {}
|
||||
var _tree_directories = {}
|
||||
|
||||
const TREE_SCRIPT = 'Script'
|
||||
const TREE_DIR = 'Directory'
|
||||
|
||||
@onready var _ctrls = {
|
||||
run_tests_button = $VBox/Buttons/RunTests,
|
||||
run_selected = $VBox/Buttons/RunSelected,
|
||||
test_tree = $VBox/Tabs/Tests,
|
||||
settings_vbox = $VBox/Tabs/SettingsScroll/Settings,
|
||||
tabs = $VBox/Tabs,
|
||||
bg = $Bg
|
||||
}
|
||||
|
||||
@export var bg_color : Color = Color(.36, .36, .36) :
|
||||
get: return bg_color
|
||||
set(val):
|
||||
bg_color = val
|
||||
if(is_inside_tree()):
|
||||
$Bg.color = bg_color
|
||||
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
_gut_runner = GutRunnerScene.instantiate()
|
||||
$Bg.color = bg_color
|
||||
_ctrls.tabs.set_tab_title(0, 'Tests')
|
||||
_ctrls.tabs.set_tab_title(1, 'Settings')
|
||||
|
||||
_config_gui = GutConfigGui.new(_ctrls.settings_vbox)
|
||||
|
||||
_ctrls.test_tree.hide_root = true
|
||||
add_child(_gut_runner)
|
||||
|
||||
# TODO This might not need to be called deferred after changing GutUtils to
|
||||
# an all static class.
|
||||
call_deferred('_post_ready')
|
||||
|
||||
|
||||
func _draw():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
var gut = _gut_runner.get_gut()
|
||||
if(!gut.is_running()):
|
||||
var r = Rect2(Vector2(0, 0), get_rect().size)
|
||||
draw_rect(r, Color.BLACK, false, 2)
|
||||
|
||||
|
||||
func _post_ready():
|
||||
var gut = _gut_runner.get_gut()
|
||||
gut.start_run.connect(_on_gut_run_started)
|
||||
gut.end_run.connect(_on_gut_run_ended)
|
||||
_refresh_tree_and_settings()
|
||||
|
||||
|
||||
func _set_meta_for_script_tree_item(item, script, test=null):
|
||||
var meta = {
|
||||
type = TREE_SCRIPT,
|
||||
script = script.path,
|
||||
inner_class = script.inner_class_name,
|
||||
test = ''
|
||||
}
|
||||
|
||||
if(test != null):
|
||||
meta.test = test.name
|
||||
|
||||
item.set_metadata(0, meta)
|
||||
|
||||
|
||||
func _set_meta_for_directory_tree_item(item, path, temp_item):
|
||||
var meta = {
|
||||
type = TREE_DIR,
|
||||
path = path,
|
||||
temp_item = temp_item
|
||||
}
|
||||
item.set_metadata(0, meta)
|
||||
|
||||
|
||||
func _get_script_tree_item(script, parent_item):
|
||||
if(!_tree_scripts.has(script.path)):
|
||||
var item = _ctrls.test_tree.create_item(parent_item)
|
||||
item.set_text(0, script.path.get_file())
|
||||
item.set_icon(0, _script_icon)
|
||||
_tree_scripts[script.path] = item
|
||||
_set_meta_for_script_tree_item(item, script)
|
||||
|
||||
return _tree_scripts[script.path]
|
||||
|
||||
|
||||
func _get_directory_tree_item(path):
|
||||
var parent = _tree_root
|
||||
if(!_tree_directories.has(path)):
|
||||
|
||||
var item : TreeItem = null
|
||||
if(parent != _tree_root):
|
||||
item = parent.create_child(0)
|
||||
else:
|
||||
item = parent.create_child()
|
||||
|
||||
_tree_directories[path] = item
|
||||
item.collapsed = false
|
||||
item.set_text(0, path)
|
||||
item.set_icon(0, _folder_icon)
|
||||
item.set_icon_modulate(0, Color.ROYAL_BLUE)
|
||||
# temp_item is used in calls with move_before since you must use
|
||||
# move_before or move_after to reparent tree items. This ensures that
|
||||
# there is an item on all directories. These are deleted later.
|
||||
var temp_item = item.create_child()
|
||||
temp_item.set_text(0, '<temp>')
|
||||
|
||||
_set_meta_for_directory_tree_item(item, path, temp_item)
|
||||
|
||||
return _tree_directories[path]
|
||||
|
||||
|
||||
func _find_dir_item_to_move_before(path):
|
||||
var max_matching_len = 0
|
||||
var best_parent = null
|
||||
|
||||
# Go through all the directory items finding the one that has the longest
|
||||
# path that contains our path.
|
||||
for key in _tree_directories.keys():
|
||||
if(path != key and path.begins_with(key) and key.length() > max_matching_len):
|
||||
max_matching_len = key.length()
|
||||
best_parent = _tree_directories[key]
|
||||
|
||||
var to_return = null
|
||||
if(best_parent != null):
|
||||
to_return = best_parent.get_metadata(0).temp_item
|
||||
return to_return
|
||||
|
||||
|
||||
func _reorder_dir_items():
|
||||
var the_keys = _tree_directories.keys()
|
||||
the_keys.sort()
|
||||
for key in _tree_directories.keys():
|
||||
var to_move = _tree_directories[key]
|
||||
to_move.collapsed = false
|
||||
var move_before = _find_dir_item_to_move_before(key)
|
||||
if(move_before != null):
|
||||
to_move.move_before(move_before)
|
||||
var new_text = key.substr(move_before.get_parent().get_metadata(0).path.length())
|
||||
to_move.set_text(0, new_text)
|
||||
|
||||
|
||||
func _remove_dir_temp_items():
|
||||
for key in _tree_directories.keys():
|
||||
var item = _tree_directories[key].get_metadata(0).temp_item
|
||||
_tree_directories[key].remove_child(item)
|
||||
|
||||
|
||||
func _add_dir_and_script_tree_items():
|
||||
var tree : Tree = _ctrls.test_tree
|
||||
tree.clear()
|
||||
_tree_root = _ctrls.test_tree.create_item()
|
||||
|
||||
var scripts = _gut_runner.get_gut().get_test_collector().scripts
|
||||
for script in scripts:
|
||||
var dir_item = _get_directory_tree_item(script.path.get_base_dir())
|
||||
var item = _get_script_tree_item(script, dir_item)
|
||||
|
||||
if(script.inner_class_name != ''):
|
||||
var inner_item = tree.create_item(item)
|
||||
inner_item.set_text(0, script.inner_class_name)
|
||||
_set_meta_for_script_tree_item(inner_item, script)
|
||||
item = inner_item
|
||||
|
||||
for test in script.tests:
|
||||
var test_item = tree.create_item(item)
|
||||
test_item.set_text(0, test.name)
|
||||
_set_meta_for_script_tree_item(test_item, script, test)
|
||||
|
||||
|
||||
func _populate_tree():
|
||||
_add_dir_and_script_tree_items()
|
||||
_tree_root.set_collapsed_recursive(true)
|
||||
_tree_root.set_collapsed(false)
|
||||
_reorder_dir_items()
|
||||
_remove_dir_temp_items()
|
||||
|
||||
|
||||
func _refresh_tree_and_settings():
|
||||
_config.apply_options(_gut_runner.get_gut())
|
||||
_gut_runner.set_gut_config(_config)
|
||||
_populate_tree()
|
||||
|
||||
# ---------------------------
|
||||
# Events
|
||||
# ---------------------------
|
||||
func _on_gut_run_started():
|
||||
_ctrls.run_tests_button.disabled = true
|
||||
_ctrls.run_selected.visible = false
|
||||
_ctrls.tabs.visible = false
|
||||
_ctrls.bg.visible = false
|
||||
_ctrls.run_tests_button.text = 'Running'
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _on_gut_run_ended():
|
||||
_ctrls.run_tests_button.disabled = false
|
||||
_ctrls.run_selected.visible = true
|
||||
_ctrls.tabs.visible = true
|
||||
_ctrls.bg.visible = true
|
||||
_ctrls.run_tests_button.text = 'Run All'
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _on_run_tests_pressed():
|
||||
run_all()
|
||||
|
||||
|
||||
func _on_run_selected_pressed():
|
||||
run_selected()
|
||||
|
||||
|
||||
func _on_tests_item_activated():
|
||||
run_selected()
|
||||
|
||||
# ---------------------------
|
||||
# Public
|
||||
# ---------------------------
|
||||
func get_gut():
|
||||
return _gut_runner.get_gut()
|
||||
|
||||
|
||||
func get_config():
|
||||
return _config
|
||||
|
||||
|
||||
func run_all():
|
||||
_config.options.selected = ''
|
||||
_config.options.inner_class_name = ''
|
||||
_config.options.unit_test_name = ''
|
||||
run_tests()
|
||||
|
||||
|
||||
func run_tests(options = null):
|
||||
if(options == null):
|
||||
_config.options = _config_gui.get_options(_config.options)
|
||||
else:
|
||||
_config.options = options
|
||||
|
||||
# We ar running from within the game, so we should not exit, ever.
|
||||
_config.options.should_exit_on_success = false
|
||||
_config.options.should_exit = false
|
||||
|
||||
_gut_runner.get_gut().get_test_collector().clear()
|
||||
_gut_runner.set_gut_config(_config)
|
||||
_gut_runner.run_tests()
|
||||
|
||||
|
||||
func run_selected():
|
||||
var sel_item = _ctrls.test_tree.get_selected()
|
||||
if(sel_item == null):
|
||||
return
|
||||
|
||||
var options = _config_gui.get_options(_config.options)
|
||||
var meta = sel_item.get_metadata(0)
|
||||
if(meta.type == TREE_SCRIPT):
|
||||
options.selected = meta.script.get_file()
|
||||
options.inner_class_name = meta.inner_class
|
||||
options.unit_test_name = meta.test
|
||||
elif(meta.type == TREE_DIR):
|
||||
options.dirs = [meta.path]
|
||||
options.include_subdirectories = true
|
||||
options.selected = ''
|
||||
options.inner_class_name = ''
|
||||
options.unit_test_name = ''
|
||||
|
||||
run_tests(options)
|
||||
|
||||
|
||||
func load_config_file(path):
|
||||
_config.load_options(path)
|
||||
_config.options.selected = ''
|
||||
_config.options.inner_class_name = ''
|
||||
_config.options.unit_test_name = ''
|
||||
_config_gui.load_file(path)
|
||||
|
||||
|
||||
# ##############################################################################
|
||||
# The MIT License (MIT)
|
||||
# =====================
|
||||
#
|
||||
# Copyright (c) 2025 Tom "Butch" Wesley
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# ##############################################################################
|
||||
1
addons/gut/gui/GutControl.gd.uid
Normal file
1
addons/gut/gui/GutControl.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cqlvpwidawld6
|
||||
63
addons/gut/gui/GutControl.tscn
Normal file
63
addons/gut/gui/GutControl.tscn
Normal file
@@ -0,0 +1,63 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://4jb53yqktyfg"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cqlvpwidawld6" path="res://addons/gut/gui/GutControl.gd" id="1_eprql"]
|
||||
|
||||
[node name="GutControl" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
offset_right = 295.0
|
||||
offset_bottom = 419.0
|
||||
script = ExtResource("1_eprql")
|
||||
|
||||
[node name="Bg" type="ColorRect" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.36, 0.36, 0.36, 1)
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Tabs" type="TabContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Tests" type="Tree" parent="VBox/Tabs"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
hide_root = true
|
||||
|
||||
[node name="SettingsScroll" type="ScrollContainer" parent="VBox/Tabs"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Settings" type="VBoxContainer" parent="VBox/Tabs/SettingsScroll"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Buttons" type="HBoxContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RunTests" type="Button" parent="VBox/Buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Run All"
|
||||
|
||||
[node name="RunSelected" type="Button" parent="VBox/Buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Run Selected"
|
||||
|
||||
[connection signal="item_activated" from="VBox/Tabs/Tests" to="." method="_on_tests_item_activated"]
|
||||
[connection signal="pressed" from="VBox/Buttons/RunTests" to="." method="_on_run_tests_pressed"]
|
||||
[connection signal="pressed" from="VBox/Buttons/RunSelected" to="." method="_on_run_selected_pressed"]
|
||||
88
addons/gut/gui/GutEditorWindow.gd
Normal file
88
addons/gut/gui/GutEditorWindow.gd
Normal file
@@ -0,0 +1,88 @@
|
||||
@tool
|
||||
extends Window
|
||||
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
|
||||
@onready var _chk_always_on_top = $Layout/WinControls/OnTop
|
||||
|
||||
var _bottom_panel = null
|
||||
var _ready_to_go = false
|
||||
var _gut_shortcuts = []
|
||||
|
||||
var gut_plugin = null
|
||||
var interface = null
|
||||
|
||||
|
||||
func _unhandled_key_input(event: InputEvent) -> void:
|
||||
if(event is InputEventKey):
|
||||
if(_gut_shortcuts.has(event.as_text_keycode())):
|
||||
get_tree().root.push_input(event)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var pref_size = GutEditorGlobals.user_prefs.gut_window_size.value
|
||||
if(pref_size.x < 0):
|
||||
size = Vector2(800, 800)
|
||||
else:
|
||||
size = pref_size
|
||||
always_on_top = GutEditorGlobals.user_prefs.gut_window_on_top.value
|
||||
_chk_always_on_top.button_pressed = always_on_top
|
||||
|
||||
|
||||
# --------
|
||||
# Events
|
||||
# --------
|
||||
func _on_on_top_toggled(toggled_on: bool) -> void:
|
||||
always_on_top = toggled_on
|
||||
GutEditorGlobals.user_prefs.gut_window_on_top.value = toggled_on
|
||||
|
||||
|
||||
func _on_size_changed() -> void:
|
||||
if(_ready_to_go):
|
||||
GutEditorGlobals.user_prefs.gut_window_size.value = size
|
||||
|
||||
|
||||
func _on_close_requested() -> void:
|
||||
gut_plugin.toggle_windowed()
|
||||
|
||||
|
||||
|
||||
func _on_vert_layout_pressed() -> void:
|
||||
_bottom_panel.results_vert_layout()
|
||||
|
||||
|
||||
func _on_horiz_layout_pressed() -> void:
|
||||
_bottom_panel.results_horiz_layout()
|
||||
|
||||
|
||||
# --------
|
||||
# Public
|
||||
# --------
|
||||
func add_gut_panel(panel : Control):
|
||||
$Layout.add_child(panel)
|
||||
panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
panel.visible = true
|
||||
_bottom_panel = panel
|
||||
_ready_to_go = true
|
||||
|
||||
panel.owner = self
|
||||
|
||||
# This stunk to figure out.
|
||||
theme = interface.get_editor_theme()
|
||||
var settings = interface.get_editor_settings()
|
||||
$ColorRect.color = settings.get_setting("interface/theme/base_color")
|
||||
|
||||
set_gut_shortcuts(_bottom_panel._ctrls.shortcut_dialog)
|
||||
|
||||
|
||||
func remove_panel():
|
||||
$Layout.remove_child(_bottom_panel)
|
||||
_bottom_panel.owner = null
|
||||
|
||||
|
||||
func set_gut_shortcuts(shortcuts_dialog):
|
||||
_gut_shortcuts.clear()
|
||||
for btn in shortcuts_dialog.all_buttons:
|
||||
_gut_shortcuts.append(btn.get_input_event().as_text_keycode())
|
||||
1
addons/gut/gui/GutEditorWindow.gd.uid
Normal file
1
addons/gut/gui/GutEditorWindow.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://crp2af6k4nmf5
|
||||
127
addons/gut/gui/GutEditorWindow.tscn
Normal file
127
addons/gut/gui/GutEditorWindow.tscn
Normal file
@@ -0,0 +1,127 @@
|
||||
[gd_scene load_steps=10 format=3 uid="uid://dnnvwlplf1km7"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://crp2af6k4nmf5" path="res://addons/gut/gui/GutEditorWindow.gd" id="1_qevl3"]
|
||||
[ext_resource type="Texture2D" uid="uid://ljc2viafngwd" path="res://addons/gut/images/HSplitContainer.svg" id="2_xw0o2"]
|
||||
[ext_resource type="Texture2D" uid="uid://bhew20crsywxr" path="res://addons/gut/images/VSplitContainer.svg" id="3_fqfwy"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qevl3"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 8.0
|
||||
content_margin_right = 8.0
|
||||
content_margin_bottom = 8.0
|
||||
bg_color = Color(0.115499996, 0.132, 0.15949999, 1)
|
||||
corner_detail = 1
|
||||
anti_aliasing = false
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_af010"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 12.0
|
||||
content_margin_right = 8.0
|
||||
content_margin_bottom = 8.0
|
||||
bg_color = Color(0.21, 0.24, 0.29, 1)
|
||||
border_color = Color(0.08399999, 0.095999986, 0.116, 1)
|
||||
corner_radius_top_left = 6
|
||||
corner_radius_top_right = 6
|
||||
corner_radius_bottom_right = 6
|
||||
corner_radius_bottom_left = 6
|
||||
corner_detail = 5
|
||||
anti_aliasing = false
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xw0o2"]
|
||||
content_margin_left = 0.0
|
||||
content_margin_top = 8.0
|
||||
content_margin_right = 0.0
|
||||
content_margin_bottom = 0.0
|
||||
bg_color = Color(0.21, 0.24, 0.29, 1)
|
||||
border_color = Color(0.08399999, 0.095999986, 0.116, 1)
|
||||
corner_radius_bottom_right = 6
|
||||
corner_radius_bottom_left = 6
|
||||
corner_detail = 5
|
||||
anti_aliasing = false
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fqfwy"]
|
||||
content_margin_left = 12.0
|
||||
content_margin_top = 8.0
|
||||
content_margin_right = 12.0
|
||||
content_margin_bottom = 8.0
|
||||
bg_color = Color(0.14699998, 0.16799998, 0.203, 1)
|
||||
corner_radius_top_left = 6
|
||||
corner_radius_top_right = 6
|
||||
corner_radius_bottom_right = 6
|
||||
corner_radius_bottom_left = 6
|
||||
corner_detail = 5
|
||||
anti_aliasing = false
|
||||
|
||||
[sub_resource type="Theme" id="Theme_fqfwy"]
|
||||
Editor/colors/accent_color = Color(0.44, 0.73, 0.98, 1)
|
||||
Editor/colors/background = Color(0.115499996, 0.132, 0.15949999, 1)
|
||||
Editor/colors/base_color = Color(0.21, 0.24, 0.29, 1)
|
||||
EditorStyles/styles/Background = SubResource("StyleBoxFlat_qevl3")
|
||||
EditorStyles/styles/BottomPanel = SubResource("StyleBoxFlat_af010")
|
||||
EditorStyles/styles/Content = SubResource("StyleBoxFlat_xw0o2")
|
||||
Panel/styles/panel = SubResource("StyleBoxFlat_fqfwy")
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_qevl3"]
|
||||
|
||||
[node name="GutEditorWindow" type="Window"]
|
||||
oversampling_override = 1.0
|
||||
title = "GUT"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(1408, 1728)
|
||||
min_size = Vector2i(800, 600)
|
||||
script = ExtResource("1_qevl3")
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = SubResource("Theme_fqfwy")
|
||||
color = Color(0.18717614, 0.18717614, 0.18717614, 1)
|
||||
|
||||
[node name="Layout" type="VBoxContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="WinControls" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MenuBar" type="MenuBar" parent="Layout/WinControls"]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
flat = true
|
||||
prefer_global_menu = false
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="Layout/WinControls"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="OnTop" type="CheckButton" parent="Layout/WinControls"]
|
||||
layout_mode = 2
|
||||
text = "Always on Top"
|
||||
|
||||
[node name="HorizLayout" type="Button" parent="Layout/WinControls"]
|
||||
texture_filter = 1
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
button_group = SubResource("ButtonGroup_qevl3")
|
||||
icon = ExtResource("2_xw0o2")
|
||||
icon_alignment = 1
|
||||
|
||||
[node name="VertLayout" type="Button" parent="Layout/WinControls"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_qevl3")
|
||||
icon = ExtResource("3_fqfwy")
|
||||
|
||||
[connection signal="close_requested" from="." to="." method="_on_close_requested"]
|
||||
[connection signal="size_changed" from="." to="." method="_on_size_changed"]
|
||||
[connection signal="toggled" from="Layout/WinControls/OnTop" to="." method="_on_on_top_toggled"]
|
||||
[connection signal="pressed" from="Layout/WinControls/HorizLayout" to="." method="_on_horiz_layout_pressed"]
|
||||
[connection signal="pressed" from="Layout/WinControls/VertLayout" to="." method="_on_vert_layout_pressed"]
|
||||
36
addons/gut/gui/GutLogo.tscn
Normal file
36
addons/gut/gui/GutLogo.tscn
Normal file
@@ -0,0 +1,36 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://bjkn8mhx2fmt1"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b8lvgepb64m8t" path="res://addons/gut/gui/gut_logo.gd" id="1_ba6lh"]
|
||||
[ext_resource type="Texture2D" uid="uid://dyxbmyvpkkcvs" path="res://addons/gut/images/GutIconV2_base.png" id="2_ba6lh"]
|
||||
[ext_resource type="Texture2D" uid="uid://dx0yxxn5q7doc" path="res://addons/gut/images/eyey.png" id="3_rc8fb"]
|
||||
|
||||
[node name="Logo" type="Node2D"]
|
||||
script = ExtResource("1_ba6lh")
|
||||
|
||||
[node name="BaseLogo" type="Sprite2D" parent="."]
|
||||
scale = Vector2(0.5, 0.5)
|
||||
texture = ExtResource("2_ba6lh")
|
||||
|
||||
[node name="LeftEye" type="Sprite2D" parent="BaseLogo"]
|
||||
visible = false
|
||||
position = Vector2(-238, 16)
|
||||
texture = ExtResource("3_rc8fb")
|
||||
|
||||
[node name="RightEye" type="Sprite2D" parent="BaseLogo"]
|
||||
visible = false
|
||||
position = Vector2(239, 16)
|
||||
texture = ExtResource("3_rc8fb")
|
||||
|
||||
[node name="ResetTimer" type="Timer" parent="."]
|
||||
wait_time = 5.0
|
||||
one_shot = true
|
||||
|
||||
[node name="FaceButton" type="Button" parent="."]
|
||||
modulate = Color(1, 1, 1, 0)
|
||||
offset_left = -141.0
|
||||
offset_top = -113.0
|
||||
offset_right = 140.0
|
||||
offset_bottom = 175.0
|
||||
|
||||
[connection signal="timeout" from="ResetTimer" to="." method="_on_reset_timer_timeout"]
|
||||
[connection signal="pressed" from="FaceButton" to="." method="_on_face_button_pressed"]
|
||||
243
addons/gut/gui/GutRunner.gd
Normal file
243
addons/gut/gui/GutRunner.gd
Normal file
@@ -0,0 +1,243 @@
|
||||
# ##############################################################################
|
||||
# This class joins together GUT, GUT Gui, GutConfig and is THE way to kick off a
|
||||
# run of a test suite.
|
||||
#
|
||||
# This creates its own instance of gut.gd that it manages. You can set the
|
||||
# gut.gd instance if you need to for testing.
|
||||
#
|
||||
# Set gut_config to an instance of a configured gut_config.gd instance prior to
|
||||
# running tests.
|
||||
#
|
||||
# This will create a GUI and wire it up and apply gut_config.gd options.
|
||||
#
|
||||
# Running tests: Call run_tests
|
||||
# ##############################################################################
|
||||
extends Node2D
|
||||
|
||||
const EXIT_OK = 0
|
||||
const EXIT_ERROR = 1
|
||||
|
||||
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')
|
||||
|
||||
var runner_json_path = null
|
||||
var result_bbcode_path = null
|
||||
var result_json_path = null
|
||||
|
||||
var lgr = GutUtils.get_logger()
|
||||
var gut_config = null
|
||||
|
||||
var error_tracker = GutUtils.get_error_tracker()
|
||||
|
||||
var _hid_gut = null;
|
||||
# Lazy loaded gut instance. Settable for testing purposes.
|
||||
var gut = _hid_gut :
|
||||
get:
|
||||
if(_hid_gut == null):
|
||||
_hid_gut = Gut.new(lgr)
|
||||
_hid_gut.error_tracker = error_tracker
|
||||
return _hid_gut
|
||||
set(val):
|
||||
_hid_gut = val
|
||||
|
||||
var _wrote_results = false
|
||||
var _ran_from_editor = false
|
||||
|
||||
@onready var _gut_layer = $GutLayer
|
||||
@onready var _gui = $GutLayer/GutScene
|
||||
|
||||
|
||||
func _ready():
|
||||
GutUtils.WarningsManager.apply_warnings_dictionary(
|
||||
GutUtils.warnings_at_start)
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
if(!_wrote_results and _ran_from_editor):
|
||||
_write_results_for_gut_panel()
|
||||
|
||||
|
||||
func _setup_gui(show_gui):
|
||||
if(show_gui):
|
||||
_gui.gut = gut
|
||||
var printer = gut.logger.get_printer('gui')
|
||||
printer.set_textbox(_gui.get_textbox())
|
||||
else:
|
||||
gut.logger.disable_printer('gui', true)
|
||||
_gui.visible = false
|
||||
|
||||
var opts = gut_config.options
|
||||
_gui.set_font_size(opts.font_size)
|
||||
_gui.set_font(opts.font_name)
|
||||
if(opts.font_color != null and opts.font_color.is_valid_html_color()):
|
||||
_gui.set_default_font_color(Color(opts.font_color))
|
||||
if(opts.background_color != null and opts.background_color.is_valid_html_color()):
|
||||
_gui.set_background_color(Color(opts.background_color))
|
||||
|
||||
_gui.set_opacity(min(1.0, float(opts.opacity) / 100))
|
||||
_gui.use_compact_mode(opts.compact_mode)
|
||||
|
||||
|
||||
func _write_results_for_gut_panel():
|
||||
var content = _gui.get_textbox().get_parsed_text() #_gut.logger.get_gui_bbcode()
|
||||
var f = FileAccess.open(result_bbcode_path, FileAccess.WRITE)
|
||||
if(f != null):
|
||||
f.store_string(content)
|
||||
f = null # closes file
|
||||
else:
|
||||
push_error('Could not save bbcode, result = ', FileAccess.get_open_error())
|
||||
|
||||
var exporter = ResultExporter.new()
|
||||
# TODO this should be checked and _wrote_results should maybe not be set, or
|
||||
# maybe we do not care. Whichever, it should be clear.
|
||||
var _f_result = exporter.write_json_file(gut, result_json_path)
|
||||
_wrote_results = true
|
||||
|
||||
|
||||
func _handle_quit(should_exit, should_exit_on_success, override_exit_code=EXIT_OK):
|
||||
var quitting_time = should_exit or \
|
||||
(should_exit_on_success and gut.get_fail_count() == 0)
|
||||
|
||||
if(!quitting_time):
|
||||
if(should_exit_on_success):
|
||||
lgr.log("There are failing tests, exit manually.")
|
||||
_gui.use_compact_mode(false)
|
||||
return
|
||||
|
||||
# For some reason, tests fail asserting that quit was called with 0 if we
|
||||
# do not do this, but everything is defaulted so I don't know why it gets
|
||||
# null.
|
||||
var exit_code = GutUtils.nvl(override_exit_code, EXIT_OK)
|
||||
|
||||
if(gut.get_fail_count() > 0):
|
||||
exit_code = EXIT_ERROR
|
||||
|
||||
# Overwrite the exit code with the post_script's exit code if it is set
|
||||
var post_hook_inst = gut.get_post_run_script_instance()
|
||||
if(post_hook_inst != null and post_hook_inst.get_exit_code() != null):
|
||||
exit_code = post_hook_inst.get_exit_code()
|
||||
|
||||
quit(exit_code)
|
||||
|
||||
|
||||
func _end_run(override_exit_code=EXIT_OK):
|
||||
if(_ran_from_editor):
|
||||
_write_results_for_gut_panel()
|
||||
|
||||
GutErrorTracker.deregister_logger(error_tracker)
|
||||
|
||||
_handle_quit(gut_config.options.should_exit,
|
||||
gut_config.options.should_exit_on_success,
|
||||
override_exit_code)
|
||||
|
||||
|
||||
# -------------
|
||||
# Events
|
||||
# -------------
|
||||
func _on_tests_finished():
|
||||
_end_run()
|
||||
|
||||
|
||||
# -------------
|
||||
# Public
|
||||
# -------------
|
||||
# For internal use only, but still public. Consider it "protected" and you
|
||||
# don't have my permission to call this, unless "you" is "me".
|
||||
func run_from_editor():
|
||||
_ran_from_editor = true
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
runner_json_path = GutUtils.nvl(runner_json_path, GutEditorGlobals.editor_run_gut_config_path)
|
||||
result_bbcode_path = GutUtils.nvl(result_bbcode_path, GutEditorGlobals.editor_run_bbcode_results_path)
|
||||
result_json_path = GutUtils.nvl(result_json_path, GutEditorGlobals.editor_run_json_results_path)
|
||||
|
||||
if(gut_config == null):
|
||||
gut_config = GutConfig.new()
|
||||
gut_config.load_options(runner_json_path)
|
||||
|
||||
call_deferred('run_tests')
|
||||
|
||||
|
||||
func run_tests(show_gui=true):
|
||||
_setup_gui(show_gui)
|
||||
|
||||
if(gut_config.options.dirs.size() + gut_config.options.tests.size() == 0):
|
||||
var err_text = "You do not have any directories configured, so GUT " + \
|
||||
"doesn't know where to find the tests. Tell GUT where to find the " + \
|
||||
"tests and GUT shall run the tests."
|
||||
lgr.error(err_text)
|
||||
push_error(err_text)
|
||||
_end_run(EXIT_ERROR)
|
||||
return
|
||||
|
||||
var install_check_text = GutUtils.make_install_check_text()
|
||||
if(install_check_text != GutUtils.INSTALL_OK_TEXT):
|
||||
print("\n\n", GutUtils.version_numbers.get_version_text())
|
||||
lgr.error(install_check_text)
|
||||
push_error(install_check_text)
|
||||
_end_run(EXIT_ERROR)
|
||||
return
|
||||
|
||||
gut.add_children_to = self
|
||||
if(gut.get_parent() == null):
|
||||
if(gut_config.options.gut_on_top):
|
||||
_gut_layer.add_child(gut)
|
||||
else:
|
||||
add_child(gut)
|
||||
|
||||
if(!gut.end_run.is_connected(_on_tests_finished)):
|
||||
gut.end_run.connect(_on_tests_finished)
|
||||
|
||||
gut_config.apply_options(gut)
|
||||
var run_rest_of_scripts = gut_config.options.unit_test_name == ''
|
||||
GutErrorTracker.register_logger(error_tracker)
|
||||
gut.test_scripts(run_rest_of_scripts)
|
||||
|
||||
|
||||
func set_gut_config(which):
|
||||
gut_config = which
|
||||
|
||||
|
||||
# for backwards compatibility
|
||||
func get_gut():
|
||||
return gut
|
||||
|
||||
|
||||
func quit(exit_code):
|
||||
# Sometimes quitting takes a few seconds. This gives some indicator
|
||||
# of what is going on.
|
||||
_gui.set_title("Exiting")
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
lgr.info(str('Exiting with code ', exit_code))
|
||||
get_tree().quit(exit_code)
|
||||
|
||||
|
||||
|
||||
|
||||
# ##############################################################################
|
||||
# The MIT License (MIT)
|
||||
# =====================
|
||||
#
|
||||
# Copyright (c) 2025 Tom "Butch" Wesley
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# ##############################################################################
|
||||
1
addons/gut/gui/GutRunner.gd.uid
Normal file
1
addons/gut/gui/GutRunner.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://eg8k46gd42a4
|
||||
12
addons/gut/gui/GutRunner.tscn
Normal file
12
addons/gut/gui/GutRunner.tscn
Normal file
@@ -0,0 +1,12 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://bqy3ikt6vu4b5"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://eg8k46gd42a4" path="res://addons/gut/gui/GutRunner.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://m28heqtswbuq" path="res://addons/gut/GutScene.tscn" id="2_6ruxb"]
|
||||
|
||||
[node name="GutRunner" type="Node2D"]
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="GutLayer" type="CanvasLayer" parent="."]
|
||||
layer = 128
|
||||
|
||||
[node name="GutScene" parent="GutLayer" instance=ExtResource("2_6ruxb")]
|
||||
7
addons/gut/gui/GutSceneTheme.tres
Normal file
7
addons/gut/gui/GutSceneTheme.tres
Normal file
@@ -0,0 +1,7 @@
|
||||
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://cstkhwkpajvqu"]
|
||||
|
||||
[ext_resource type="FontFile" uid="uid://c6c7gnx36opr0" path="res://addons/gut/fonts/AnonymousPro-Regular.ttf" id="1_df57p"]
|
||||
|
||||
[resource]
|
||||
default_font = ExtResource("1_df57p")
|
||||
Label/fonts/font = ExtResource("1_df57p")
|
||||
161
addons/gut/gui/MinGui.tscn
Normal file
161
addons/gut/gui/MinGui.tscn
Normal file
@@ -0,0 +1,161 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://cnqqdfsn80ise"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://cstkhwkpajvqu" path="res://addons/gut/gui/GutSceneTheme.tres" id="1_farmq"]
|
||||
[ext_resource type="FontFile" uid="uid://bnh0lslf4yh87" path="res://addons/gut/fonts/CourierPrime-Regular.ttf" id="2_a2e2l"]
|
||||
[ext_resource type="Script" uid="uid://blvhsbnsvfyow" path="res://addons/gut/gui/gut_gui.gd" id="2_eokrf"]
|
||||
[ext_resource type="PackedScene" uid="uid://bvrqqgjpyouse" path="res://addons/gut/gui/ResizeHandle.tscn" id="4_xrhva"]
|
||||
|
||||
[node name="Min" type="Panel"]
|
||||
clip_contents = true
|
||||
custom_minimum_size = Vector2(280, 145)
|
||||
offset_right = 280.0
|
||||
offset_bottom = 145.0
|
||||
theme = ExtResource("1_farmq")
|
||||
script = ExtResource("2_eokrf")
|
||||
|
||||
[node name="MainBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="TitleBar" type="Panel" parent="MainBox"]
|
||||
custom_minimum_size = Vector2(0, 25)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TitleBox" type="HBoxContainer" parent="MainBox/TitleBar"]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = 2.0
|
||||
offset_bottom = 3.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="Spacer1" type="CenterContainer" parent="MainBox/TitleBar/TitleBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Title" type="Label" parent="MainBox/TitleBar/TitleBox"]
|
||||
layout_mode = 2
|
||||
text = "Title"
|
||||
|
||||
[node name="Spacer2" type="CenterContainer" parent="MainBox/TitleBar/TitleBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="TimeLabel" type="Label" parent="MainBox/TitleBar/TitleBox"]
|
||||
layout_mode = 2
|
||||
text = "0.000s"
|
||||
|
||||
[node name="Body" type="HBoxContainer" parent="MainBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LeftMargin" type="CenterContainer" parent="MainBox/Body"]
|
||||
custom_minimum_size = Vector2(5, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="BodyRows" type="VBoxContainer" parent="MainBox/Body"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ProgressBars" type="HBoxContainer" parent="MainBox/Body/BodyRows"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox/Body/BodyRows/ProgressBars"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="MainBox/Body/BodyRows/ProgressBars/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "T:"
|
||||
|
||||
[node name="ProgressTest" type="ProgressBar" parent="MainBox/Body/BodyRows/ProgressBars/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
value = 25.0
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="MainBox/Body/BodyRows/ProgressBars"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="MainBox/Body/BodyRows/ProgressBars/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = "S:"
|
||||
|
||||
[node name="ProgressScript" type="ProgressBar" parent="MainBox/Body/BodyRows/ProgressBars/HBoxContainer2"]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
value = 75.0
|
||||
|
||||
[node name="PathDisplay" type="VBoxContainer" parent="MainBox/Body/BodyRows"]
|
||||
clip_contents = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Path" type="Label" parent="MainBox/Body/BodyRows/PathDisplay"]
|
||||
layout_mode = 2
|
||||
theme_override_fonts/font = ExtResource("2_a2e2l")
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "res://test/integration/whatever"
|
||||
clip_text = true
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox/Body/BodyRows/PathDisplay"]
|
||||
clip_contents = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="S3" type="CenterContainer" parent="MainBox/Body/BodyRows/PathDisplay/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(5, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="File" type="Label" parent="MainBox/Body/BodyRows/PathDisplay/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_fonts/font = ExtResource("2_a2e2l")
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "test_this_thing.gd"
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[node name="Footer" type="HBoxContainer" parent="MainBox/Body/BodyRows"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HandleLeft" parent="MainBox/Body/BodyRows/Footer" node_paths=PackedStringArray("resize_control") instance=ExtResource("4_xrhva")]
|
||||
layout_mode = 2
|
||||
orientation = 0
|
||||
resize_control = NodePath("../../../../..")
|
||||
vertical_resize = false
|
||||
|
||||
[node name="SwitchModes" type="Button" parent="MainBox/Body/BodyRows/Footer"]
|
||||
layout_mode = 2
|
||||
text = "Expand"
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="MainBox/Body/BodyRows/Footer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Continue" type="Button" parent="MainBox/Body/BodyRows/Footer"]
|
||||
layout_mode = 2
|
||||
text = "Continue
|
||||
"
|
||||
|
||||
[node name="HandleRight" parent="MainBox/Body/BodyRows/Footer" node_paths=PackedStringArray("resize_control") instance=ExtResource("4_xrhva")]
|
||||
layout_mode = 2
|
||||
resize_control = NodePath("../../../../..")
|
||||
vertical_resize = false
|
||||
|
||||
[node name="RightMargin" type="CenterContainer" parent="MainBox/Body"]
|
||||
custom_minimum_size = Vector2(5, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="MainBox"]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 2)
|
||||
layout_mode = 2
|
||||
215
addons/gut/gui/NormalGui.tscn
Normal file
215
addons/gut/gui/NormalGui.tscn
Normal file
@@ -0,0 +1,215 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://duxblir3vu8x7"]
|
||||
|
||||
[ext_resource type="Theme" uid="uid://cstkhwkpajvqu" path="res://addons/gut/gui/GutSceneTheme.tres" id="1_5hlsm"]
|
||||
[ext_resource type="Script" uid="uid://blvhsbnsvfyow" path="res://addons/gut/gui/gut_gui.gd" id="2_fue6q"]
|
||||
[ext_resource type="FontFile" uid="uid://bnh0lslf4yh87" path="res://addons/gut/fonts/CourierPrime-Regular.ttf" id="2_u5uc1"]
|
||||
[ext_resource type="PackedScene" uid="uid://bvrqqgjpyouse" path="res://addons/gut/gui/ResizeHandle.tscn" id="4_2r8a8"]
|
||||
|
||||
[node name="Large" type="Panel"]
|
||||
custom_minimum_size = Vector2(500, 150)
|
||||
offset_right = 632.0
|
||||
offset_bottom = 260.0
|
||||
theme = ExtResource("1_5hlsm")
|
||||
script = ExtResource("2_fue6q")
|
||||
|
||||
[node name="MainBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="TitleBar" type="Panel" parent="MainBox"]
|
||||
custom_minimum_size = Vector2(0, 25)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TitleBox" type="HBoxContainer" parent="MainBox/TitleBar"]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = 2.0
|
||||
offset_bottom = 3.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="Spacer1" type="CenterContainer" parent="MainBox/TitleBar/TitleBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Title" type="Label" parent="MainBox/TitleBar/TitleBox"]
|
||||
layout_mode = 2
|
||||
text = "Title"
|
||||
|
||||
[node name="Spacer2" type="CenterContainer" parent="MainBox/TitleBar/TitleBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="TimeLabel" type="Label" parent="MainBox/TitleBar/TitleBox"]
|
||||
custom_minimum_size = Vector2(90, 0)
|
||||
layout_mode = 2
|
||||
text = "999.999s"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MainBox/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="OutputBG" type="ColorRect" parent="MainBox/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
color = Color(0.0745098, 0.0705882, 0.0784314, 1)
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/OutputBG"]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="S2" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(5, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TestOutput" type="RichTextLabel" parent="MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 2
|
||||
bbcode_enabled = true
|
||||
scroll_following = true
|
||||
autowrap_mode = 0
|
||||
selection_enabled = true
|
||||
|
||||
[node name="S1" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/OutputBG/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(5, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ControlBox" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="S1" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||
custom_minimum_size = Vector2(5, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ProgressBars" type="VBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TestBox" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/TestBox"]
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Tests"
|
||||
|
||||
[node name="ProgressTest" type="ProgressBar" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/TestBox"]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
value = 25.0
|
||||
|
||||
[node name="ScriptBox" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/ScriptBox"]
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Scripts"
|
||||
|
||||
[node name="ProgressScript" type="ProgressBar" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/ProgressBars/ScriptBox"]
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
value = 75.0
|
||||
|
||||
[node name="PathDisplay" type="VBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Path" type="Label" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 6
|
||||
theme_override_fonts/font = ExtResource("2_u5uc1")
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "res://test/integration/whatever"
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="S3" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(5, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="File" type="Label" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/PathDisplay/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_fonts/font = ExtResource("2_u5uc1")
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "test_this_thing.gd"
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[node name="Buttons" type="VBoxContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Continue" type="Button" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/Buttons"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
text = "Continue
|
||||
"
|
||||
|
||||
[node name="WordWrap" type="CheckButton" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox/Buttons"]
|
||||
layout_mode = 2
|
||||
text = "Word Wrap"
|
||||
|
||||
[node name="S3" type="CenterContainer" parent="MainBox/HBoxContainer/VBoxContainer/ControlBox"]
|
||||
custom_minimum_size = Vector2(5, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="BottomPad" type="CenterContainer" parent="MainBox"]
|
||||
custom_minimum_size = Vector2(0, 5)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Footer" type="HBoxContainer" parent="MainBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SidePad1" type="CenterContainer" parent="MainBox/Footer"]
|
||||
custom_minimum_size = Vector2(2, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ResizeHandle3" parent="MainBox/Footer" node_paths=PackedStringArray("resize_control") instance=ExtResource("4_2r8a8")]
|
||||
custom_minimum_size = Vector2(25, 25)
|
||||
layout_mode = 2
|
||||
orientation = 0
|
||||
resize_control = NodePath("../../..")
|
||||
|
||||
[node name="SwitchModes" type="Button" parent="MainBox/Footer"]
|
||||
layout_mode = 2
|
||||
text = "Compact
|
||||
"
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="MainBox/Footer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ResizeHandle2" parent="MainBox/Footer" node_paths=PackedStringArray("resize_control") instance=ExtResource("4_2r8a8")]
|
||||
custom_minimum_size = Vector2(25, 25)
|
||||
layout_mode = 2
|
||||
resize_control = NodePath("../../..")
|
||||
|
||||
[node name="SidePad2" type="CenterContainer" parent="MainBox/Footer"]
|
||||
custom_minimum_size = Vector2(2, 2.08165e-12)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="BottomPad2" type="CenterContainer" parent="MainBox"]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 2)
|
||||
layout_mode = 2
|
||||
352
addons/gut/gui/OutputText.gd
Normal file
352
addons/gut/gui/OutputText.gd
Normal file
@@ -0,0 +1,352 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
var PanelControls = load('res://addons/gut/gui/panel_controls.gd')
|
||||
|
||||
# ##############################################################################
|
||||
# Keeps search results from the TextEdit
|
||||
# ##############################################################################
|
||||
class TextEditSearcher:
|
||||
var te : TextEdit
|
||||
var _last_term = ''
|
||||
var _last_pos = Vector2(-1, -1)
|
||||
var _ignore_caret_change = false
|
||||
|
||||
func set_text_edit(which):
|
||||
te = which
|
||||
te.caret_changed.connect(_on_caret_changed)
|
||||
|
||||
|
||||
func _on_caret_changed():
|
||||
if(_ignore_caret_change):
|
||||
_ignore_caret_change = false
|
||||
else:
|
||||
_last_pos = _get_caret();
|
||||
|
||||
|
||||
func _get_caret():
|
||||
return Vector2(te.get_caret_column(), te.get_caret_line())
|
||||
|
||||
|
||||
func _set_caret_and_sel(pos, len):
|
||||
te.set_caret_line(pos.y)
|
||||
te.set_caret_column(pos.x)
|
||||
if(len > 0):
|
||||
te.select(pos.y, pos.x, pos.y, pos.x + len)
|
||||
|
||||
|
||||
func _find(term, search_flags):
|
||||
var pos = _get_caret()
|
||||
if(term == _last_term):
|
||||
if(search_flags == 0):
|
||||
pos = _last_pos
|
||||
pos.x += 1
|
||||
else:
|
||||
pos = _last_pos
|
||||
pos.x -= 1
|
||||
|
||||
var result = te.search(term, search_flags, pos.y, pos.x)
|
||||
# print('searching from ', pos, ' for "', term, '" = ', result)
|
||||
if(result.y != -1):
|
||||
_ignore_caret_change = true
|
||||
_set_caret_and_sel(result, term.length())
|
||||
_last_pos = result
|
||||
|
||||
_last_term = term
|
||||
|
||||
func find_next(term):
|
||||
_find(term, 0)
|
||||
|
||||
func find_prev(term):
|
||||
_find(term, te.SEARCH_BACKWARDS)
|
||||
|
||||
|
||||
# ##############################################################################
|
||||
# Start OutputText control code
|
||||
# ##############################################################################
|
||||
@onready var _ctrls = {
|
||||
output = $Output,
|
||||
settings_bar = $Settings,
|
||||
use_colors = $Settings/UseColors,
|
||||
word_wrap = $Settings/WordWrap,
|
||||
|
||||
copy_button = $Toolbar/CopyButton,
|
||||
clear_button = $Toolbar/ClearButton,
|
||||
show_search = $Toolbar/ShowSearch,
|
||||
caret_position = $Toolbar/LblPosition,
|
||||
|
||||
search_bar = {
|
||||
bar = $Search,
|
||||
search_term = $Search/SearchTerm,
|
||||
}
|
||||
}
|
||||
|
||||
var _sr = TextEditSearcher.new()
|
||||
var _highlighter : CodeHighlighter
|
||||
var _font_name = null
|
||||
var _user_prefs = GutEditorGlobals.user_prefs
|
||||
var _font_name_pctrl = null
|
||||
var _font_size_pctrl = null
|
||||
|
||||
var keywords = [
|
||||
['Failed', Color.RED],
|
||||
['Passed', Color.GREEN],
|
||||
['Pending', Color.YELLOW],
|
||||
['Risky', Color.YELLOW],
|
||||
['Orphans', Color.YELLOW],
|
||||
['WARNING', Color.YELLOW],
|
||||
['ERROR', Color.RED],
|
||||
['ExpectedError', Color.LIGHT_BLUE],
|
||||
]
|
||||
|
||||
|
||||
# Automatically used when running the OutputText scene from the editor. Changes
|
||||
# to this method only affect test-running the control through the editor.
|
||||
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(30)
|
||||
|
||||
_ctrls.output.queue_redraw()
|
||||
load_file('user://.gut_editor.bbcode')
|
||||
await get_tree().process_frame
|
||||
|
||||
show_search(true)
|
||||
_ctrls.output.set_caret_line(0)
|
||||
_ctrls.output.scroll_vertical = 0
|
||||
_ctrls.output.caret_changed.connect(_on_caret_changed)
|
||||
|
||||
|
||||
func _ready():
|
||||
if(get_parent() is SubViewport):
|
||||
return
|
||||
|
||||
_sr.set_text_edit(_ctrls.output)
|
||||
_ctrls.use_colors.icon = get_theme_icon('RichTextEffect', 'EditorIcons')
|
||||
_ctrls.show_search.icon = get_theme_icon('Search', 'EditorIcons')
|
||||
_ctrls.word_wrap.icon = get_theme_icon('Loop', 'EditorIcons')
|
||||
|
||||
_setup_colors()
|
||||
_ctrls.use_colors.button_pressed = true
|
||||
_use_highlighting(true)
|
||||
|
||||
if(get_parent() == get_tree().root):
|
||||
_test_running_setup()
|
||||
|
||||
_ctrls.settings_bar.visible = false
|
||||
_add_other_ctrls()
|
||||
|
||||
|
||||
func _add_other_ctrls():
|
||||
var fname = GutUtils.gut_fonts.DEFAULT_CUSTOM_FONT_NAME
|
||||
if(_user_prefs != null):
|
||||
fname = _user_prefs.output_font_name.value
|
||||
_font_name_pctrl = PanelControls.SelectControl.new('Font', fname, GutUtils.avail_fonts,
|
||||
"The font, you know, for the text below. Change it, see what it does.")
|
||||
_font_name_pctrl.changed.connect(_on_font_name_changed)
|
||||
_font_name_pctrl.label.size_flags_horizontal = SIZE_SHRINK_BEGIN
|
||||
_ctrls.settings_bar.add_child(_font_name_pctrl)
|
||||
set_all_fonts(fname)
|
||||
|
||||
var fsize = 30
|
||||
if(_user_prefs != null):
|
||||
fsize = _user_prefs.output_font_size.value
|
||||
_font_size_pctrl = PanelControls.NumberControl.new('Font Size', fsize , 5, 100,
|
||||
"The size of 'The Font'.")
|
||||
_font_size_pctrl.changed.connect(_on_font_size_changed)
|
||||
_font_size_pctrl.label.size_flags_horizontal = SIZE_SHRINK_BEGIN
|
||||
_ctrls.settings_bar.add_child(_font_size_pctrl)
|
||||
set_font_size(fsize)
|
||||
|
||||
|
||||
# ------------------
|
||||
# Private
|
||||
# ------------------
|
||||
|
||||
# Call this after changes in colors and the like to get them to apply. reloads
|
||||
# the text of the output control.
|
||||
func _refresh_output():
|
||||
var orig_pos = _ctrls.output.scroll_vertical
|
||||
var text = _ctrls.output.text
|
||||
|
||||
_ctrls.output.text = text
|
||||
_ctrls.output.scroll_vertical = orig_pos
|
||||
|
||||
|
||||
func _create_highlighter(default_color=Color(1, 1, 1, 1)):
|
||||
var to_return = CodeHighlighter.new()
|
||||
|
||||
to_return.function_color = default_color
|
||||
to_return.number_color = default_color
|
||||
to_return.symbol_color = default_color
|
||||
to_return.member_variable_color = default_color
|
||||
|
||||
for keyword in keywords:
|
||||
to_return.add_keyword_color(keyword[0], keyword[1])
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func _setup_colors():
|
||||
_ctrls.output.clear()
|
||||
_highlighter = _create_highlighter()
|
||||
_ctrls.output.queue_redraw()
|
||||
|
||||
|
||||
|
||||
func _use_highlighting(should):
|
||||
if(should):
|
||||
_ctrls.output.syntax_highlighter = _highlighter
|
||||
else:
|
||||
_ctrls.output.syntax_highlighter = null
|
||||
_refresh_output()
|
||||
|
||||
# ------------------
|
||||
# Events
|
||||
# ------------------
|
||||
func _on_caret_changed():
|
||||
var txt = str("line:",_ctrls.output.get_caret_line(), ' col:', _ctrls.output.get_caret_column())
|
||||
_ctrls.caret_position.text = str(txt)
|
||||
|
||||
func _on_font_size_changed():
|
||||
set_font_size(_font_size_pctrl.value)
|
||||
if(_user_prefs != null):
|
||||
_user_prefs.output_font_size.value = _font_size_pctrl.value
|
||||
_user_prefs.output_font_size.save_it()
|
||||
|
||||
func _on_font_name_changed():
|
||||
set_all_fonts(_font_name_pctrl.text)
|
||||
if(_user_prefs != null):
|
||||
_user_prefs.output_font_name.value = _font_name_pctrl.text
|
||||
_user_prefs.output_font_name.save_it()
|
||||
|
||||
func _on_CopyButton_pressed():
|
||||
copy_to_clipboard()
|
||||
|
||||
func _on_UseColors_pressed():
|
||||
_use_highlighting(_ctrls.use_colors.button_pressed)
|
||||
|
||||
func _on_ClearButton_pressed():
|
||||
clear()
|
||||
|
||||
func _on_ShowSearch_pressed():
|
||||
show_search(_ctrls.show_search.button_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.keycode == KEY_ESCAPE):
|
||||
show_search(false)
|
||||
|
||||
func _on_WordWrap_pressed():
|
||||
if(_ctrls.word_wrap.button_pressed):
|
||||
_ctrls.output.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY
|
||||
else:
|
||||
_ctrls.output.wrap_mode = TextEdit.LINE_WRAPPING_NONE
|
||||
|
||||
_ctrls.output.queue_redraw()
|
||||
|
||||
func _on_settings_pressed():
|
||||
_ctrls.settings_bar.visible = $Toolbar/ShowSettings.button_pressed
|
||||
|
||||
# ------------------
|
||||
# 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.button_pressed = should
|
||||
|
||||
|
||||
func search(text, start_pos, highlight=true):
|
||||
return _sr.find_next(text)
|
||||
|
||||
|
||||
func copy_to_clipboard():
|
||||
var selected = _ctrls.output.get_selected_text()
|
||||
if(selected != ''):
|
||||
DisplayServer.clipboard_set(selected)
|
||||
else:
|
||||
DisplayServer.clipboard_set(_ctrls.output.text)
|
||||
|
||||
|
||||
func clear():
|
||||
_ctrls.output.text = ''
|
||||
|
||||
|
||||
func _set_font(custom_name, theme_font_name):
|
||||
var font = GutUtils.gut_fonts.get_font_for_theme_font_name(theme_font_name, custom_name)
|
||||
_ctrls.output.add_theme_font_override(theme_font_name, font)
|
||||
|
||||
|
||||
func set_all_fonts(base_name):
|
||||
_font_name = GutUtils.nvl(base_name, 'Default')
|
||||
|
||||
_set_font(base_name, 'font')
|
||||
_set_font(base_name, 'normal_font')
|
||||
_set_font(base_name, 'bold_font')
|
||||
_set_font(base_name, 'italics_font')
|
||||
_set_font(base_name, 'bold_italics_font')
|
||||
|
||||
|
||||
func set_font_size(new_size):
|
||||
_ctrls.output.set("theme_override_font_sizes/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 = FileAccess.open(path, FileAccess.READ)
|
||||
if(f == null):
|
||||
return
|
||||
|
||||
var t = f.get_as_text()
|
||||
f = null # closes file
|
||||
_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.set_caret_line(line)
|
||||
1
addons/gut/gui/OutputText.gd.uid
Normal file
1
addons/gut/gui/OutputText.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cax5phqs8acmu
|
||||
121
addons/gut/gui/OutputText.tscn
Normal file
121
addons/gut/gui/OutputText.tscn
Normal file
@@ -0,0 +1,121 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://bqmo4dj64c7yl"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cax5phqs8acmu" path="res://addons/gut/gui/OutputText.gd" id="1"]
|
||||
[ext_resource type="Texture2D" uid="uid://bvo0uao7deu0q" path="res://addons/gut/icon.png" id="2_b4xqv"]
|
||||
|
||||
[sub_resource type="DPITexture" id="DPITexture_lygvu"]
|
||||
_source = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\"><path fill=\"#ff5d5d\" d=\"M2 1v8.586l1.293-1.293a1 1 0 0 1 1.414 0L7 10.587l2.293-2.293a1 1 0 0 1 1.414 0L13 10.586l1-1V6H9V1H2zm8 0v4h4zm-6 9.414-2 2V15h12v-2.586l-.293.293a1 1 0 0 1-1.414 0L10 10.414l-2.293 2.293a1 1 0 0 1-1.414 0L4 10.414z\"/></svg>
|
||||
"
|
||||
|
||||
[sub_resource type="CodeHighlighter" id="CodeHighlighter_8ynmy"]
|
||||
number_color = Color(1, 1, 1, 1)
|
||||
symbol_color = Color(1, 1, 1, 1)
|
||||
function_color = Color(1, 1, 1, 1)
|
||||
member_variable_color = Color(1, 1, 1, 1)
|
||||
keyword_colors = {
|
||||
"ERROR": Color(1, 0, 0, 1),
|
||||
"ExpectedError": Color(0.6784314, 0.84705883, 0.9019608, 1),
|
||||
"Failed": Color(1, 0, 0, 1),
|
||||
"Orphans": Color(1, 1, 0, 1),
|
||||
"Passed": Color(0, 1, 0, 1),
|
||||
"Pending": Color(1, 1, 0, 1),
|
||||
"Risky": Color(1, 1, 0, 1),
|
||||
"WARNING": Color(1, 1, 0, 1)
|
||||
}
|
||||
|
||||
[node name="OutputText" type="VBoxContainer"]
|
||||
offset_right = 862.0
|
||||
offset_bottom = 523.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Toolbar" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShowSearch" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Search"
|
||||
toggle_mode = true
|
||||
icon = ExtResource("2_b4xqv")
|
||||
|
||||
[node name="ShowSettings" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Settings"
|
||||
toggle_mode = true
|
||||
text = "..."
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="LblPosition" type="Label" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CopyButton" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
text = " Copy "
|
||||
|
||||
[node name="ClearButton" type="Button" parent="Toolbar"]
|
||||
layout_mode = 2
|
||||
text = " Clear "
|
||||
|
||||
[node name="Settings" type="HBoxContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="WordWrap" type="Button" parent="Settings"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Word Wrap"
|
||||
toggle_mode = true
|
||||
icon = SubResource("DPITexture_lygvu")
|
||||
|
||||
[node name="UseColors" type="Button" parent="Settings"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Colorized Text"
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
icon = SubResource("DPITexture_lygvu")
|
||||
|
||||
[node name="Output" type="TextEdit" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 30
|
||||
deselect_on_focus_loss_enabled = false
|
||||
virtual_keyboard_enabled = false
|
||||
middle_mouse_paste_enabled = false
|
||||
scroll_smooth = true
|
||||
syntax_highlighter = SubResource("CodeHighlighter_8ynmy")
|
||||
highlight_all_occurrences = true
|
||||
highlight_current_line = true
|
||||
|
||||
[node name="Search" type="HBoxContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SearchTerm" type="LineEdit" parent="Search"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="SearchNext" type="Button" parent="Search"]
|
||||
layout_mode = 2
|
||||
text = "Next"
|
||||
|
||||
[node name="SearchPrev" type="Button" parent="Search"]
|
||||
layout_mode = 2
|
||||
text = "Prev"
|
||||
|
||||
[connection signal="pressed" from="Toolbar/ShowSearch" to="." method="_on_ShowSearch_pressed"]
|
||||
[connection signal="pressed" from="Toolbar/ShowSettings" to="." method="_on_settings_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="pressed" from="Settings/WordWrap" to="." method="_on_WordWrap_pressed"]
|
||||
[connection signal="pressed" from="Settings/UseColors" to="." method="_on_UseColors_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_submitted" 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"]
|
||||
107
addons/gut/gui/ResizeHandle.gd
Normal file
107
addons/gut/gui/ResizeHandle.gd
Normal file
@@ -0,0 +1,107 @@
|
||||
@tool
|
||||
extends ColorRect
|
||||
# #############################################################################
|
||||
# Resize Handle control. Place onto a control. Set the orientation, then
|
||||
# set the control that this should resize. Then you can resize the control
|
||||
# by dragging this thing around. It's pretty neat.
|
||||
# #############################################################################
|
||||
enum ORIENTATION {
|
||||
LEFT,
|
||||
RIGHT
|
||||
}
|
||||
|
||||
@export var orientation := ORIENTATION.RIGHT :
|
||||
get: return orientation
|
||||
set(val):
|
||||
orientation = val
|
||||
queue_redraw()
|
||||
@export var resize_control : Control = null
|
||||
@export var vertical_resize := true
|
||||
|
||||
var _line_width = .5
|
||||
var _line_color = Color(.4, .4, .4)
|
||||
var _active_line_color = Color(.3, .3, .3)
|
||||
var _invalid_line_color = Color(1, 0, 0)
|
||||
|
||||
var _line_space = 3
|
||||
var _num_lines = 8
|
||||
|
||||
var _mouse_down = false
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
|
||||
|
||||
func _draw():
|
||||
var c = _line_color
|
||||
if(resize_control == null):
|
||||
c = _invalid_line_color
|
||||
elif(_mouse_down):
|
||||
c = _active_line_color
|
||||
|
||||
if(orientation == ORIENTATION.LEFT):
|
||||
_draw_resize_handle_left(c)
|
||||
else:
|
||||
_draw_resize_handle_right(c)
|
||||
|
||||
|
||||
func _gui_input(event):
|
||||
if(resize_control == null):
|
||||
return
|
||||
|
||||
if(orientation == ORIENTATION.LEFT):
|
||||
_handle_left_input(event)
|
||||
else:
|
||||
_handle_right_input(event)
|
||||
|
||||
|
||||
# Draw the lines in the corner to show where you can
|
||||
# drag to resize the dialog
|
||||
func _draw_resize_handle_right(draw_color):
|
||||
var br = size
|
||||
|
||||
for i in range(_num_lines):
|
||||
var start = br - Vector2(i * _line_space, 0)
|
||||
var end = br - Vector2(0, i * _line_space)
|
||||
draw_line(start, end, draw_color, _line_width, true)
|
||||
|
||||
|
||||
func _draw_resize_handle_left(draw_color):
|
||||
var bl = Vector2(0, size.y)
|
||||
|
||||
for i in range(_num_lines):
|
||||
var start = bl + Vector2(i * _line_space, 0)
|
||||
var end = bl - Vector2(0, i * _line_space)
|
||||
draw_line(start, end, draw_color, _line_width, true)
|
||||
|
||||
|
||||
func _handle_right_input(event : InputEvent):
|
||||
if(event is InputEventMouseMotion):
|
||||
if(_mouse_down and
|
||||
event.global_position.x > 0 and
|
||||
event.global_position.y < DisplayServer.window_get_size().y):
|
||||
|
||||
if(vertical_resize):
|
||||
resize_control.size.y += event.relative.y
|
||||
resize_control.size.x += event.relative.x
|
||||
elif(event is InputEventMouseButton):
|
||||
if(event.button_index == MOUSE_BUTTON_LEFT):
|
||||
_mouse_down = event.pressed
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _handle_left_input(event : InputEvent):
|
||||
if(event is InputEventMouseMotion):
|
||||
if(_mouse_down and
|
||||
event.global_position.x > 0 and
|
||||
event.global_position.y < DisplayServer.window_get_size().y):
|
||||
|
||||
var start_size = resize_control.size
|
||||
resize_control.size.x -= event.relative.x
|
||||
if(resize_control.size.x != start_size.x):
|
||||
resize_control.global_position.x += event.relative.x
|
||||
|
||||
if(vertical_resize):
|
||||
resize_control.size.y += event.relative.y
|
||||
elif(event is InputEventMouseButton):
|
||||
if(event.button_index == MOUSE_BUTTON_LEFT):
|
||||
_mouse_down = event.pressed
|
||||
queue_redraw()
|
||||
1
addons/gut/gui/ResizeHandle.gd.uid
Normal file
1
addons/gut/gui/ResizeHandle.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://duf6rfdqr6yoc
|
||||
8
addons/gut/gui/ResizeHandle.tscn
Normal file
8
addons/gut/gui/ResizeHandle.tscn
Normal file
@@ -0,0 +1,8 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bvrqqgjpyouse"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://duf6rfdqr6yoc" path="res://addons/gut/gui/ResizeHandle.gd" id="1_oi5ed"]
|
||||
|
||||
[node name="ResizeHandle" type="ColorRect"]
|
||||
custom_minimum_size = Vector2(20, 20)
|
||||
color = Color(1, 1, 1, 0)
|
||||
script = ExtResource("1_oi5ed")
|
||||
368
addons/gut/gui/ResultsTree.gd
Normal file
368
addons/gut/gui/ResultsTree.gd
Normal file
@@ -0,0 +1,368 @@
|
||||
@tool
|
||||
extends Tree
|
||||
|
||||
var _show_orphans = true
|
||||
var show_orphans = true :
|
||||
get: return _show_orphans
|
||||
set(val): _show_orphans = val
|
||||
|
||||
|
||||
var _hide_passing = true
|
||||
var hide_passing = true :
|
||||
get: return _hide_passing
|
||||
set(val): _hide_passing = val
|
||||
|
||||
|
||||
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'),
|
||||
}
|
||||
|
||||
@export var script_entry_color : Color = Color(0, 0, 0, .2) :
|
||||
set(val):
|
||||
if(val != null):
|
||||
script_entry_color = val
|
||||
@export var column_0_color : Color = Color(1, 1, 1, 0) :
|
||||
set(val):
|
||||
if(val != null):
|
||||
column_0_color = val
|
||||
@export var column_1_color : Color = Color(0, 0, 0, .2):
|
||||
set(val):
|
||||
if(val != null):
|
||||
column_1_color = val
|
||||
|
||||
|
||||
|
||||
var _max_icon_width = 10
|
||||
var _root : TreeItem
|
||||
|
||||
|
||||
@onready var lbl_overlay = $TextOverlay
|
||||
|
||||
|
||||
signal selected(script_path, inner_class, test_name, line_number)
|
||||
|
||||
func _debug_ready():
|
||||
hide_passing = false
|
||||
load_json_file('user://gut_temp_directory/gut_editor.json')
|
||||
|
||||
|
||||
func _ready():
|
||||
_root = create_item()
|
||||
set_hide_root(true)
|
||||
columns = 2
|
||||
set_column_expand(0, true)
|
||||
set_column_expand_ratio(0, 5)
|
||||
|
||||
set_column_expand_ratio(1, 1)
|
||||
set_column_expand(1, true)
|
||||
|
||||
item_selected.connect(_on_tree_item_selected)
|
||||
|
||||
if(get_parent() == get_tree().root):
|
||||
_debug_ready()
|
||||
|
||||
|
||||
# -------------------
|
||||
# Private
|
||||
# -------------------
|
||||
func _get_line_number_from_assert_msg(msg):
|
||||
var line = -1
|
||||
if(msg.find('at line') > 0):
|
||||
line = msg.split("at line")[-1].split(" ")[-1].to_int()
|
||||
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 _find_script_item_with_path(path):
|
||||
var items = _root.get_children()
|
||||
var to_return = null
|
||||
|
||||
var idx = 0
|
||||
while(idx < items.size() and to_return == null):
|
||||
var item = items[idx]
|
||||
if(item.get_metadata(0).path == path):
|
||||
to_return = item
|
||||
else:
|
||||
idx += 1
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func _add_script_tree_item(script_path, script_json):
|
||||
var path_info = _get_path_and_inner_class_name_from_test_path(script_path)
|
||||
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 = 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,
|
||||
"inner_passing":0,
|
||||
"inner_tests":0
|
||||
}
|
||||
item.set_metadata(0, meta)
|
||||
item.set_custom_bg_color(0, script_entry_color)
|
||||
item.set_custom_bg_color(1, script_entry_color)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
func _add_assert_item(text, icon, parent_item):
|
||||
# print(' * adding assert')
|
||||
var assert_item = 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(0, column_0_color)
|
||||
assert_item.set_custom_bg_color(1, column_1_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.orphan_count == 0)
|
||||
if(_hide_passing and test_json['status'] == 'pass' and no_orphans_to_show):
|
||||
return
|
||||
|
||||
var item = 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_alignment(1, HORIZONTAL_ALIGNMENT_RIGHT)
|
||||
item.set_custom_bg_color(1, column_1_color)
|
||||
|
||||
item.set_metadata(0, meta)
|
||||
item.set_icon_max_width(0, _max_icon_width)
|
||||
item.set_custom_bg_color(0, column_0_color)
|
||||
|
||||
if(status == 'pass' and no_orphans_to_show):
|
||||
item.set_icon(0, _icons.green)
|
||||
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)
|
||||
|
||||
var orphan_text = 'orphans'
|
||||
if(test_json.orphan_count == 1):
|
||||
orphan_text = 'orphan'
|
||||
orphan_text = str(int(test_json.orphan_count), ' ', orphan_text)
|
||||
|
||||
if(!no_orphans_to_show):
|
||||
var orphan_item = _add_assert_item(orphan_text, _icons.yellow, item)
|
||||
for o in test_json.orphans:
|
||||
var orphan_entry = create_item(orphan_item)
|
||||
orphan_entry.set_text(0, o)
|
||||
orphan_entry.set_custom_bg_color(0, column_0_color)
|
||||
orphan_entry.set_custom_bg_color(1, column_1_color)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
func _add_script_to_tree(key, script_json):
|
||||
var tests = script_json['tests']
|
||||
var test_keys = tests.keys()
|
||||
var s_item = _add_script_tree_item(key, script_json)
|
||||
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
|
||||
|
||||
if(s_item.get_children().size() == 0):
|
||||
if(script_json.props.skipped):
|
||||
_add_assert_item("Skipped", _icons.yellow, s_item)
|
||||
s_item.set_text(1, "Skipped")
|
||||
else:
|
||||
s_item.free()
|
||||
else:
|
||||
var total_text = str('All ', test_keys.size(), ' passed')
|
||||
if(bad_count == 0):
|
||||
s_item.collapsed = true
|
||||
else:
|
||||
total_text = str(int(test_keys.size() - bad_count), '/', int(test_keys.size()), ' passed')
|
||||
s_item.set_text(1, total_text)
|
||||
|
||||
|
||||
func _free_childless_scripts():
|
||||
var items = _root.get_children()
|
||||
for item in items:
|
||||
var next_item = item.get_next()
|
||||
if(item.get_children().size() == 0):
|
||||
item.free()
|
||||
item = next_item
|
||||
|
||||
|
||||
func _show_all_passed():
|
||||
if(_root.get_children().size() == 0):
|
||||
add_centered_text('Everything passed!')
|
||||
|
||||
|
||||
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 add_count = 0
|
||||
for key in script_keys:
|
||||
add_count += 1
|
||||
_add_script_to_tree(key, scripts[key])
|
||||
|
||||
_free_childless_scripts()
|
||||
if(add_count == 0):
|
||||
add_centered_text('Nothing was run')
|
||||
else:
|
||||
_show_all_passed()
|
||||
# -------------------
|
||||
# Events
|
||||
# -------------------
|
||||
func _on_tree_item_selected():
|
||||
var item = get_selected()
|
||||
var item_meta = item.get_metadata(0)
|
||||
var item_type = null
|
||||
|
||||
# Only select the left side of the tree item, cause I like that better.
|
||||
# you can still click the right, but only the left gets highlighted.
|
||||
if(item.is_selected(1)):
|
||||
item.deselect(1)
|
||||
item.select(0)
|
||||
|
||||
if(item_meta == null):
|
||||
return
|
||||
else:
|
||||
item_type = item_meta.type
|
||||
|
||||
var script_path = '';
|
||||
var line = -1;
|
||||
var test_name = ''
|
||||
var inner_class = ''
|
||||
|
||||
if(item_type == 'test'):
|
||||
var s_item = item.get_parent()
|
||||
script_path = s_item.get_metadata(0)['path']
|
||||
inner_class = s_item.get_metadata(0)['inner_class']
|
||||
line = -1
|
||||
test_name = item.get_text(0)
|
||||
elif(item_type == 'assert'):
|
||||
var s_item = item.get_parent().get_parent()
|
||||
script_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))
|
||||
test_name = item.get_parent().get_text(0)
|
||||
elif(item_type == 'script'):
|
||||
script_path = item.get_metadata(0)['path']
|
||||
if(item.get_parent() != _root):
|
||||
inner_class = item.get_text(0)
|
||||
line = -1
|
||||
test_name = ''
|
||||
else:
|
||||
return
|
||||
|
||||
selected.emit(script_path, inner_class, test_name, line)
|
||||
|
||||
|
||||
# -------------------
|
||||
# Public
|
||||
# -------------------
|
||||
func load_json_file(path):
|
||||
var file = FileAccess.open(path, FileAccess.READ)
|
||||
var text = ''
|
||||
if(file != null):
|
||||
text = file.get_as_text()
|
||||
|
||||
if(text != ''):
|
||||
var test_json_conv = JSON.new()
|
||||
var result = test_json_conv.parse(text)
|
||||
if(result != OK):
|
||||
add_centered_text(str(path, " has invalid json in it \n",
|
||||
'Error ', result, "@", test_json_conv.get_error_line(), "\n",
|
||||
test_json_conv.get_error_message()))
|
||||
return
|
||||
|
||||
var data = test_json_conv.get_data()
|
||||
load_json_results(data)
|
||||
else:
|
||||
add_centered_text(str(path, ' was empty or does not exist.'))
|
||||
|
||||
|
||||
func load_json_results(j):
|
||||
clear()
|
||||
if(_root == null):
|
||||
_root = create_item()
|
||||
|
||||
_load_result_tree(j)
|
||||
|
||||
|
||||
#func clear():
|
||||
#clear()
|
||||
#_root = create_item()
|
||||
|
||||
|
||||
func set_summary_min_width(width):
|
||||
set_column_custom_minimum_width(1, width)
|
||||
|
||||
|
||||
func add_centered_text(t):
|
||||
lbl_overlay.visible = true
|
||||
lbl_overlay.text = t
|
||||
|
||||
|
||||
func clear_centered_text():
|
||||
lbl_overlay.visible = false
|
||||
lbl_overlay.text = ''
|
||||
|
||||
|
||||
func collapse_all():
|
||||
set_collapsed_on_all(_root, true)
|
||||
|
||||
|
||||
func expand_all():
|
||||
set_collapsed_on_all(_root, false)
|
||||
|
||||
|
||||
func set_collapsed_on_all(item, value):
|
||||
item.set_collapsed_recursive(value)
|
||||
if(item == _root and value):
|
||||
item.set_collapsed(false)
|
||||
1
addons/gut/gui/ResultsTree.gd.uid
Normal file
1
addons/gut/gui/ResultsTree.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dehdhn78qv5tr
|
||||
34
addons/gut/gui/ResultsTree.tscn
Normal file
34
addons/gut/gui/ResultsTree.tscn
Normal file
@@ -0,0 +1,34 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://dls5r5f6157nq"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dehdhn78qv5tr" path="res://addons/gut/gui/ResultsTree.gd" id="1_b4uub"]
|
||||
|
||||
[node name="ResultsTree" type="Tree"]
|
||||
offset_right = 1082.0
|
||||
offset_bottom = 544.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
columns = 2
|
||||
hide_root = true
|
||||
script = ExtResource("1_b4uub")
|
||||
|
||||
[node name="TextOverlay" type="Label" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="ResultsTree" type="VBoxContainer" parent="."]
|
||||
custom_minimum_size = Vector2(10, 10)
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_right = -70.0
|
||||
offset_bottom = -104.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
171
addons/gut/gui/RunAtCursor.gd
Normal file
171
addons/gut/gui/RunAtCursor.gd
Normal file
@@ -0,0 +1,171 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
var EditorCaretContextNotifier = load('res://addons/gut/editor_caret_context_notifier.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 _caret_notifier = null
|
||||
|
||||
var _last_info = {
|
||||
script = null,
|
||||
inner_class = null,
|
||||
method = null
|
||||
}
|
||||
|
||||
var disabled = false :
|
||||
set(val):
|
||||
disabled = val
|
||||
if(is_inside_tree()):
|
||||
_ctrls.btn_script.disabled = val
|
||||
_ctrls.btn_inner.disabled = val
|
||||
_ctrls.btn_method.disabled = val
|
||||
var method_prefix = 'test_'
|
||||
var inner_class_prefix = 'Test'
|
||||
var menu_manager = null :
|
||||
set(val):
|
||||
menu_manager = val
|
||||
menu_manager.run_script.connect(_on_BtnRunScript_pressed)
|
||||
menu_manager.run_at_cursor.connect(run_at_cursor)
|
||||
menu_manager.rerun.connect(rerun)
|
||||
menu_manager.run_inner_class.connect(_on_BtnRunInnerClass_pressed)
|
||||
menu_manager.run_test.connect(_on_BtnRunMethod_pressed)
|
||||
_update_buttons(_last_info)
|
||||
|
||||
|
||||
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
|
||||
_ctrls.arrow_1.visible = false
|
||||
_ctrls.arrow_2.visible = false
|
||||
|
||||
_caret_notifier = EditorCaretContextNotifier.new()
|
||||
add_child(_caret_notifier)
|
||||
_caret_notifier.it_changed.connect(_on_caret_notifer_changed)
|
||||
|
||||
disabled = disabled
|
||||
|
||||
|
||||
func _on_caret_notifer_changed(data):
|
||||
if(data.is_test_script):
|
||||
_last_info = data
|
||||
_update_buttons(_last_info)
|
||||
|
||||
|
||||
# ----------------
|
||||
# Private
|
||||
# ----------------
|
||||
|
||||
func _update_buttons(info):
|
||||
_ctrls.lbl_none.visible = false
|
||||
_ctrls.btn_script.visible = info.script != null
|
||||
|
||||
if(info.script != null and info.is_test_script):
|
||||
_ctrls.btn_script.text = info.script.resource_path.get_file()
|
||||
|
||||
_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.tooltip_text = str("Run all tests in Inner-Test-Class ", info.inner_class)
|
||||
|
||||
var is_test_method = info.method != null and info.method.begins_with(method_prefix)
|
||||
_ctrls.btn_method.visible = is_test_method
|
||||
_ctrls.arrow_2.visible = is_test_method
|
||||
if(is_test_method):
|
||||
_ctrls.btn_method.text = str(info.method)
|
||||
_ctrls.btn_method.tooltip_text = str("Run test ", info.method)
|
||||
|
||||
if(menu_manager != null):
|
||||
menu_manager.disable_menu("run_script", info.script == null)
|
||||
menu_manager.disable_menu("run_inner_class", info.inner_class == null)
|
||||
menu_manager.disable_menu("run_at_cursor", info.script == null)
|
||||
menu_manager.disable_menu("run_test", is_test_method)
|
||||
menu_manager.disable_menu("rerun", _last_run_info == {})
|
||||
# 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.
|
||||
_update_size.call_deferred()
|
||||
|
||||
|
||||
func _update_size():
|
||||
custom_minimum_size.x = _ctrls.btn_method.size.x + _ctrls.btn_method.position.x
|
||||
|
||||
var _last_run_info = {}
|
||||
func _emit_run_tests(info):
|
||||
_last_run_info = info.duplicate()
|
||||
run_tests.emit(info)
|
||||
|
||||
# ----------------
|
||||
# Events
|
||||
# ----------------
|
||||
func _on_BtnRunScript_pressed():
|
||||
var info = _last_info.duplicate()
|
||||
info.script = info.script.resource_path.get_file()
|
||||
info.inner_class = null
|
||||
info.method = null
|
||||
_emit_run_tests(info)
|
||||
|
||||
|
||||
func _on_BtnRunInnerClass_pressed():
|
||||
var info = _last_info.duplicate()
|
||||
info.script = info.script.resource_path.get_file()
|
||||
info.method = null
|
||||
_emit_run_tests(info)
|
||||
|
||||
|
||||
func _on_BtnRunMethod_pressed():
|
||||
var info = _last_info.duplicate()
|
||||
info.script = info.script.resource_path.get_file()
|
||||
_emit_run_tests(info)
|
||||
|
||||
|
||||
# ----------------
|
||||
# Public
|
||||
# ----------------
|
||||
func rerun():
|
||||
if(_last_run_info != {}):
|
||||
_emit_run_tests(_last_run_info)
|
||||
|
||||
|
||||
func run_at_cursor():
|
||||
if(_ctrls.btn_method.visible):
|
||||
_on_BtnRunMethod_pressed()
|
||||
elif(_ctrls.btn_inner.visible):
|
||||
_on_BtnRunInnerClass_pressed()
|
||||
elif(_ctrls.btn_script.visible):
|
||||
_on_BtnRunScript_pressed()
|
||||
else:
|
||||
print("nothing selected")
|
||||
|
||||
|
||||
func get_script_button():
|
||||
return _ctrls.btn_script
|
||||
|
||||
|
||||
func get_inner_button():
|
||||
return _ctrls.btn_inner
|
||||
|
||||
|
||||
func get_test_button():
|
||||
return _ctrls.btn_method
|
||||
|
||||
|
||||
func set_inner_class_prefix(value):
|
||||
_caret_notifier.inner_class_prefix = value
|
||||
|
||||
|
||||
func apply_gut_config(gut_config):
|
||||
_caret_notifier.script_prefix = gut_config.options.prefix
|
||||
_caret_notifier.script_suffix = gut_config.options.suffix
|
||||
1
addons/gut/gui/RunAtCursor.gd.uid
Normal file
1
addons/gut/gui/RunAtCursor.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c4gmgdl1xwflw
|
||||
60
addons/gut/gui/RunAtCursor.tscn
Normal file
60
addons/gut/gui/RunAtCursor.tscn
Normal file
@@ -0,0 +1,60 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://0yunjxtaa8iw"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c4gmgdl1xwflw" path="res://addons/gut/gui/RunAtCursor.gd" id="1"]
|
||||
[ext_resource type="Texture2D" uid="uid://6wra5rxmfsrl" path="res://addons/gut/gui/arrow.png" id="3"]
|
||||
|
||||
[node name="RunAtCursor" type="Control"]
|
||||
custom_minimum_size = Vector2(510, 0)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_right = 1.0
|
||||
offset_bottom = -527.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LblNoneSelected" type="Label" parent="HBox"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "<None>"
|
||||
|
||||
[node name="BtnRunScript" type="Button" parent="HBox"]
|
||||
layout_mode = 2
|
||||
text = "test_test.gd"
|
||||
|
||||
[node name="Arrow1" type="TextureButton" parent="HBox"]
|
||||
custom_minimum_size = Vector2(24, 0)
|
||||
layout_mode = 2
|
||||
texture_normal = ExtResource("3")
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="BtnRunInnerClass" type="Button" parent="HBox"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Run all tests in Inner-Test-Class TestAssertNe"
|
||||
text = "TestAssertNe"
|
||||
|
||||
[node name="Arrow2" type="TextureButton" parent="HBox"]
|
||||
custom_minimum_size = Vector2(24, 0)
|
||||
layout_mode = 2
|
||||
texture_normal = ExtResource("3")
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="BtnRunMethod" type="Button" parent="HBox"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Run test test_fails_with_integers_equal"
|
||||
text = "test_fails_with_integers_equal"
|
||||
|
||||
[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"]
|
||||
216
addons/gut/gui/RunExternally.gd
Normal file
216
addons/gut/gui/RunExternally.gd
Normal file
@@ -0,0 +1,216 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
# I'm probably going to put this back in later and I don't want to create it
|
||||
# again. Yeah, yeah, yeah.
|
||||
# class DotsAnimator:
|
||||
# var text = ''
|
||||
# var dot = '.'
|
||||
# var max_dots = 3
|
||||
# var dot_delay = .5
|
||||
|
||||
# var _anim_text = ''
|
||||
# var _elapsed_time = 0.0
|
||||
# var _cur_dots = 0
|
||||
|
||||
# func get_animated_text():
|
||||
# return _anim_text
|
||||
|
||||
# func add_time(delta):
|
||||
# _elapsed_time += delta
|
||||
# if(_elapsed_time > dot_delay):
|
||||
# _elapsed_time = 0
|
||||
# _cur_dots += 1
|
||||
# if(_cur_dots > max_dots):
|
||||
# _cur_dots = 0
|
||||
|
||||
# _anim_text = text.rpad(text.length() + _cur_dots, dot)
|
||||
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
|
||||
@onready var btn_kill_it = $BgControl/VBox/Kill
|
||||
@onready var bg_control = $BgControl
|
||||
|
||||
var _pipe_results = {}
|
||||
var _debug_mode = false
|
||||
var _std_thread : Thread
|
||||
var _escape_regex : RegEx = RegEx.new()
|
||||
var _text_buffer = ''
|
||||
|
||||
var bottom_panel = null :
|
||||
set(val):
|
||||
bottom_panel = val
|
||||
bottom_panel.resized.connect(_on_bottom_panel_resized)
|
||||
var blocking_mode = "Blocking"
|
||||
var additional_arguments = []
|
||||
var remove_escape_characters = true
|
||||
@export var bg_color = Color.WHITE:
|
||||
set(val):
|
||||
bg_color = val
|
||||
if(is_inside_tree()):
|
||||
bg_control.get("theme_override_styles/panel").bg_color = bg_color
|
||||
|
||||
|
||||
func _debug_ready():
|
||||
_debug_mode = true
|
||||
additional_arguments = ['-gselect', 'test_awaiter.gd', '-gconfig', 'res://.gutconfig.json'] # '-gunit_test_name', 'test_can_clear_spies'
|
||||
blocking_mode = "NonBlocking"
|
||||
run_tests()
|
||||
|
||||
|
||||
func _ready():
|
||||
_escape_regex.compile("\\x1b\\[[0-9;]*m")
|
||||
btn_kill_it.visible = false
|
||||
|
||||
if(get_parent() == get_tree().root):
|
||||
_debug_ready.call_deferred()
|
||||
bg_color = bg_color
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if(_pipe_results != {}):
|
||||
if(!OS.is_process_running(_pipe_results.pid)):
|
||||
_end_non_blocking()
|
||||
|
||||
|
||||
# ----------
|
||||
# Private
|
||||
# ----------
|
||||
func _center_me():
|
||||
position = get_parent().size / 2.0 - size / 2.0
|
||||
|
||||
|
||||
func _output_text(text, should_scroll = true):
|
||||
if(_debug_mode):
|
||||
print(text)
|
||||
else:
|
||||
if(remove_escape_characters):
|
||||
text = _escape_regex.sub(text, '', true)
|
||||
|
||||
if(bottom_panel != null):
|
||||
bottom_panel.add_output_text(text)
|
||||
if(should_scroll):
|
||||
_scroll_output_pane(-1)
|
||||
else:
|
||||
_text_buffer += text
|
||||
|
||||
|
||||
func _scroll_output_pane(line):
|
||||
if(!_debug_mode and bottom_panel != null):
|
||||
var txt_ctrl = bottom_panel.get_text_output_control().get_rich_text_edit()
|
||||
if(line == -1):
|
||||
line = txt_ctrl.get_line_count()
|
||||
txt_ctrl.scroll_vertical = line
|
||||
|
||||
|
||||
func _add_arguments_to_output():
|
||||
if(additional_arguments.size() != 0):
|
||||
_output_text(
|
||||
str("Run Mode arguments: ", ' '.join(additional_arguments), "\n\n")
|
||||
)
|
||||
|
||||
|
||||
func _load_json():
|
||||
if(_debug_mode):
|
||||
pass # could load file and print it if we want.
|
||||
elif(bottom_panel != null):
|
||||
bottom_panel.load_result_json()
|
||||
|
||||
|
||||
func _run_blocking(options):
|
||||
btn_kill_it.visible = false
|
||||
var output = []
|
||||
await get_tree().create_timer(.1).timeout
|
||||
|
||||
OS.execute(OS.get_executable_path(), options, output, true)
|
||||
|
||||
_output_text(output[0])
|
||||
_add_arguments_to_output()
|
||||
_scroll_output_pane(-1)
|
||||
|
||||
_load_json()
|
||||
queue_free()
|
||||
|
||||
|
||||
func _read_non_blocking_stdio():
|
||||
while(OS.is_process_running(_pipe_results.pid)):
|
||||
while(_pipe_results.stderr.get_length() > 0):
|
||||
_output_text(_pipe_results.stderr.get_line() + "\n")
|
||||
|
||||
while(_pipe_results.stdio.get_length() > 0):
|
||||
_output_text(_pipe_results.stdio.get_line() + "\n")
|
||||
|
||||
# without this, things start to lock up.
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
func _run_non_blocking(options):
|
||||
_pipe_results = OS.execute_with_pipe(OS.get_executable_path(), options, false)
|
||||
_std_thread = Thread.new()
|
||||
_std_thread.start(_read_non_blocking_stdio)
|
||||
btn_kill_it.visible = true
|
||||
|
||||
|
||||
func _end_non_blocking():
|
||||
_add_arguments_to_output()
|
||||
_scroll_output_pane(-1)
|
||||
|
||||
_load_json()
|
||||
|
||||
_pipe_results = {}
|
||||
_std_thread.wait_to_finish()
|
||||
_std_thread = null
|
||||
queue_free()
|
||||
if(_debug_mode):
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
|
||||
# ----------------
|
||||
# Events
|
||||
# ----------------
|
||||
func _on_kill_pressed() -> void:
|
||||
if(_pipe_results != {} and OS.is_process_running(_pipe_results.pid)):
|
||||
OS.kill(_pipe_results.pid)
|
||||
btn_kill_it.visible = false
|
||||
|
||||
|
||||
func _on_color_rect_gui_input(event: InputEvent) -> void:
|
||||
if(event is InputEventMouseMotion):
|
||||
if(event.button_mask == MOUSE_BUTTON_MASK_LEFT):
|
||||
position += event.relative
|
||||
|
||||
|
||||
func _on_bottom_panel_resized():
|
||||
_center_me()
|
||||
|
||||
|
||||
# ----------------
|
||||
# Public
|
||||
# ----------------
|
||||
func run_tests():
|
||||
_center_me()
|
||||
|
||||
var options = ["-s", "res://addons/gut/gut_cmdln.gd", "-graie", "-gdisable_colors",
|
||||
"-gconfig", GutEditorGlobals.editor_run_gut_config_path]
|
||||
options.append_array(additional_arguments)
|
||||
|
||||
if(blocking_mode == 'Blocking'):
|
||||
_run_blocking(options)
|
||||
else:
|
||||
_run_non_blocking(options)
|
||||
|
||||
|
||||
func get_godot_help():
|
||||
_text_buffer = ''
|
||||
var options = ["--help", "--headless"]
|
||||
await _run_blocking(options)
|
||||
return _text_buffer
|
||||
|
||||
|
||||
func get_gut_help():
|
||||
_text_buffer = ''
|
||||
var options = ["-s", "res://addons/gut/gut_cmdln.gd", "-gh", "--headless"]
|
||||
await _run_blocking(options)
|
||||
return _text_buffer
|
||||
1
addons/gut/gui/RunExternally.gd.uid
Normal file
1
addons/gut/gui/RunExternally.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bi8pg352un4om
|
||||
65
addons/gut/gui/RunExternally.tscn
Normal file
65
addons/gut/gui/RunExternally.tscn
Normal file
@@ -0,0 +1,65 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cftcb0e6g7tu1"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bi8pg352un4om" path="res://addons/gut/gui/RunExternally.gd" id="1_lrqqi"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_haowt"]
|
||||
bg_color = Color(0.025935074, 0.17817128, 0.30283752, 1)
|
||||
corner_radius_top_left = 20
|
||||
corner_radius_top_right = 20
|
||||
corner_radius_bottom_right = 20
|
||||
corner_radius_bottom_left = 20
|
||||
shadow_size = 5
|
||||
shadow_offset = Vector2(7, 7)
|
||||
|
||||
[node name="DoShellOut" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
offset_right = 774.0
|
||||
offset_bottom = 260.0
|
||||
script = ExtResource("1_lrqqi")
|
||||
bg_color = Color(0.025935074, 0.17817128, 0.30283752, 1)
|
||||
|
||||
[node name="BgControl" type="Panel" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_haowt")
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="BgControl"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Spacer" type="CenterContainer" parent="BgControl/VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Title" type="Label" parent="BgControl/VBox"]
|
||||
layout_mode = 2
|
||||
text = "Running Tests"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Spacer2" type="CenterContainer" parent="BgControl/VBox"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Kill" type="Button" parent="BgControl/VBox"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(200, 50)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
text = "Stop"
|
||||
|
||||
[node name="Spacer3" type="CenterContainer" parent="BgControl/VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[connection signal="gui_input" from="BgControl" to="." method="_on_color_rect_gui_input"]
|
||||
[connection signal="pressed" from="BgControl/VBox/Kill" to="." method="_on_kill_pressed"]
|
||||
250
addons/gut/gui/RunResults.gd
Normal file
250
addons/gut/gui/RunResults.gd
Normal file
@@ -0,0 +1,250 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
|
||||
var _interface = null
|
||||
var _output_control = null
|
||||
|
||||
@onready var _ctrls = {
|
||||
tree = $VBox/Output/Scroll/Tree,
|
||||
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 _ready():
|
||||
if(get_parent() is SubViewport):
|
||||
return
|
||||
|
||||
var f = null
|
||||
if ($FontSampler.get_label_settings() == null) :
|
||||
f = get_theme_default_font()
|
||||
else :
|
||||
f = $FontSampler.get_label_settings().font
|
||||
var s_size = f.get_string_size("000 of 000 passed")
|
||||
_ctrls.tree.set_summary_min_width(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.tree.hide_passing = true
|
||||
_ctrls.toolbar.hide_passing.button_pressed = false
|
||||
_ctrls.tree.show_orphans = true
|
||||
_ctrls.tree.selected.connect(_on_item_selected)
|
||||
|
||||
if(get_parent() == get_tree().root):
|
||||
_test_running_setup()
|
||||
|
||||
call_deferred('_update_min_width')
|
||||
|
||||
|
||||
func _test_running_setup():
|
||||
_ctrls.tree.hide_passing = true
|
||||
_ctrls.tree.show_orphans = true
|
||||
|
||||
_ctrls.toolbar.hide_passing.text = '[hp]'
|
||||
_ctrls.tree.load_json_file(GutEditorGlobals.editor_run_json_results_path)
|
||||
|
||||
|
||||
func _set_toolbutton_icon(btn, icon_name, text):
|
||||
if(Engine.is_editor_hint()):
|
||||
btn.icon = get_theme_icon(icon_name, 'EditorIcons')
|
||||
else:
|
||||
btn.text = str('[', text, ']')
|
||||
|
||||
|
||||
func _update_min_width():
|
||||
custom_minimum_size.x = _ctrls.toolbar.toolbar.size.x
|
||||
|
||||
|
||||
func _open_script_in_editor(path, line_number):
|
||||
if(_interface == null):
|
||||
print('Too soon, wait a bit and try again.')
|
||||
return
|
||||
|
||||
var r = load(path)
|
||||
if(line_number != null and 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')
|
||||
|
||||
|
||||
# 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):
|
||||
if(te == null):
|
||||
print("No Text editor to get line number for")
|
||||
return 0;
|
||||
|
||||
var result = null
|
||||
var line = Vector2i(0, 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, line.y, line.x)
|
||||
if(result.x != -1):
|
||||
line = result
|
||||
else:
|
||||
string_found = false
|
||||
i += 1
|
||||
|
||||
return line.y
|
||||
|
||||
|
||||
func _goto_code(path, line, method_name='', inner_class =''):
|
||||
if(_interface == null):
|
||||
print('going to ', [path, line, method_name, inner_class])
|
||||
return
|
||||
|
||||
_open_script_in_editor(path, line)
|
||||
if(line == -1):
|
||||
var search_strings = []
|
||||
if(inner_class != ''):
|
||||
search_strings.append(inner_class)
|
||||
|
||||
if(method_name != ''):
|
||||
search_strings.append(method_name)
|
||||
|
||||
await get_tree().process_frame
|
||||
line = _get_line_number_for_seq_search(search_strings,
|
||||
_interface.get_script_editor().get_current_editor().get_base_editor())
|
||||
if(line != null and 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 != null and line != -1):
|
||||
_output_control.scroll_to_line(line)
|
||||
|
||||
|
||||
|
||||
|
||||
# --------------
|
||||
# Events
|
||||
# --------------
|
||||
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():
|
||||
_ctrls.tree.hide_passing = !_ctrls.toolbar.hide_passing.button_pressed
|
||||
_ctrls.tree.load_json_file(GutEditorGlobals.editor_run_json_results_path)
|
||||
|
||||
|
||||
func _on_item_selected(script_path, inner_class, test_name, line):
|
||||
if(_ctrls.toolbar.show_script.button_pressed):
|
||||
_goto_code(script_path, line, test_name, inner_class)
|
||||
if(_ctrls.toolbar.scroll_output.button_pressed):
|
||||
_goto_output(script_path, test_name, inner_class)
|
||||
|
||||
|
||||
|
||||
|
||||
# --------------
|
||||
# Public
|
||||
# --------------
|
||||
func add_centered_text(t):
|
||||
_ctrls.tree.add_centered_text(t)
|
||||
|
||||
|
||||
func clear_centered_text():
|
||||
_ctrls.tree.clear_centered_text()
|
||||
|
||||
|
||||
func clear():
|
||||
_ctrls.tree.clear()
|
||||
clear_centered_text()
|
||||
|
||||
|
||||
func set_interface(which):
|
||||
_interface = which
|
||||
|
||||
|
||||
func collapse_all():
|
||||
_ctrls.tree.collapse_all()
|
||||
|
||||
|
||||
func expand_all():
|
||||
_ctrls.tree.expand_all()
|
||||
|
||||
|
||||
func collapse_selected():
|
||||
var item = _ctrls.tree.get_selected()
|
||||
if(item != null):
|
||||
_ctrls.tree.set_collapsed_on_all(item, true)
|
||||
|
||||
|
||||
func expand_selected():
|
||||
var item = _ctrls.tree.get_selected()
|
||||
if(item != null):
|
||||
_ctrls.tree.set_collapsed_on_all(item, false)
|
||||
|
||||
|
||||
func set_show_orphans(should):
|
||||
_ctrls.tree.show_orphans = should
|
||||
|
||||
|
||||
func set_font(font_name, size):
|
||||
pass
|
||||
# var dyn_font = FontFile.new()
|
||||
# var font_data = FontFile.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
|
||||
|
||||
|
||||
func load_json_results(j):
|
||||
_ctrls.tree.load_json_results(j)
|
||||
1
addons/gut/gui/RunResults.gd.uid
Normal file
1
addons/gut/gui/RunResults.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://chnko3073tkcv
|
||||
109
addons/gut/gui/RunResults.tscn
Normal file
109
addons/gut/gui/RunResults.tscn
Normal file
@@ -0,0 +1,109 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://4gyyn12um08h"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://chnko3073tkcv" path="res://addons/gut/gui/RunResults.gd" id="1"]
|
||||
[ext_resource type="Texture2D" uid="uid://bvo0uao7deu0q" path="res://addons/gut/icon.png" id="2_1k8e0"]
|
||||
[ext_resource type="PackedScene" uid="uid://dls5r5f6157nq" path="res://addons/gut/gui/ResultsTree.tscn" id="2_o808v"]
|
||||
|
||||
[node name="RunResults" type="Control"]
|
||||
custom_minimum_size = Vector2(345, 0)
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
offset_right = 709.0
|
||||
offset_bottom = 321.0
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="Toolbar" type="HBoxContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
|
||||
[node name="Expand" type="Button" parent="VBox/Toolbar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
icon = ExtResource("2_1k8e0")
|
||||
|
||||
[node name="Collapse" type="Button" parent="VBox/Toolbar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
icon = ExtResource("2_1k8e0")
|
||||
|
||||
[node name="Sep" type="ColorRect" parent="VBox/Toolbar"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(2, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LblAll" type="Label" parent="VBox/Toolbar"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "All:"
|
||||
|
||||
[node name="ExpandAll" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
icon = ExtResource("2_1k8e0")
|
||||
|
||||
[node name="CollapseAll" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
icon = ExtResource("2_1k8e0")
|
||||
|
||||
[node name="Sep2" type="ColorRect" parent="VBox/Toolbar"]
|
||||
custom_minimum_size = Vector2(2, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HidePassing" type="CheckBox" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
text = "Passing"
|
||||
|
||||
[node name="Sep3" type="ColorRect" parent="VBox/Toolbar"]
|
||||
custom_minimum_size = Vector2(2, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LblSync" type="Label" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
text = "Sync:"
|
||||
|
||||
[node name="ShowScript" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
icon = ExtResource("2_1k8e0")
|
||||
|
||||
[node name="ScrollOutput" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
icon = ExtResource("2_1k8e0")
|
||||
|
||||
[node name="Output" type="Panel" parent="VBox"]
|
||||
self_modulate = Color(1, 1, 1, 0.541176)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="VBox/Output"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Tree" parent="VBox/Output/Scroll" instance=ExtResource("2_o808v")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="FontSampler" type="Label" parent="."]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_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"]
|
||||
7
addons/gut/gui/Settings.tscn
Normal file
7
addons/gut/gui/Settings.tscn
Normal file
@@ -0,0 +1,7 @@
|
||||
[gd_scene format=3 uid="uid://cvvvtsah38l0e"]
|
||||
|
||||
[node name="Settings" type="VBoxContainer"]
|
||||
offset_right = 388.0
|
||||
offset_bottom = 586.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
321
addons/gut/gui/ShellOutOptions.gd
Normal file
321
addons/gut/gui/ShellOutOptions.gd
Normal file
@@ -0,0 +1,321 @@
|
||||
@tool
|
||||
extends ConfirmationDialog
|
||||
|
||||
const RUN_MODE_EDITOR = 'Editor'
|
||||
const RUN_MODE_BLOCKING = 'Blocking'
|
||||
const RUN_MODE_NON_BLOCKING = 'NonBlocking'
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
|
||||
@onready var _bad_arg_dialog = $AcceptDialog
|
||||
@onready var _main_container = $ScrollContainer/VBoxContainer
|
||||
|
||||
var _blurb_style_box = StyleBoxEmpty.new()
|
||||
var _opt_maker_setup = false
|
||||
var _arg_vbox : VBoxContainer = null
|
||||
var _my_ok_button : Button = null
|
||||
|
||||
# Run mode button stuff
|
||||
var _run_mode_theme = load('res://addons/gut/gui/EditorRadioButton.tres')
|
||||
var _button_group = ButtonGroup.new()
|
||||
var _btn_in_editor : Button = null
|
||||
var _btn_blocking : Button = null
|
||||
var _btn_non_blocking : Button = null
|
||||
var _txt_additional_arguments = null
|
||||
var _btn_godot_help = null
|
||||
var _btn_gut_help = null
|
||||
|
||||
|
||||
var opt_maker = null
|
||||
var default_path = GutEditorGlobals.run_externally_options_path
|
||||
# I like this. It holds values loaded/saved which makes for an easy
|
||||
# reset mechanism. Hit OK; values get written to this object (not the file
|
||||
# system). Hit Cancel; values are reloaded from this object. Call the
|
||||
# save/load methods to interact with the file system.
|
||||
#
|
||||
# Downside: If the keys/sections in the config file change, this ends up
|
||||
# preserving old data. So you gotta find a way to clean it out
|
||||
# somehow.
|
||||
# Downside solved: Clear the config file at the start of the save method.
|
||||
var _config_file = ConfigFile.new()
|
||||
|
||||
var _run_mode = RUN_MODE_EDITOR
|
||||
var run_mode = _run_mode:
|
||||
set(val):
|
||||
_run_mode = val
|
||||
if(is_inside_tree()):
|
||||
_btn_in_editor.button_pressed = _run_mode == RUN_MODE_EDITOR
|
||||
if(_btn_in_editor.button_pressed):
|
||||
_btn_in_editor.pressed.emit()
|
||||
_btn_blocking.button_pressed = _run_mode == RUN_MODE_BLOCKING
|
||||
if(_btn_blocking.button_pressed):
|
||||
_btn_blocking.pressed.emit()
|
||||
_btn_non_blocking.button_pressed = _run_mode == RUN_MODE_NON_BLOCKING
|
||||
if(_btn_non_blocking.button_pressed):
|
||||
_btn_non_blocking.pressed.emit()
|
||||
get():
|
||||
return _run_mode
|
||||
|
||||
|
||||
var additional_arguments = '' :
|
||||
get():
|
||||
if(_opt_maker_setup):
|
||||
return opt_maker.controls.additional_arguments.value
|
||||
else:
|
||||
return additional_arguments
|
||||
|
||||
|
||||
func _debug_ready():
|
||||
popup_centered()
|
||||
default_path = GutEditorGlobals.temp_directory.path_join('test_external_run_options.cfg')
|
||||
exclusive = false
|
||||
|
||||
var save_btn = Button.new()
|
||||
save_btn.text = 'save'
|
||||
save_btn.pressed.connect(func():
|
||||
save_to_file()
|
||||
print(_config_file.encode_to_text()))
|
||||
save_btn.position = Vector2(100, 20)
|
||||
save_btn.size = Vector2(100, 100)
|
||||
get_tree().root.add_child(save_btn)
|
||||
|
||||
var load_btn = Button.new()
|
||||
load_btn.text = 'load'
|
||||
load_btn.pressed.connect(func():
|
||||
load_from_file()
|
||||
print(_config_file.encode_to_text()))
|
||||
load_btn.position = Vector2(100, 130)
|
||||
load_btn.size = Vector2(100, 100)
|
||||
get_tree().root.add_child(load_btn)
|
||||
|
||||
var show_btn = Button.new()
|
||||
show_btn.text = 'Show'
|
||||
show_btn.pressed.connect(popup_centered)
|
||||
show_btn.position = Vector2(100, 250)
|
||||
show_btn.size = Vector2(100, 100)
|
||||
get_tree().root.add_child(show_btn)
|
||||
|
||||
|
||||
func _ready():
|
||||
opt_maker = GutUtils.OptionMaker.new(_main_container)
|
||||
_add_controls()
|
||||
|
||||
if(get_parent() == get_tree().root):
|
||||
_debug_ready.call_deferred()
|
||||
|
||||
_my_ok_button = Button.new()
|
||||
_my_ok_button.text = 'OK'
|
||||
_my_ok_button.pressed.connect(_validate_and_confirm)
|
||||
get_ok_button().add_sibling(_my_ok_button)
|
||||
get_ok_button().modulate.a = 0.0
|
||||
get_ok_button().text = ''
|
||||
get_ok_button().disabled = true
|
||||
|
||||
canceled.connect(reset)
|
||||
_button_group.pressed.connect(_on_mode_button_pressed)
|
||||
run_mode = run_mode
|
||||
|
||||
|
||||
func _validate_and_confirm():
|
||||
if(validate_arguments()):
|
||||
_save_to_config_file(_config_file)
|
||||
confirmed.emit()
|
||||
hide()
|
||||
else:
|
||||
var dlg_text = str("Invalid arguments. The following cannot be used:\n",
|
||||
' '.join(_invalid_args))
|
||||
|
||||
if(run_mode == RUN_MODE_BLOCKING):
|
||||
dlg_text += str("\nThese cannot be used with blocking mode:\n",
|
||||
' '.join(_invalid_blocking_args))
|
||||
|
||||
_bad_arg_dialog.dialog_text = dlg_text
|
||||
_bad_arg_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_mode_button_pressed(which):
|
||||
if(which == _btn_in_editor):
|
||||
_arg_vbox.modulate.a = .3
|
||||
else:
|
||||
_arg_vbox.modulate.a = 1.0
|
||||
|
||||
_txt_additional_arguments.value_ctrl.editable = which != _btn_in_editor
|
||||
if(which == _btn_in_editor):
|
||||
_run_mode = RUN_MODE_EDITOR
|
||||
elif(which == _btn_blocking):
|
||||
_run_mode = RUN_MODE_BLOCKING
|
||||
elif(which == _btn_non_blocking):
|
||||
_run_mode = RUN_MODE_NON_BLOCKING
|
||||
|
||||
|
||||
func _add_run_mode_button(text, desc_label, description):
|
||||
var btn = Button.new()
|
||||
btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
btn.toggle_mode = true
|
||||
btn.text = text
|
||||
btn.button_group = _button_group
|
||||
btn.theme = _run_mode_theme
|
||||
btn.pressed.connect(func(): desc_label.text = str('[b]', text, "[/b]\n", description))
|
||||
|
||||
return btn
|
||||
|
||||
|
||||
func _add_blurb(text):
|
||||
var ctrl = opt_maker.add_blurb(text)
|
||||
ctrl.set("theme_override_styles/normal", _blurb_style_box)
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_title(text):
|
||||
var ctrl = opt_maker.add_title(text)
|
||||
ctrl.get_child(0).horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
return ctrl
|
||||
|
||||
|
||||
func _add_controls():
|
||||
_add_title("Run Modes")
|
||||
_add_blurb(
|
||||
"Choose how GUT will launch tests. Normally you just run them through the editor, but now " +
|
||||
"you can run them externally. This is an experimental feature. It has been tested on Mac " +
|
||||
"and Windows. Your results may vary. Feedback welcome at [url]https://github.com/bitwes/Gut/issues[/url].\n ")
|
||||
|
||||
var button_desc_box = HBoxContainer.new()
|
||||
var button_box = VBoxContainer.new()
|
||||
var button_desc = RichTextLabel.new()
|
||||
button_desc.fit_content = true
|
||||
button_desc.bbcode_enabled = true
|
||||
button_desc.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
button_desc.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_main_container.add_child(button_desc_box)
|
||||
button_desc_box.add_child(button_box)
|
||||
button_desc_box.add_child(button_desc)
|
||||
|
||||
_btn_in_editor = _add_run_mode_button("In Editor (default)", button_desc,
|
||||
"This is the default. Runs through the editor. When an error occurs " +
|
||||
"the debugger is invoked. [b]print[/b] output " +
|
||||
"appears in the Output panel and errors show up in the Debugger panel.")
|
||||
button_box.add_child(_btn_in_editor)
|
||||
_btn_blocking = _add_run_mode_button("Externally - Blocking", button_desc,
|
||||
"Debugger is not enabled, and cannot be enabled. All output (print, errors, warnings, etc) " +
|
||||
"appears in the GUT panel, and [b]not[/b] the Output or Debugger panels. \n" +
|
||||
"The Editor cannot be used while tests are running. If you are trying to test for errors, this " +
|
||||
"mode provides the best output.")
|
||||
button_box.add_child(_btn_blocking)
|
||||
_btn_non_blocking = _add_run_mode_button("Externally - NonBlocking", button_desc,
|
||||
"Debugger is not enabled, and cannot be enabled. All output (print, errors, warnings, etc) " +
|
||||
"appears in the GUT panel, and [b]not[/b] the Output or Debugger panels. \n" +
|
||||
"Test output is streamed to the GUT panel. The editor is not blocked, but can be less " +
|
||||
"responsive when there is a lot of output. This is the only mode that supports the --headless argument." )
|
||||
button_box.add_child(_btn_non_blocking)
|
||||
|
||||
_add_title("Command Line Arguments")
|
||||
_arg_vbox = VBoxContainer.new()
|
||||
_main_container.add_child(_arg_vbox)
|
||||
opt_maker.base_container = _arg_vbox
|
||||
_txt_additional_arguments = opt_maker.add_value("additional_arguments", additional_arguments, '', '')
|
||||
_txt_additional_arguments.value_ctrl.placeholder_text = "Put your arguments here. Ex: --verbose -glog 0"
|
||||
_txt_additional_arguments.value_ctrl.select_all_on_focus = false
|
||||
_add_blurb(
|
||||
"Supply any command line options for GUT and/or Godot when running externally. You cannot use " +
|
||||
"spaces in values. See the Godot and GUT documentation for valid arguments. GUT arguments " +
|
||||
"specified here take precedence over your config.")
|
||||
_add_blurb("[b]Be Careful[/b] There are plenty of argument combinations that may make this " +
|
||||
"act wrong/odd/bad/horrible. Some arguments you might [i]want[/i] " +
|
||||
"to use but [b]shouldn't[/b] are checked for, but not that many. Choose your arguments carefully (generally good advice).")
|
||||
|
||||
opt_maker.base_container = _main_container
|
||||
_add_title("Display CLI Help")
|
||||
_add_blurb("You can use these buttons to get a list of valid GUT and Godot options. They print the CLI help text for each to the [b]Output Panel[/b].")
|
||||
_btn_godot_help = Button.new()
|
||||
_btn_godot_help.text = "Print Godot CLI Help"
|
||||
_main_container.add_child(_btn_godot_help)
|
||||
_btn_godot_help.pressed.connect(func():
|
||||
await _show_help("get_godot_help"))
|
||||
|
||||
_btn_gut_help = Button.new()
|
||||
_btn_gut_help.text = "Print GUT CLI Help"
|
||||
_main_container.add_child(_btn_gut_help)
|
||||
_btn_gut_help.pressed.connect(func():
|
||||
await _show_help("get_gut_help"))
|
||||
|
||||
_opt_maker_setup = true
|
||||
|
||||
|
||||
func _show_help(help_method_name):
|
||||
_btn_godot_help.disabled = true
|
||||
_btn_gut_help.disabled = true
|
||||
var re = GutUtils.RunExternallyScene.instantiate()
|
||||
add_child(re)
|
||||
re.visible = false
|
||||
var text = await re.call(help_method_name)
|
||||
print(text)
|
||||
re.queue_free()
|
||||
_btn_godot_help.disabled = false
|
||||
_btn_gut_help.disabled = false
|
||||
if(GutEditorGlobals.gut_plugin != null):
|
||||
GutEditorGlobals.gut_plugin.show_output_panel()
|
||||
|
||||
|
||||
func _save_to_config_file(f : ConfigFile):
|
||||
f.clear()
|
||||
f.set_value('main', 'run_mode', run_mode)
|
||||
f.set_value('main', 'additional_arguments', opt_maker.controls.additional_arguments.value)
|
||||
|
||||
|
||||
func save_to_file(path = default_path):
|
||||
_save_to_config_file(_config_file)
|
||||
_config_file.save(path)
|
||||
|
||||
|
||||
func _load_from_config_file(f):
|
||||
run_mode = f.get_value('main', 'run_mode', RUN_MODE_EDITOR)
|
||||
opt_maker.controls.additional_arguments.value = \
|
||||
f.get_value('main', 'additional_arguments', '')
|
||||
|
||||
|
||||
func load_from_file(path = default_path):
|
||||
_config_file.load(path)
|
||||
_load_from_config_file(_config_file)
|
||||
|
||||
|
||||
func reset():
|
||||
_load_from_config_file(_config_file)
|
||||
|
||||
|
||||
func get_additional_arguments_array():
|
||||
return additional_arguments.split(" ", false)
|
||||
|
||||
|
||||
func should_run_externally():
|
||||
return run_mode != RUN_MODE_EDITOR
|
||||
|
||||
|
||||
var _invalid_args = [
|
||||
'-d', '--debug',
|
||||
'-s', '--script',
|
||||
'-e', '--editor'
|
||||
]
|
||||
var _invalid_blocking_args = [
|
||||
'--headless'
|
||||
]
|
||||
func validate_arguments():
|
||||
var arg_array = get_additional_arguments_array()
|
||||
var i = 0
|
||||
var invalid_found = false
|
||||
while i < _invalid_args.size() and !invalid_found:
|
||||
if(arg_array.has(_invalid_args[i])):
|
||||
invalid_found = true
|
||||
i += 1
|
||||
|
||||
if(run_mode == RUN_MODE_BLOCKING):
|
||||
i = 0
|
||||
while i < _invalid_blocking_args.size() and !invalid_found:
|
||||
if(arg_array.has(_invalid_blocking_args[i])):
|
||||
invalid_found = true
|
||||
i += 1
|
||||
|
||||
return !invalid_found
|
||||
|
||||
|
||||
func get_godot_help():
|
||||
return ''
|
||||
1
addons/gut/gui/ShellOutOptions.gd.uid
Normal file
1
addons/gut/gui/ShellOutOptions.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c64u22kybimgi
|
||||
34
addons/gut/gui/ShellOutOptions.tscn
Normal file
34
addons/gut/gui/ShellOutOptions.tscn
Normal file
@@ -0,0 +1,34 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://ckv5eh8xyrwbk"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c64u22kybimgi" path="res://addons/gut/gui/ShellOutOptions.gd" id="1_ht2pf"]
|
||||
|
||||
[node name="ShellOutOptions" type="ConfirmationDialog"]
|
||||
oversampling_override = 1.0
|
||||
title = "GUT Run Mode (Experimental)"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(516, 557)
|
||||
visible = true
|
||||
script = ExtResource("1_ht2pf")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="."]
|
||||
custom_minimum_size = Vector2(500, 500)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="AcceptDialog" type="AcceptDialog" parent="."]
|
||||
oversampling_override = 1.0
|
||||
size = Vector2i(399, 106)
|
||||
dialog_text = "Invalid arguments. The following cannot be used:
|
||||
-d --debug -s --script"
|
||||
154
addons/gut/gui/ShortcutButton.gd
Normal file
154
addons/gut/gui/ShortcutButton.gd
Normal file
@@ -0,0 +1,154 @@
|
||||
@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 _editing = false
|
||||
|
||||
var _modifier_keys = [KEY_ALT, KEY_CTRL, 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():
|
||||
if(_key_disp == ''):
|
||||
_key_disp = NO_SHORTCUT
|
||||
_ctrls.shortcut_label.text = _key_disp
|
||||
|
||||
|
||||
func _is_shift_only_modifier():
|
||||
return _source_event.shift_pressed and \
|
||||
!(_source_event.alt_pressed or \
|
||||
_source_event.ctrl_pressed or \
|
||||
_source_event.meta_pressed) \
|
||||
and !_is_modifier(_source_event.keycode)
|
||||
|
||||
|
||||
func _has_modifier(event):
|
||||
return event.alt_pressed or event.ctrl_pressed or \
|
||||
event.meta_pressed or event.shift_pressed
|
||||
|
||||
|
||||
func _is_modifier(keycode):
|
||||
return _modifier_keys.has(keycode)
|
||||
|
||||
|
||||
func _edit_mode(should):
|
||||
_editing = 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):
|
||||
if(_has_modifier(event) and !_is_modifier(event.get_keycode_with_modifiers())):
|
||||
_source_event = event
|
||||
_key_disp = OS.get_keycode_string(event.get_keycode_with_modifiers())
|
||||
else:
|
||||
_source_event = InputEventKey.new()
|
||||
_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():
|
||||
cancel()
|
||||
|
||||
|
||||
func _on_ClearButton_pressed():
|
||||
clear_shortcut()
|
||||
|
||||
# ---------------
|
||||
# Public
|
||||
# ---------------
|
||||
func to_s():
|
||||
return OS.get_keycode_string(_source_event.get_keycode_with_modifiers())
|
||||
|
||||
|
||||
func is_valid():
|
||||
return _has_modifier(_source_event) and !_is_shift_only_modifier()
|
||||
|
||||
|
||||
func get_shortcut():
|
||||
var to_return = Shortcut.new()
|
||||
to_return.events.append(_source_event)
|
||||
return to_return
|
||||
|
||||
func get_input_event():
|
||||
return _source_event
|
||||
|
||||
func set_shortcut(sc):
|
||||
if(sc == null or sc.events == null || sc.events.size() <= 0):
|
||||
clear_shortcut()
|
||||
else:
|
||||
_source_event = sc.events[0]
|
||||
_key_disp = to_s()
|
||||
_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
|
||||
|
||||
|
||||
func cancel():
|
||||
if(_editing):
|
||||
_edit_mode(false)
|
||||
_source_event = _pre_edit_event
|
||||
_key_disp = to_s()
|
||||
_display_shortcut()
|
||||
1
addons/gut/gui/ShortcutButton.gd.uid
Normal file
1
addons/gut/gui/ShortcutButton.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://k6hvvpekp0xw
|
||||
55
addons/gut/gui/ShortcutButton.tscn
Normal file
55
addons/gut/gui/ShortcutButton.tscn
Normal file
@@ -0,0 +1,55 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://sfb1fw8j6ufu"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://k6hvvpekp0xw" path="res://addons/gut/gui/ShortcutButton.gd" id="1"]
|
||||
|
||||
[node name="ShortcutButton" type="Control"]
|
||||
custom_minimum_size = Vector2(210, 30)
|
||||
layout_mode = 3
|
||||
anchor_right = 0.123
|
||||
anchor_bottom = 0.04
|
||||
offset_right = 68.304
|
||||
offset_bottom = 6.08
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Layout" type="HBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="lblShortcut" type="Label" parent="Layout"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 7
|
||||
text = "<None>"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="Layout"]
|
||||
custom_minimum_size = Vector2(10, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SetButton" type="Button" parent="Layout"]
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
text = "Set"
|
||||
|
||||
[node name="SaveButton" type="Button" parent="Layout"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
text = "Save"
|
||||
|
||||
[node name="CancelButton" type="Button" parent="Layout"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
||||
|
||||
[node name="ClearButton" type="Button" parent="Layout"]
|
||||
custom_minimum_size = Vector2(60, 0)
|
||||
layout_mode = 2
|
||||
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"]
|
||||
120
addons/gut/gui/ShortcutDialog.gd
Normal file
120
addons/gut/gui/ShortcutDialog.gd
Normal file
@@ -0,0 +1,120 @@
|
||||
@tool
|
||||
extends ConfirmationDialog
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
var default_path = GutEditorGlobals.editor_shortcuts_path
|
||||
|
||||
|
||||
@onready var scbtn_run_all = $Scroll/Layout/CRunAll/ShortcutButton
|
||||
@onready var scbtn_run_current_script = $Scroll/Layout/CRunCurrentScript/ShortcutButton
|
||||
@onready var scbtn_run_current_inner = $Scroll/Layout/CRunCurrentInner/ShortcutButton
|
||||
@onready var scbtn_run_current_test = $Scroll/Layout/CRunCurrentTest/ShortcutButton
|
||||
@onready var scbtn_run_at_cursor = $Scroll/Layout/CRunAtCursor/ShortcutButton
|
||||
@onready var scbtn_rerun = $Scroll/Layout/CRerun/ShortcutButton
|
||||
@onready var scbtn_panel = $Scroll/Layout/CPanelButton/ShortcutButton
|
||||
@onready var scbtn_windowed = $Scroll/Layout/CToggleWindowed/ShortcutButton
|
||||
|
||||
|
||||
@onready var all_buttons = [
|
||||
scbtn_run_all, scbtn_run_current_script, scbtn_run_current_inner,
|
||||
scbtn_run_current_test, scbtn_run_at_cursor, scbtn_rerun,
|
||||
scbtn_panel, scbtn_windowed
|
||||
]
|
||||
|
||||
|
||||
func _debug_ready():
|
||||
popup_centered()
|
||||
|
||||
var btn = Button.new()
|
||||
btn.text = "show"
|
||||
get_tree().root.add_child(btn)
|
||||
btn.pressed.connect(popup)
|
||||
btn.position = Vector2(100, 100)
|
||||
btn.size = Vector2(100, 100)
|
||||
|
||||
size_changed.connect(func(): title = str(size))
|
||||
|
||||
|
||||
func _ready():
|
||||
for scbtn in all_buttons:
|
||||
scbtn.connect('start_edit', _on_edit_start.bind(scbtn))
|
||||
scbtn.connect('end_edit', _on_edit_end)
|
||||
|
||||
canceled.connect(_on_cancel)
|
||||
|
||||
# Sizing this window on different monitors, especially compared to what it
|
||||
# looks like if you just run this project is annoying. This is what I came
|
||||
# up with after getting annoyed. You probably won't be looking at this
|
||||
# very often so it's fine...until it isn't.
|
||||
size = Vector2(DisplayServer.screen_get_size()) * Vector2(.5, .8)
|
||||
|
||||
if(get_parent() == get_tree().root):
|
||||
_debug_ready.call_deferred()
|
||||
|
||||
|
||||
|
||||
func _cancel_all():
|
||||
for scbtn in all_buttons:
|
||||
scbtn.cancel()
|
||||
|
||||
|
||||
# ------------
|
||||
# Events
|
||||
# ------------
|
||||
func _on_cancel():
|
||||
_cancel_all()
|
||||
load_shortcuts()
|
||||
|
||||
|
||||
func _on_edit_start(which):
|
||||
for scbtn in all_buttons:
|
||||
if(scbtn != which):
|
||||
scbtn.disable_set(true)
|
||||
scbtn.disable_clear(true)
|
||||
|
||||
|
||||
func _on_edit_end():
|
||||
for scbtn in all_buttons:
|
||||
scbtn.disable_set(false)
|
||||
scbtn.disable_clear(false)
|
||||
|
||||
|
||||
# ------------
|
||||
# Public
|
||||
# ------------
|
||||
func save_shortcuts():
|
||||
save_shortcuts_to_file(default_path)
|
||||
|
||||
|
||||
func save_shortcuts_to_file(path):
|
||||
var f = ConfigFile.new()
|
||||
f.set_value('main', 'panel_button', scbtn_panel.get_shortcut())
|
||||
f.set_value('main', 'rerun', scbtn_rerun.get_shortcut())
|
||||
f.set_value('main', 'run_all', scbtn_run_all.get_shortcut())
|
||||
f.set_value('main', 'run_at_cursor', scbtn_run_at_cursor.get_shortcut())
|
||||
f.set_value('main', 'run_current_inner', scbtn_run_current_inner.get_shortcut())
|
||||
f.set_value('main', 'run_current_script', scbtn_run_current_script.get_shortcut())
|
||||
f.set_value('main', 'run_current_test', scbtn_run_current_test.get_shortcut())
|
||||
f.set_value('main', 'toggle_windowed', scbtn_windowed.get_shortcut())
|
||||
f.save(path)
|
||||
|
||||
|
||||
func load_shortcuts():
|
||||
load_shortcuts_from_file(default_path)
|
||||
|
||||
|
||||
func load_shortcuts_from_file(path):
|
||||
var f = ConfigFile.new()
|
||||
# as long as this shortcut is never modified, this is fine, otherwise
|
||||
# each thing should get its own default instead.
|
||||
var empty = Shortcut.new()
|
||||
|
||||
f.load(path)
|
||||
scbtn_panel.set_shortcut(f.get_value('main', 'panel_button', empty))
|
||||
scbtn_rerun.set_shortcut(f.get_value('main', 'rerun', empty))
|
||||
scbtn_run_all.set_shortcut(f.get_value('main', 'run_all', empty))
|
||||
scbtn_run_at_cursor.set_shortcut(f.get_value('main', 'run_at_cursor', empty))
|
||||
scbtn_run_current_inner.set_shortcut(f.get_value('main', 'run_current_inner', empty))
|
||||
scbtn_run_current_script.set_shortcut(f.get_value('main', 'run_current_script', empty))
|
||||
scbtn_run_current_test.set_shortcut(f.get_value('main', 'run_current_test', empty))
|
||||
scbtn_windowed.set_shortcut(f.get_value('main', 'toggle_windowed', empty))
|
||||
1
addons/gut/gui/ShortcutDialog.gd.uid
Normal file
1
addons/gut/gui/ShortcutDialog.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dc5jgemxslgvl
|
||||
207
addons/gut/gui/ShortcutDialog.tscn
Normal file
207
addons/gut/gui/ShortcutDialog.tscn
Normal file
@@ -0,0 +1,207 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://dj5ve0bq7xa5j"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dc5jgemxslgvl" path="res://addons/gut/gui/ShortcutDialog.gd" id="1_qq8qn"]
|
||||
[ext_resource type="PackedScene" uid="uid://sfb1fw8j6ufu" path="res://addons/gut/gui/ShortcutButton.tscn" id="2_i3wie"]
|
||||
|
||||
[node name="ShortcutDialog" type="ConfirmationDialog"]
|
||||
oversampling_override = 1.0
|
||||
title = "GUT Shortcuts"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(1920, 1728)
|
||||
visible = true
|
||||
script = ExtResource("1_qq8qn")
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="."]
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1912.0
|
||||
offset_bottom = 1679.0
|
||||
|
||||
[node name="Layout" type="VBoxContainer" parent="Scroll"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ShortcutDescription" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "Shortcuts for:
|
||||
- Buttons in Panel
|
||||
- Project->Tools->GUT menu items
|
||||
Shortcuts that only apply to menus are labeled."
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="TopPad" type="CenterContainer" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 5)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CPanelButton" type="HBoxContainer" parent="Scroll/Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Scroll/Layout/CPanelButton"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Show/Hide GUT"
|
||||
|
||||
[node name="ShortcutButton" parent="Scroll/Layout/CPanelButton" instance=ExtResource("2_i3wie")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShortcutDescription2" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "[i]Show/hide the gut panel or move focus to/away from the GUT window.
|
||||
[/i]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="CRunAll" type="HBoxContainer" parent="Scroll/Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Scroll/Layout/CRunAll"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run All"
|
||||
|
||||
[node name="ShortcutButton" parent="Scroll/Layout/CRunAll" instance=ExtResource("2_i3wie")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShortcutDescription3" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "[i]Run the entire test suite.[/i]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="CRunCurrentScript" type="HBoxContainer" parent="Scroll/Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Scroll/Layout/CRunCurrentScript"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run Current Script"
|
||||
|
||||
[node name="ShortcutButton" parent="Scroll/Layout/CRunCurrentScript" instance=ExtResource("2_i3wie")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShortcutDescription4" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "[i]Run all tests in the currently selected script.[/i]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="CRunCurrentInner" type="HBoxContainer" parent="Scroll/Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Scroll/Layout/CRunCurrentInner"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run Current Inner Class"
|
||||
|
||||
[node name="ShortcutButton" parent="Scroll/Layout/CRunCurrentInner" instance=ExtResource("2_i3wie")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShortcutDescription5" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "[i]Run only the currently selected inner test class if one is selected.[/i]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="CRunCurrentTest" type="HBoxContainer" parent="Scroll/Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Scroll/Layout/CRunCurrentTest"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run Current Test"
|
||||
|
||||
[node name="ShortcutButton" parent="Scroll/Layout/CRunCurrentTest" instance=ExtResource("2_i3wie")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShortcutDescription6" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "[i]Run only the currently selected test, if one is selected[/i]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="CRunAtCursor" type="HBoxContainer" parent="Scroll/Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Scroll/Layout/CRunAtCursor"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Run At Cursor (menu only)"
|
||||
|
||||
[node name="ShortcutButton" parent="Scroll/Layout/CRunAtCursor" instance=ExtResource("2_i3wie")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShortcutDescription7" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "[i]Run the most specific test/inner class/script based on where the cursor is.[/i]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="CRerun" type="HBoxContainer" parent="Scroll/Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Scroll/Layout/CRerun"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Rerun (menu only)"
|
||||
|
||||
[node name="ShortcutButton" parent="Scroll/Layout/CRerun" instance=ExtResource("2_i3wie")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShortcutDescription8" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "[i]Rerun the test(s) that were last run."
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
|
||||
[node name="CToggleWindowed" type="HBoxContainer" parent="Scroll/Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Scroll/Layout/CToggleWindowed"]
|
||||
custom_minimum_size = Vector2(50, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 7
|
||||
text = "Toggle Windowed"
|
||||
|
||||
[node name="ShortcutButton" parent="Scroll/Layout/CToggleWindowed" instance=ExtResource("2_i3wie")]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShortcutDescription9" type="RichTextLabel" parent="Scroll/Layout"]
|
||||
custom_minimum_size = Vector2(0, 20)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "[i]Toggle GUT in the bottom panel or a separate window.[/i]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
125
addons/gut/gui/about.gd
Normal file
125
addons/gut/gui/about.gd
Normal file
@@ -0,0 +1,125 @@
|
||||
@tool
|
||||
extends AcceptDialog
|
||||
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
|
||||
var _bbcode = \
|
||||
"""
|
||||
[center]GUT {gut_version}[/center]
|
||||
|
||||
[center][b]GUT Links[/b]
|
||||
{gut_link_table}[/center]
|
||||
|
||||
[center][b]VSCode Extension Links[/b]
|
||||
{vscode_link_table}[/center]
|
||||
|
||||
[center]You can support GUT development at
|
||||
{donate_link}
|
||||
|
||||
Thanks for using GUT!
|
||||
[/center]
|
||||
"""
|
||||
|
||||
var _gut_links = [
|
||||
[&"Documentation", &"https://gut.readthedocs.io"],
|
||||
[&"What's New", &"https://github.com/bitwes/Gut/releases/tag/v{gut_version}"],
|
||||
[&"Repo", &"https://github.com/bitwes/gut"],
|
||||
[&"Report Bugs", &"https://github.com/bitwes/gut/issues"]
|
||||
]
|
||||
|
||||
var _vscode_links = [
|
||||
["Repo", "https://github.com/bitwes/gut-extension"],
|
||||
["Market Place", "https://marketplace.visualstudio.com/items?itemName=bitwes.gut-extension"]
|
||||
]
|
||||
|
||||
var _donate_link = "https://buymeacoffee.com/bitwes"
|
||||
|
||||
@onready var _logo = $Logo
|
||||
|
||||
|
||||
func _ready():
|
||||
if(get_parent() is SubViewport):
|
||||
return
|
||||
|
||||
_vert_center_logo()
|
||||
$Logo.disabled = true
|
||||
$HBox/Scroll/RichTextLabel.text = _make_text()
|
||||
|
||||
|
||||
func _color_link(link_text):
|
||||
return str("[color=ROYAL_BLUE]", link_text, "[/color]")
|
||||
|
||||
|
||||
func _link_table(entries):
|
||||
var text = ''
|
||||
for entry in entries:
|
||||
text += str("[cell][right]", entry[0], "[/right][/cell]")
|
||||
var link = str("[url]", entry[1], "[/url]")
|
||||
if(entry[1].length() > 60):
|
||||
link = str("[url=", entry[1], "]", entry[1].substr(0, 50), "...[/url]")
|
||||
|
||||
text += str("[cell][left]", _color_link(link), "[/left][/cell]\n")
|
||||
return str('[table=2]', text, '[/table]')
|
||||
|
||||
|
||||
func _make_text():
|
||||
var gut_link_table = _link_table(_gut_links)
|
||||
var vscode_link_table = _link_table(_vscode_links)
|
||||
|
||||
var text = _bbcode.format({
|
||||
"gut_link_table":gut_link_table,
|
||||
"vscode_link_table":vscode_link_table,
|
||||
"donate_link":_color_link(str('[url]', _donate_link, '[/url]')),
|
||||
"gut_version":GutUtils.version_numbers.gut_version,
|
||||
})
|
||||
return text
|
||||
|
||||
|
||||
func _vert_center_logo():
|
||||
_logo.position.y = size.y / 2.0
|
||||
|
||||
|
||||
# -----------
|
||||
# Events
|
||||
# -----------
|
||||
func _on_rich_text_label_meta_clicked(meta: Variant) -> void:
|
||||
OS.shell_open(str(meta))
|
||||
|
||||
|
||||
func _on_mouse_entered() -> void:
|
||||
pass#_logo.active = true
|
||||
|
||||
|
||||
func _on_mouse_exited() -> void:
|
||||
pass#_logo.active = false
|
||||
|
||||
|
||||
var _odd_ball_eyes_l = 1.1
|
||||
var _odd_ball_eyes_r = .7
|
||||
func _on_rich_text_label_meta_hover_started(meta: Variant) -> void:
|
||||
if(meta == _gut_links[0][1]):
|
||||
_logo.set_eye_color(Color.RED)
|
||||
elif(meta.find("releases/tag/") > 0):
|
||||
_logo.set_eye_color(Color.GREEN)
|
||||
elif(meta == _gut_links[2][1]):
|
||||
_logo.set_eye_color(Color.PURPLE)
|
||||
elif(meta == _gut_links[3][1]):
|
||||
_logo.set_eye_scale(1.2)
|
||||
elif(meta == _vscode_links[0][1]):
|
||||
_logo.set_eye_scale(.5, .5)
|
||||
elif(meta == _vscode_links[1][1]):
|
||||
_logo.set_eye_scale(_odd_ball_eyes_l, _odd_ball_eyes_r)
|
||||
var temp = _odd_ball_eyes_l
|
||||
_odd_ball_eyes_l = _odd_ball_eyes_r
|
||||
_odd_ball_eyes_r = temp
|
||||
elif(meta == _donate_link):
|
||||
_logo.active = false
|
||||
|
||||
|
||||
func _on_rich_text_label_meta_hover_ended(meta: Variant) -> void:
|
||||
if(meta == _donate_link):
|
||||
_logo.active = true
|
||||
|
||||
|
||||
func _on_logo_pressed() -> void:
|
||||
_logo.disabled = !_logo.disabled
|
||||
1
addons/gut/gui/about.gd.uid
Normal file
1
addons/gut/gui/about.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://g7qu8ihdt3pd
|
||||
56
addons/gut/gui/about.tscn
Normal file
56
addons/gut/gui/about.tscn
Normal file
@@ -0,0 +1,56 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://dqbkylpsatcqm"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://g7qu8ihdt3pd" path="res://addons/gut/gui/about.gd" id="1_bg86c"]
|
||||
[ext_resource type="PackedScene" uid="uid://bjkn8mhx2fmt1" path="res://addons/gut/gui/GutLogo.tscn" id="3_kpic4"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q8rky"]
|
||||
bg_color = Color(0, 0, 0, 0.49803922)
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kpic4"]
|
||||
|
||||
[node name="About" type="AcceptDialog"]
|
||||
oversampling_override = 1.0
|
||||
title = "About GUT"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(1500, 800)
|
||||
visible = true
|
||||
min_size = Vector2i(800, 800)
|
||||
script = ExtResource("1_bg86c")
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="."]
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1492.0
|
||||
offset_bottom = 751.0
|
||||
alignment = 1
|
||||
|
||||
[node name="MakeRoomForLogo" type="CenterContainer" parent="HBox"]
|
||||
custom_minimum_size = Vector2(200, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="HBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="HBox/Scroll"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_q8rky")
|
||||
theme_override_styles/focus = SubResource("StyleBoxEmpty_kpic4")
|
||||
bbcode_enabled = true
|
||||
fit_content = true
|
||||
|
||||
[node name="Logo" parent="." instance=ExtResource("3_kpic4")]
|
||||
modulate = Color(0.74509805, 0.74509805, 0.74509805, 1)
|
||||
position = Vector2(151, 265)
|
||||
scale = Vector2(0.8, 0.8)
|
||||
active = true
|
||||
disabled = true
|
||||
|
||||
[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"]
|
||||
[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"]
|
||||
[connection signal="meta_clicked" from="HBox/Scroll/RichTextLabel" to="." method="_on_rich_text_label_meta_clicked"]
|
||||
[connection signal="meta_hover_ended" from="HBox/Scroll/RichTextLabel" to="." method="_on_rich_text_label_meta_hover_ended"]
|
||||
[connection signal="meta_hover_started" from="HBox/Scroll/RichTextLabel" to="." method="_on_rich_text_label_meta_hover_started"]
|
||||
[connection signal="pressed" from="Logo" to="." method="_on_logo_pressed"]
|
||||
BIN
addons/gut/gui/arrow.png
Normal file
BIN
addons/gut/gui/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 B |
40
addons/gut/gui/arrow.png.import
Normal file
40
addons/gut/gui/arrow.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://6wra5rxmfsrl"
|
||||
path="res://.godot/imported/arrow.png-2b5b2d838b5b3467cf300ac2da1630d9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/gui/arrow.png"
|
||||
dest_files=["res://.godot/imported/arrow.png-2b5b2d838b5b3467cf300ac2da1630d9.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
68
addons/gut/gui/editor_globals.gd
Normal file
68
addons/gut/gui/editor_globals.gd
Normal file
@@ -0,0 +1,68 @@
|
||||
@tool
|
||||
|
||||
static var GutUserPreferences = load("res://addons/gut/gui/gut_user_preferences.gd")
|
||||
static var temp_directory = 'user://gut_temp_directory'
|
||||
|
||||
static var editor_run_gut_config_path = 'gut_editor_config.json':
|
||||
# This avoids having to use path_join wherever we want to reference this
|
||||
# path. The value is not supposed to change. Could it be a constant
|
||||
# instead? Probably, but I didn't like repeating the directory part.
|
||||
# Do I like that this is a bit witty. Absolutely.
|
||||
get: return temp_directory.path_join(editor_run_gut_config_path)
|
||||
# Should this print a message or something instead? Probably, but then I'd
|
||||
# be repeating even more code than if this was just a constant. So I didn't,
|
||||
# even though I wanted to make the message a easter eggish fun message.
|
||||
# I didn't, so this dumb comment will have to serve as the easter eggish fun.
|
||||
set(v):
|
||||
print("Be sure to document your code. Never trust comments.")
|
||||
|
||||
|
||||
static var editor_run_bbcode_results_path = 'gut_editor.bbcode':
|
||||
get: return temp_directory.path_join(editor_run_bbcode_results_path)
|
||||
set(v): pass
|
||||
|
||||
|
||||
static var editor_run_json_results_path = 'gut_editor.json':
|
||||
get: return temp_directory.path_join(editor_run_json_results_path)
|
||||
set(v): pass
|
||||
|
||||
|
||||
static var editor_shortcuts_path = 'gut_editor_shortcuts.cfg' :
|
||||
get: return temp_directory.path_join(editor_shortcuts_path)
|
||||
set(v): pass
|
||||
|
||||
static var run_externally_options_path = 'gut_editor_run_externally.cfg' :
|
||||
get: return temp_directory.path_join(run_externally_options_path)
|
||||
set(v): pass
|
||||
|
||||
static var _user_prefs = null
|
||||
static var user_prefs = _user_prefs :
|
||||
# workaround not being able to reference EditorInterface when not in
|
||||
# the editor. This shouldn't be referenced by anything not in the
|
||||
# editor.
|
||||
get:
|
||||
if(_user_prefs == null and Engine.is_editor_hint()):
|
||||
# This is sometimes used when not in the editor. Avoid parser error
|
||||
# for EditorInterface.
|
||||
_user_prefs = GutUserPreferences.new(GutUtils.get_editor_interface().get_editor_settings())
|
||||
return _user_prefs
|
||||
static var gut_plugin = null
|
||||
|
||||
static func create_temp_directory():
|
||||
DirAccess.make_dir_recursive_absolute(temp_directory)
|
||||
|
||||
|
||||
static func is_being_edited_in_editor(which):
|
||||
if(!Engine.is_editor_hint()):
|
||||
return false
|
||||
|
||||
var trav = which
|
||||
var is_scene_root = false
|
||||
var editor_root = which.get_tree().edited_scene_root
|
||||
while(trav != null and !is_scene_root):
|
||||
is_scene_root = editor_root == trav
|
||||
if(!is_scene_root):
|
||||
trav = trav.get_parent()
|
||||
return is_scene_root
|
||||
|
||||
|
||||
1
addons/gut/gui/editor_globals.gd.uid
Normal file
1
addons/gut/gui/editor_globals.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cbi00ubn046c2
|
||||
282
addons/gut/gui/gut_config_gui.gd
Normal file
282
addons/gut/gui/gut_config_gui.gd
Normal file
@@ -0,0 +1,282 @@
|
||||
var PanelControls = load("res://addons/gut/gui/panel_controls.gd")
|
||||
var GutConfig = load('res://addons/gut/gut_config.gd')
|
||||
|
||||
const DIRS_TO_LIST = 6
|
||||
|
||||
# specific titles that we need to do stuff with
|
||||
var _titles = {
|
||||
dirs = null
|
||||
}
|
||||
|
||||
var _cfg_ctrls = {}
|
||||
var opt_maker = null
|
||||
|
||||
func _init(cont):
|
||||
opt_maker = GutUtils.OptionMaker.new(cont)
|
||||
_cfg_ctrls = opt_maker.controls
|
||||
# _base_container = cont
|
||||
|
||||
|
||||
func _add_save_load():
|
||||
var ctrl = PanelControls.SaveLoadControl.new('Config', '', '')
|
||||
|
||||
ctrl.save_path_chosen.connect(_on_save_path_chosen)
|
||||
ctrl.load_path_chosen.connect(_on_load_path_chosen)
|
||||
|
||||
#_cfg_ctrls['save_load'] = ctrl
|
||||
opt_maker.add_ctrl('save_load', ctrl)
|
||||
return ctrl
|
||||
|
||||
# ------------------
|
||||
# Events
|
||||
# ------------------
|
||||
func _on_save_path_chosen(path):
|
||||
save_file(path)
|
||||
|
||||
|
||||
func _on_load_path_chosen(path):
|
||||
load_file.bind(path).call_deferred()
|
||||
|
||||
# ------------------
|
||||
# Public
|
||||
# ------------------
|
||||
func get_config_issues():
|
||||
var to_return = []
|
||||
var has_directory = false
|
||||
|
||||
for i in range(DIRS_TO_LIST):
|
||||
var key = str('directory_', i)
|
||||
var path = _cfg_ctrls[key].value
|
||||
if(path != null and path != ''):
|
||||
has_directory = true
|
||||
if(!DirAccess.dir_exists_absolute(path)):
|
||||
_cfg_ctrls[key].mark_invalid(true)
|
||||
to_return.append(str('Test directory ', path, ' does not exist.'))
|
||||
else:
|
||||
_cfg_ctrls[key].mark_invalid(false)
|
||||
else:
|
||||
_cfg_ctrls[key].mark_invalid(false)
|
||||
|
||||
if(!has_directory):
|
||||
to_return.append('You do not have any directories set.')
|
||||
_titles.dirs.mark_invalid(true)
|
||||
else:
|
||||
_titles.dirs.mark_invalid(false)
|
||||
|
||||
if(!_cfg_ctrls.suffix.value.ends_with('.gd')):
|
||||
_cfg_ctrls.suffix.mark_invalid(true)
|
||||
to_return.append("Script suffix must end in '.gd'")
|
||||
else:
|
||||
_cfg_ctrls.suffix.mark_invalid(false)
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func clear():
|
||||
opt_maker.clear()
|
||||
|
||||
|
||||
func save_file(path):
|
||||
var gcfg = GutConfig.new()
|
||||
gcfg.options = get_options({})
|
||||
gcfg.save_file(path)
|
||||
|
||||
|
||||
|
||||
func load_file(path):
|
||||
var gcfg = GutConfig.new()
|
||||
gcfg.load_options(path)
|
||||
clear()
|
||||
set_options(gcfg.options)
|
||||
|
||||
|
||||
# --------------
|
||||
# SUPER dumb but VERY fun hack to hide settings. The various _add methods will
|
||||
# return what they add. If you want to hide it, just assign the result to this.
|
||||
# YES, I could have just put .visible at the end, but I didn't think of that
|
||||
# until just now, and this was fun, non-permanent and the .visible at the end
|
||||
# isn't as obvious as hide_this =
|
||||
#
|
||||
# Also, we can't just skip adding the controls because other things are looking
|
||||
# for them and things start to blow up if you don't add them.
|
||||
var hide_this = null :
|
||||
set(val):
|
||||
val.visible = false
|
||||
|
||||
# --------------
|
||||
|
||||
func set_options(opts):
|
||||
var options = opts.duplicate()
|
||||
|
||||
# _add_title('Save/Load')
|
||||
_add_save_load()
|
||||
|
||||
opt_maker.add_title("Settings")
|
||||
opt_maker.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.")
|
||||
opt_maker.add_float("wait_log_delay", options.wait_log_delay, "Wait Log Delay", 0.1, 0.0, 999.1,
|
||||
"How long to wait before displaying 'Awaiting' messages.")
|
||||
opt_maker.add_boolean('ignore_pause', options.ignore_pause, 'Ignore Pause',
|
||||
"Ignore calls to pause_before_teardown")
|
||||
opt_maker.add_boolean('hide_orphans', options.hide_orphans, 'Hide Orphans',
|
||||
'Do not display orphan counts in output.')
|
||||
opt_maker.add_boolean('should_exit', options.should_exit, 'Exit on Finish',
|
||||
"Exit when tests finished.")
|
||||
opt_maker.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.")
|
||||
opt_maker.add_select('double_strategy', 'Script Only', ['Include Native', 'Script Only'], 'Double Strategy',
|
||||
'"Include Native" will include native methods in Doubles. "Script Only" will not. ' + "\n" + \
|
||||
'The native method override warning is disabled when creating Doubles.' + "\n" + \
|
||||
'This is the default, you can override this at the script level or when creating doubles.')
|
||||
_cfg_ctrls.double_strategy.value = GutUtils.get_enum_value(
|
||||
options.double_strategy, GutUtils.DOUBLE_STRATEGY, GutUtils.DOUBLE_STRATEGY.SCRIPT_ONLY)
|
||||
|
||||
|
||||
opt_maker.add_title("Fail Error Types")
|
||||
opt_maker.add_boolean("error_tracking", !options.no_error_tracking, 'Track Errors',
|
||||
"Enable/Disable GUT's ability to detect engine and push errors.")
|
||||
opt_maker.add_boolean('engine_errors_cause_failure', options.failure_error_types.has(GutConfig.FAIL_ERROR_TYPE_ENGINE),
|
||||
'Engine', 'Any script/engine error that occurs during a test will cause the test to fail.')
|
||||
opt_maker.add_boolean('push_error_errors_cause_failure', options.failure_error_types.has(GutConfig.FAIL_ERROR_TYPE_PUSH_ERROR),
|
||||
'Push', 'Any error generated by a call to push_error that occurs during a test will cause the test to fail.')
|
||||
opt_maker.add_boolean('gut_errors_cause_failure', options.failure_error_types.has(GutConfig.FAIL_ERROR_TYPE_GUT),
|
||||
'GUT', 'Any internal GUT error that occurs while a test is running will cause it to fail..')
|
||||
|
||||
|
||||
opt_maker.add_title('Runner Appearance')
|
||||
hide_this = opt_maker.add_boolean("gut_on_top", options.gut_on_top, "On Top",
|
||||
"The GUT Runner appears above children added during tests.")
|
||||
opt_maker.add_number('opacity', options.opacity, 'Opacity', 0, 100,
|
||||
"The opacity of GUT when tests are running.")
|
||||
hide_this = opt_maker.add_boolean('should_maximize', options.should_maximize, 'Maximize',
|
||||
"Maximize GUT when tests are being run.")
|
||||
opt_maker.add_boolean('compact_mode', options.compact_mode, 'Compact Mode',
|
||||
'The runner will be in compact mode. This overrides Maximize.')
|
||||
opt_maker.add_select('font_name', options.font_name, GutUtils.avail_fonts, 'Font',
|
||||
"The font to use for text output in the Gut Runner.")
|
||||
opt_maker.add_number('font_size', options.font_size, 'Font Size', 5, 100,
|
||||
"The font size for text output in the Gut Runner.")
|
||||
hide_this = opt_maker.add_color('font_color', options.font_color, 'Font Color',
|
||||
"The font color for text output in the Gut Runner.")
|
||||
opt_maker.add_color('background_color', options.background_color, 'Background Color',
|
||||
"The background color for text output in the Gut Runner.")
|
||||
opt_maker.add_boolean('disable_colors', options.disable_colors, 'Disable Formatting',
|
||||
'Disable formatting and colors used in the Runner. Does not affect panel output.')
|
||||
|
||||
|
||||
_titles.dirs = opt_maker.add_title('Test Directories')
|
||||
opt_maker.add_boolean('include_subdirs', options.include_subdirs, 'Include Subdirs',
|
||||
"Include subdirectories of the directories configured below.")
|
||||
|
||||
var dirs_to_load = options.configured_dirs
|
||||
if(options.dirs.size() > dirs_to_load.size()):
|
||||
dirs_to_load = options.dirs
|
||||
|
||||
for i in range(DIRS_TO_LIST):
|
||||
var value = ''
|
||||
if(dirs_to_load.size() > i):
|
||||
value = dirs_to_load[i]
|
||||
|
||||
var test_dir = opt_maker.add_directory(str('directory_', i), value, str(i))
|
||||
test_dir.enabled_button.visible = true
|
||||
test_dir.enabled_button.button_pressed = options.dirs.has(value)
|
||||
|
||||
|
||||
opt_maker.add_title("XML Output")
|
||||
opt_maker.add_save_file_anywhere("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.")
|
||||
opt_maker.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.")
|
||||
|
||||
|
||||
opt_maker.add_title('Hooks')
|
||||
opt_maker.add_file('pre_run_script', options.pre_run_script, 'Pre-Run Hook',
|
||||
'This script will be run by GUT before any tests are run.')
|
||||
opt_maker.add_file('post_run_script', options.post_run_script, 'Post-Run Hook',
|
||||
'This script will be run by GUT after all tests are run.')
|
||||
|
||||
|
||||
opt_maker.add_title('Misc')
|
||||
opt_maker.add_value('prefix', options.prefix, 'Script Prefix',
|
||||
"The filename prefix for all test scripts.")
|
||||
opt_maker.add_value('suffix', options.suffix, 'Script Suffix',
|
||||
"Script suffix, including .gd extension. For example '_foo.gd'.")
|
||||
opt_maker.add_float('paint_after', options.paint_after, 'Paint After', .05, 0.0, 1.0,
|
||||
"How long GUT will wait before pausing for 1 frame to paint the screen. 0 is never.")
|
||||
|
||||
|
||||
|
||||
func get_options(base_opts):
|
||||
var to_return = base_opts.duplicate()
|
||||
|
||||
# Settings
|
||||
to_return.log_level = _cfg_ctrls.log_level.value
|
||||
to_return.wait_log_delay = _cfg_ctrls.wait_log_delay.value
|
||||
to_return.ignore_pause = _cfg_ctrls.ignore_pause.value
|
||||
to_return.hide_orphans = _cfg_ctrls.hide_orphans.value
|
||||
to_return.should_exit = _cfg_ctrls.should_exit.value
|
||||
to_return.should_exit_on_success = _cfg_ctrls.should_exit_on_success.value
|
||||
to_return.double_strategy = _cfg_ctrls.double_strategy.value
|
||||
|
||||
# Runner Appearance
|
||||
to_return.font_name = _cfg_ctrls.font_name.text
|
||||
to_return.font_size = _cfg_ctrls.font_size.value
|
||||
to_return.should_maximize = _cfg_ctrls.should_maximize.value
|
||||
to_return.compact_mode = _cfg_ctrls.compact_mode.value
|
||||
to_return.opacity = _cfg_ctrls.opacity.value
|
||||
to_return.background_color = _cfg_ctrls.background_color.value.to_html()
|
||||
to_return.font_color = _cfg_ctrls.font_color.value.to_html()
|
||||
to_return.disable_colors = _cfg_ctrls.disable_colors.value
|
||||
to_return.gut_on_top = _cfg_ctrls.gut_on_top.value
|
||||
to_return.paint_after = _cfg_ctrls.paint_after.value
|
||||
|
||||
# Fail Error Types
|
||||
to_return.no_error_tracking = !_cfg_ctrls.error_tracking
|
||||
|
||||
var fail_error_types = []
|
||||
if(_cfg_ctrls.engine_errors_cause_failure.value):
|
||||
fail_error_types.append(GutConfig.FAIL_ERROR_TYPE_ENGINE)
|
||||
if(_cfg_ctrls.push_error_errors_cause_failure.value):
|
||||
fail_error_types.append(GutConfig.FAIL_ERROR_TYPE_PUSH_ERROR)
|
||||
if(_cfg_ctrls.gut_errors_cause_failure.value):
|
||||
fail_error_types.append(GutConfig.FAIL_ERROR_TYPE_GUT)
|
||||
to_return.failure_error_types = fail_error_types
|
||||
|
||||
# Directories
|
||||
to_return.include_subdirs = _cfg_ctrls.include_subdirs.value
|
||||
var dirs = []
|
||||
var configured_dirs = []
|
||||
for i in range(DIRS_TO_LIST):
|
||||
var key = str('directory_', i)
|
||||
var ctrl = _cfg_ctrls[key]
|
||||
if(ctrl.value != '' and ctrl.value != null):
|
||||
configured_dirs.append(ctrl.value)
|
||||
if(ctrl.enabled_button.button_pressed):
|
||||
dirs.append(ctrl.value)
|
||||
to_return.dirs = dirs
|
||||
to_return.configured_dirs = configured_dirs
|
||||
|
||||
# XML Output
|
||||
to_return.junit_xml_file = _cfg_ctrls.junit_xml_file.value
|
||||
to_return.junit_xml_timestamp = _cfg_ctrls.junit_xml_timestamp.value
|
||||
|
||||
# Hooks
|
||||
to_return.pre_run_script = _cfg_ctrls.pre_run_script.value
|
||||
to_return.post_run_script = _cfg_ctrls.post_run_script.value
|
||||
|
||||
# Misc
|
||||
to_return.prefix = _cfg_ctrls.prefix.value
|
||||
to_return.suffix = _cfg_ctrls.suffix.value
|
||||
|
||||
return to_return
|
||||
|
||||
|
||||
func mark_saved():
|
||||
for key in _cfg_ctrls:
|
||||
_cfg_ctrls[key].mark_unsaved(false)
|
||||
1
addons/gut/gui/gut_config_gui.gd.uid
Normal file
1
addons/gut/gui/gut_config_gui.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://chosc1tvfaduq
|
||||
239
addons/gut/gui/gut_gui.gd
Normal file
239
addons/gut/gui/gut_gui.gd
Normal file
@@ -0,0 +1,239 @@
|
||||
extends Control
|
||||
# ##############################################################################
|
||||
# This is the decoupled GUI for gut.gd
|
||||
#
|
||||
# This is a "generic" interface between a GUI and gut.gd. It assumes there are
|
||||
# certain controls with specific names. It will then interact with those
|
||||
# controls based on signals emitted from gut.gd in order to give the user
|
||||
# feedback about the progress of the test run and the results.
|
||||
#
|
||||
# Optional controls are marked as such in the _ctrls dictionary. The names
|
||||
# of the controls can be found in _populate_ctrls.
|
||||
# ##############################################################################
|
||||
var _gut = null
|
||||
|
||||
var _ctrls = {
|
||||
btn_continue = null,
|
||||
path_dir = null,
|
||||
path_file = null,
|
||||
prog_script = null,
|
||||
prog_test = null,
|
||||
rtl = null, # optional
|
||||
rtl_bg = null, # required if rtl exists
|
||||
switch_modes = null,
|
||||
time_label = null,
|
||||
title = null,
|
||||
title_bar = null,
|
||||
tgl_word_wrap = null, # optional
|
||||
}
|
||||
|
||||
var _title_mouse = {
|
||||
down = false
|
||||
}
|
||||
|
||||
|
||||
signal switch_modes()
|
||||
|
||||
var _max_position = Vector2(100, 100)
|
||||
|
||||
func _ready():
|
||||
_populate_ctrls()
|
||||
|
||||
_ctrls.btn_continue.visible = false
|
||||
_ctrls.btn_continue.pressed.connect(_on_continue_pressed)
|
||||
_ctrls.switch_modes.pressed.connect(_on_switch_modes_pressed)
|
||||
_ctrls.title_bar.gui_input.connect(_on_title_bar_input)
|
||||
if(_ctrls.tgl_word_wrap != null):
|
||||
_ctrls.tgl_word_wrap.toggled.connect(_on_word_wrap_toggled)
|
||||
|
||||
_ctrls.prog_script.value = 0
|
||||
_ctrls.prog_test.value = 0
|
||||
_ctrls.path_dir.text = ''
|
||||
_ctrls.path_file.text = ''
|
||||
_ctrls.time_label.text = ''
|
||||
|
||||
_max_position = get_display_size() - Vector2(30, _ctrls.title_bar.size.y)
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
if(_gut != null and _gut.is_running()):
|
||||
set_elapsed_time(_gut.get_elapsed_time())
|
||||
|
||||
|
||||
# ------------------
|
||||
# Private
|
||||
# ------------------
|
||||
func get_display_size():
|
||||
return get_viewport().get_visible_rect().size
|
||||
|
||||
|
||||
func _populate_ctrls():
|
||||
# Brute force, but flexible. This allows for all the controls to exist
|
||||
# anywhere, and as long as they all have the right name, they will be
|
||||
# found.
|
||||
_ctrls.btn_continue = _get_first_child_named('Continue', self)
|
||||
_ctrls.path_dir = _get_first_child_named('Path', self)
|
||||
_ctrls.path_file = _get_first_child_named('File', self)
|
||||
_ctrls.prog_script = _get_first_child_named('ProgressScript', self)
|
||||
_ctrls.prog_test = _get_first_child_named('ProgressTest', self)
|
||||
_ctrls.rtl = _get_first_child_named('TestOutput', self)
|
||||
_ctrls.rtl_bg = _get_first_child_named('OutputBG', self)
|
||||
_ctrls.switch_modes = _get_first_child_named("SwitchModes", self)
|
||||
_ctrls.time_label = _get_first_child_named('TimeLabel', self)
|
||||
_ctrls.title = _get_first_child_named("Title", self)
|
||||
_ctrls.title_bar = _get_first_child_named("TitleBar", self)
|
||||
_ctrls.tgl_word_wrap = _get_first_child_named("WordWrap", self)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
# ------------------
|
||||
# Events
|
||||
# ------------------
|
||||
func _on_title_bar_input(event : InputEvent):
|
||||
if(event is InputEventMouseMotion):
|
||||
if(_title_mouse.down):
|
||||
position += event.relative
|
||||
position.x = clamp(position.x, 0, _max_position.x)
|
||||
position.y = clamp(position.y, 0, _max_position.y)
|
||||
elif(event is InputEventMouseButton):
|
||||
if(event.button_index == MOUSE_BUTTON_LEFT):
|
||||
_title_mouse.down = event.pressed
|
||||
|
||||
|
||||
func _on_continue_pressed():
|
||||
_gut.end_teardown_pause()
|
||||
|
||||
|
||||
func _on_gut_start_run():
|
||||
if(_ctrls.rtl != null):
|
||||
_ctrls.rtl.clear()
|
||||
set_num_scripts(_gut.get_test_collector().scripts.size())
|
||||
|
||||
|
||||
func _on_gut_end_run():
|
||||
_ctrls.prog_test.value = _ctrls.prog_test.max_value
|
||||
_ctrls.prog_script.value = _ctrls.prog_script.max_value
|
||||
|
||||
|
||||
func _on_gut_start_script(script_obj):
|
||||
next_script(script_obj.get_full_name(), script_obj.tests.size())
|
||||
|
||||
|
||||
func _on_gut_end_script():
|
||||
pass
|
||||
|
||||
|
||||
func _on_gut_start_test(test_name):
|
||||
next_test(test_name)
|
||||
|
||||
|
||||
func _on_gut_end_test():
|
||||
pass
|
||||
|
||||
|
||||
func _on_gut_start_pause():
|
||||
pause_before_teardown()
|
||||
|
||||
|
||||
func _on_gut_end_pause():
|
||||
_ctrls.btn_continue.visible = false
|
||||
|
||||
|
||||
func _on_switch_modes_pressed():
|
||||
switch_modes.emit()
|
||||
|
||||
|
||||
func _on_word_wrap_toggled(toggled):
|
||||
_ctrls.rtl.autowrap_mode = toggled
|
||||
# ------------------
|
||||
# Public
|
||||
# ------------------
|
||||
func set_num_scripts(val):
|
||||
_ctrls.prog_script.value = 0
|
||||
_ctrls.prog_script.max_value = val
|
||||
|
||||
|
||||
func next_script(path, num_tests):
|
||||
_ctrls.prog_script.value += 1
|
||||
_ctrls.prog_test.value = 0
|
||||
_ctrls.prog_test.max_value = num_tests
|
||||
|
||||
_ctrls.path_dir.text = path.get_base_dir()
|
||||
_ctrls.path_file.text = path.get_file()
|
||||
|
||||
|
||||
func next_test(__test_name):
|
||||
_ctrls.prog_test.value += 1
|
||||
|
||||
|
||||
func pause_before_teardown():
|
||||
_ctrls.btn_continue.visible = true
|
||||
|
||||
|
||||
func set_gut(g):
|
||||
if(_gut == g):
|
||||
return
|
||||
_gut = g
|
||||
g.start_run.connect(_on_gut_start_run)
|
||||
g.end_run.connect(_on_gut_end_run)
|
||||
|
||||
g.start_script.connect(_on_gut_start_script)
|
||||
g.end_script.connect(_on_gut_end_script)
|
||||
|
||||
g.start_test.connect(_on_gut_start_test)
|
||||
g.end_test.connect(_on_gut_end_test)
|
||||
|
||||
g.start_pause_before_teardown.connect(_on_gut_start_pause)
|
||||
g.end_pause_before_teardown.connect(_on_gut_end_pause)
|
||||
|
||||
func get_gut():
|
||||
return _gut
|
||||
|
||||
func get_textbox():
|
||||
return _ctrls.rtl
|
||||
|
||||
func set_elapsed_time(t):
|
||||
_ctrls.time_label.text = str("%6.1f" % t, 's')
|
||||
|
||||
|
||||
func set_bg_color(c):
|
||||
_ctrls.rtl_bg.color = c
|
||||
|
||||
|
||||
func set_title(text):
|
||||
_ctrls.title.text = text
|
||||
|
||||
|
||||
func to_top_left():
|
||||
self.position = Vector2(5, 5)
|
||||
|
||||
|
||||
func to_bottom_right():
|
||||
var win_size = get_display_size()
|
||||
self.position = win_size - Vector2(self.size) - Vector2(5, 5)
|
||||
|
||||
|
||||
func align_right():
|
||||
var win_size = get_display_size()
|
||||
self.position.x = win_size.x - self.size.x -5
|
||||
self.position.y = 5
|
||||
self.size.y = win_size.y - 10
|
||||
1
addons/gut/gui/gut_gui.gd.uid
Normal file
1
addons/gut/gui/gut_gui.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://blvhsbnsvfyow
|
||||
225
addons/gut/gui/gut_logo.gd
Normal file
225
addons/gut/gui/gut_logo.gd
Normal file
@@ -0,0 +1,225 @@
|
||||
@tool
|
||||
extends Node2D
|
||||
|
||||
class Eyeball:
|
||||
extends Node2D
|
||||
|
||||
var _should_draw_laser = false
|
||||
var _laser_end_pos = Vector2.ZERO
|
||||
var _laser_timer : Timer = null
|
||||
var _color_tween : Tween
|
||||
var _size_tween : Tween
|
||||
|
||||
var sprite : Sprite2D = null
|
||||
var default_position = Vector2(0, 0)
|
||||
var move_radius = 25
|
||||
var move_center = Vector2(0, 0)
|
||||
var default_color = Color(0.31, 0.31, 0.31)
|
||||
var _color = default_color :
|
||||
set(val):
|
||||
_color = val
|
||||
queue_redraw()
|
||||
var color = _color :
|
||||
set(val):
|
||||
_start_color_tween(_color, val)
|
||||
get(): return _color
|
||||
var default_size = 70
|
||||
var _size = default_size :
|
||||
set(val):
|
||||
_size = val
|
||||
queue_redraw()
|
||||
var size = _size :
|
||||
set(val):
|
||||
_start_size_tween(_size, val)
|
||||
get(): return _size
|
||||
|
||||
|
||||
func _init(node):
|
||||
sprite = node
|
||||
default_position = sprite.position
|
||||
move_center = sprite.position
|
||||
# hijack the original sprite, because I want to draw it here but keep
|
||||
# the original in the scene for layout.
|
||||
position = sprite.position
|
||||
sprite.get_parent().add_child(self)
|
||||
sprite.visible = false
|
||||
|
||||
|
||||
func _ready():
|
||||
_laser_timer = Timer.new()
|
||||
_laser_timer.wait_time = .1
|
||||
_laser_timer.one_shot = true
|
||||
add_child(_laser_timer)
|
||||
_laser_timer.timeout.connect(func(): _should_draw_laser = false)
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
if(_should_draw_laser):
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _start_color_tween(old_color, new_color):
|
||||
if(_color_tween != null and _color_tween.is_running()):
|
||||
_color_tween.kill()
|
||||
_color_tween = create_tween()
|
||||
_color_tween.tween_property(self, '_color', new_color, .3).from(old_color)
|
||||
_color_tween.play()
|
||||
|
||||
|
||||
func _start_size_tween(old_size, new_size):
|
||||
if(_size_tween != null and _size_tween.is_running()):
|
||||
_size_tween.kill()
|
||||
_size_tween = create_tween()
|
||||
_size_tween.tween_property(self, '_size', new_size, .3).from(old_size)
|
||||
_size_tween.play()
|
||||
|
||||
|
||||
var _laser_size = 20.0
|
||||
func _draw() -> void:
|
||||
draw_circle(Vector2.ZERO, size, color, true, -1, true)
|
||||
if(_should_draw_laser):
|
||||
var end_pos = (_laser_end_pos - global_position) * 2
|
||||
var laser_size = _laser_size * (float(size)/float(default_size))
|
||||
draw_line(Vector2.ZERO, end_pos, color, laser_size)
|
||||
draw_line(Vector2.ZERO, end_pos, Color(1, 1, 1, .5), laser_size * .8)
|
||||
|
||||
|
||||
# There's a bug in here where the eye shakes like crazy. It's a feature
|
||||
# now. Don't fix it.
|
||||
func look_at_local_position(local_pos):
|
||||
var dir = position.direction_to(local_pos)
|
||||
var dist = position.distance_to(local_pos)
|
||||
position = move_center + (dir * min(dist, move_radius))
|
||||
position.x = clamp(position.x, move_center.x - move_radius, move_center.x + move_radius)
|
||||
position.y = clamp(position.y, move_center.y - move_radius, move_center.y + move_radius)
|
||||
|
||||
|
||||
func reset():
|
||||
color = default_color
|
||||
size = default_size
|
||||
|
||||
|
||||
func eye_laser(global_pos):
|
||||
_should_draw_laser = true
|
||||
_laser_end_pos = global_pos
|
||||
_laser_timer.start()
|
||||
|
||||
|
||||
func _stop_laser():
|
||||
_should_draw_laser = false
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
var GutEditorGlobals = load('res://addons/gut/gui/editor_globals.gd')
|
||||
# Active means it's actively doing stuff. When this is not active the eyes
|
||||
# won't follow, but you can still make the sizes change by calling methods on
|
||||
# this.
|
||||
@export var active = false :
|
||||
set(val):
|
||||
active = val
|
||||
if(!active and is_inside_tree()):
|
||||
left_eye.position = left_eye.default_position
|
||||
right_eye.position = right_eye.default_position
|
||||
# When disabled, this will reset to default and you can't make it do anything.
|
||||
@export var disabled = false :
|
||||
set(val):
|
||||
disabled = val
|
||||
if(disabled and is_inside_tree()):
|
||||
left_eye.position = left_eye.default_position
|
||||
right_eye.position = right_eye.default_position
|
||||
left_eye.reset()
|
||||
right_eye.reset()
|
||||
modulate = Color.GRAY
|
||||
$BaseLogo.texture = _no_shine
|
||||
else:
|
||||
$BaseLogo.texture = _normal
|
||||
modulate = Color.WHITE
|
||||
|
||||
@onready var _reset_timer = $ResetTimer
|
||||
@onready var _face_button = $FaceButton
|
||||
@onready var left_eye : Eyeball = Eyeball.new($BaseLogo/LeftEye)
|
||||
@onready var right_eye : Eyeball = Eyeball.new($BaseLogo/RightEye)
|
||||
|
||||
var _no_shine = load("res://addons/gut/images/GutIconV2_no_shine.png")
|
||||
var _normal = load("res://addons/gut/images/GutIconV2_base.png")
|
||||
var _is_in_edited_scene = false
|
||||
|
||||
signal pressed
|
||||
|
||||
func _debug_ready():
|
||||
position = Vector2(500, 500)
|
||||
active = true
|
||||
|
||||
|
||||
func _ready():
|
||||
_is_in_edited_scene = GutEditorGlobals.is_being_edited_in_editor(self)
|
||||
|
||||
if(get_parent() == get_tree().root):
|
||||
_debug_ready()
|
||||
|
||||
disabled = disabled
|
||||
active = active
|
||||
left_eye.move_center.x -= 20
|
||||
right_eye.move_center.x += 10
|
||||
_face_button.modulate.a = 0.0
|
||||
|
||||
|
||||
func _process(_delta):
|
||||
if(active and !disabled and !_is_in_edited_scene):
|
||||
left_eye.look_at_local_position(get_local_mouse_position())
|
||||
right_eye.look_at_local_position(get_local_mouse_position())
|
||||
|
||||
|
||||
# ----------------
|
||||
# Events
|
||||
# ----------------
|
||||
func _on_reset_timer_timeout() -> void:
|
||||
left_eye.reset()
|
||||
right_eye.reset()
|
||||
|
||||
|
||||
func _on_face_button_pressed() -> void:
|
||||
pressed.emit()
|
||||
|
||||
|
||||
# ----------------
|
||||
# Public
|
||||
# ----------------
|
||||
func set_eye_scale(left, right=left):
|
||||
if(disabled or _is_in_edited_scene):
|
||||
return
|
||||
left_eye.size = left_eye.default_size * left
|
||||
right_eye.size = right_eye.default_size * right
|
||||
_reset_timer.start()
|
||||
|
||||
|
||||
func reset_eye_size():
|
||||
if(disabled or _is_in_edited_scene):
|
||||
return
|
||||
left_eye.size = left_eye.default_size
|
||||
right_eye.size = right_eye.default_size
|
||||
|
||||
|
||||
func set_eye_color(left, right=left):
|
||||
if(disabled or _is_in_edited_scene):
|
||||
return
|
||||
left_eye.color = left
|
||||
right_eye.color = right
|
||||
_reset_timer.start()
|
||||
|
||||
|
||||
func reset_eye_color():
|
||||
if(disabled or _is_in_edited_scene):
|
||||
return
|
||||
left_eye.color = left_eye.default_color
|
||||
right_eye.color = right_eye.default_color
|
||||
|
||||
|
||||
# I removed the eye lasers because they aren't ready yet. I've already spent
|
||||
# too much time on this logo. It's great, I love it...but it's been long
|
||||
# enough. This gives me, or someone else, something to do later.
|
||||
#func eye_lasers(global_pos):
|
||||
#left_eye.eye_laser(global_pos)
|
||||
#right_eye.eye_laser(global_pos)
|
||||
1
addons/gut/gui/gut_logo.gd.uid
Normal file
1
addons/gut/gui/gut_logo.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b8lvgepb64m8t
|
||||
79
addons/gut/gui/gut_user_preferences.gd
Normal file
79
addons/gut/gui/gut_user_preferences.gd
Normal file
@@ -0,0 +1,79 @@
|
||||
class GutEditorPref:
|
||||
var gut_pref_prefix = 'gut/'
|
||||
var pname = '__not_set__'
|
||||
var default = null
|
||||
var value = '__not_set__'
|
||||
var _settings = null
|
||||
|
||||
func _init(n, d, s):
|
||||
pname = n
|
||||
default = d
|
||||
_settings = s
|
||||
load_it()
|
||||
|
||||
func _prefstr():
|
||||
var to_return = str(gut_pref_prefix, pname)
|
||||
return to_return
|
||||
|
||||
func save_it():
|
||||
_settings.set_setting(_prefstr(), value)
|
||||
|
||||
func load_it():
|
||||
if(_settings.has_setting(_prefstr())):
|
||||
value = _settings.get_setting(_prefstr())
|
||||
else:
|
||||
value = default
|
||||
|
||||
func erase():
|
||||
_settings.erase(_prefstr())
|
||||
|
||||
|
||||
const EMPTY = '-- NOT_SET --'
|
||||
|
||||
# -- Editor ONLY Settings --
|
||||
var output_font_name = null
|
||||
var output_font_size = null
|
||||
var hide_result_tree = null
|
||||
var hide_output_text = null
|
||||
var hide_settings = null
|
||||
var use_colors = null # ? might be output panel
|
||||
var run_externally = null
|
||||
var run_externally_options_dialog_size = null
|
||||
var shortcuts_dialog_size = null
|
||||
var gut_window_size = null
|
||||
var gut_window_on_top = null
|
||||
|
||||
|
||||
func _init(editor_settings):
|
||||
output_font_name = GutEditorPref.new('output_font_name', 'CourierPrime', editor_settings)
|
||||
output_font_size = GutEditorPref.new('output_font_size', 30, editor_settings)
|
||||
hide_result_tree = GutEditorPref.new('hide_result_tree', false, editor_settings)
|
||||
hide_output_text = GutEditorPref.new('hide_output_text', false, editor_settings)
|
||||
hide_settings = GutEditorPref.new('hide_settings', false, editor_settings)
|
||||
use_colors = GutEditorPref.new('use_colors', true, editor_settings)
|
||||
run_externally = GutEditorPref.new('run_externally', false, editor_settings)
|
||||
run_externally_options_dialog_size = GutEditorPref.new('run_externally_options_dialog_size', Vector2i(-1, -1), editor_settings)
|
||||
shortcuts_dialog_size = GutEditorPref.new('shortcuts_dialog_size', Vector2i(-1, -1), editor_settings)
|
||||
gut_window_size = GutEditorPref.new('editor_window_size', Vector2i(-1, -1), editor_settings)
|
||||
gut_window_on_top = GutEditorPref.new('editor_window_on_top', false, editor_settings)
|
||||
|
||||
|
||||
func save_it():
|
||||
for prop in get_property_list():
|
||||
var val = get(prop.name)
|
||||
if(val is GutEditorPref):
|
||||
val.save_it()
|
||||
|
||||
|
||||
func load_it():
|
||||
for prop in get_property_list():
|
||||
var val = get(prop.name)
|
||||
if(val is GutEditorPref):
|
||||
val.load_it()
|
||||
|
||||
|
||||
func erase_all():
|
||||
for prop in get_property_list():
|
||||
var val = get(prop.name)
|
||||
if(val is GutEditorPref):
|
||||
val.erase()
|
||||
1
addons/gut/gui/gut_user_preferences.gd.uid
Normal file
1
addons/gut/gui/gut_user_preferences.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dsndkn6whyiov
|
||||
124
addons/gut/gui/option_maker.gd
Normal file
124
addons/gut/gui/option_maker.gd
Normal file
@@ -0,0 +1,124 @@
|
||||
var PanelControls = load("res://addons/gut/gui/panel_controls.gd")
|
||||
|
||||
# All titles so we can free them when we want.
|
||||
var _all_titles = []
|
||||
|
||||
|
||||
var base_container = null
|
||||
# All the various PanelControls indexed by thier keys.
|
||||
var controls = {}
|
||||
|
||||
|
||||
func _init(cont):
|
||||
base_container = cont
|
||||
|
||||
|
||||
func add_title(text):
|
||||
var row = PanelControls.BaseGutPanelControl.new(text, text)
|
||||
base_container.add_child(row)
|
||||
row.connect('draw', _on_title_cell_draw.bind(row))
|
||||
_all_titles.append(row)
|
||||
return row
|
||||
|
||||
|
||||
func add_ctrl(key, ctrl):
|
||||
controls[key] = ctrl
|
||||
base_container.add_child(ctrl)
|
||||
|
||||
|
||||
func add_number(key, value, disp_text, v_min, v_max, hint=''):
|
||||
var ctrl = PanelControls.NumberControl.new(disp_text, value, v_min, v_max, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func add_float(key, value, disp_text, step, v_min, v_max, hint=''):
|
||||
var ctrl = PanelControls.FloatControl.new(disp_text, value, step, v_min, v_max, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func add_select(key, value, values, disp_text, hint=''):
|
||||
var ctrl = PanelControls.SelectControl.new(disp_text, value, values, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func add_value(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.StringControl.new(disp_text, value, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
func add_multiline_text(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.MultiLineStringControl.new(disp_text, value, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
func add_boolean(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.BooleanControl.new(disp_text, value, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
func add_directory(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.DirectoryControl.new(disp_text, value, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
ctrl.dialog.title = disp_text
|
||||
return ctrl
|
||||
|
||||
|
||||
func add_file(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.DirectoryControl.new(disp_text, value, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
ctrl.dialog.file_mode = ctrl.dialog.FILE_MODE_OPEN_FILE
|
||||
ctrl.dialog.title = disp_text
|
||||
return ctrl
|
||||
|
||||
|
||||
func add_save_file_anywhere(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.DirectoryControl.new(disp_text, value, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
ctrl.dialog.file_mode = ctrl.dialog.FILE_MODE_SAVE_FILE
|
||||
ctrl.dialog.access = ctrl.dialog.ACCESS_FILESYSTEM
|
||||
ctrl.dialog.title = disp_text
|
||||
return ctrl
|
||||
|
||||
|
||||
func add_color(key, value, disp_text, hint=''):
|
||||
var ctrl = PanelControls.ColorControl.new(disp_text, value, hint)
|
||||
add_ctrl(key, ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
var _blurbs = 0
|
||||
func add_blurb(text):
|
||||
var ctrl = RichTextLabel.new()
|
||||
ctrl.fit_content = true
|
||||
ctrl.bbcode_enabled = true
|
||||
ctrl.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
ctrl.text = text
|
||||
add_ctrl(str("blurb_", _blurbs), ctrl)
|
||||
return ctrl
|
||||
|
||||
|
||||
# ------------------
|
||||
# Events
|
||||
# ------------------
|
||||
func _on_title_cell_draw(which):
|
||||
which.draw_rect(Rect2(Vector2(0, 0), which.size), Color(0, 0, 0, .15))
|
||||
|
||||
|
||||
# ------------------
|
||||
# Public
|
||||
# ------------------
|
||||
|
||||
func clear():
|
||||
for key in controls:
|
||||
controls[key].free()
|
||||
|
||||
controls.clear()
|
||||
|
||||
for entry in _all_titles:
|
||||
entry.free()
|
||||
|
||||
_all_titles.clear()
|
||||
1
addons/gut/gui/option_maker.gd.uid
Normal file
1
addons/gut/gui/option_maker.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bjahqsqo645sf
|
||||
484
addons/gut/gui/panel_controls.gd
Normal file
484
addons/gut/gui/panel_controls.gd
Normal file
@@ -0,0 +1,484 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class BaseGutPanelControl:
|
||||
extends HBoxContainer
|
||||
var label = Label.new()
|
||||
var _lbl_unsaved = Label.new()
|
||||
var _lbl_invalid = Label.new()
|
||||
|
||||
var value = null:
|
||||
get: return get_value()
|
||||
set(val): set_value(val)
|
||||
|
||||
signal changed
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
mouse_filter = MOUSE_FILTER_PASS
|
||||
|
||||
label.size_flags_horizontal = label.SIZE_EXPAND_FILL
|
||||
label.mouse_filter = label.MOUSE_FILTER_STOP
|
||||
add_child(label)
|
||||
|
||||
_lbl_unsaved.text = '*'
|
||||
_lbl_unsaved.visible = false
|
||||
add_child(_lbl_unsaved)
|
||||
|
||||
_lbl_invalid.text = '!'
|
||||
_lbl_invalid.visible = false
|
||||
add_child(_lbl_invalid)
|
||||
|
||||
label.text = title
|
||||
label.tooltip_text = hint
|
||||
|
||||
|
||||
func mark_unsaved(is_it=true):
|
||||
_lbl_unsaved.visible = is_it
|
||||
|
||||
|
||||
func mark_invalid(is_it):
|
||||
_lbl_invalid.visible = is_it
|
||||
|
||||
# -- Virtual --
|
||||
#
|
||||
# value_ctrl (all should declare the value_ctrl)
|
||||
#
|
||||
func set_value(value):
|
||||
pass
|
||||
|
||||
func get_value():
|
||||
pass
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class NumberControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = SpinBox.new()
|
||||
|
||||
func _init(title, val, v_min, v_max, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
value_ctrl.value = val
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.min_value = v_min
|
||||
value_ctrl.max_value = v_max
|
||||
value_ctrl.value_changed.connect(_on_value_changed)
|
||||
value_ctrl.select_all_on_focus = true
|
||||
add_child(value_ctrl)
|
||||
|
||||
func _on_value_changed(new_value):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.value
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.value = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class FloatControl:
|
||||
extends NumberControl
|
||||
|
||||
func _init(title, val, step, v_min, v_max, hint=""):
|
||||
super._init(title, val, v_min, v_max, hint)
|
||||
value_ctrl.step = step
|
||||
value_ctrl.value = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class StringControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = LineEdit.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.text = val
|
||||
value_ctrl.text_changed.connect(_on_text_changed)
|
||||
value_ctrl.select_all_on_focus = true
|
||||
add_child(value_ctrl)
|
||||
if(title == ''):
|
||||
label.visible = false
|
||||
|
||||
func _on_text_changed(new_value):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.text
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.text = val
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class MultiLineStringControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = TextEdit.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
add_child(vbox)
|
||||
label.reparent(vbox)
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.text = val
|
||||
value_ctrl.text_changed.connect(_on_text_changed)
|
||||
value_ctrl.scroll_fit_content_height = true
|
||||
vbox.add_child(value_ctrl)
|
||||
|
||||
func _on_text_changed(new_value):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.text
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.text = val
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class BooleanControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = CheckBox.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
value_ctrl.button_pressed = val
|
||||
value_ctrl.toggled.connect(_on_button_toggled)
|
||||
add_child(value_ctrl)
|
||||
|
||||
func _on_button_toggled(new_value):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.button_pressed
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.button_pressed = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# value is "selected" and is gettable and settable
|
||||
# text is the text value of the selected item, it is gettable only
|
||||
# ------------------------------------------------------------------------------
|
||||
class SelectControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = OptionButton.new()
|
||||
|
||||
var text = '' :
|
||||
get: return value_ctrl.get_item_text(value_ctrl.selected)
|
||||
set(val): pass
|
||||
|
||||
func _init(title, val, choices, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
var select_idx = 0
|
||||
for i in range(choices.size()):
|
||||
value_ctrl.add_item(choices[i])
|
||||
if(val == choices[i]):
|
||||
select_idx = i
|
||||
value_ctrl.selected = select_idx
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.item_selected.connect(_on_item_selected)
|
||||
add_child(value_ctrl)
|
||||
|
||||
func _on_item_selected(idx):
|
||||
changed.emit()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.selected
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.selected = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class ColorControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl = ColorPickerButton.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.color = val
|
||||
add_child(value_ctrl)
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.color
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.color = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class DirectoryControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var value_ctrl := LineEdit.new()
|
||||
var dialog := FileDialog.new()
|
||||
var enabled_button = CheckButton.new()
|
||||
|
||||
var _btn_dir := Button.new()
|
||||
|
||||
func _init(title, val, hint=""):
|
||||
super._init(title, val, hint)
|
||||
|
||||
label.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
|
||||
_btn_dir.text = '...'
|
||||
_btn_dir.pressed.connect(_on_dir_button_pressed)
|
||||
|
||||
value_ctrl.text = val
|
||||
value_ctrl.size_flags_horizontal = value_ctrl.SIZE_EXPAND_FILL
|
||||
value_ctrl.select_all_on_focus = true
|
||||
value_ctrl.text_changed.connect(_on_value_changed)
|
||||
|
||||
dialog.file_mode = dialog.FILE_MODE_OPEN_DIR
|
||||
dialog.unresizable = false
|
||||
dialog.dir_selected.connect(_on_selected)
|
||||
dialog.file_selected.connect(_on_selected)
|
||||
|
||||
enabled_button.button_pressed = true
|
||||
enabled_button.visible = false
|
||||
|
||||
add_child(enabled_button)
|
||||
add_child(value_ctrl)
|
||||
add_child(_btn_dir)
|
||||
add_child(dialog)
|
||||
|
||||
func _update_display():
|
||||
var is_empty = value_ctrl.text == ''
|
||||
enabled_button.button_pressed = !is_empty
|
||||
enabled_button.disabled = is_empty
|
||||
|
||||
|
||||
func _ready():
|
||||
if(Engine.is_editor_hint()):
|
||||
dialog.size = Vector2(1000, 700)
|
||||
else:
|
||||
dialog.size = Vector2(500, 350)
|
||||
_update_display()
|
||||
|
||||
func _on_value_changed(new_text):
|
||||
_update_display()
|
||||
|
||||
func _on_selected(path):
|
||||
value_ctrl.text = path
|
||||
_update_display()
|
||||
|
||||
func _on_dir_button_pressed():
|
||||
dialog.current_dir = value_ctrl.text
|
||||
dialog.popup_centered()
|
||||
|
||||
func get_value():
|
||||
return value_ctrl.text
|
||||
|
||||
func set_value(val):
|
||||
value_ctrl.text = val
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Features:
|
||||
# Buttons to pick res://, user://, or anywhere on the OS.
|
||||
# ------------------------------------------------------------------------------
|
||||
class FileDialogSuperPlus:
|
||||
extends FileDialog
|
||||
|
||||
var show_diretory_types = true :
|
||||
set(val) :
|
||||
show_diretory_types = val
|
||||
_update_display()
|
||||
|
||||
var show_res = true :
|
||||
set(val) :
|
||||
show_res = val
|
||||
_update_display()
|
||||
|
||||
var show_user = true :
|
||||
set(val) :
|
||||
show_user = val
|
||||
_update_display()
|
||||
|
||||
var show_os = true :
|
||||
set(val) :
|
||||
show_os = val
|
||||
_update_display()
|
||||
|
||||
var _dir_type_hbox = null
|
||||
var _btn_res = null
|
||||
var _btn_user = null
|
||||
var _btn_os = null
|
||||
|
||||
func _ready():
|
||||
_init_controls()
|
||||
_update_display()
|
||||
|
||||
|
||||
func _init_controls():
|
||||
_dir_type_hbox = HBoxContainer.new()
|
||||
|
||||
_btn_res = Button.new()
|
||||
_btn_user = Button.new()
|
||||
_btn_os = Button.new()
|
||||
var spacer1 = CenterContainer.new()
|
||||
spacer1.size_flags_horizontal = spacer1.SIZE_EXPAND_FILL
|
||||
var spacer2 = spacer1.duplicate()
|
||||
|
||||
_dir_type_hbox.add_child(spacer1)
|
||||
_dir_type_hbox.add_child(_btn_res)
|
||||
_dir_type_hbox.add_child(_btn_user)
|
||||
_dir_type_hbox.add_child(_btn_os)
|
||||
_dir_type_hbox.add_child(spacer2)
|
||||
|
||||
_btn_res.text = 'res://'
|
||||
_btn_user.text = 'user://'
|
||||
_btn_os.text = ' OS '
|
||||
|
||||
get_vbox().add_child(_dir_type_hbox)
|
||||
get_vbox().move_child(_dir_type_hbox, 0)
|
||||
|
||||
_btn_res.pressed.connect(func(): access = ACCESS_RESOURCES)
|
||||
_btn_user.pressed.connect(func(): access = ACCESS_USERDATA)
|
||||
_btn_os.pressed.connect(func(): access = ACCESS_FILESYSTEM)
|
||||
|
||||
|
||||
func _update_display():
|
||||
if(is_inside_tree()):
|
||||
_dir_type_hbox.visible = show_diretory_types
|
||||
_btn_res.visible = show_res
|
||||
_btn_user.visible = show_user
|
||||
_btn_os.visible = show_os
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
class SaveLoadControl:
|
||||
extends BaseGutPanelControl
|
||||
|
||||
var btn_load = Button.new()
|
||||
var btn_save = Button.new()
|
||||
|
||||
var dlg_load := FileDialogSuperPlus.new()
|
||||
var dlg_save := FileDialogSuperPlus.new()
|
||||
|
||||
signal save_path_chosen(path)
|
||||
signal load_path_chosen(path)
|
||||
|
||||
func _init(title, val, hint):
|
||||
super._init(title, val, hint)
|
||||
|
||||
btn_load.text = "Load"
|
||||
btn_load.custom_minimum_size.x = 100
|
||||
btn_load.pressed.connect(_on_load_pressed)
|
||||
add_child(btn_load)
|
||||
|
||||
btn_save.text = "Save As"
|
||||
btn_save.custom_minimum_size.x = 100
|
||||
btn_save.pressed.connect(_on_save_pressed)
|
||||
add_child(btn_save)
|
||||
|
||||
dlg_load.file_mode = dlg_load.FILE_MODE_OPEN_FILE
|
||||
dlg_load.unresizable = false
|
||||
dlg_load.dir_selected.connect(_on_load_selected)
|
||||
dlg_load.file_selected.connect(_on_load_selected)
|
||||
add_child(dlg_load)
|
||||
|
||||
dlg_save.file_mode = dlg_save.FILE_MODE_SAVE_FILE
|
||||
dlg_save.unresizable = false
|
||||
dlg_save.dir_selected.connect(_on_save_selected)
|
||||
dlg_save.file_selected.connect(_on_save_selected)
|
||||
add_child(dlg_save)
|
||||
|
||||
|
||||
func _ready():
|
||||
if(Engine.is_editor_hint()):
|
||||
dlg_load.size = Vector2(1000, 700)
|
||||
dlg_save.size = Vector2(1000, 700)
|
||||
else:
|
||||
dlg_load.size = Vector2(500, 350)
|
||||
dlg_save.size = Vector2(500, 350)
|
||||
|
||||
func _on_load_selected(path):
|
||||
load_path_chosen.emit(path)
|
||||
|
||||
func _on_save_selected(path):
|
||||
save_path_chosen.emit(path)
|
||||
|
||||
func _on_load_pressed():
|
||||
dlg_load.popup_centered()
|
||||
|
||||
func _on_save_pressed():
|
||||
dlg_save.popup_centered()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# This one was never used in gut_config_gui...but I put some work into it and
|
||||
# I'm a sucker for that kinda thing. Delete this when you get tired of looking
|
||||
# at it.
|
||||
# ------------------------------------------------------------------------------
|
||||
# class Vector2Ctrl:
|
||||
# extends VBoxContainer
|
||||
|
||||
# var value = Vector2(-1, -1) :
|
||||
# get:
|
||||
# return get_value()
|
||||
# set(val):
|
||||
# set_value(val)
|
||||
# var disabled = false :
|
||||
# get:
|
||||
# return get_disabled()
|
||||
# set(val):
|
||||
# set_disabled(val)
|
||||
# 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
|
||||
1
addons/gut/gui/panel_controls.gd.uid
Normal file
1
addons/gut/gui/panel_controls.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://db54jy04d8w7p
|
||||
BIN
addons/gut/gui/play.png
Normal file
BIN
addons/gut/gui/play.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 143 B |
40
addons/gut/gui/play.png.import
Normal file
40
addons/gut/gui/play.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cr6tvdv0ve6cv"
|
||||
path="res://.godot/imported/play.png-5c90e88e8136487a183a099d67a7de24.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/gut/gui/play.png"
|
||||
dest_files=["res://.godot/imported/play.png-5c90e88e8136487a183a099d67a7de24.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
19
addons/gut/gui/run_from_editor.gd
Normal file
19
addons/gut/gui/run_from_editor.gd
Normal file
@@ -0,0 +1,19 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# This is the entry point when running tests from the editor.
|
||||
#
|
||||
# This script should conform to, or ignore, the strictest warning settings.
|
||||
# ------------------------------------------------------------------------------
|
||||
extends Node2D
|
||||
|
||||
var GutLoader : Object
|
||||
|
||||
func _init() -> void:
|
||||
GutLoader = load("res://addons/gut/gut_loader.gd")
|
||||
|
||||
|
||||
@warning_ignore("unsafe_method_access")
|
||||
func _ready() -> void:
|
||||
var runner : Node = load("res://addons/gut/gui/GutRunner.tscn").instantiate()
|
||||
add_child(runner)
|
||||
runner.run_from_editor()
|
||||
GutLoader.restore_ignore_addons()
|
||||
1
addons/gut/gui/run_from_editor.gd.uid
Normal file
1
addons/gut/gui/run_from_editor.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bwf2iuidqfkpl
|
||||
6
addons/gut/gui/run_from_editor.tscn
Normal file
6
addons/gut/gui/run_from_editor.tscn
Normal file
@@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bgj3fm5d8yvjw"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bwf2iuidqfkpl" path="res://addons/gut/gui/run_from_editor.gd" id="1_53pap"]
|
||||
|
||||
[node name="RunFromEditor" type="Node2D"]
|
||||
script = ExtResource("1_53pap")
|
||||
Reference in New Issue
Block a user