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,13 @@
[gd_scene format=2]
[node name="EmptyStateContainer" type="CenterContainer"]
offset_right = 1902.0
offset_bottom = 100.0
custom_minimum_size = Vector2( 0, 100 )
[node name="Label" type="Label" parent="."]
offset_left = 879.0
offset_top = 43.0
offset_right = 1022.0
offset_bottom = 57.0
text = "No stories to compile."

View file

@ -0,0 +1,153 @@
@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 VBoxContainer
# Hiding this type to prevent registration of "private" nodes.
# See https://github.com/godotengine/godot-proposals/issues/1047
# class_name InkStoryConfiguration
# ############################################################################ #
# Signals
# ############################################################################ #
signal configuration_changed(story_configuration)
signal remove_button_pressed(story_configuration)
signal build_button_pressed(story_configuration)
signal source_file_button_pressed(story_configuration)
signal target_file_button_pressed(story_configuration)
signal watched_folder_button_pressed(story_configuration)
# ############################################################################ #
# Properties
# ############################################################################ #
var editor_interface: InkEditorInterface = null
# ############################################################################ #
# Nodes
# ############################################################################ #
@onready var story_label = find_child("StoryLabel")
@onready var remove_button = find_child("RemoveButton")
@onready var build_button = find_child("BuildButton")
@onready var source_file_line_edit = find_child("SourceFileLineEdit")
@onready var source_file_dialog_button = find_child("SourceFileDialogButton")
@onready var target_file_line_edit = find_child("TargetFileLineEdit")
@onready var target_file_dialog_button = find_child("TargetFileDialogButton")
@onready var watched_folder_label = find_child("WatchedFolderLabel")
@onready var watched_folder_container = find_child("WatchedFolderHBoxContainer")
@onready var watched_folder_line_edit = find_child("WatchedFolderLineEdit")
@onready var watched_folder_dialog_button = find_child("WatchedFolderDialogButton")
@onready var background_color_rect = find_child("BackgroundColorRect")
# ############################################################################ #
# 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:
return
_apply_custom_header_color()
_set_button_icons()
_connect_signals()
show_watched_folder(false)
# ############################################################################ #
# Signals
# ############################################################################ #
func _configuration_entered(_new_text: String):
_configuration_focus_exited()
func _configuration_focus_exited():
emit_signal("configuration_changed", self)
func _remove_button_pressed():
emit_signal("remove_button_pressed", self)
func _build_button_pressed():
emit_signal("build_button_pressed", self)
func _source_file_button_pressed():
emit_signal("source_file_button_pressed", self)
func _target_file_button_pressed():
emit_signal("target_file_button_pressed", self)
func _watched_folder_button_pressed():
emit_signal("watched_folder_button_pressed", self)
# ############################################################################ #
# Public Methods
# ############################################################################ #
@warning_ignore("shadowed_variable")
func show_watched_folder(show: bool):
watched_folder_label.visible = show
watched_folder_container.visible = show
func disable_all_buttons(disable: bool):
remove_button.disabled = disable
build_button.disabled = disable
# ############################################################################ #
# Private Methods
# ############################################################################ #
func _apply_custom_header_color():
var header_color = editor_interface.get_custom_header_color()
if header_color != Color.TRANSPARENT:
background_color_rect.color = header_color
func _set_button_icons():
var folder_icon = get_theme_icon("Folder", "EditorIcons")
source_file_dialog_button.icon = folder_icon
target_file_dialog_button.icon = folder_icon
watched_folder_dialog_button.icon = folder_icon
var trash_icon = get_theme_icon("Remove", "EditorIcons")
remove_button.icon = trash_icon
func _connect_signals():
source_file_line_edit.connect("text_submitted", Callable(self, "_configuration_entered"))
source_file_line_edit.connect("focus_exited", Callable(self, "_configuration_focus_exited"))
target_file_line_edit.connect("text_submitted", Callable(self, "_configuration_entered"))
target_file_line_edit.connect("focus_exited", Callable(self, "_configuration_focus_exited"))
source_file_dialog_button.connect("pressed", Callable(self, "_source_file_button_pressed"))
target_file_dialog_button.connect("pressed", Callable(self, "_target_file_button_pressed"))
watched_folder_dialog_button.connect("pressed", Callable(self, "_watched_folder_button_pressed"))
remove_button.connect("pressed", Callable(self, "_remove_button_pressed"))
build_button.connect("pressed", Callable(self, "_build_button_pressed"))

