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>
15 KiB
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.EntryconMultiLine = true- Texto multi-línea- AutoComplete Entry (custom) - Entry con dropdown de sugerencias
Características críticas:
OnChangedcallback ejecutado en cada teclaOnSubmitcallback al presionar EnterPlaceHoldertextValidationcallbacks- ReadOnly mode
FocusGained/FocusLostcallbacks
Requisito zcatgui:
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:
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:
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:
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
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
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:
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:
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)
// 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:
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
// 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:
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:
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
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
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.
// 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:
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:
type ButtonColors struct {
ActivoButtonFondo Color
InactivoButtonFondo Color
NuevoButtonFondo Color
EliminarButtonFondo Color
}
7.2 Hot-Reload de Colores
// 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
dataManager.StartAutoSaveTimer()
// Guarda config cada N segundos si isDirty == true
8.2 File Watcher (Hot-Reload Config)
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
empresasen 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
- Table con edición in-situ - 80% del trabajo
- Split panels draggables - Layout principal
- Select/Dropdown - 20+ lugares de uso
- Sistema de focus - Navegación teclado
- 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
- Sin fyne.Do(): Código más simple, sin threading hell
- Performance: Software rendering predecible
- Control total: Sin "magia" del framework
- Testing: Funciones puras, fácil de testear
- 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