rush_sync_server/ui/
widget.rs

1// ## FILE: src/ui/widget.rs - KOMPRIMIERTE VERSION
2use crate::core::prelude::*;
3use crate::input::state::InputStateBackup;
4use ratatui::widgets::Paragraph;
5
6/// Core Widget trait - essentielles Rendering
7pub trait Widget {
8    fn render(&self) -> Paragraph<'_>;
9    fn handle_input(&mut self, key: KeyEvent) -> Option<String>;
10}
11
12/// Cursor-fähige Widgets
13pub trait CursorWidget: Widget {
14    fn render_with_cursor(&self) -> (Paragraph<'_>, Option<(u16, u16)>);
15}
16
17/// State-Management für Widgets
18pub trait StatefulWidget<T = InputStateBackup> {
19    fn export_state(&self) -> T;
20    fn import_state(&mut self, state: T);
21}
22
23/// Animierte Widgets (Blinken, etc.)
24pub trait AnimatedWidget {
25    fn tick(&mut self);
26}
27
28/// Vollständiges Input-Widget (kombiniert alle Traits)
29pub trait InputWidget: Widget + CursorWidget + StatefulWidget + AnimatedWidget {}
30
31// Blanket Implementation
32impl<T> InputWidget for T where T: Widget + CursorWidget + StatefulWidget + AnimatedWidget {}
33
34/// Widget Utilities
35pub mod utils {
36    use super::*;
37    use ratatui::{
38        style::Style,
39        widgets::{Block, Borders},
40    };
41
42    pub fn simple_text(content: &str, style: Style) -> Paragraph<'_> {
43        Paragraph::new(content.to_string())
44            .style(style)
45            .block(Block::default().borders(Borders::NONE))
46    }
47
48    pub fn has_cursor<T: Widget>(_: &T) -> bool {
49        std::any::type_name::<T>().contains("CursorWidget")
50    }
51}
52
53/// Beispiel-Implementierungen für Tests
54#[cfg(test)]
55mod examples {
56    use super::*;
57
58    #[derive(Debug)]
59    pub struct SimpleWidget(String);
60
61    impl Widget for SimpleWidget {
62        fn render(&self) -> Paragraph<'_> {
63            utils::simple_text(&self.0, ratatui::style::Style::default())
64        }
65
66        fn handle_input(&mut self, _: KeyEvent) -> Option<String> {
67            None
68        }
69    }
70
71    #[derive(Debug)]
72    pub struct FullInputWidget {
73        content: String,
74        cursor_pos: usize,
75        visible: bool,
76    }
77
78    impl Widget for FullInputWidget {
79        fn render(&self) -> Paragraph<'_> {
80            self.render_with_cursor().0
81        }
82
83        fn handle_input(&mut self, _: KeyEvent) -> Option<String> {
84            Some("handled".to_string())
85        }
86    }
87
88    impl CursorWidget for FullInputWidget {
89        fn render_with_cursor(&self) -> (Paragraph<'_>, Option<(u16, u16)>) {
90            let para = utils::simple_text(&self.content, ratatui::style::Style::default());
91            let cursor = if self.visible {
92                Some((self.cursor_pos as u16, 0))
93            } else {
94                None
95            };
96            (para, cursor)
97        }
98    }
99
100    impl StatefulWidget for FullInputWidget {
101        fn export_state(&self) -> InputStateBackup {
102            InputStateBackup {
103                content: self.content.clone(),
104                history: vec![],
105                cursor_pos: self.cursor_pos,
106            }
107        }
108
109        fn import_state(&mut self, state: InputStateBackup) {
110            self.content = state.content;
111            self.cursor_pos = state.cursor_pos;
112        }
113    }
114
115    impl AnimatedWidget for FullInputWidget {
116        fn tick(&mut self) {
117            self.visible = !self.visible;
118        }
119    }
120
121    #[test]
122    fn test_widget_system() {
123        let mut simple = SimpleWidget("test".to_string());
124        let _para = simple.render();
125        assert_eq!(simple.handle_input(KeyEvent::from(KeyCode::Enter)), None);
126
127        let mut full = FullInputWidget {
128            content: "input".to_string(),
129            cursor_pos: 5,
130            visible: true,
131        };
132
133        // Test alle Traits
134        let _para = full.render();
135        let (_para, cursor) = full.render_with_cursor();
136        assert_eq!(cursor, Some((5, 0)));
137
138        let state = full.export_state();
139        full.content = "changed".to_string();
140        full.import_state(state);
141        assert_eq!(full.content, "input");
142
143        let old_visible = full.visible;
144        full.tick();
145        assert_ne!(full.visible, old_visible);
146    }
147}
148
149/// Migration Helper für bestehenden Code
150pub mod compat {
151    pub use super::{
152        AnimatedWidget as Tickable, CursorWidget as RenderWithCursor,
153        InputWidget as InputWidgetFull, StatefulWidget as Stateful, Widget,
154    };
155}