use std::ops::{Deref, DerefMut};
use ratatui_core::{buffer::Buffer, layout::Rect};
use crate::element::Elements;
use crate::hooks::Hooks;
use crate::insets::Insets;
use crate::node::{Layout, WidthConstraint};
#[macro_export]
macro_rules! impl_slot_children {
($t:ty) => {
impl $crate::ChildCollector for $t {
type Collector = $crate::Elements;
type Output = $crate::ComponentWithSlot<$t>;
fn finish(self, collector: $crate::Elements) -> $crate::ComponentWithSlot<$t> {
$crate::ComponentWithSlot::new(self, collector)
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventResult {
Consumed,
Ignored,
}
pub struct Tracked<S> {
inner: S,
dirty: bool,
}
impl<S> Tracked<S> {
pub fn new(inner: S) -> Self {
Self { inner, dirty: true }
}
pub fn is_dirty(&self) -> bool {
self.dirty
}
pub fn clear_dirty(&mut self) {
self.dirty = false;
}
pub fn read(&self) -> &S {
&self.inner
}
}
impl<S> Deref for Tracked<S> {
type Target = S;
fn deref(&self) -> &S {
&self.inner
}
}
impl<S> DerefMut for Tracked<S> {
fn deref_mut(&mut self) -> &mut S {
self.dirty = true;
&mut self.inner
}
}
pub trait Component: Send + Sync + 'static {
type State: Send + Sync + Default + 'static;
#[doc(hidden)]
fn props_as_any(&self) -> &dyn std::any::Any
where
Self: Sized,
{
self
}
#[doc(hidden)]
fn render(&self, _area: Rect, _buf: &mut Buffer, _state: &Self::State) {}
#[doc(hidden)]
fn desired_height(&self, _width: u16, _state: &Self::State) -> Option<u16> {
None
}
#[doc(hidden)]
fn handle_event_capture(
&self,
_event: &crossterm::event::Event,
_state: &mut Tracked<Self::State>,
) -> EventResult {
EventResult::Ignored
}
#[doc(hidden)]
fn handle_event(
&self,
_event: &crossterm::event::Event,
_state: &mut Tracked<Self::State>,
) -> EventResult {
EventResult::Ignored
}
#[doc(hidden)]
fn is_focusable(&self, _state: &Self::State) -> bool {
false
}
#[doc(hidden)]
fn cursor_position(&self, _area: Rect, _state: &Self::State) -> Option<(u16, u16)> {
None
}
#[doc(hidden)]
fn initial_state(&self) -> Option<Self::State> {
None
}
#[doc(hidden)]
fn content_inset(&self, _state: &Self::State) -> Insets {
Insets::ZERO
}
#[doc(hidden)]
fn layout(&self) -> Layout {
Layout::default()
}
#[doc(hidden)]
fn width_constraint(&self) -> WidthConstraint {
WidthConstraint::default()
}
#[doc(hidden)]
fn lifecycle(&self, _hooks: &mut Hooks<Self, Self::State>, _state: &Self::State)
where
Self: Sized,
{
}
#[doc(hidden)]
fn view(&self, _state: &Self::State, children: Elements) -> Elements {
children
}
fn update(
&self,
hooks: &mut Hooks<Self, Self::State>,
state: &Self::State,
children: Elements,
) -> Elements
where
Self: Sized,
{
self.lifecycle(hooks, state);
self.view(state, children)
}
}
#[derive(Debug, Default, Clone)]
pub struct VStack;
impl VStack {
pub fn builder() -> Self {
Self
}
pub fn build(self) -> Self {
self
}
}
#[eye_declare_macros::component(props = VStack, children = Elements, crate_path = crate)]
fn vstack(_props: &VStack, children: Elements) -> Elements {
children
}
#[derive(Debug, Default, Clone)]
pub struct HStack;
impl HStack {
pub fn builder() -> Self {
Self
}
pub fn build(self) -> Self {
self
}
}
#[eye_declare_macros::component(props = HStack, children = Elements, crate_path = crate)]
fn hstack(_props: &HStack, hooks: &mut Hooks<HStack, ()>, children: Elements) -> Elements {
hooks.use_layout(Layout::Horizontal);
children
}
#[derive(Debug, Default, Clone, typed_builder::TypedBuilder)]
pub struct Column {
#[builder(default, setter(into))]
pub width: WidthConstraint,
}
#[eye_declare_macros::component(props = Column, children = Elements, crate_path = crate)]
fn column(props: &Column, hooks: &mut Hooks<Column, ()>, children: Elements) -> Elements {
hooks.use_width_constraint(props.width);
children
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tracked_starts_dirty() {
let t = Tracked::new(42u32);
assert!(t.is_dirty());
}
#[test]
fn tracked_deref_does_not_set_dirty() {
let mut t = Tracked::new(42u32);
t.clear_dirty();
assert!(!t.is_dirty());
let _val = *t;
assert!(!t.is_dirty());
}
#[test]
fn tracked_deref_mut_sets_dirty() {
let mut t = Tracked::new(42u32);
t.clear_dirty();
assert!(!t.is_dirty());
*t = 99;
assert!(t.is_dirty());
}
#[test]
fn tracked_clear_dirty_resets() {
let mut t = Tracked::new(42u32);
assert!(t.is_dirty());
t.clear_dirty();
assert!(!t.is_dirty());
}
}