zcatgui/docs/research/LEGO_PANELS_SYSTEM.md
reugenio 6ac3856ae2 feat: zcatgui v0.5.0 - Complete widget library + research docs
Widgets implemented (13 total):
- Label: Static text with alignment
- Button: With importance levels (primary/normal/danger)
- TextInput: Single-line text entry with cursor
- Checkbox: Boolean toggle
- Select: Dropdown selection
- List: Scrollable selectable list
- Focus: Focus manager with tab navigation
- Table: Editable table with dirty tracking, keyboard nav
- Split: HSplit/VSplit draggable panels
- Panel: Container with title bar, collapsible
- Modal: Dialogs (alert, confirm, inputDialog)
- AutoComplete: ComboBox with prefix/contains/fuzzy matching

Core improvements:
- InputState now tracks keyboard state (keys_down, key_events)
- Full keyboard navigation for Table widget

Research documentation:
- WIDGET_COMPARISON.md: zcatgui vs DVUI vs Gio vs zcatui
- SIMIFACTU_ADVANCEDTABLE.md: Analysis of 10K LOC table component
- LEGO_PANELS_SYSTEM.md: Modular panel composition architecture

Examples:
- widgets_demo.zig: All basic widgets showcase
- table_demo.zig: Table, Split, Panel demonstration

All tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 11:00:49 +01:00

378 lines
9.1 KiB
Markdown

