add plugin system with plugin/layout managers
- plugin_manager: scans res://plugins/*/plugin.cfg, loads tile definitions - plugin_tile: new base class for plugin tiles, extends ModuleBase - layout_manager: save/restore tile grid positions per named layout - migrate existing cpu/memory/testing tiles into system_monitor plugin - add module_resized signal to DashboardGrid for auto-save - dashboard reads from PluginManager + LayoutManager instead of hardcoded paths - project.godot registers PluginManager and LayoutManager autoloads - AGENTS.md updated with new structure and conventions
This commit is contained in:
parent
63af41ea61
commit
f43676e46c
19 changed files with 528 additions and 39 deletions
|
|
@ -4,13 +4,13 @@ extends PanelContainer
|
|||
|
||||
@onready var grid: DashboardGrid = %DashboardGrid
|
||||
|
||||
var _modules: Array = []
|
||||
var _tile_instances: Array[Control] = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_set_background()
|
||||
if not Engine.is_editor_hint():
|
||||
_add_modules()
|
||||
_load_tiles_from_plugins()
|
||||
|
||||
|
||||
func _set_background() -> void:
|
||||
|
|
@ -24,28 +24,124 @@ func _set_background() -> void:
|
|||
add_theme_stylebox_override("panel", bg)
|
||||
|
||||
|
||||
func _add_modules() -> void:
|
||||
var cpu := preload("res://panels/cpu/cpu_module.tscn").instantiate()
|
||||
grid.place_module(cpu, 0, 0)
|
||||
_modules.append(cpu)
|
||||
func _load_tiles_from_plugins() -> void:
|
||||
if not PluginManager.has_method("get_all_tile_defs"):
|
||||
return
|
||||
|
||||
var mem := preload("res://panels/memory/memory_module.tscn").instantiate()
|
||||
grid.place_module(mem, 1, 0)
|
||||
_modules.append(mem)
|
||||
var all_tile_defs: Array[Dictionary] = PluginManager.get_all_tile_defs()
|
||||
if all_tile_defs.is_empty():
|
||||
return
|
||||
|
||||
var test := preload("res://panels/testing/testing_module.tscn").instantiate()
|
||||
grid.place_module(test, 2, 0)
|
||||
_modules.append(test)
|
||||
# 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 pos: Dictionary = layout_data[tid]
|
||||
var def := _find_tile_def(all_tile_defs, tid)
|
||||
if def.is_empty():
|
||||
continue
|
||||
var instance := PluginManager.instantiate_tile(tid)
|
||||
if instance == null:
|
||||
continue
|
||||
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
|
||||
|
||||
# Connect auto-save signals
|
||||
grid.module_placed.connect(_save_layout)
|
||||
if grid.has_signal("module_resized"):
|
||||
grid.module_resized.connect(_save_layout)
|
||||
|
||||
# Start refresh timer
|
||||
_start_refresh_timer()
|
||||
|
||||
|
||||
func _start_refresh_timer() -> void:
|
||||
var interval: float = 1.0
|
||||
if ConfigManager.has_method("get_setting"):
|
||||
var val: Variant = ConfigManager.get_setting("performance_refresh_interval", 1.0)
|
||||
if typeof(val) == TYPE_FLOAT or typeof(val) == TYPE_INT:
|
||||
interval = float(val)
|
||||
|
||||
var timer := Timer.new()
|
||||
timer.timeout.connect(_refresh_modules)
|
||||
timer.autostart = true
|
||||
timer.wait_time = 1.0
|
||||
timer.wait_time = interval
|
||||
add_child(timer)
|
||||
|
||||
|
||||
func _refresh_modules() -> void:
|
||||
var data: Dictionary = {}
|
||||
for mod in _modules:
|
||||
for mod in _tile_instances:
|
||||
if is_instance_valid(mod) and mod.has_method("refresh"):
|
||||
mod.refresh(data)
|
||||
|
||||
|
||||
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 {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue