Pad/doc/editor_implementation_plan.md
Greg Pomerantz 0dac354f2a fix(editor): wire key events to cursor movement
- Replace gtx.Event(nil) with key.Filter{Focus: focusedID} and
  key.FocusFilter{Target: focusedID} so Gio correctly routes key
  and edit events to the focused editor text field.

- Convert EditorState.CursorPosition (byte offset) to line/column
  coordinates for accurate cursor rendering.

- Add debug logging in HandleKeyDown and HandleCursorMove.
2026-06-03 07:41:18 -04:00

4.1 KiB

Editor Implementation Plan

1. Design Principles

  • Single-Owner Pattern: The logic goroutine is the exclusive authority for modifying the editor buffer, cursor, and selection state.
  • Asynchronous I/O: File saving and loading operations are delegated to the worker pool. The UI never blocks on disk operations.
  • Buffer-Based Editing: For performance, the editor will eventually utilize a gap buffer or rope data structure. For MVP, string manipulation is acceptable if performance targets are met.
  • Event-Driven: Keyboard input, mouse interactions, and menu actions are passed through the existing inputChan to the logic goroutine.

2. Core State Structure

The State struct in internal/editor/state.go will be extended to track active editing session data:

type EditorState struct {
    Buffer          string        // Document content (or gap buffer)
    CursorPosition  int           // Byte offset
    SelectionStart  int           // -1 if no selection
    SelectionEnd    int           // -1 if no selection
    UndoStack       []EditCommand // For undo/redo
    Dirty           bool          // Needs save
}

3. Implementation Phases

Phase 1: Basic Buffer Management & Cursor

  • Define EditorState (Cursor, Selection, Dirty flag).
  • Implement cursor navigation (Arrow keys: Left, Right, Up, Down).
  • Implement basic buffer updates (Insert character, Delete/Backspace).
  • Implement cursor display and blink animation.
  • Update EditorLayout to display cursor and selection highlight.

Phase 2: File IO Integration

  • Implement SaveFile handler: Dispatch WriteFileTask to worker pool.
  • Implement LoadFile handler: Dispatch ReadFileTask to worker pool (existing in logic.go).
  • Status bar integration: Show "Saving..." indicator, "Modified" status.

Phase 3: Text Editing Operations

  • Implement Cut/Copy/Paste interactions.
  • Implement multi-line text navigation (Home/End, PageUp/PageDown).
  • Implement text selection display and mouse interaction.
  • Implement soft-wrap toggle (already exists, need to verify logic).

Phase 4: Polish & Advanced Features

  • Undo/Redo stack implementation.
  • Performance testing with large files (>1MB).

4. Interaction Flow (Example: Keyboard/Mouse Input)

  1. User performs an action (types a character, taps an icon).
  2. UI (Renderer) registers the element's interaction (event.Op for keys, gesture.Add for mouse) within its clip context.
  3. Main Loop captures the event (using key.Filter for keys, gesture.Update for mouse).
  4. Main Loop generates an InputEvent and sends it to inputChan.
  5. Logic Goroutine receives the event, triggers the corresponding Handler in TheState.
  6. Logic Goroutine modifies state, and sends updated elements back through frameChan.
  7. Renderer paints the new frame.

5. Performance Targets

Operation Target Mechanism
Typing latency < 16ms Single-owner logic update, immediate frame redraw
File Load (1MB) < 100ms Async worker pool read, background loading
File Save < 100ms Async worker pool write
Undo/Redo < 10ms O(1) or O(N) memory-based buffer update

7. Input Handling Mechanism

7.1 Mouse/Touch Input

  • Uses gesture.Click and gesture.Scroll.
  • Registered via event.Op and gesture Add() within the clip context of the element.
  • Processed by Renderer.CheckGestures and dispatched via InputEvent to the logic goroutine.

7.2 Keyboard Input

  • Registration: Elements register as input handlers using event.Op(gtx.Ops, elementID). This tags the current clip context for input routing.
  • Focus: Focus is managed by the logic goroutine. When an element is focused, key.FocusCmd{Tag: elementID} is submitted to the operation stack.
  • Filtering: Keyboard events are processed in the main loop using key.Filter{Focus: focusedElementID} to ensure events are only routed to the active element.
  • Routing: key.Event (for key presses) and key.EditEvent (for text input) are converted into InputEvent structs and sent to the logic goroutine via inputChan for state updates.