# Sistema Lego Panels de Simifactu
> Fecha: 2025-12-09
> Proposito: Documentar arquitectura Lego Panels para aplicar en zcatgui
---
## Resumen
**Lego Panels** es una arquitectura de composicion modular de UI donde:
- Cada panel es **autonomo** (maneja su propio estado, UI y logica)
- Los paneles son **reutilizables** (mismo panel en diferentes ventanas)
- Las ventanas se construyen **componiendo** paneles (no herencia)
- La comunicacion usa **patron Observer** (paneles no se conocen entre si)
**Resultados en Simifactu:**
- 83 modulos
- ~112 lineas por archivo (target: 150 max)
- 85-98% reutilizacion de codigo
- 3x mas rapido crear ventanas nuevas
---
## 1. Principios Core
### 1.1 Panel Autonomo
Cada panel:
- Tiene su propio estado interno
- Construye su propia UI
- Maneja sus propios eventos
- No conoce a otros paneles
- Se comunica via DataManager (observer)
### 1.2 Composicion vs Herencia
```
MAL: WindowA hereda de BaseWindow y override metodos
BIEN: WindowA compone PanelX + PanelY + PanelZ
```
### 1.3 Single Source of Truth
DataManager es el hub central:
- Todas las entidades pasan por DataManager
- Paneles observan cambios
- Sin comunicacion directa panel-a-panel
---
## 2. Patrones de Composicion
### 2.1 Vertical Composite (2 paneles)
```
┌─────────────────────┐
│ Top Panel │
├─────────────────────┤
│ Bottom Panel │
└─────────────────────┘
```
**Uso**: Division simple top/bottom
### 2.2 Center Composite (3 paneles)
```
┌─────────────────────┐
│ WHO Detail │
├─────────────────────┤
│ Document Detail │
├─────────────────────┤
│ Lines │
└─────────────────────┘
```
**Uso**: Detalle de documento (master-detail-lines)
### 2.3 Config Composite (HSplit + dynamic)
```
┌────────────┬────────────────────┐
│ │ │
│ Categories │ Dynamic Editor │
│ (nav) │ (table/form/etc) │
│ │ │
└────────────┴────────────────────┘
```
**Uso**: Configuracion (lista izq + editor der cambiante)
### 2.4 Docs Composite (2 columnas)
```
┌────────────────┬───────────────────┐
│ │ WHO Compact │
│ Doc List ├───────────────────┤
│ │ Document Detail │
└────────────────┴───────────────────┘
```
**Uso**: Lista de documentos con preview
---
## 3. Interfaz AutonomousPanel
```go
type AutonomousPanel interface {
// Identidad
GetPanelID() string // "who_list", "doc_detail"
GetPanelType() string // "list", "detail", "composite"
GetEntityType() string // "WHO", "Document", "Line"
// Estado
GetSelectedEntity() interface{}
SetSelectedEntity(interface{}) error
// UI
BuildUI() fyne.CanvasObject
Refresh()
// Lifecycle
Initialize() error
Destroy() error
}
```
---
## 4. Patron Observer
### 4.1 Registro
```go
// Panel se registra para recibir cambios de "Document"
dataManager.AddObserverForType("Document", myPanel)
```
### 4.2 Notificacion
```go
// Cuando cambia un documento
dataManager.NotifyObserversWithChange(NewDataChange(
entityType: "Document",
changeType: "UPDATE",
data: doc,
))
```
### 4.3 Recepcion
```go
// Panel responde al cambio
func (p *MyPanel) OnDataChanged(timestamp time.Time) {
// Refrescar UI si afecta mis datos
}
```
### 4.4 Dual Notification
```go
OnDataChanged() // Cambios UI locales
OnDataChangedDB() // Cambios en DB (invalidar cache + recargar)
```
---
## 5. Aplicacion a zcatgui
### 5.1 Propuesta de Interfaz
```zig
/// Panel autonomo
pub const AutonomousPanel = struct {
/// Identificador unico
id: []const u8,
/// Tipo de panel
panel_type: PanelType,
/// Build UI - retorna commands
build_fn: *const fn(*Context) void,
/// Refresh callback
refresh_fn: ?*const fn(*AutonomousPanel) void = null,
/// Estado interno (opaco)
state: *anyopaque,
/// Destructor
deinit_fn: ?*const fn(*AutonomousPanel) void = null,
};
pub const PanelType = enum {
list, // Lista de items
detail, // Detalle de un item
table, // Tabla editable
composite, // Compuesto de otros paneles
};
```
### 5.2 Patron Composite
```zig
/// Composite vertical (2 paneles)
pub const VerticalComposite = struct {
top: *AutonomousPanel,
bottom: *AutonomousPanel,
split_ratio: f32 = 0.5,
pub fn build(self: *VerticalComposite, ctx: *Context) void {
const split = widgets.split.vsplit(ctx, self.split_ratio);
self.top.build_fn(ctx.withArea(split.first));
self.bottom.build_fn(ctx.withArea(split.second));
}
};
/// HSplit composite (lista + detalle)
pub const HSplitComposite = struct {
left: *AutonomousPanel,
right: *AutonomousPanel,
split_ratio: f32 = 0.3,
pub fn build(self: *HSplitComposite, ctx: *Context) void {
const split = widgets.split.hsplit(ctx, self.split_ratio);
self.left.build_fn(ctx.withArea(split.first));
self.right.build_fn(ctx.withArea(split.second));
}
};
```
### 5.3 DataManager Simplificado
```zig
/// Observer callback
pub const DataObserver = struct {
on_data_changed: ?*const fn(entity_type: []const u8, data: ?*anyopaque) void = null,
context: ?*anyopaque = null,
};
/// Data manager (singleton)
pub const DataManager = struct {
observers: std.StringHashMap(std.ArrayList(DataObserver)),
pub fn addObserver(self: *DataManager, entity_type: []const u8, observer: DataObserver) void {
// ...
}
pub fn notifyChange(self: *DataManager, entity_type: []const u8, data: ?*anyopaque) void {
if (self.observers.get(entity_type)) |observers| {
for (observers.items) |obs| {
if (obs.on_data_changed) |callback| {
callback(entity_type, data);
}
}
}
}
};
```
---
## 6. Ejemplo de Uso
### 6.1 Definir Panel Simple
```zig
const CustomerListPanel = struct {
state: ListState,
customers: []Customer,
pub fn build(ctx: *Context) void {
widgets.panel.panel(ctx, "Customers", .{});
const result = widgets.list.list(ctx, &state, customers);
if (result.selection_changed) {
dataManager.notifyChange("Customer", customers[result.selected.?]);
}
}
};
```
### 6.2 Definir Panel Detalle
```zig
const CustomerDetailPanel = struct {
state: FormState,
customer: ?Customer,
pub fn build(ctx: *Context) void {
widgets.panel.panel(ctx, "Customer Detail", .{});
if (self.customer) |c| {
widgets.label.label(ctx, c.name);
widgets.label.label(ctx, c.email);
} else {
widgets.label.label(ctx, "Select a customer");
}
}
pub fn onDataChanged(entity_type: []const u8, data: ?*anyopaque) void {
if (std.mem.eql(u8, entity_type, "Customer")) {
self.customer = @ptrCast(data);
}
}
};
```
### 6.3 Componer en Ventana
```zig
pub fn main() !void {
var list_panel = CustomerListPanel{};
var detail_panel = CustomerDetailPanel{};
// Registrar observers
dataManager.addObserver("Customer", .{
.on_data_changed = detail_panel.onDataChanged,
});
// Componer
var composite = HSplitComposite{
.left = &list_panel.asPanel(),
.right = &detail_panel.asPanel(),
.split_ratio = 0.3,
};
// Main loop
while (running) {
composite.build(&ctx);
// ...
}
}
```
---
## 7. Plan de Implementacion
### Fase 1: Panel Interface (2 horas)
- Definir AutonomousPanel trait/interface
- Helpers para crear paneles
### Fase 2: Composites Basicos (2 horas)
- VerticalComposite
- HSplitComposite
- TabComposite (tabs)
### Fase 3: DataManager Simple (3 horas)
- Observer registration
- Notify observers
- Entity type filtering
### Fase 4: Ejemplo Completo (2 horas)
- Master-detail demo
- Config-like layout demo
**Total estimado: 9 horas**
---
## 8. Beneficios Esperados
1. **Modularidad**: Paneles independientes, faciles de testear
2. **Reutilizacion**: Mismo panel en multiples ventanas
3. **Mantenibilidad**: Bugs aislados a paneles especificos
4. **Escalabilidad**: Nuevas ventanas = componer paneles existentes
5. **Decoupling**: Sin dependencias directas entre paneles
---
## 9. Referencias
- `/mnt/cello2/arno/re/recode/go/simifactu-fyne/docs/arquitectura_canonica/01_filosofia_lego.md`
- `/mnt/cello2/arno/re/recode/go/simifactu-fyne/docs/AUDITORIA_ARQUITECTURA_LEGO_V3_SEPT16.md`
- `/mnt/cello2/arno/re/recode/go/simifactu-fyne/internal/ui/panels_v3/panels/`