View file

@ -0,0 +1,189 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://addons/inkgd/editor/panel/stories/ink_story_configuration.gd" type="Script" id=1]
[sub_resource type="Image" id=3]
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=2]
flags = 4
flags = 4
image = SubResource( 3 )
size = Vector2( 16, 16 )
[node name="StoryConfiguration" type="VBoxContainer"]
offset_right = 1902.0
offset_bottom = 87.0
theme_override_constants/separation = 5
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="HeaderMarginContainer" type="MarginContainer" parent="."]
offset_right = 1902.0
offset_bottom = 32.0
theme_override_constants/margin_right = 0
theme_override_constants/margin_top = 0
theme_override_constants/margin_left = 0
theme_override_constants/margin_bottom = 0
[node name="BackgroundColorRect" type="ColorRect" parent="HeaderMarginContainer"]
offset_right = 1902.0
offset_bottom = 32.0
color = Color( 0.268314, 0.291712, 0.340784, 1 )
[node name="MarginContainer" type="MarginContainer" parent="HeaderMarginContainer"]
offset_right = 1902.0
offset_bottom = 32.0
theme_override_constants/margin_right = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_left = 25
theme_override_constants/margin_bottom = 5
[node name="HBoxContainer" type="HBoxContainer" parent="HeaderMarginContainer/MarginContainer"]
offset_left = 25.0
offset_top = 5.0
offset_right = 1897.0
offset_bottom = 27.0
[node name="StoryLabel" type="Label" parent="HeaderMarginContainer/MarginContainer/HBoxContainer"]
offset_top = 4.0
offset_right = 1711.0
offset_bottom = 18.0
size_flags_horizontal = 3
text = "0"
[node name="RemoveButton" type="Button" parent="HeaderMarginContainer/MarginContainer/HBoxContainer"]
offset_left = 1715.0
offset_right = 1803.0
offset_bottom = 22.0
mouse_filter = 1
theme_override_constants/h_separation = 8
text = "Remove"
icon = SubResource( 2 )
__meta__ = {
"_edit_use_anchors_": false,
"_editor_description_": "Compile the story manually, based on the configuration below."
}
[node name="BuildButton" type="Button" parent="HeaderMarginContainer/MarginContainer/HBoxContainer"]
offset_left = 1807.0
offset_right = 1872.0
offset_bottom = 22.0
mouse_filter = 1
theme_override_constants/h_separation = 8
text = "Compile"
__meta__ = {
"_edit_use_anchors_": false,
"_editor_description_": "Compile the story manually, based on the configuration below."
}
[node name="ConfigurationMarginContainer" type="MarginContainer" parent="."]
offset_top = 37.0
offset_right = 1902.0
offset_bottom = 117.0
theme_override_constants/margin_right = 0
theme_override_constants/margin_top = 0
theme_override_constants/margin_left = 5
theme_override_constants/margin_bottom = 0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="GridContainer" type="GridContainer" parent="ConfigurationMarginContainer"]
offset_left = 5.0
offset_right = 1902.0
offset_bottom = 80.0
size_flags_horizontal = 3
columns = 2
[node name="SourceFileLabel" type="Label" parent="ConfigurationMarginContainer/GridContainer"]
offset_right = 946.0
offset_bottom = 24.0
tooltip_text = "The input path, a.k.a the original ink story."
mouse_filter = 1
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Source File (*.ink)"
[node name="SourceFileHBoxContainer" type="HBoxContainer" parent="ConfigurationMarginContainer/GridContainer"]
offset_left = 950.0
offset_right = 1896.0
offset_bottom = 24.0
size_flags_horizontal = 3
[node name="SourceFileLineEdit" type="LineEdit" parent="ConfigurationMarginContainer/GridContainer/SourceFileHBoxContainer"]
offset_right = 914.0
offset_bottom = 24.0
size_flags_horizontal = 3
[node name="SourceFileDialogButton" type="Button" parent="ConfigurationMarginContainer/GridContainer/SourceFileHBoxContainer"]
offset_left = 918.0
offset_right = 946.0
offset_bottom = 24.0
icon = SubResource( 2 )
[node name="TargetFileLabel" type="Label" parent="ConfigurationMarginContainer/GridContainer"]
offset_top = 28.0
offset_right = 946.0
offset_bottom = 52.0
tooltip_text = "The output path, a.k.a the JSON file compiled from the original ink story."
mouse_filter = 1
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Target File (*.json)"
[node name="TargetFileHBoxContainer" type="HBoxContainer" parent="ConfigurationMarginContainer/GridContainer"]
offset_left = 950.0
offset_top = 28.0
offset_right = 1896.0
offset_bottom = 52.0
size_flags_horizontal = 3
[node name="TargetFileLineEdit" type="LineEdit" parent="ConfigurationMarginContainer/GridContainer/TargetFileHBoxContainer"]
offset_right = 914.0
offset_bottom = 24.0
size_flags_horizontal = 3
[node name="TargetFileDialogButton" type="Button" parent="ConfigurationMarginContainer/GridContainer/TargetFileHBoxContainer"]
offset_left = 918.0
offset_right = 946.0
offset_bottom = 24.0
icon = SubResource( 2 )
[node name="WatchedFolderLabel" type="Label" parent="ConfigurationMarginContainer/GridContainer"]
offset_top = 56.0
offset_right = 946.0
offset_bottom = 80.0
tooltip_text = "The directory to watch for changes in Ink sources.
This path is optional, leave it blank to disable automatic recompilation for that story."
mouse_filter = 1
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Watched Folder"
[node name="WatchedFolderHBoxContainer" type="HBoxContainer" parent="ConfigurationMarginContainer/GridContainer"]
offset_left = 950.0
offset_top = 56.0
offset_right = 1896.0
offset_bottom = 80.0
size_flags_horizontal = 3
[node name="WatchedFolderLineEdit" type="LineEdit" parent="ConfigurationMarginContainer/GridContainer/WatchedFolderHBoxContainer"]
offset_right = 914.0
offset_bottom = 24.0
size_flags_horizontal = 3
[node name="WatchedFolderDialogButton" type="Button" parent="ConfigurationMarginContainer/GridContainer/WatchedFolderHBoxContainer"]
offset_left = 918.0
offset_right = 946.0
offset_bottom = 24.0
icon = SubResource( 2 )

