oo-ide 0.0.4

∞ is a terminal IDE focused on low distraction, high usability.
Documentation
use ratatui::layout::Rect;
use ratatui::Frame;

use crate::input::{KeyEvent, MouseEvent};
use crate::operation::{Event, Operation};
use crate::settings::Settings;
use crate::theme::Theme;

/// Identifier for a widget within a view's focus ring.
/// Each view assigns its own static string IDs; they need not be globally unique.
pub type WidgetId = &'static str;

/// Shared focus navigation operations.
///
/// Used as `Operation::Focus(FocusOp)` — any view that uses `FocusRing`
/// handles these in its `handle_operation`. Views that don't use focus
/// simply ignore them.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FocusOp {
    /// Cycle to the next widget in the focus ring (Tab).
    Next,
    /// Cycle to the previous widget in the focus ring (Shift+Tab).
    Prev,
    /// Move focus upward in a 2D layout.
    Up,
    /// Move focus downward in a 2D layout.
    Down,
    /// Move focus leftward in a 2D layout.
    Left,
    /// Move focus rightward in a 2D layout.
    Right,
}

/// The result of a widget handling an input event.
///
/// Preserves the existing "pure key -> ops" contract: widgets never mutate
/// themselves in `handle_key`; they return operations.
pub struct InputResult {
    /// Operations to emit (same as `Vec<Operation>` from `View::handle_key`).
    pub ops: Vec<Operation>,
    /// If `true`, the event was consumed and should not propagate to the
    /// view's global key handler.
    pub consumed: bool,
}

impl InputResult {
    /// The widget handled the event and produced these operations.
    pub fn consumed(ops: Vec<Operation>) -> Self {
        Self {
            ops,
            consumed: true,
        }
    }

    /// The widget did not handle the event.
    pub fn ignored() -> Self {
        Self {
            ops: vec![],
            consumed: false,
        }
    }
}

/// A widget that can receive focus, handle input, and render itself.
///
/// This is NOT a replacement for ratatui's `Widget` trait. It is a
/// higher-level composition trait used by views to manage interactive
/// sub-components. Widgets still use ratatui primitives internally.
pub trait FocusableWidget {
    /// Unique id within this view's focus ring.
    fn id(&self) -> WidgetId;

    /// Handle a key event while this widget has focus.
    /// Returns ops + consumed flag. Must not mutate `self`.
    fn handle_key(&self, key: KeyEvent) -> InputResult;

    /// Handle a mouse event. `area` is the widget's last rendered rect.
    fn handle_mouse(&self, _mouse: MouseEvent, _area: Rect) -> InputResult {
        InputResult::ignored()
    }

    /// Apply a single operation directed at this widget.
    /// Returns `Some(event)` if the operation was handled.
    fn handle_operation(&mut self, op: &Operation, settings: &Settings) -> Option<Event>;

    /// Render the widget into the given area.
    /// `focused` indicates whether this widget currently has focus
    /// (for styling: cursor visibility, border highlighting, etc.).
    fn render(&self, frame: &mut Frame, area: Rect, focused: bool, theme: &Theme);

    /// Whether this widget wants to capture Tab key presses, preventing
    /// the view from using Tab for focus cycling.
    /// Default: `false`. Override for multi-line text inputs.
    fn captures_tab(&self) -> bool {
        false
    }

    /// Whether this widget accepts focus at all.
    /// Default: `true`. Override for decorative-only widgets.
    fn focusable(&self) -> bool {
        true
    }
}