53 lines
3.1 KiB
Markdown
53 lines
3.1 KiB
Markdown
# Iced Architecture Patterns (bDS Rust Rewrite)
|
|
|
|
Iced 0.13, wgpu renderer, muda for native menus, rfd for file dialogs.
|
|
|
|
## Message Design
|
|
|
|
- Root `Message` enum in `app.rs`: `MenuEvent(MenuId)`, `Noop`, plus child view variants.
|
|
- Child view messages wrapped in parent enum variants: `Message::Editor(editor::Message)`.
|
|
- Keep messages flat where possible -- avoid deep nesting beyond two levels.
|
|
- Use `Task<Message>` for async operations (file I/O, dialogs, database queries).
|
|
- Noop absorbs events that need no action (e.g., unhandled menu IDs).
|
|
|
|
## Subscription Model
|
|
|
|
- Menu events: `iced::event::listen_with` polling `MenuEvent::receiver().try_recv()` each tick.
|
|
- Future: filesystem watcher subscription for external db/file changes.
|
|
- Subscriptions are declarative -- `subscription()` returns the full set every frame; Iced diffs internally.
|
|
- Each subscription keyed by a unique ID to avoid duplicates.
|
|
|
|
## Custom Widget Pattern (CodeEditor)
|
|
|
|
- Implements `iced::advanced::Widget<Message, Theme, Renderer>`.
|
|
- Persistent state via `widget::tree::State` holding `EditorState` (cursor pos, selection, scroll offset).
|
|
- `tag()` returns `TypeId::of::<EditorState>()` for widget tree identity.
|
|
- `state()` returns `State::new(EditorState::default())` for initialization.
|
|
- `on_event()` handles keyboard/mouse input, returns `Status::Captured` or `Status::Ignored`.
|
|
- `draw()` uses `renderer.fill_quad()` for backgrounds/cursors/gutters, `renderer.fill_text()` for content.
|
|
- Takes `&mut EditorBuffer` reference for direct mutation on input -- buffer is not owned by the widget.
|
|
- Focus management: `is_focused` flag in `EditorState`, set on mouse press inside bounds, cleared on press outside.
|
|
|
|
## Platform Integration
|
|
|
|
- muda menus built once at startup; call `init_for_nsapp()` on macOS before event loop.
|
|
- Menu IDs stored in a map (`HashMap<MenuId, Message>`) for routing `MenuEvent` to app `Message`.
|
|
- macOS lifecycle hooks (`application:openFile:`, `openURLs:`) via objc2 -- deferred to M2.
|
|
- rfd dialogs: `rfd::AsyncFileDialog` spawned behind `Task::perform`, result mapped to a `Message` variant.
|
|
|
|
## Rendering
|
|
|
|
- Iced uses cosmic-text internally for text shaping and layout.
|
|
- Custom widgets call `renderer.fill_text()` with a `Text` struct: font, size, bounds, horizontal/vertical alignment.
|
|
- Use `Font::MONOSPACE` for the editor, default system font for UI chrome.
|
|
- `renderer.fill_quad()` for solid-color rectangles: backgrounds, cursor bar, gutter column, selections.
|
|
- Clipping: call `renderer.with_layer()` to restrict drawing to widget bounds.
|
|
|
|
## State Management
|
|
|
|
- Top-level app state lives in `BdsApp` struct (db connection, open buffers, UI flags).
|
|
- Child widget state in `widget::tree::State` -- survives across `view()` calls as long as widget identity matches.
|
|
- `EditorBuffer` (rope-based text buffer via ropey) owned by `BdsApp`, passed as `&mut` to widget on `view()` and `on_event()`.
|
|
- Database connection (`bds_core::db::Database`) opened once at startup, held in `BdsApp`, not cloned.
|
|
- No global mutable state -- everything flows through the `Message` -> `update()` -> `view()` cycle.
|