This commit is contained in:
Gerard Gascón 2025-04-24 17:23:34 +02:00
commit b99855351d
434 changed files with 50357 additions and 0 deletions

View file

@ -0,0 +1,306 @@
@tool
# ############################################################################ #
# Copyright © 2019-2022 Frédéric Maquin <fred@ephread.com>
# Licensed under the MIT License.
# See LICENSE in the project root for license information.
# ############################################################################ #
extends Control
# Hiding this type to prevent registration of "private" nodes.
# See https://github.com/godotengine/godot-proposals/issues/1047
# class_name InkConfigurationPanel
# ############################################################################ #
# Imports
# ############################################################################ #
var InkExecutionConfiguration = load("res://addons/inkgd/editor/common/executors/structures/ink_execution_configuration.gd")
var InkConfigurationTester = load("res://addons/inkgd/editor/common/executors/ink_configuration_tester.gd")
var InkCSharpValidator = preload("res://addons/inkgd/editor/common/ink_csharp_validator.gd")
var InkRichDialog = load("res://addons/inkgd/editor/panel/common/ink_rich_dialog.tscn")
# ############################################################################ #
# Enums
# ############################################################################ #
## Represents which configuration setting triggered the file dialog.
enum FileDialogSelection {
UNKNOWN,
MONO_EXECUTABLE,
INKLECATE_EXECUTABLE
}
# ############################################################################ #
# Constants
# ############################################################################ #
const BOM = "\ufeff"
# ############################################################################ #
# Properties
# ############################################################################ #
var editor_interface: InkEditorInterface = null
var configuration: InkConfiguration = null
# ############################################################################ #
# Private Properties
# ############################################################################ #
var _file_dialog = EditorFileDialog.new()
## Configuration item for which the FileDialog is currently shown.
##
## Unknown by default.
var _file_dialog_selection = FileDialogSelection.UNKNOWN
# ############################################################################ #
# Nodes
# ############################################################################ #
@onready var _test_button = find_child("TestButton")
@onready var _use_mono_label = find_child("UseMonoLabel")
@onready var _use_mono_checkbox = find_child("UseMonoCheckBox")
@onready var _mono_label = find_child("MonoLabel")
@onready var _mono_container = find_child("MonoH")
@onready var _mono_line_edit = find_child("MonoLineEdit")
@onready var _mono_dialog_button = find_child("MonoDialogButton")
@onready var _executable_line_edit = find_child("ExecutableLineEdit")
@onready var _executable_dialog_button = find_child("ExecutableDialogButton")
@onready var _recompilation_mode_button = find_child("RecompilationModeOptionButton")
@onready var _mono_support_container = find_child("MonoSupportV")
@onready var _mono_support_documentation_button = find_child("DocumentationButton")
@onready var _mono_support_presence_label = _mono_support_container.find_child("PresenceLabel")
@onready var _mono_support_refresh_button = _mono_support_container.find_child("RefreshButton")
# ############################################################################ #
# Overrides
# ############################################################################ #
func _ready():
# FIXME: This needs investigating.
# Sanity check. It seems the editor instantiates tools script on their
# own, probably to add them to its tree. In that case, they won't have
# their dependencies injected, so we're not doing anything.
if editor_interface == null || configuration == null:
print("[inkgd] [INFO] Ink Configuration Tab: dependencies not met, ignoring.")
return
_set_button_icons()
_apply_configuration()
_connect_signals()
_check_runtime_presence()
_mono_support_container.visible = _can_run_mono()
add_child(_file_dialog)
# ############################################################################ #
# Signal Receivers
# ############################################################################ #
func _configuration_entered(_new_text: String):
_configuration_focus_exited()
func _configuration_focus_exited():
configuration.mono_path = _mono_line_edit.text
configuration.inklecate_path = _executable_line_edit.text
configuration.persist()
func _use_mono_toggled(_toggled: bool):
configuration.use_mono = !configuration.use_mono
configuration.persist()
_update_mono_availability(false)
func _mono_button_pressed():
_reset_file_dialog()
_file_dialog_selection = FileDialogSelection.MONO_EXECUTABLE
_file_dialog.current_path = configuration.mono_path
_file_dialog.current_dir = configuration.mono_path.get_base_dir()
_file_dialog.current_file = configuration.mono_path.get_file()
_file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
_file_dialog.popup_centered(Vector2(1280, 800) * editor_interface.scale)
func _executable_button_pressed():
_reset_file_dialog()
_file_dialog_selection = FileDialogSelection.INKLECATE_EXECUTABLE
_file_dialog.current_file = configuration.inklecate_path
_file_dialog.current_dir = configuration.inklecate_path.get_base_dir()
_file_dialog.current_file = configuration.inklecate_path.get_file()
_file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
_file_dialog.popup_centered(Vector2(1280, 800) * editor_interface.scale)
func _recompilation_mode_button_selected(index):
configuration.compilation_mode = index
configuration.persist()
func _test_button_pressed():
var test_configuration = InkExecutionConfiguration.new(configuration, false, true)
var tester = InkConfigurationTester.new(test_configuration)
var result = tester.test_availability()
# NOTE: At the moment, inklecate doesn't support a subcommand that would just
# exit with 0 so `_contains_inklecate_output_prefix` will always be executed.
if result.success:
var dialog = AcceptDialog.new()
add_child(dialog)
dialog.title = "Success"
dialog.dialog_text = "The configuration seems to be valid!"
dialog.popup_centered()
else:
var dialog = InkRichDialog.instantiate()
add_child(dialog)
dialog.title = "Error"
dialog.message_text = "Something went wrong while testing inklecate's setup. Please see the output below."
dialog.output_text = result.output
dialog.update_layout(editor_interface.scale)
dialog.popup_centered(Vector2(700, 400) * editor_interface.scale)
func _on_file_selected(path: String):
match _file_dialog_selection:
FileDialogSelection.MONO_EXECUTABLE:
configuration.mono_path = ProjectSettings.globalize_path(path)
_update_save_and_cleanup(configuration.mono_path, _mono_line_edit)
FileDialogSelection.INKLECATE_EXECUTABLE:
configuration.inklecate_path = ProjectSettings.globalize_path(path)
_update_save_and_cleanup(configuration.inklecate_path, _executable_line_edit)
_:
printerr("[inkgd] [ERROR] Unknown FileDialogSelection, failed to save FileDialog file.")
_file_dialog_selection = FileDialogSelection.UNKNOWN
func _check_runtime_presence():
var ink_engine_runtime = InkCSharpValidator.new().get_runtime_path()
var is_present = !ink_engine_runtime.is_empty()
if is_present:
_mono_support_presence_label.add_theme_color_override("font_color", Color.GREEN)
_mono_support_presence_label.text = "PRESENT"
else:
_mono_support_presence_label.add_theme_color_override("font_color", Color.RED)
_mono_support_presence_label.text = "MISSING"
func _mono_support_documentation_pressed():
OS.shell_open("https://inkgd.readthedocs.io/en/latest/advanced/migrating_to_godot_mono.html")
# ############################################################################ #
# Private helpers
# ############################################################################ #
func _reset_file_dialog():
_file_dialog.current_file = ""
_file_dialog.clear_filters()
func _update_save_and_cleanup(value, line_edit):
line_edit.text = value
line_edit.queue_redraw()
configuration.persist()
func _apply_configuration():
var compilation_mode = configuration.compilation_mode
var item_count = _recompilation_mode_button.get_item_count()
if compilation_mode >= 0 && compilation_mode < item_count:
_recompilation_mode_button.select(configuration.compilation_mode)
else:
_recompilation_mode_button.select(0)
_mono_line_edit.text = configuration.mono_path
_executable_line_edit.text = configuration.inklecate_path
_update_mono_availability(true)
func _update_mono_availability(updates_checkbox: bool = false):
var is_running_on_windows: bool = editor_interface.is_running_on_windows
var is_control_visible: bool = !is_running_on_windows && configuration.use_mono
_use_mono_label.visible = !is_running_on_windows
_use_mono_checkbox.visible = !is_running_on_windows
_mono_label.visible = is_control_visible
_mono_container.visible = is_control_visible
if updates_checkbox:
_use_mono_checkbox.set_pressed(configuration.use_mono)
func _set_button_icons():
var folder_icon = get_theme_icon("Folder", "EditorIcons")
var reload_icon = get_theme_icon("Reload", "EditorIcons")
var instance_icon = get_theme_icon("Instance", "EditorIcons")
_mono_dialog_button.icon = folder_icon
_executable_dialog_button.icon = folder_icon
_mono_support_documentation_button.icon = instance_icon
_mono_support_refresh_button.icon = reload_icon
func _connect_signals():
editor_interface.editor_filesystem.connect("filesystem_changed", Callable(self, "_check_runtime_presence"))
_test_button.connect("pressed", Callable(self, "_test_button_pressed"))
_use_mono_checkbox.connect("toggled", Callable(self, "_use_mono_toggled"))
_mono_line_edit.connect("text_submitted", Callable(self, "_configuration_entered"))
_executable_line_edit.connect("text_submitted", Callable(self, "_configuration_entered"))
_mono_line_edit.connect("focus_exited", Callable(self, "_configuration_focus_exited"))
_executable_line_edit.connect("focus_exited", Callable(self, "_configuration_focus_exited"))
_mono_dialog_button.connect("pressed", Callable(self, "_mono_button_pressed"))
_executable_dialog_button.connect("pressed", Callable(self, "_executable_button_pressed"))
_recompilation_mode_button.connect("item_selected", Callable(self, "_recompilation_mode_button_selected"))
_mono_support_documentation_button.connect("pressed", Callable(self, "_mono_support_documentation_pressed"))
_mono_support_refresh_button.connect("pressed", Callable(self, "_check_runtime_presence"))
_file_dialog.connect("file_selected", Callable(self, "_on_file_selected"))
func _can_run_mono():
return type_exists("_GodotSharp")

