zcatgui/docs/ARCHITECTURE.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

529 lines
14 KiB
Markdown

# zCatGui - Arquitectura y Decisiones de Diseño
> Documento de referencia para el desarrollo de zCatGui
> Última actualización: 2025-12-09
---
## 1. Visión del Proyecto
### 1.1 Objetivo
Crear una librería GUI immediate-mode para Zig con:
1. **Máxima compatibilidad** - Funciona en cualquier ordenador (viejo HP, nuevo Lenovo, servidor SSH)
2. **Software rendering** - No depende de GPU/drivers
3. **Sistema de macros** - Piedra angular: grabar y reproducir acciones
4. **Cross-platform** - Linux, Windows, macOS
5. **Control total** - Sin "magia" del framework, estado explícito
### 1.2 Filosofía
> "Máxima compatibilidad, mínimas dependencias, control total del usuario"
- **Simple > Complejo**: microui demostró que 1,100 LOC son suficientes
- **Explícito > Implícito**: Estado visible, sin callbacks ocultos
- **Funciona siempre**: Software rendering primero, GPU opcional después
---
## 2. Paradigma: Immediate Mode
### 2.1 ¿Qué es Immediate Mode?
```
while (running) {
events = pollEvents(); // 1. Obtener input
updateState(events); // 2. TÚ actualizas estado
commands = drawUI(state); // 3. Generar comandos de dibujo
render(commands); // 4. Renderizar
}
```
**La UI es una función pura del estado**: `UI = f(Estado)`
### 2.2 Immediate vs Retained Mode
| Aspecto | Immediate (zCatGui) | Retained (Fyne) |
|---------|---------------------|-----------------|
| Estado | Tú lo manejas | Framework lo mantiene |
| Callbacks | No hay | Muchos |
| Threading | Simple, predecible | fyne.Do() hell (402 usos) |
| Debugging | Fácil, estado visible | Difícil, estado oculto |
| Testing | Funciones puras | Mock objects complejos |
### 2.3 Por qué Immediate Mode
1. **Sin fyne.Do()**: No hay threading hell
2. **Estado explícito**: Debug simple
3. **Testing trivial**: Funciones puras
4. **Control total**: Sin sorpresas
---
## 3. Arquitectura de Capas
```
┌─────────────────────────────────────────────────────────────┐
│ Capa 4: Widgets de alto nivel │
│ (Table, Panel, Select, Modal, etc.) │
├─────────────────────────────────────────────────────────────┤
│ Capa 3: Sistema de Macros │
│ (Grabación, Reproducción, Inyección de teclas) │
├─────────────────────────────────────────────────────────────┤
│ Capa 2: Core UI │
│ (Context, Layout, Style, Input, Commands) │
├─────────────────────────────────────────────────────────────┤
│ Capa 1: Rendering │
│ (Software Rasterizer, Framebuffer, Fonts) │
├─────────────────────────────────────────────────────────────┤
│ Capa 0: Backend │
│ (SDL2 - ventanas, eventos, display) │
└─────────────────────────────────────────────────────────────┘
```
### 3.1 Capa 0: Backend (SDL2)
**Responsabilidades:**
- Crear/manejar ventana
- Capturar eventos (teclado, mouse)
- Mostrar framebuffer en pantalla
**Por qué SDL2:**
- Cross-platform probado
- Fácil de usar desde Zig
- Tiene software renderer
### 3.2 Capa 1: Rendering
**Componentes:**
- `Framebuffer`: Array 2D de pixels RGBA
- `SoftwareRasterizer`: drawRect, drawLine, drawText
- `Font`: Bitmap fonts + TTF opcional
**Approach: Command List (estilo microui)**
```zig
pub const DrawCommand = union(enum) {
rect: RectCommand,
text: TextCommand,
line: LineCommand,
clip: ClipCommand,
clip_end,
};
```
### 3.3 Capa 2: Core UI
**Componentes:**
- `Context`: Estado global, ID system, command list
- `Layout`: Constraints (reutilizar de zcatui)
- `Style`: Color, fonts (reutilizar de zcatui)
- `Input`: Estado de teclado/mouse
### 3.4 Capa 3: Sistema de Macros
**Piedra angular del proyecto.**
Ver sección 5 para detalles.
### 3.5 Capa 4: Widgets
**Prioritarios (de Simifactu):**
1. Table (editable)
2. Input (text entry)
3. Select (dropdown)
4. Panel (con título)
5. Split (HSplit/VSplit)
6. Button
7. Modal
---
## 4. Decisiones de Diseño Consensuadas
### 4.1 Rendering: Software por Defecto
**Decisión**: Software rendering, GPU opcional en el futuro.
**Razones:**
- Funciona en cualquier ordenador
- SSH con X11 forwarding funciona
- No depende de drivers GPU
- Simple de debugear
**Implementación:**
```
Widgets → DrawCommands → Software Rasterizer → Framebuffer → SDL_Texture → Display
```
### 4.2 Backend: SDL2
**Decisión**: SDL2 como único backend inicial.
**Razones:**
- Cross-platform probado (Linux/Windows/Mac)
- API simple
- Tiene software renderer
- Fácil integración con Zig
**Futuro opcional:**
- X11 directo (Linux)
- Win32 directo (Windows)
### 4.3 Fonts: Híbrido
**Decisión**: Bitmap embebido + TTF opcional.
**Razones:**
- Bitmap siempre funciona (no dependencias)
- TTF para flexibilidad (stb_truetype)
### 4.4 Enfoque Híbrido para Desarrollo
**Decisión**: Estudiar DVUI/microui, implementar desde cero.
**Razones:**
- Aprender haciendo
- Control total del código
- Sin dependencias no deseadas
- Evitar fork con bagaje innecesario
### 4.5 Macros: Teclas Raw
**Decisión**: Grabar teclas literales, no comandos abstractos.
**Ver sección 5.**
---
## 5. Sistema de Macros
### 5.1 Principio Fundamental
**Grabar teclas raw, reproducir teclas raw.**
```
Usuario pulsa: Tab, Tab, Enter, "texto", Escape
Grabamos: [Tab, Tab, Enter, t, e, x, t, o, Escape]
Reproducimos: Inyectamos exactamente esas teclas
```
### 5.2 Por qué Teclas Raw (no Comandos)
| Enfoque | Pros | Contras |
|---------|------|---------|
| **Teclas raw** | Simple, mínima memoria, como Vim | Depende del estado inicial |
| Comandos abstractos | Más robusto | Complejo, más memoria |
**Decisión**: Teclas raw porque:
1. KISS - menos código = menos bugs
2. Vim lo hace así y funciona
3. El estado inicial es controlable
### 5.3 Manejo del Ratón
**Casi todo lo que hace el ratón se puede expresar como teclado:**
| Acción ratón | Equivalente teclado |
|--------------|---------------------|
| Click en botón | Tab hasta focus + Enter |
| Click en fila 5 | Flechas hasta fila 5 |
| Scroll down | PageDown o flechas |
| Drag splitter | Ctrl+flechas |
**Estrategia:**
1. **Fase 1**: Solo teclado (macros de teclas)
2. **Fase 2**: Mouse → traducir a teclas equivalentes
### 5.4 API
```zig
pub const MacroRecorder = struct {
events: ArrayList(KeyEvent),
recording: bool,
pub fn start(self: *MacroRecorder) void {
self.events.clearRetainingCapacity();
self.recording = true;
}
pub fn stop(self: *MacroRecorder) []const KeyEvent {
self.recording = false;
return self.events.items;
}
pub fn record(self: *MacroRecorder, key: KeyEvent) void {
if (self.recording) {
self.events.append(key) catch {};
}
}
pub fn save(self: *MacroRecorder, path: []const u8) !void {
// Serializar a archivo
}
pub fn load(allocator: Allocator, path: []const u8) !MacroRecorder {
// Deserializar de archivo
}
};
pub const MacroPlayer = struct {
pub fn play(
events: []const KeyEvent,
inject_fn: *const fn(KeyEvent) void,
delay_ms: u32,
) void {
for (events) |event| {
inject_fn(event);
if (delay_ms > 0) {
std.Thread.sleep(delay_ms * std.time.ns_per_ms);
}
}
}
};
```
### 5.5 Casos de Uso
1. **Testing automatizado**: Grabar sesión → test
2. **Tutoriales**: Macros que se ejecutan paso a paso
3. **Repetición**: Tarea repetitiva → hotkey
4. **Debugging**: "¿Cómo llegaste a este bug?" → envía macro
5. **Demos**: Grabar demos que se auto-reproducen
---
## 6. Flujo de Datos
### 6.1 Event Loop Principal
```zig
pub fn run(app: *App) !void {
while (app.running) {
// 1. Poll eventos del backend
while (backend.pollEvent()) |raw_event| {
// 2. Traducir a KeyEvent/MouseEvent
const event = translateEvent(raw_event);
// 3. Grabar si macro activo
if (app.macro_recorder.recording) {
app.macro_recorder.record(event);
}
// 4. Actualizar estado de input
app.input.update(event);
}
// 5. Ejecutar lógica de UI (immediate mode)
app.context.beginFrame();
app.drawUI(); // Usuario define esto
app.context.endFrame();
// 6. Renderizar commands
for (app.context.commands.items) |cmd| {
app.rasterizer.execute(cmd);
}
// 7. Presentar framebuffer
backend.present(app.framebuffer);
}
}
```
### 6.2 Widget Pattern
```zig
pub fn button(ctx: *Context, label: []const u8) bool {
// 1. Obtener ID único
const id = ctx.getId(label);
// 2. Obtener bounds del layout
const bounds = ctx.layout.nextRect();
// 3. Verificar interacción
const hovered = bounds.contains(ctx.input.mousePos());
const pressed = hovered and ctx.input.mouseDown(.left);
const clicked = hovered and ctx.input.mouseReleased(.left);
// 4. Determinar estilo
const bg_color = if (pressed) pressed_color
else if (hovered) hover_color
else normal_color;
// 5. Emitir comandos de dibujo
ctx.pushCommand(.{ .rect = .{
.bounds = bounds,
.color = bg_color,
}});
ctx.pushCommand(.{ .text = .{
.pos = bounds.center(),
.text = label,
.color = text_color,
}});
// 6. Retornar si fue clickeado
return clicked;
}
```
---
## 7. Estructura de Archivos
```
zcatgui/
├── src/
│ ├── zcatgui.zig # Entry point, re-exports
│ │
│ ├── core/
│ │ ├── context.zig # Context, ID system
│ │ ├── layout.zig # Constraints (de zcatui)
│ │ ├── style.zig # Color, Style (de zcatui)
│ │ ├── input.zig # InputState
│ │ └── command.zig # DrawCommand
│ │
│ ├── widgets/
│ │ ├── button.zig
│ │ ├── label.zig
│ │ ├── input.zig
│ │ ├── select.zig
│ │ ├── table.zig # CRÍTICO
│ │ ├── panel.zig
│ │ ├── split.zig
│ │ └── modal.zig
│ │
│ ├── render/
│ │ ├── software.zig # Rasterizer
│ │ ├── framebuffer.zig # Pixel buffer
│ │ └── font.zig # Fonts
│ │
│ ├── backend/
│ │ ├── backend.zig # Interface
│ │ └── sdl2.zig # SDL2 impl
│ │
│ └── macro/
│ ├── event.zig # MacroEvent
│ ├── recorder.zig # Grabador
│ ├── player.zig # Reproductor
│ └── storage.zig # Persistencia
├── examples/
├── docs/
├── build.zig
└── CLAUDE.md
```
---
## 8. Código Reutilizable de zcatui
### 8.1 Layout System
Reutilizar de `zcatui/src/layout.zig`:
- `Constraint`: length, min, max, percentage, ratio, fill
- `Layout`: vertical, horizontal
- `Rect`: x, y, width, height, contains, intersection
### 8.2 Style System
Reutilizar de `zcatui/src/style.zig`:
- `Color`: rgb, indexed, ANSI colors
- `Style`: foreground, background, modifiers
**Adaptar para GUI:**
- Añadir alpha channel (RGBA)
- Añadir border_radius (futuro)
- Añadir shadow (futuro)
### 8.3 Conceptos
- Focus management pattern
- Theme system
- Event patterns
---
## 9. Plan de Desarrollo
### Fase 1: Core + Macros (1 semana)
**Objetivo**: Event loop funcional con grabación de macros.
- [ ] SDL2 backend (ventana, eventos)
- [ ] Framebuffer + software rasterizer (rect, text)
- [ ] Context básico
- [ ] MacroRecorder/MacroPlayer
- [ ] Button + Label (para probar)
### Fase 2: Widgets Esenciales (2 semanas)
- [ ] Input (text entry)
- [ ] Select (dropdown)
- [ ] Checkbox
- [ ] List
- [ ] Layout system (de zcatui)
- [ ] Focus management
### Fase 3: Widgets Avanzados (2 semanas)
- [ ] Table editable (CRÍTICO)
- [ ] Split panels
- [ ] Panel con título
- [ ] Modal/Popup
### Fase 4: Pulido (1 semana)
- [ ] Themes
- [ ] TTF fonts
- [ ] Documentación
- [ ] Examples completos
---
## 10. Métricas de Éxito
### 10.1 Funcionales
- [ ] Funciona en Linux sin GPU dedicada
- [ ] Funciona via SSH con X11 forwarding
- [ ] Macros funcionan: grabar → reproducir
- [ ] Table editable comparable a Simifactu
### 10.2 Código
- Target: ~5,000-10,000 LOC total
- Tests para core y widgets
- Examples ejecutables
### 10.3 Performance
- 60 FPS con UI típica
- Startup < 100ms
- Memoria < 50MB
---
## 11. Referencias
### Librerías Estudiadas
| Librería | Lo que tomamos |
|----------|----------------|
| microui | Arquitectura, command list |
| DVUI | Patterns Zig, backend abstraction |
| Dear ImGui | API design, ID system |
| Gio | Constraint system |
### Documentación de Investigación
- `docs/research/GIO_UI_ANALYSIS.md`
- `docs/research/IMMEDIATE_MODE_LIBS.md`
- `docs/research/SIMIFACTU_FYNE_ANALYSIS.md`
### Links Externos
- [microui](https://github.com/rxi/microui)
- [DVUI](https://github.com/david-vanderson/dvui)
- [Dear ImGui](https://github.com/ocornut/imgui)
- [Gio](https://gioui.org/)