View file

@ -0,0 +1,524 @@
@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 InkStoryPanel
# ############################################################################ #
# Imports
# ############################################################################ #
var InkConfiguration = load("res://addons/inkgd/editor/common/ink_configuration.gd")
var InkCompilationConfiguration = load("res://addons/inkgd/editor/common/executors/structures/ink_compilation_configuration.gd")
var InkCompiler = load("res://addons/inkgd/editor/common/executors/ink_compiler.gd")
var InkRichDialog = load("res://addons/inkgd/editor/panel/common/ink_rich_dialog.tscn")
var InkProgressDialog = load("res://addons/inkgd/editor/panel/common/ink_progress_dialog.tscn")
var InkStoryConfigurationScene = load("res://addons/inkgd/editor/panel/stories/ink_story_configuration.tscn")
var EmptyStateContainerScene = load("res://addons/inkgd/editor/panel/stories/empty_state_container.tscn")
# ############################################################################ #
# Signals
# ############################################################################ #
signal _compiled()
# ############################################################################ #
# Enums
# ############################################################################ #
enum FileDialogSelection {
UNKNOWN,
SOURCE_FILE,
TARGET_FILE,
WATCHED_FOLDER
}
# ############################################################################ #
# Properties
# ############################################################################ #
var editor_interface: InkEditorInterface
var configuration: InkConfiguration
var progress_texture: AnimatedTexture
# ############################################################################ #
# Private Properties
# ############################################################################ #
var _scrollbar_max_value = -1
var _compilers: Dictionary = {}
var _file_dialog = EditorFileDialog.new()
## Configuration item for which the FileDialog is currently shown.
##
## Unknown by default.
var _file_dialog_selection = FileDialogSelection.UNKNOWN
## The story index for which the FileDialog is currenlty shown.
##
## -1 by default or when the file dialog currently displayed doesn't
## concern the stories source/target files.
var _file_dialog_selection_story_index = -1
var _current_story_node = null
var _progress_dialog = null
# ############################################################################ #
# Nodes
# ############################################################################ #
@onready var _empty_state_container = EmptyStateContainerScene.instantiate()
@onready var _build_all_button = find_child("BuildAllButton")
@onready var _add_new_story_button = find_child("AddNewStoryButton")
@onready var _story_configuration_container = find_child("StoryConfigurationVBoxContainer")
@onready var _scroll_container = find_child("ScrollContainer")
# ############################################################################ #
# 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 || progress_texture == null:
print("[inkgd] [INFO] Ink Stories Tab: dependencies not met, ignoring.")
return
configuration.connect("compilation_mode_changed", Callable(self, "_compilation_mode_changed"))
editor_interface.editor_filesystem.connect("resources_reimported", Callable(self, "_resources_reimported"))
_story_configuration_container.add_child(_empty_state_container)
add_child(_file_dialog)
var add_icon = get_theme_icon("Add", "EditorIcons")
_add_new_story_button.icon = add_icon
_load_story_configurations()
_connect_signals()
_compilation_mode_changed(configuration.compilation_mode)
# ############################################################################ #
# Signal Receivers
# ############################################################################ #
func _resources_reimported(resources):
call_deferred("_recompile_if_necessary", resources)
func _compilation_mode_changed(compilation_mode: int):
var show_folder = (compilation_mode == InkConfiguration.BuildMode.AFTER_CHANGE)
for child in _story_configuration_container.get_children():
child.show_watched_folder(show_folder)
func _source_file_button_pressed(node):
_reset_file_dialog()
var index = _get_story_configuration_index(node)
_file_dialog_selection = FileDialogSelection.SOURCE_FILE
_file_dialog_selection_story_index = index
var story_configuration = _get_story_configuration_at_index(index)
var path = story_configuration.source_file_line_edit.text
_file_dialog.current_path = path
_file_dialog.current_dir = path.get_base_dir()
_file_dialog.current_file = path.get_file()
_file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
_file_dialog.add_filter("*.ink;Ink source file")
_file_dialog.popup_centered(Vector2(1280, 800) * editor_interface.scale)
func _target_file_button_pressed(node):
_reset_file_dialog()
var index = _get_story_configuration_index(node)
_file_dialog_selection = FileDialogSelection.TARGET_FILE
_file_dialog_selection_story_index = index
var story_configuration = _get_story_configuration_at_index(index)
var path = story_configuration.target_file_line_edit.text
_file_dialog.current_path = path
_file_dialog.current_dir = path.get_base_dir()
_file_dialog.current_file = path.get_file()
_file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
_file_dialog.add_filter("*.json;Compiled Ink story")
_file_dialog.popup_centered(Vector2(1280, 800) * editor_interface.scale)
func _watched_folder_button_pressed(node):
_reset_file_dialog()
var index = _get_story_configuration_index(node)
_file_dialog_selection = FileDialogSelection.WATCHED_FOLDER
_file_dialog_selection_story_index = index
var story_configuration = _get_story_configuration_at_index(index)
var path = story_configuration.watched_folder_line_edit.text
_file_dialog.current_path = path
_file_dialog.current_dir = path.get_base_dir()
_file_dialog.current_file = path.get_file()
_file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
_file_dialog.popup_centered(Vector2(1280, 800) * editor_interface.scale)
func _build_all_button_pressed():
_compile_all_stories()
func _add_new_story_button_pressed():
_add_new_story_configuration()
func _configuration_changed(_node):
_persist_configuration()
func _remove_button_pressed(node):
var index = _get_story_configuration_index(node)
configuration.remove_story_configuration_at_index(index)
# TODO: Rebuild from scratch instead.
var parent = node.get_parent()
if parent != null:
parent.remove_child(node)
node.queue_free()
if _story_configuration_container.get_child_count() == 0:
_story_configuration_container.add_child(_empty_state_container)
else:
var i = 0
for child in _story_configuration_container.get_children():
# Not using "is InkStoryConfiguration", because it requires a type
# declaration. Node Types register in the editor and we don't want
# that. This is a bit hacky, but until the proposal is accepted,
# it prevents cluttering the "Create new node" list.
if "story_label" in child:
child.story_label.text = "Story %d" % (i + 1)
i += 1
_persist_configuration()
func _build_button_pressed(node):
var index = _get_story_configuration_index(node)
var story_configuration = configuration.get_story_configuration_at_index(index)
if story_configuration == null:
printerr("[inkgd] [ERROR] No configurations found for Story %d" % (index + 1))
return
_compile_story(story_configuration, node)
func _compile_all_stories():
_disable_all_buttons(true)
var number_of_stories = configuration.stories.size()
var current_story_index = 0
_progress_dialog = InkProgressDialog.instantiate()
add_child(_progress_dialog)
_progress_dialog.update_layout(editor_interface.scale)
_progress_dialog.popup_centered(Vector2(600, 100) * editor_interface.scale)
for story_configuration in configuration.stories:
var source_file_path: String = configuration.get_source_file_path(story_configuration)
_progress_dialog.current_step_name = source_file_path.get_file()
_compile_story(story_configuration)
await self._compiled
@warning_ignore("integer_division")
_progress_dialog.progress = float(100 * (current_story_index + 1) / number_of_stories)
current_story_index += 1
remove_child(_progress_dialog)
_progress_dialog.queue_free()
_progress_dialog = null
_disable_all_buttons(false)
func _compile_story(story_configuration, node = null):
var source_file_path = configuration.get_source_file_path(story_configuration)
var target_file_path = configuration.get_target_file_path(story_configuration)
if node != null:
_current_story_node = node
node.build_button.icon = progress_texture
_disable_all_buttons(true)
var compiler_configuration = InkCompilationConfiguration.new(
configuration,
true,
node != null,
source_file_path,
target_file_path
)
var compiler = InkCompiler.new(compiler_configuration)
_compilers[compiler.identifier] = compiler
compiler.connect("story_compiled", Callable(self, "_handle_compilation"))
compiler.compile_story()
func _handle_compilation(result):
if _current_story_node != null:
var button = _current_story_node.build_button
button.icon = null
_disable_all_buttons(false)
_current_story_node = null
if result.user_triggered:
if result.success:
if result.output && !result.output.is_empty():
var dialog = InkRichDialog.instantiate()
add_child(dialog)
dialog.title = "Success!"
dialog.message_text = "The story was successfully compiled."
dialog.output_text = result.output
dialog.update_layout(editor_interface.scale)
dialog.popup_centered(Vector2(700, 400) * editor_interface.scale)
else:
var dialog = AcceptDialog.new()
add_child(dialog)
dialog.title = "Success!"
dialog.dialog_text = "The story was successfully compiled."
dialog.popup_centered()
_reimport_compiled_stories()
else:
var dialog = InkRichDialog.instantiate()
add_child(dialog)
dialog.title = "Error"
dialog.message_text = "The story could not be compiled. See inklecate's output below."
dialog.output_text = result.output
dialog.update_layout(editor_interface.scale)
dialog.popup_centered(Vector2(700, 400) * editor_interface.scale)
else:
_reimport_compiled_stories()
if _compilers.has(result.identifier):
_compilers.erase(result.identifier)
emit_signal("_compiled")
func _on_file_selected(path: String):
var index = _file_dialog_selection_story_index
match _file_dialog_selection:
FileDialogSelection.SOURCE_FILE:
var story_configuration = _get_story_configuration_at_index(index)
if story_configuration == null:
return
var localized_path = ProjectSettings.localize_path(path)
var source_line_edit = story_configuration.source_file_line_edit
source_line_edit.text = localized_path
source_line_edit.queue_redraw()
if story_configuration.target_file_line_edit.text.is_empty():
var target_line_edit = story_configuration.target_file_line_edit
target_line_edit.text = localized_path + ".json"
target_line_edit.queue_redraw()
if story_configuration.watched_folder_line_edit.text.is_empty():
var watched_folder_line_edit = story_configuration.watched_folder_line_edit
watched_folder_line_edit.text = localized_path.get_base_dir()
watched_folder_line_edit.queue_redraw()
_persist_configuration()
FileDialogSelection.TARGET_FILE:
var story_configuration = _get_story_configuration_at_index(index)
if story_configuration == null:
return
var localized_path = ProjectSettings.localize_path(path)
var line_edit = story_configuration.target_file_line_edit
line_edit.text = localized_path
line_edit.queue_redraw()
_persist_configuration()
FileDialogSelection.WATCHED_FOLDER:
var story_configuration = _get_story_configuration_at_index(index)
if story_configuration == null:
return
var localized_path = ProjectSettings.localize_path(path)
var line_edit = story_configuration.watched_folder_line_edit
line_edit.text = localized_path
line_edit.queue_redraw()
_persist_configuration()
_:
printerr("[inkgd] [ERROR] Unknown FileDialogSelection, failed to save FileDialog file.")
_file_dialog_selection = FileDialogSelection.UNKNOWN
func _scrollbar_changed():
var max_value = _scroll_container.get_v_scroll_bar().max_value
if _scrollbar_max_value == max_value && _scrollbar_max_value != -1:
return
_scrollbar_max_value = max_value
_scroll_container.scroll_vertical = max_value
# ############################################################################ #
# Private helpers
# ############################################################################ #
func _reset_file_dialog():
_file_dialog.current_path = "res://"
_file_dialog.current_dir = "res://"
_file_dialog.current_file = ""
_file_dialog.clear_filters()
func _persist_configuration():
configuration.stories.clear()
if _empty_state_container.get_parent() == null:
configuration.stories.clear()
for node in _story_configuration_container.get_children():
# Not using "is InkStoryConfiguration", because it requires a type
# declaration. Node Types register in the editor and we don't want
# that. This is a bit hacky, but until the proposal is accepted,
# it prevents cluttering the "Create new node" list.
if !("story_label" in node):
continue
configuration.append_new_story_configuration(
node.source_file_line_edit.text,
node.target_file_line_edit.text,
node.watched_folder_line_edit.text
)
configuration.persist()
func _load_story_configurations():
for story_configuration in configuration.stories:
var node = _add_new_story_configuration()
node.source_file_line_edit.text = configuration.get_source_file_path(story_configuration)
node.target_file_line_edit.text = configuration.get_target_file_path(story_configuration)
node.watched_folder_line_edit.text = configuration.get_watched_folder_path(story_configuration)
func _add_new_story_configuration():
var story_configuration = InkStoryConfigurationScene.instantiate()
story_configuration.editor_interface = editor_interface
story_configuration.connect("configuration_changed", Callable(self, "_configuration_changed"))
story_configuration.connect("remove_button_pressed", Callable(self, "_remove_button_pressed"))
story_configuration.connect("build_button_pressed", Callable(self, "_build_button_pressed"))
story_configuration.connect("source_file_button_pressed", Callable(self, "_source_file_button_pressed"))
story_configuration.connect("target_file_button_pressed", Callable(self, "_target_file_button_pressed"))
story_configuration.connect("watched_folder_button_pressed", Callable(self, "_watched_folder_button_pressed"))
if _empty_state_container.get_parent() != null:
_story_configuration_container.remove_child(_empty_state_container)
_story_configuration_container.add_child(story_configuration)
var count = _story_configuration_container.get_child_count()
story_configuration.story_label.text = "Story %d" % count
var show_folder = (configuration.compilation_mode == InkConfiguration.BuildMode.AFTER_CHANGE)
story_configuration.show_watched_folder(show_folder)
return story_configuration
func _reimport_compiled_stories():
editor_interface.scan_file_system()
func _get_story_configuration_index(node) -> int:
return _story_configuration_container.get_children().find(node)
func _get_story_configuration_at_index(index: int):
if index >= 0 && _story_configuration_container.get_child_count():
return _story_configuration_container.get_children()[index]
return null
func _recompile_if_necessary(resources: PackedStringArray):
# Making sure the resources have been imported before recompiling.
await get_tree().create_timer(0.5).timeout
for story_configuration in configuration.stories:
var watched_folder_path: String = configuration.get_watched_folder_path(story_configuration)
if watched_folder_path.is_empty():
return
for resource in resources:
if resource.begins_with(watched_folder_path):
_compile_story(story_configuration)
break
func _disable_all_buttons(disable: bool):
_add_new_story_button.disabled = disable
_build_all_button.disabled = disable
for child in _story_configuration_container.get_children():
child.disable_all_buttons(disable)
func _connect_signals():
_build_all_button.connect("pressed", Callable(self, "_build_all_button_pressed"))
_add_new_story_button.connect("pressed", Callable(self, "_add_new_story_button_pressed"))
_file_dialog.connect("file_selected", Callable(self, "_on_file_selected"))
_file_dialog.connect("dir_selected", Callable(self, "_on_file_selected"))
_scroll_container.get_v_scroll_bar().connect("changed", Callable(self, "_scrollbar_changed"))