View file

@ -0,0 +1,258 @@
[gd_scene load_steps=6 format=3 uid="uid://cbuep470x6krw"]
[ext_resource type="Script" path="res://addons/inkgd/editor/panel/configuration/ink_configuration_panel.gd" id="1"]
[sub_resource type="Image" id="Image_h71nq"]
data = {
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
"format": "LumAlpha8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="7"]
image = SubResource("Image_h71nq")
[sub_resource type="Image" id="Image_e0kbj"]
data = {
"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
"format": "LumAlpha8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="5"]
image = SubResource("Image_e0kbj")
[node name="Configuration" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource("1")
[node name="ScrollContainer" type="ScrollContainer" parent="."]
layout_mode = 0
anchor_right = 1.0
anchor_bottom = 1.0
pivot_offset = Vector2(1346, -25)
[node name="V" type="VBoxContainer" parent="ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 25
[node name="InklecateV" type="VBoxContainer" parent="ScrollContainer/V"]
layout_mode = 2
size_flags_horizontal = 3
[node name="H" type="HBoxContainer" parent="ScrollContainer/V/InklecateV"]
layout_mode = 2
[node name="Label" type="Label" parent="ScrollContainer/V/InklecateV/H"]
layout_mode = 2
size_flags_horizontal = 3
text = "Inklecate"
[node name="TestButton" type="Button" parent="ScrollContainer/V/InklecateV/H"]
layout_mode = 2
mouse_filter = 1
text = "Test configuration"
[node name="M" type="MarginContainer" parent="ScrollContainer/V/InklecateV"]
layout_mode = 2
[node name="Panel" type="Panel" parent="ScrollContainer/V/InklecateV/M"]
self_modulate = Color(1, 1, 1, 0.686275)
layout_mode = 2
[node name="M" type="MarginContainer" parent="ScrollContainer/V/InklecateV/M"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="V" type="VBoxContainer" parent="ScrollContainer/V/InklecateV/M/M"]
layout_mode = 2
size_flags_horizontal = 3
[node name="G" type="GridContainer" parent="ScrollContainer/V/InklecateV/M/M/V"]
layout_mode = 2
size_flags_horizontal = 3
columns = 2
[node name="UseMonoLabel" type="Label" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
tooltip_text = "When toggled, run inklecate through Mono.
Only enable this setting is you want to use an older version of inklecate or you have a custom setup.
Modern versions of inklecate come bundled with a mono runtime on all platforms."
mouse_filter = 1
text = "Use Mono / .NET Core"
[node name="UseMonoCheckBox" type="CheckBox" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
layout_mode = 2
text = "Yes"
[node name="MonoLabel" type="Label" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
tooltip_text = "The path to Mono."
mouse_filter = 1
text = "Mono / .NET Core Executable"
[node name="MonoH" type="HBoxContainer" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
layout_mode = 2
size_flags_horizontal = 3
[node name="MonoLineEdit" type="LineEdit" parent="ScrollContainer/V/InklecateV/M/M/V/G/MonoH"]
layout_mode = 2
size_flags_horizontal = 3
text = "/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono"
[node name="MonoDialogButton" type="Button" parent="ScrollContainer/V/InklecateV/M/M/V/G/MonoH"]
layout_mode = 2
icon = SubResource("7")
[node name="ExecutableLabel" type="Label" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
tooltip_text = "The path to inklecate."
mouse_filter = 1
text = "Executable"
[node name="ExecutableH" type="HBoxContainer" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
layout_mode = 2
size_flags_horizontal = 3
[node name="ExecutableLineEdit" type="LineEdit" parent="ScrollContainer/V/InklecateV/M/M/V/G/ExecutableH"]
layout_mode = 2
size_flags_horizontal = 3
text = "/opt/homebrew/bin/inklecate"
[node name="ExecutableDialogButton" type="Button" parent="ScrollContainer/V/InklecateV/M/M/V/G/ExecutableH"]
layout_mode = 2
icon = SubResource("7")
[node name="Recompilation Mode" type="Label" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
tooltip_text = "Define if/when your stories should be recompiled.
Manual: no automatic compilation.
During Build: every time the project is built/ran, the stories will be recompiled.
On change: as soon as an Ink resource is reimported by Godot, trigger a recompilation. A folder to watch can be defined in the \"Story\" tab."
mouse_filter = 1
text = "Recompilation Mode"
[node name="RecompilationModeOptionButton" type="OptionButton" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
layout_mode = 2
item_count = 3
selected = 0
popup/item_0/text = "Manual"
popup/item_0/id = 0
popup/item_1/text = "During Build"
popup/item_1/id = 1
popup/item_2/text = "On change (experimental)"
popup/item_2/id = 2
[node name="InkDirectoryLabel" type="Label" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
tooltip_text = "The path to Mono."
mouse_filter = 1
text = "Mono"
[node name="InkDirectoryH" type="HBoxContainer" parent="ScrollContainer/V/InklecateV/M/M/V/G"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
[node name="InkDirectoryLineEdit" type="LineEdit" parent="ScrollContainer/V/InklecateV/M/M/V/G/InkDirectoryH"]
layout_mode = 2
size_flags_horizontal = 3
[node name="InkDIrectoryDialogButton" type="Button" parent="ScrollContainer/V/InklecateV/M/M/V/G/InkDirectoryH"]
layout_mode = 2
icon = SubResource("5")
[node name="MonoSupportV" type="VBoxContainer" parent="ScrollContainer/V"]
layout_mode = 2
size_flags_horizontal = 3
[node name="H" type="HBoxContainer" parent="ScrollContainer/V/MonoSupportV"]
layout_mode = 2
[node name="Label" type="Label" parent="ScrollContainer/V/MonoSupportV/H"]
layout_mode = 2
size_flags_horizontal = 3
text = "Mono Support"
[node name="DocumentationButton" type="Button" parent="ScrollContainer/V/MonoSupportV/H"]
layout_mode = 2
mouse_filter = 1
theme_override_constants/h_separation = 8
text = "Documentation"
[node name="M" type="MarginContainer" parent="ScrollContainer/V/MonoSupportV"]
layout_mode = 2
[node name="Panel" type="Panel" parent="ScrollContainer/V/MonoSupportV/M"]
self_modulate = Color(1, 1, 1, 0.686275)
layout_mode = 2
[node name="M" type="MarginContainer" parent="ScrollContainer/V/MonoSupportV/M"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="V" type="VBoxContainer" parent="ScrollContainer/V/MonoSupportV/M/M"]
layout_mode = 2
size_flags_horizontal = 3
[node name="G" type="GridContainer" parent="ScrollContainer/V/MonoSupportV/M/M/V"]
layout_mode = 2
size_flags_horizontal = 3
columns = 2
[node name="DLLLabel" type="Label" parent="ScrollContainer/V/MonoSupportV/M/M/V/G"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
tooltip_text = "Whether the ink runtime DLL (ink-engine-runtime.dll) can be found in the project or not."
mouse_filter = 1
text = "ink DLL"
[node name="DLLH" type="HBoxContainer" parent="ScrollContainer/V/MonoSupportV/M/M/V/G"]
layout_mode = 2
size_flags_horizontal = 3
[node name="PresenceLabel" type="Label" parent="ScrollContainer/V/MonoSupportV/M/M/V/G/DLLH"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
mouse_filter = 1
theme_override_colors/font_color = Color(1, 0, 0, 1)
text = "MISSING"
[node name="RefreshButton" type="Button" parent="ScrollContainer/V/MonoSupportV/M/M/V/G/DLLH"]
layout_mode = 2
theme_override_constants/h_separation = 8
text = "Scan"
icon = SubResource("7")