aimcal-cli 0.12.1

AIM - Analyze. Interact. Manage Your Time, with calendar support
Documentation
// SPDX-FileCopyrightText: 2025-2026 Zexin Yuan <aim@yzx9.xyz>
//
// SPDX-License-Identifier: Apache-2.0

use std::cell::RefCell;

use ratatui::{crossterm::event::KeyEvent, prelude::*};

use crate::tui::dispatcher::Dispatcher;

#[derive(Debug, PartialEq, Eq)]
pub enum Message {
    Handled,
    CursorUpdated,
    Exit,
}

pub trait Component<S> {
    /// Renders the component into the given area.
    fn render(&self, store: &RefCell<S>, area: Rect, buf: &mut Buffer);

    /// The cursor position (row, column) for the component, if applicable.
    fn get_cursor_position(&self, _store: &RefCell<S>, _area: Rect) -> Option<(u16, u16)> {
        None // Default implementation returns no cursor position
    }

    /// Handles key events for the component.
    fn on_key(
        &mut self,
        _dispatcher: &mut Dispatcher,
        _store: &RefCell<S>,
        _area: Rect,
        _event: KeyEvent,
    ) -> Option<Message> {
        None // Default implementation does nothing
    }

    /// Activates the component, allowing it to initialize resources or state.
    fn activate(&mut self, _dispatcher: &mut Dispatcher, _store: &RefCell<S>) {}

    /// Deactivates the component, allowing it to clean up resources or state.
    fn deactivate(&mut self, _dispatcher: &mut Dispatcher, _store: &RefCell<S>) {}
}

impl<S, T> Component<S> for Box<T>
where
    T: Component<S> + ?Sized,
{
    fn render(&self, store: &RefCell<S>, area: Rect, buf: &mut Buffer) {
        (**self).render(store, area, buf);
    }

    fn get_cursor_position(&self, store: &RefCell<S>, area: Rect) -> Option<(u16, u16)> {
        (**self).get_cursor_position(store, area)
    }

    fn on_key(
        &mut self,
        dispatcher: &mut Dispatcher,
        store: &RefCell<S>,
        area: Rect,
        event: KeyEvent,
    ) -> Option<Message> {
        (**self).on_key(dispatcher, store, area, event)
    }

    fn activate(&mut self, dispatcher: &mut Dispatcher, store: &RefCell<S>) {
        (**self).activate(dispatcher, store);
    }

    fn deactivate(&mut self, dispatcher: &mut Dispatcher, store: &RefCell<S>) {
        (**self).deactivate(dispatcher, store);
    }
}