Files
fps_project_1/addons/proton_scatter/src/documentation/documentation.gd

285 lines
7.1 KiB
GDScript

@tool
extends PopupPanel
# Formats and displays the DocumentationData provided by other parts of the addon
# TODO: Adjust title font size based on the editor font size / scaling
const DocumentationInfo = preload("./documentation_info.gd")
const SpecialPages = preload("./pages/special_pages.gd")
var _pages := {}
var _items := {}
var _categories_roots := {}
var _modifiers_root: TreeItem
var _edited_text: String
var _accent_color := Color.CORNFLOWER_BLUE
var _editor_scale := 1.0
var _header_size := 20
var _sub_header_size := 16
var _populated := false
@onready var tree: Tree = $HSplitContainer/Tree
@onready var label: RichTextLabel = $HSplitContainer/RichTextLabel
func _ready() -> void:
tree.create_item() # Create tree root
tree.hide_root = true
tree.item_selected.connect(_on_item_selected)
add_page(SpecialPages.get_scatter_documentation(), tree.create_item())
add_page(SpecialPages.get_item_documentation(), tree.create_item())
add_page(SpecialPages.get_shape_documentation(), tree.create_item())
add_page(SpecialPages.get_cache_documentation(), tree.create_item())
_modifiers_root = tree.create_item()
add_page(SpecialPages.get_modifiers_documentation(), _modifiers_root)
_populate()
# Fed from the StackPanel scene, before the ready function
func set_editor_plugin(editor_plugin: EditorPlugin) -> void:
if not editor_plugin:
return
var editor_interface := editor_plugin.get_editor_interface()
var editor_settings := editor_interface.get_editor_settings()
_accent_color = editor_settings.get("interface/theme/accent_color")
_editor_scale = editor_interface.get_editor_scale()
func show_page(page_name: String) -> void:
if not page_name in _items:
return
var item: TreeItem = _items[page_name]
item.select(0)
popup_centered(Vector2i(900, 600))
# Generate a formatted string from the DocumentationInfo input.
# This string will be stored and later displayed in the RichTextLabel so we
# we don't have to regenerate it everytime we look at another page.
func add_page(info: DocumentationInfo, item: TreeItem = null) -> void:
if not item:
var root: TreeItem = _get_or_create_tree_root(info.get_category())
item = tree.create_item(root)
item.set_text(0, info.get_title())
_begin_formatting()
# Page title
_format_title(info.get_title())
# Paragraphs
for p in info.get_paragraphs():
_format_paragraph(p)
# Parameters
if not info.get_parameters().is_empty():
_format_subtitle("Parameters")
for p in info.get_parameters():
_format_parameter(p)
# Warnings
if not info.get_warnings().is_empty():
_format_subtitle("Warnings")
for w in info.get_warnings():
_format_warning(w)
_pages[item] = _get_formatted_text()
_items[info.get_title()] = item
func _populate():
if _populated: # Already generated the documentation pages
return
var path = _get_root_folder() + "/src/modifiers/"
var result := {}
_discover_modifiers_recursive(path, result)
var names := result.keys()
names.sort()
for n in names:
var info = result[n]
add_page(info)
_populated = true
func _discover_modifiers_recursive(path, result) -> void:
var dir = DirAccess.open(path)
dir.list_dir_begin()
var path_root = dir.get_current_dir() + "/"
while true:
var file = dir.get_next()
if file == "":
break
if file == "base_modifier.gd":
continue
if dir.current_is_dir():
_discover_modifiers_recursive(path_root + file, result)
continue
if not file.ends_with(".gd") and not file.ends_with(".gdc"):
continue
var full_path = path_root + file
var script = load(full_path)
if not script or not script.can_instantiate():
print("Error: Failed to load script ", file)
continue
var modifier = script.new()
var info: DocumentationInfo = modifier.documentation
info.set_title(modifier.display_name)
info.set_category(modifier.category)
if modifier.use_edge_data:
info.add_warning(
"This modifier uses edge data (represented by the blue lines
on the Scatter node). These edges are usually locked to the
local XZ plane, (except for the Path shape when they are
NOT closed). If you can't see these lines, make sure to have at
least one Shape crossing the ProtonScatter local XZ plane.",
1)
if modifier.deprecated:
info.add_warning(
"This modifier has been deprecated. It won't receive any updates
and will be deleted in a future update.",
2)
result[modifier.display_name] = info
dir.list_dir_end()
func _get_root_folder() -> String:
var script: Script = get_script()
var path: String = script.get_path().get_base_dir()
var folders = path.right(-6) # Remove the res://
var tokens = folders.split('/')
return "res://" + tokens[0] + "/" + tokens[1]
func _get_or_create_tree_root(root_name: String) -> TreeItem:
if root_name in _categories_roots:
return _categories_roots[root_name]
var root = tree.create_item(_modifiers_root)
root.set_text(0, root_name)
root.set_selectable(0, false)
_categories_roots[root_name] = root
return root
func _begin_formatting() -> void:
_edited_text = ""
func _get_formatted_text() -> String:
return _edited_text
func _format_title(text: String) -> void:
_edited_text += "[font_size=" + var_to_str(_header_size * _editor_scale) + "]"
_edited_text += "[color=" + _accent_color.to_html() + "]"
_edited_text += "[center][b]"
_edited_text += text
_edited_text += "[/b][/center]"
_edited_text += "[/color]"
_edited_text += "[/font_size]"
_format_line_break(2)
func _format_subtitle(text: String) -> void:
_edited_text += "[font_size=" + var_to_str(_header_size * _editor_scale) + "]"
_edited_text += "[color=" + _accent_color.to_html() + "]"
_edited_text += "[b]" + text + "[/b]"
_edited_text += "[/color]"
_edited_text += "[/font_size]"
_format_line_break(2)
func _format_line_break(count := 1) -> void:
for i in count:
_edited_text += "\n"
func _format_paragraph(text: String) -> void:
_edited_text += "[p]" + text + "[/p]"
_format_line_break(2)
func _format_parameter(p) -> void:
var root_folder = _get_root_folder()
_edited_text += "[indent]"
if not p.type.is_empty():
var file_name = p.type.to_lower() + ".svg"
_edited_text += "[img]" + root_folder + "/icons/types/" + file_name + "[/img] "
_edited_text += "[b]" + p.name + "[/b] "
match p.cost:
1:
_edited_text += "[img]" + root_folder + "/icons/arrow_log.svg[/img]"
2:
_edited_text += "[img]" + root_folder + "/icons/arrow_linear.svg[/img]"
3:
_edited_text += "[img]" + root_folder + "/icons/arrow_exp.svg[/img]"
_format_line_break(2)
_edited_text += "[indent]" + p.description + "[/indent]"
_format_line_break(2)
for warning in p.warnings:
if not warning.text.is_empty():
_format_warning(warning)
_edited_text += "[/indent]"
func _format_warning(w, indent := true) -> void:
if indent:
_edited_text += "[indent]"
var color := "Darkgray"
match w.importance:
1:
color = "yellow"
2:
color = "red"
_edited_text += "[color=" + color + "][i]" + w.text + "[/i][/color]\n"
if indent:
_edited_text += "[/indent]"
_format_line_break(1)
func _on_item_selected() -> void:
var selected: TreeItem = tree.get_selected()
if _pages.has(selected):
var text: String = _pages[selected]
label.set_text(text)
else:
label.set_text("[center] Under construction [/center]")