zcatgui/docs/research/SIMIFACTU_FYNE_ANALYSIS.md
reugenio 59c597fc18 feat: zCatGui v0.1.0 - Initial project setup
Immediate Mode GUI library for Zig with software rendering.

Core features:
- SDL2 backend for cross-platform window/events
- Software rasterizer (works everywhere, including SSH)
- Macro recording system (cornerstone feature, like Vim)
- Command-list rendering (DrawRect, DrawText, etc.)
- Layout system with constraints
- Color/Style system with themes

Project structure:
- src/core/: context, command, input, layout, style
- src/macro/: MacroRecorder, MacroPlayer, MacroStorage
- src/render/: Framebuffer, SoftwareRenderer, Font
- src/backend/: Backend interface, SDL2 implementation
- examples/: hello.zig, macro_demo.zig
- docs/: Architecture, research (Gio, immediate-mode libs, Simifactu)

Build: zig build (requires SDL2-devel)
Tests: 16 tests passing

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

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

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.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:

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 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