View file

@ -0,0 +1,75 @@
[gd_scene load_steps=4 format=3 uid="uid://b16uj28y2mqmk"]
[ext_resource type="Script" path="res://addons/inkgd/editor/panel/stories/ink_story_panel.gd" id="1"]
[sub_resource type="Image" id="Image_3omov"]
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="2"]
image = SubResource("Image_3omov")
[node name="Story" 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
[node name="StoriesVBoxContainer" type="VBoxContainer" parent="ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="HBoxContainer" type="HBoxContainer" parent="ScrollContainer/StoriesVBoxContainer"]
layout_mode = 2
alignment = 2
[node name="Label" type="Label" parent="ScrollContainer/StoriesVBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "Managed Stories"
[node name="AddNewStoryButton" type="Button" parent="ScrollContainer/StoriesVBoxContainer/HBoxContainer"]
layout_mode = 2
mouse_filter = 1
theme_override_constants/h_separation = 8
text = "New story"
icon = SubResource("2")
[node name="VSeparator" type="VSeparator" parent="ScrollContainer/StoriesVBoxContainer/HBoxContainer"]
layout_mode = 2
[node name="BuildAllButton" type="Button" parent="ScrollContainer/StoriesVBoxContainer/HBoxContainer"]
layout_mode = 2
mouse_filter = 1
theme_override_constants/h_separation = 10
text = "Compile All"
[node name="MarginContainer" type="MarginContainer" parent="ScrollContainer/StoriesVBoxContainer"]
layout_mode = 2
[node name="Panel" type="Panel" parent="ScrollContainer/StoriesVBoxContainer/MarginContainer"]
self_modulate = Color(1, 1, 1, 0.686275)
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="ScrollContainer/StoriesVBoxContainer/MarginContainer"]
layout_mode = 2
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="StoryConfigurationVBoxContainer" type="VBoxContainer" parent="ScrollContainer/StoriesVBoxContainer/MarginContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 15