zcatgui/docs/research/SIMIFACTU_FYNE_ANALYSIS.md
reugenio c4ea6422dc style: Use consistent lowercase naming (zcatgui, not zCatGui)
Apply TEAM_STANDARDS Norma #25: project names use lowercase
everywhere - repo, directory, module, documentation, imports.

No CamelCase in project names. Consistency = less cognitive friction.

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

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

617 lines
15 KiB
Markdown

# Análisis: Widgets y Funcionalidades de Fyne en Simifactu
> Investigación realizada: 2025-12-09
> Propósito: Extraer requisitos reales de una aplicación de producción
---
## Resumen Ejecutivo
**Simifactu** es una aplicación de facturación empresarial desarrollada en Go con Fyne v2. Este análisis extrae todos los widgets, layouts, y funcionalidades que zcatgui necesitaría para soportar una aplicación similar.
**Proyecto analizado**: `/mnt/cello2/arno/re/recode/go/simifactu/`
---
## 1. Widgets Básicos Usados
### 1.1 Entry Widgets (Campos de Texto)
**Uso masivo**: Prácticamente todos los paneles usan Entry fields.
**Variantes detectadas:**
- `widget.Entry` - Campo de texto básico (single-line)
- `widget.Entry` con `MultiLine = true` - Texto multi-línea
- **AutoComplete Entry** (custom) - Entry con dropdown de sugerencias
**Características críticas:**
- `OnChanged` callback ejecutado en cada tecla
- `OnSubmit` callback al presionar Enter
- `PlaceHolder` text
- `Validation` callbacks
- **ReadOnly** mode
- `FocusGained/FocusLost` callbacks
**Requisito zcatgui:**
```zig
pub const Input = struct {
text: []const u8,
cursor: usize,
placeholder: []const u8,
multiline: bool,
suggestions: ?[][]const u8, // Para autocomplete
on_changed: ?*const fn([]const u8) void,
on_submit: ?*const fn([]const u8) void,
readonly: bool,
};
```
### 1.2 Button Widgets
**Variantes detectadas:**
- `widget.Button` - Botón estándar
- **Button3D** (custom) - Botón con efecto 3D, altura reducida
**Usos identificados:**
- Botones CRUD: Nuevo, Guardar, Eliminar
- Navegación: `<<`, `<`, `>`, `>>`
- Acciones contextuales: Exportar, Importar, Duplicar
**Requisito zcatgui:**
```zig
pub const Button = struct {
label: []const u8,
on_tapped: *const fn() void,
disabled: bool,
style: Style,
importance: enum { primary, secondary, danger },
};
```
### 1.3 Select/Dropdown Widgets
**Variantes detectadas:**
- `widget.Select` - Dropdown básico
- **SelectEntry** - Combo editable (dropdown + entry)
**Usos identificados:**
- Tipo IVA: "21%", "10%", "4%", "0%", "Exento"
- Régimen Equivalencia
- Tipo Documento: "Presupuesto", "Albarán", "Factura"
- Estado Documento: "Borrador", "Confirmado", "Enviado"
- Forma de Pago
**Requisito zcatgui:**
```zig
pub const Select = struct {
options: [][]const u8,
selected: usize,
on_changed: *const fn(usize) void,
placeholder: []const u8,
allow_custom: bool,
};
```
### 1.4 Checkbox
**Usos detectados:**
- "Es Sociedad"
- Selección múltiple en importación
**Requisito zcatgui:**
```zig
pub const Checkbox = struct {
checked: bool,
label: []const u8,
on_changed: *const fn(bool) void,
};
```
---
## 2. Widgets Avanzados/Complejos
### 2.1 Table Widget ⭐ CRÍTICO
**El widget MÁS importante** de Simifactu - usado en prácticamente todas las vistas.
**Implementación actual:**
- Fork personalizado de `fyne/widget/table.go`
- **AdvancedTable** wrapper (1000+ líneas)
**Características implementadas (TODAS necesarias):**
#### Virtualización y Performance
- Lazy rendering (solo filas visibles)
- Scroll eficiente con cache de celdas
- Invalidación selectiva de cache
#### Navegación Teclado
```
Flechas: Up/Down/Left/Right (navegación entre celdas)
Enter: Editar celda actual
Space: Activar edición con celda vacía
Tab: Siguiente celda editable
Escape: Cancelar edición / Revertir cambios
Ctrl+N: Nueva fila
Ctrl+B/Supr: Borrar fila
Home/End: Primera/Última fila
```
#### Edición In-Situ
- Doble-click para editar celda
- Entry overlay que aparece sobre celda
- Auto-submit al cambiar fila
- Validación por columna
- **Tipos de celda**: Text, Number, Money, Date, Select
#### Indicadores Visuales de Estado
- Filas nuevas: Icono verde
- Filas modificadas: Icono naranja
- Filas borradas: Icono rojo
#### Ordenamiento (Sorting)
- Click en header para ordenar
- Indicadores visuales ▲/▼
#### Colores Dinámicos
```zig
pub const TableColors = struct {
header_background: Color,
header_text: Color,
row_normal: Color,
row_hover: Color,
cell_active: Color,
cell_editing: Color,
selection: Color,
};
```
#### Schema-Driven Configuration
```zig
pub const TableSchema = struct {
columns: []ColumnDef,
show_state_indicators: bool,
allow_keyboard_nav: bool,
allow_edit: bool,
allow_sort: bool,
};
pub const ColumnDef = struct {
name: []const u8,
width: f32,
column_type: enum { text, number, money, date, select },
editable: bool,
validator: ?*const fn([]const u8) bool,
};
```
### 2.2 InnerWindow Widget ⭐ CRÍTICO
**Fork personalizado de Fyne** - contenedor con barra de título y bordes.
**Características:**
- Título personalizable y dinámico
- Barra de título con color configurable
- Borde exterior con color configurable
- Fondo contenido separado
- Botón cerrar opcional
- Callbacks: `OnFocusGained`, `OnFocusLost`, `OnTappedBar`
- Resize programático
**Uso en Simifactu:**
- Todos los paneles principales están en InnerWindows
- Layout: 3-4 InnerWindows en HSplit/VSplit
**Requisito zcatgui:**
```zig
pub const Panel = struct {
title: []const u8,
content: Widget,
border: BorderStyle,
title_style: Style,
closable: bool,
on_focus_gained: ?*const fn() void,
on_focus_lost: ?*const fn() void,
};
```
### 2.3 List Widget
**Uso detectado:**
- Navegador estilo OpenOffice (carpetas izquierda, archivos derecha)
**Requisito zcatgui:**
```zig
pub const List = struct {
items: []ListItem,
selected: i32,
on_selected: *const fn(usize) void,
};
pub const ListItem = struct {
text: []const u8,
icon: ?Symbol,
};
```
---
## 3. Layouts Usados
### 3.1 Container Layouts (Fyne)
```go
// Border - contenido con bordes fijos
container.NewBorder(top, bottom, left, right, center)
// VBox/HBox - stacks
container.NewVBox(widget1, widget2, widget3)
container.NewHBox(widget1, widget2)
// Grid
container.NewGridWithColumns(3, campo1, campo2, campo3)
// HSplit/VSplit - CRÍTICO
split := container.NewHSplit(leftPanel, rightPanel)
split.SetOffset(0.25) // 25% izquierda
// Stack - overlay
container.NewStack(background, content, overlayPopup)
// Padded
container.NewPadded(widget)
```
### 3.2 Layout Principal de Simifactu
```
┌───────────────────────────────────────────────────────────┐
│ Status Line (Border Top - altura fija) │
├─────────┬─────────────────────┬────────────────────────────┤
│ WHO │ WHO Detail │ Document Detail (FULL) │
│ List │ ─────────────────── │ - Cabecera (40%) │
│ (20%) │ Documents List │ - Líneas (60%) │
│ │ │ │
│ │ (VSplit 50/50) │ (VSplit integrado) │
│ │ (35%) │ (45%) │
└─────────┴─────────────────────┴────────────────────────────┘
HSplit(0.20) HSplit(0.4347)
```
**Requisito zcatgui:**
```zig
pub const Split = struct {
direction: enum { horizontal, vertical },
children: [2]Widget,
offset: f32, // 0.0-1.0
draggable: bool, // Ctrl+flechas para ajustar
};
```
---
## 4. Diálogos y Popups
### 4.1 Modal Dialogs Detectados
```go
// Confirmación (Yes/No)
dialog.ShowConfirm("Eliminar", "¿Seguro?", callback, window)
// Información (solo OK)
dialog.ShowInformation("Título", "Mensaje", window)
// Error (solo OK)
dialog.ShowError(err, window)
// Entrada de texto
dialog.ShowEntryDialog("Nombre", "Introduzca:", "default", callback, window)
// File picker
dialog.ShowFileOpen(callback, window)
// Folder picker
dialog.ShowFolderOpen(callback, window)
```
### 4.2 Custom Dialogs Detectados
- **Import Window** - Navegador con checkboxes
- **Export Window** - Selector formato + opciones
- **Edit Date Dialog** - Picker fecha con calendario
- **Empresas Window** - Gestión multi-empresa
---
## 5. Eventos y Callbacks
### 5.1 Eventos Teclado ⭐ MUY IMPORTANTE
**OnTypedKey** - Usado en TODOS los paneles:
```go
window.Canvas().SetOnTypedKey(func(key *fyne.KeyEvent) {
// Shortcuts globales
if key.Name == fyne.KeyF2 {
showEmpresasWindow()
return
}
// Ctrl+1/2/3 - Layout presets
if mods&fyne.KeyModifierControl != 0 {
switch key.Name {
case "1": applyLayout1()
case "2": applyLayout2()
}
}
})
```
**Requisito zcatgui:**
```zig
pub const KeyEvent = struct {
key: Key,
modifiers: KeyModifiers,
};
pub const KeyModifiers = packed struct {
control: bool,
alt: bool,
shift: bool,
};
// Handler retorna si consume el evento
pub const OnKey = *const fn(KeyEvent) bool;
```
### 5.2 Eventos Mouse
```go
widget.OnTapped = func(*fyne.PointEvent) { click() }
table.OnCellDoubleTapped = func(row, col int) { edit(row, col) }
widget.OnMouseIn = func(*desktop.MouseEvent) { hover() }
widget.OnMouseOut = func(*desktop.MouseEvent) { unhover() }
```
### 5.3 Focus Events
```go
widget.OnFocusGained = func() { activate() }
widget.OnFocusLost = func() { deactivate() }
canvas.Focus(widget) // Programático
```
---
## 6. El Problema fyne.Do() ⭐⭐⭐ CRÍTICO
### 6.1 El Problema del Threading
**402 usos de `fyne.Do()`** detectados en el proyecto.
**Razón**: Fyne requiere que TODAS las operaciones UI se ejecuten en el thread principal.
```go
// PATTERN OBLIGATORIO en Fyne
go func() {
result := heavyComputation() // Background
fyne.Do(func() {
label.SetText(result) // DEBE estar en fyne.Do()
table.Refresh()
})
}()
```
**Errores si no se usa fyne.Do():**
```
panic: concurrent map writes
SIGSEGV: segmentation fault
GL context not current
```
### 6.2 Por qué zcatgui NO tiene este problema
**Immediate mode es inherentemente thread-safe por diseño:**
- No hay estado compartido del framework
- Buffer es solo memoria (no GL context)
- Render es single-threaded
- No necesita equivalente a fyne.Do()
**Pero sí necesitamos:**
- AsyncLoop para queries BD
- Timer system para auto-save
- Event queue
---
## 7. Theming y Colores
### 7.1 Sistema de Colores Dual
**Sistema 1: Colores por Panel:**
```go
type PanelColors struct {
Fondo Color
Header Color
Texto Color
Entry_Fondo Color
Entry_Texto Color
Button_Fondo Color
Tabla_Header Color
Celda_Activa Color
// ... 20+ colores por panel
}
dataManager.GetColorForPanel("who_detail", "Fondo")
```
**Sistema 2: Colores Globales Botones:**
```go
type ButtonColors struct {
ActivoButtonFondo Color
InactivoButtonFondo Color
NuevoButtonFondo Color
EliminarButtonFondo Color
}
```
### 7.2 Hot-Reload de Colores
```go
// Panel se registra como observer
dataManager.RegisterColorObserver("who_detail", panel)
// Cuando usuario cambia color
dataManager.NotifyColorsChange("who_detail")
// Panel actualiza
func (p *Panel) NotifyColorsChange() {
p.applyColors()
}
```
### 7.3 Persistencia
- Archivo texto KV: `who_detail.Fondo = 40,44,52`
- JSON backup
- Base de datos: tabla `config_backup`
---
## 8. Características Especiales
### 8.1 Auto-Save System
```go
dataManager.StartAutoSaveTimer()
// Guarda config cada N segundos si isDirty == true
```
### 8.2 File Watcher (Hot-Reload Config)
```go
dataManager.StartConfigFileWatcher()
// Detecta cambios externos, recarga automáticamente
```
### 8.3 Export/Import Data
- JSON completo (clientes + documentos + líneas)
- Exportación selectiva (checkboxes)
- Importación con merge
- Progress tracking
### 8.4 Multi-Empresa
- Tabla `empresas` en BD
- Selector empresa activa
- Datos aislados por empresa_uuid
### 8.5 Templates Sistema
- Plantillas PDF personalizables
- Editor visual
- Vista previa en tiempo real
---
## 9. Widgets Third-Party (fynex-widgets)
**Librería vendorizada**: `/third_party/fynex-widgets/`
| Widget | Descripción | Uso |
|--------|-------------|-----|
| **AutoComplete** | Entry con dropdown sugerencias | Población, Provincia, País |
| **DateEntry** | Entry con calendar picker | Fechas |
| **NumEntry** | Entry solo números | Cantidades, precios |
| **SelectEntry** | Combo editable | Custom values |
| **Calendar** | Widget calendario | Selección fecha |
| **Tooltips** | Ayuda contextual | Hover |
---
## 10. Resumen de Requisitos para zcatgui
### 10.1 Widgets Necesarios (Orden Prioridad)
| # | Widget | Prioridad | Notas |
|---|--------|-----------|-------|
| 1 | **Table** | CRÍTICA | Edición in-situ, state indicators, sorting |
| 2 | **Input** | CRÍTICA | Autocomplete, validation |
| 3 | **Select** | CRÍTICA | Dropdown selection |
| 4 | **Panel** | ALTA | Título dinámico, bordes coloreados |
| 5 | **Split** | ALTA | HSplit/VSplit draggable |
| 6 | **Button** | ALTA | Disabled, importance levels |
| 7 | **Modal** | MEDIA | Diálogos modales |
| 8 | **List** | MEDIA | Lista seleccionable |
| 9 | **Checkbox** | MEDIA | Toggle boolean |
| 10 | **Label** | BAJA | Texto estático |
### 10.2 Layouts Necesarios
| Layout | Status |
|--------|--------|
| VBox/HBox | Usar Layout constraints |
| Grid | Usar Layout constraints |
| HSplit/VSplit | **CRÍTICO - Implementar** |
| Stack (Overlay) | Para popups |
| Border | Emular con constraints |
### 10.3 Features Críticos
1. **Table con edición in-situ** - 80% del trabajo
2. **Split panels draggables** - Layout principal
3. **Select/Dropdown** - 20+ lugares de uso
4. **Sistema de focus** - Navegación teclado
5. **Hot-reload themes** - Cambio colores en runtime
### 10.4 Estimación de Trabajo
| Componente | Líneas | Tiempo |
|------------|--------|--------|
| Table editable | 1500 | 2-3 semanas |
| Select/Dropdown | 300 | 3-4 días |
| Split layout | 200 | 2-3 días |
| Panel widget | 150 | 2 días |
| AutoComplete | 250 | 3-4 días |
| Modal/Dialog | 200 | 2-3 días |
| **TOTAL** | ~2600 | 4-5 semanas |
---
## 11. Ventajas de Portar a GUI Immediate-Mode
### Por qué Vale la Pena
1. **Sin fyne.Do()**: Código más simple, sin threading hell
2. **Performance**: Software rendering predecible
3. **Control total**: Sin "magia" del framework
4. **Testing**: Funciones puras, fácil de testear
5. **Debugging**: Estado visible, reproducible
### Comparación Threading
| Fyne (Retained) | zcatgui (Immediate) |
|-----------------|---------------------|
| 402 usos fyne.Do() | 0 equivalentes |
| Callbacks async | Polling síncrono |
| Estado oculto | Estado explícito |
| Race conditions | Sin races |
---
## 12. Archivos Clave Analizados
```
/cmd/simifactu/main.go (881 líneas)
/internal/ui/components/advanced_table/*.go (2000+ líneas)
/internal/ui/panels_v3/panels/who_detail/ui_layout.go (300 líneas)
/internal/ui/panels_v3/panels/document_detail/ui_layout.go (400 líneas)
/internal/ui/dialogs/import_window.go (500 líneas)
/third_party/fynex-widgets/autocomplete.go (600 líneas)
/internal/ui/panels_v3/data/manager_colors.go (800 líneas)
```
**Total analizado**: ~10,000 líneas de código + 5000 líneas de docs