2025-04-19 18:03:50 +03:00

240 lines
6.6 KiB
Lua

--- Module for assigning layers to GUI nodes based on textures and fonts
local defold_parser = require("druid.editor_scripts.defold_parser.defold_parser")
local system = require("druid.editor_scripts.defold_parser.system.system")
local M = {}
---Create a backup of a file
---@param file_path string - The path of the file to backup
---@return string|nil - The backup file path, or nil if backup failed
local function create_backup(file_path)
local backup_path = file_path .. ".backup"
print("Creating backup at:", backup_path)
-- Read and write using system module
local content, err_read = system.read_file(file_path)
if not content then
print("Error reading original file for backup:", err_read)
return nil
end
local success, err_write = system.write_file(backup_path, content)
if not success then
print("Error creating backup file:", err_write)
return nil
end
print("Backup created successfully")
return backup_path
end
---Restore from a backup file
---@param backup_path string - The path of the backup file
---@param original_path string - The path to restore to
---@return boolean - True if restore was successful
local function restore_from_backup(backup_path, original_path)
print("Restoring from backup:", backup_path)
-- Read backup file
local content, err = system.read_file(backup_path)
if not content then
print("Error reading backup file:", err)
return false
end
-- Write to original file
local success, err = system.write_file(original_path, content)
if not success then
print("Error restoring from backup:", err)
return false
end
print("Restored successfully from backup")
return true
end
---Remove a backup file
---@param backup_path string - The path of the backup file to remove
local function remove_backup(backup_path)
print("Removing backup file:", backup_path)
local success, err = os.remove(backup_path)
if not success then
print("Warning: Could not remove backup file:", err)
print("You may want to manually remove it:", backup_path)
else
print("Backup file removed successfully")
end
end
---Assign layers to GUI nodes based on textures and fonts
---@param gui_resource string - The GUI resource to process
---@return table - Editor command to reload the resource
function M.assign_layers(gui_resource)
local gui_path = editor.get(gui_resource, "path")
print("Setting up layers for", gui_path)
-- Get the absolute path to the file
local absolute_project_path = editor.external_file_attributes(".").path
if not absolute_project_path:match("[\\/]$") then
absolute_project_path = absolute_project_path .. "/"
end
local clean_gui_path = gui_path
if clean_gui_path:sub(1, 1) == "/" then
clean_gui_path = clean_gui_path:sub(2)
end
local gui_absolute_path = absolute_project_path .. clean_gui_path
-- Create a backup before modifying the file
local backup_path = create_backup(gui_absolute_path)
if not backup_path then
print("Failed to create backup, aborting...")
return {}
end
-- Parse the GUI file using defold_parser
print("Parsing GUI file...")
local gui_data = defold_parser.load_from_file(gui_absolute_path)
if not gui_data then
print("Error: Failed to parse GUI file")
return {}
end
-- Collect all textures and fonts
print("Collecting all available textures and fonts...")
local all_textures = {}
local all_fonts = {}
-- Get textures
if gui_data.textures then
for _, texture in ipairs(gui_data.textures) do
print("Found texture:", texture.name)
all_textures[texture.name] = true
end
end
-- Get fonts
if gui_data.fonts then
for _, font in ipairs(gui_data.fonts) do
print("Found font:", font.name)
all_fonts[font.name] = true
end
end
-- Track which textures and fonts are actually used by nodes
print("Finding used textures and fonts...")
local used_layers = {}
-- First pass: find all used textures and fonts
if gui_data.nodes then
for _, node in ipairs(gui_data.nodes) do
if node.texture then
local layer_name = node.texture:match("([^/]+)")
if layer_name and all_textures[layer_name] then
used_layers[layer_name] = true
print("Node", node.id, "uses texture:", layer_name)
end
elseif node.font then
local layer_name = node.font
if all_fonts[layer_name] then
used_layers[layer_name] = true
print("Node", node.id, "uses font:", layer_name)
end
end
end
end
-- Create a set of existing layer names for faster lookup
print("Checking existing layers...")
local existing_layers = {}
if gui_data.layers then
for _, layer in ipairs(gui_data.layers) do
if layer.name then
existing_layers[layer.name] = true
print("Found existing layer:", layer.name)
end
end
end
-- Convert set to array of used layers
local layers = {}
for layer_name in pairs(used_layers) do
if not existing_layers[layer_name] then
table.insert(layers, layer_name)
print("Adding new layer:", layer_name)
else
print("Layer already exists:", layer_name)
end
end
-- Sort new layers for consistent output
table.sort(layers)
print("Found", #layers, "new layers to add")
-- Add new layers (preserving existing ones)
print("Adding new layers...")
gui_data.layers = gui_data.layers or {}
for _, layer_name in ipairs(layers) do
table.insert(gui_data.layers, {
name = layer_name,
})
end
-- Create a lookup table for faster matching - include both existing and new layers
local layer_lookup = {}
for layer_name in pairs(existing_layers) do
layer_lookup[layer_name] = true
end
for _, layer_name in ipairs(layers) do
layer_lookup[layer_name] = true
end
-- Update nodes to use the correct layer
print("Updating node layers...")
if gui_data.nodes then
for _, node in ipairs(gui_data.nodes) do
if node.texture then
local layer_name = node.texture:match("([^/]+)")
if layer_name and layer_lookup[layer_name] then
print("Assigning node", node.id, "to layer:", layer_name)
node.layer = layer_name
end
elseif node.font then
local layer_name = node.font
if layer_lookup[layer_name] then
print("Assigning node", node.id, "to layer:", layer_name)
node.layer = layer_name
end
end
end
end
-- Write the updated GUI file
print("Writing updated GUI file...")
local success = defold_parser.save_to_file(gui_absolute_path, gui_data)
if not success then
print("Error: Failed to save GUI file")
print("Attempting to restore from backup...")
local restored = restore_from_backup(backup_path, gui_absolute_path)
if not restored then
print("Critical: Failed to restore from backup. Manual intervention may be required.")
end
return {}
end
-- Everything worked, remove the backup
remove_backup(backup_path)
print("Successfully assigned layers for GUI:", gui_path)
return {}
end
return M