# 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