2026-05-20 13:08:38 -04:00
|
|
|
@tool
|
2026-05-20 22:28:46 -04:00
|
|
|
extends PanelContainer
|
2026-05-20 12:54:09 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@onready var grid: DashboardGrid = %DashboardGrid
|
|
|
|
|
|
2026-05-21 08:39:15 -04:00
|
|
|
var _tile_instances: Array[Control] = []
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
var _last_refresh: Dictionary = {} # tile_instance -> last tick ms
|
2026-05-20 12:59:50 -04:00
|
|
|
|
2026-05-20 12:54:09 -04:00
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2026-05-20 13:08:38 -04:00
|
|
|
_set_background()
|
2026-05-20 22:28:46 -04:00
|
|
|
if not Engine.is_editor_hint():
|
2026-05-21 08:39:15 -04:00
|
|
|
_load_tiles_from_plugins()
|
2026-05-20 12:59:50 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func _set_background() -> void:
|
2026-05-20 22:28:46 -04:00
|
|
|
var bg: StyleBoxFlat
|
|
|
|
|
if ConfigManager.has_method("get_color"):
|
|
|
|
|
bg = StyleBoxFlat.new()
|
|
|
|
|
bg.bg_color = ConfigManager.get_color("background_color", Color(0.08, 0.08, 0.12))
|
|
|
|
|
else:
|
|
|
|
|
bg = StyleBoxFlat.new()
|
|
|
|
|
bg.bg_color = Color(0.08, 0.08, 0.12)
|
2026-05-20 13:08:38 -04:00
|
|
|
add_theme_stylebox_override("panel", bg)
|
2026-05-20 12:59:50 -04:00
|
|
|
|
|
|
|
|
|
2026-05-21 08:39:15 -04:00
|
|
|
func _load_tiles_from_plugins() -> void:
|
|
|
|
|
if not PluginManager.has_method("get_all_tile_defs"):
|
|
|
|
|
return
|
2026-05-20 12:59:50 -04:00
|
|
|
|
2026-05-21 08:39:15 -04:00
|
|
|
var all_tile_defs: Array[Dictionary] = PluginManager.get_all_tile_defs()
|
|
|
|
|
if all_tile_defs.is_empty():
|
|
|
|
|
return
|
2026-05-20 14:14:24 -04:00
|
|
|
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
# Set grid dimensions from layout before placing tiles
|
|
|
|
|
if LayoutManager.has_method("get_grid_size"):
|
|
|
|
|
var ls := LayoutManager.get_grid_size()
|
|
|
|
|
grid.grid_columns = ls.columns
|
|
|
|
|
grid.grid_rows = ls.rows
|
|
|
|
|
grid._rebuild_grid()
|
|
|
|
|
|
2026-05-21 08:39:15 -04:00
|
|
|
# Load saved layout (if any)
|
|
|
|
|
var layout_data: Dictionary = {}
|
|
|
|
|
if LayoutManager.has_method("get_layout"):
|
|
|
|
|
layout_data = LayoutManager.get_layout()
|
|
|
|
|
|
|
|
|
|
# Track which tile_defs we've placed
|
|
|
|
|
var placed_ids: Array[String] = []
|
|
|
|
|
|
|
|
|
|
# First pass: place tiles that have a saved layout position
|
|
|
|
|
for tid in layout_data:
|
|
|
|
|
var def := _find_tile_def(all_tile_defs, tid)
|
|
|
|
|
if def.is_empty():
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
# Plugin/tile not installed — skip gracefully
|
2026-05-21 08:39:15 -04:00
|
|
|
continue
|
|
|
|
|
var instance := PluginManager.instantiate_tile(tid)
|
|
|
|
|
if instance == null:
|
|
|
|
|
continue
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
var pos: Dictionary = layout_data[tid]
|
2026-05-21 08:39:15 -04:00
|
|
|
grid.place_module(instance, pos.col, pos.row, pos.w, pos.h)
|
|
|
|
|
_tile_instances.append(instance)
|
|
|
|
|
placed_ids.append(tid)
|
|
|
|
|
|
|
|
|
|
# Second pass: place any remaining tile_defs at default positions
|
|
|
|
|
# Find the next free grid column
|
|
|
|
|
var next_col: int = 0
|
|
|
|
|
var next_row: int = 0
|
|
|
|
|
if not placed_ids.is_empty():
|
|
|
|
|
var gs := grid.get_grid_size()
|
|
|
|
|
next_col = gs.x
|
|
|
|
|
next_row = 0
|
|
|
|
|
|
|
|
|
|
for def in all_tile_defs:
|
|
|
|
|
if def.id in placed_ids:
|
|
|
|
|
continue
|
|
|
|
|
var instance := PluginManager.instantiate_tile(def.id)
|
|
|
|
|
if instance == null:
|
|
|
|
|
continue
|
|
|
|
|
grid.place_module(instance, next_col, next_row, def.default_w, def.default_h)
|
|
|
|
|
_tile_instances.append(instance)
|
|
|
|
|
placed_ids.append(def.id)
|
|
|
|
|
|
|
|
|
|
# Bump default insertion point
|
|
|
|
|
next_col += def.default_w
|
|
|
|
|
var gs := grid.get_grid_size()
|
|
|
|
|
if next_col >= gs.x and gs.x > 0:
|
|
|
|
|
next_col = 0
|
|
|
|
|
next_row = gs.y
|
|
|
|
|
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
# Connect auto-save signals and tile tracking
|
|
|
|
|
grid.module_placed.connect(_on_module_placed)
|
2026-05-21 08:39:15 -04:00
|
|
|
if grid.has_signal("module_resized"):
|
|
|
|
|
grid.module_resized.connect(_save_layout)
|
|
|
|
|
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
# Listen for plugin activation changes
|
|
|
|
|
if PluginManager.has_signal("plugin_active_changed"):
|
|
|
|
|
PluginManager.plugin_active_changed.connect(_on_plugin_active_changed)
|
|
|
|
|
|
|
|
|
|
# Listen for plugin setting changes to update cached intervals
|
|
|
|
|
if PluginManager.has_signal("plugin_setting_changed"):
|
|
|
|
|
PluginManager.plugin_setting_changed.connect(_on_plugin_setting_changed)
|
|
|
|
|
|
|
|
|
|
# Cache intervals for all placed tiles
|
|
|
|
|
for mod in _tile_instances:
|
|
|
|
|
if is_instance_valid(mod):
|
|
|
|
|
_cache_tile_interval(mod)
|
2026-05-21 08:39:15 -04:00
|
|
|
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
# Start per-tile refresh tick
|
|
|
|
|
_start_tick_timer()
|
2026-05-21 08:39:15 -04:00
|
|
|
|
2026-05-20 15:41:26 -04:00
|
|
|
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
func _start_tick_timer() -> void:
|
2026-05-20 13:08:38 -04:00
|
|
|
var timer := Timer.new()
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
timer.timeout.connect(_tick_refresh)
|
2026-05-20 13:08:38 -04:00
|
|
|
timer.autostart = true
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
timer.wait_time = 0.1
|
2026-05-20 13:08:38 -04:00
|
|
|
add_child(timer)
|
2026-05-20 12:59:50 -04:00
|
|
|
|
|
|
|
|
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
func _tick_refresh() -> void:
|
|
|
|
|
var now := Time.get_ticks_msec()
|
2026-05-20 13:08:38 -04:00
|
|
|
var data: Dictionary = {}
|
2026-05-21 08:39:15 -04:00
|
|
|
for mod in _tile_instances:
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
if not is_instance_valid(mod) or not mod.has_method("refresh"):
|
|
|
|
|
continue
|
|
|
|
|
var interval: int = mod.get_meta("update_interval_ms", 1000)
|
|
|
|
|
var last: int = _last_refresh.get(mod, 0)
|
|
|
|
|
if now - last >= interval:
|
|
|
|
|
_last_refresh[mod] = now
|
|
|
|
|
data["update_interval_ms"] = interval
|
2026-05-20 13:08:38 -04:00
|
|
|
mod.refresh(data)
|
2026-05-21 08:39:15 -04:00
|
|
|
|
|
|
|
|
|
add plugin settings, per-tile refresh, settings menu, touch-friendly UI
- Plugin settings infrastructure in PluginManager with config/plugin_settings.cfg
storage, manifest [settings] sections, CRUD API, plugin_setting_changed signal
- Per-tile refresh intervals: 100ms base tick, per-tile update_interval_ms
metadata, dynamic tween durations clamped to refresh window
- Settings menu (PopupPanel) with General/Plugins/About tabs, plugin
activation toggles, per-plugin settings cog buttons
- Plugin settings popup (PopupPanel) with dynamic UI from setting definitions
(SpinBox/CheckBox/LineEdit), auto-save on change
- Modal behavior: exclusive = true on settings windows
- Touch-friendly sizing: enlarged close buttons, cog buttons, controls, spacing
- Red corner close button redesign with rounded corners, hover/pressed states
- Split system_monitor into local_system_monitor (CPU, Memory) and test plugins
- Plugin activation/deactivation with layout preservation
2026-05-21 12:47:25 -04:00
|
|
|
func _cache_tile_interval(mod: Control) -> void:
|
|
|
|
|
var tid: String = mod.get_meta("tile_id", "")
|
|
|
|
|
if tid.is_empty():
|
|
|
|
|
return
|
|
|
|
|
var parts := tid.split("/")
|
|
|
|
|
if parts.size() < 2:
|
|
|
|
|
return
|
|
|
|
|
var pid: String = parts[0]
|
|
|
|
|
var tile_local: String = parts[1]
|
|
|
|
|
if PluginManager.has_method("get_plugin_setting"):
|
|
|
|
|
var interval: int = PluginManager.get_plugin_setting(pid, tile_local + "_update_interval_ms", 1000)
|
|
|
|
|
mod.set_meta("update_interval_ms", interval)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_module_placed(mod: Control, col: int, row: int) -> void:
|
|
|
|
|
_save_layout(mod, col, row)
|
|
|
|
|
if mod not in _tile_instances:
|
|
|
|
|
_tile_instances.append(mod)
|
|
|
|
|
_cache_tile_interval(mod)
|
|
|
|
|
# Give the new tile a chance to set up before the next refresh
|
|
|
|
|
if mod.has_method("refresh"):
|
|
|
|
|
mod.refresh({})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_plugin_setting_changed(plugin_id: String, key: String, value: Variant) -> void:
|
|
|
|
|
# Update cached interval on affected tiles
|
|
|
|
|
var suffix := "_update_interval_ms"
|
|
|
|
|
if not key.ends_with(suffix):
|
|
|
|
|
return
|
|
|
|
|
for mod in _tile_instances:
|
|
|
|
|
if not is_instance_valid(mod):
|
|
|
|
|
continue
|
|
|
|
|
var tid: String = mod.get_meta("tile_id", "")
|
|
|
|
|
if tid.begins_with(plugin_id + "/"):
|
|
|
|
|
_cache_tile_interval(mod)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_plugin_active_changed(plugin_id: String, active: bool) -> void:
|
|
|
|
|
if active:
|
|
|
|
|
_activate_plugin_tiles(plugin_id)
|
|
|
|
|
else:
|
|
|
|
|
_deactivate_plugin_tiles(plugin_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _deactivate_plugin_tiles(plugin_id: String) -> void:
|
|
|
|
|
# Save layout first to preserve tile positions
|
|
|
|
|
_save_layout()
|
|
|
|
|
|
|
|
|
|
var to_remove: Array[Control] = []
|
|
|
|
|
for mod in _tile_instances:
|
|
|
|
|
if not is_instance_valid(mod):
|
|
|
|
|
continue
|
|
|
|
|
var tid: String = mod.get_meta("tile_id", "")
|
|
|
|
|
if tid.begins_with(plugin_id + "/"):
|
|
|
|
|
to_remove.append(mod)
|
|
|
|
|
|
|
|
|
|
for mod in to_remove:
|
|
|
|
|
grid.remove_module(mod)
|
|
|
|
|
_tile_instances.erase(mod)
|
|
|
|
|
_last_refresh.erase(mod)
|
|
|
|
|
mod.queue_free()
|
|
|
|
|
|
|
|
|
|
# Do NOT save layout again — old positions are preserved in the saved layout
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _activate_plugin_tiles(plugin_id: String) -> void:
|
|
|
|
|
var tile_ids: PackedStringArray = PluginManager.get_plugin_tile_ids(plugin_id)
|
|
|
|
|
if tile_ids.is_empty():
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var layout_data: Dictionary = {}
|
|
|
|
|
if LayoutManager.has_method("get_layout"):
|
|
|
|
|
layout_data = LayoutManager.get_layout()
|
|
|
|
|
|
|
|
|
|
var gs := grid.get_grid_size()
|
|
|
|
|
for tid in tile_ids:
|
|
|
|
|
var instance := PluginManager.instantiate_tile(tid)
|
|
|
|
|
if instance == null:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
var pos: Dictionary = layout_data.get(tid, {})
|
|
|
|
|
if pos.is_empty():
|
|
|
|
|
# No saved position — skip
|
|
|
|
|
instance.queue_free()
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
var col := clampi(pos.get("col", 0), 0, gs.x - 1)
|
|
|
|
|
var row := clampi(pos.get("row", 0), 0, gs.y - 1)
|
|
|
|
|
var w := clampi(pos.get("w", 1), 1, gs.x - col)
|
|
|
|
|
var h := clampi(pos.get("h", 1), 1, gs.y - row)
|
|
|
|
|
|
|
|
|
|
# Only place if the cell is free
|
|
|
|
|
var existing := grid.get_module_at(col, row)
|
|
|
|
|
if existing != null:
|
|
|
|
|
instance.queue_free()
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
grid.place_module(instance, col, row, w, h)
|
|
|
|
|
_tile_instances.append(instance)
|
|
|
|
|
_cache_tile_interval(instance)
|
|
|
|
|
|
|
|
|
|
|
2026-05-21 08:39:15 -04:00
|
|
|
func _save_layout(_mod: Control = null, _a: int = 0, _b: int = 0) -> void:
|
|
|
|
|
if not LayoutManager.has_method("set_tile_position"):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Walk all modules in the grid and store their positions
|
|
|
|
|
var gs := grid.get_grid_size()
|
|
|
|
|
for r in range(gs.y):
|
|
|
|
|
for c in range(gs.x):
|
|
|
|
|
var mod := grid.get_module_at(c, r)
|
|
|
|
|
if mod == null:
|
|
|
|
|
continue
|
|
|
|
|
var tid: String = mod.get_meta("tile_id", "")
|
|
|
|
|
if tid.is_empty():
|
|
|
|
|
continue
|
|
|
|
|
var d := _get_module_grid_data(mod)
|
|
|
|
|
LayoutManager.set_tile_position(tid, d.col, d.row, d.w, d.h)
|
|
|
|
|
|
|
|
|
|
LayoutManager.save_layout()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Mirror of DashboardGrid._get_module_grid_data for layout saving.
|
|
|
|
|
func _get_module_grid_data(module: Control) -> Dictionary:
|
|
|
|
|
return {
|
|
|
|
|
col = module.get_meta("grid_col", 0),
|
|
|
|
|
row = module.get_meta("grid_row", 0),
|
|
|
|
|
w = module.get_meta("grid_w", 1),
|
|
|
|
|
h = module.get_meta("grid_h", 1),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Find a tile def by scoped tile_id.
|
|
|
|
|
func _find_tile_def(defs: Array[Dictionary], tile_id: String) -> Dictionary:
|
|
|
|
|
for d in defs:
|
|
|
|
|
if d.get("id", "") == tile_id:
|
|
|
|
|
return d
|
|
|
|
|
return {}
|