1mod button;
2mod checkbox;
3mod column;
4mod image;
6mod radio;
7mod scrollable;
8mod slider;
9mod row;
11mod text;
12mod text_input;
13
14use crate::colors::{ColorRegistry, PancursesColor};
15use crate::primitive::Primitive;
16use iced_native::input::{
17 keyboard, keyboard::KeyCode, mouse::Button, mouse::Event as MouseEvent, ButtonState,
18};
19use iced_native::layout::Limits;
20use iced_native::{Event, Renderer};
21use pancurses::{initscr, Input, Window};
22
23pub struct PancursesRenderer {
25 window: Window,
27 color_registry: ColorRegistry,
29}
30
31impl Default for PancursesRenderer {
32 fn default() -> Self {
34 let window = initscr();
35 pancurses::noecho();
36 pancurses::curs_set(0);
37 pancurses::start_color();
38 pancurses::use_default_colors();
39 window.keypad(true);
41
42 pancurses::mousemask(pancurses::ALL_MOUSE_EVENTS, std::ptr::null_mut());
44 Self {
45 window,
46 color_registry: Default::default(),
47 }
48 }
49}
50
51impl Renderer for PancursesRenderer {
52 type Output = Primitive;
53
54 fn layout<'a, Message>(
55 &mut self,
56 element: &iced_native::Element<'a, Message, Self>,
57 ) -> iced_native::layout::Node {
58 let limits = Limits::NONE
59 .max_width(self.window.get_max_x() as u32)
60 .max_height(self.window.get_max_y() as u32);
61 element.layout(self, &limits)
62 }
63}
64
65impl PancursesRenderer {
66 pub fn flush(&mut self) {
68 self.window.clear();
69 self.window.refresh();
70 }
71
72 pub fn handle(&self) -> Option<Vec<Event>> {
74 let input = self.window.getch();
75 match input {
76 Some(Input::Character(c)) => {
77 Some(vec![Event::Keyboard(keyboard::Event::CharacterReceived(c))])
78 }
79 Some(Input::KeyBackspace) => Some(vec![
80 Event::Keyboard(keyboard::Event::Input {
81 state: ButtonState::Pressed,
82 key_code: KeyCode::Backspace,
83 }),
84 Event::Keyboard(keyboard::Event::Input {
85 state: ButtonState::Released,
86 key_code: KeyCode::Backspace,
87 }),
88 ]),
89 Some(Input::KeyEnter) => Some(vec![
90 Event::Keyboard(keyboard::Event::Input {
91 state: ButtonState::Pressed,
92 key_code: KeyCode::Enter,
93 }),
94 Event::Keyboard(keyboard::Event::Input {
95 state: ButtonState::Released,
96 key_code: KeyCode::Enter,
97 }),
98 ]),
99 Some(Input::KeyMouse) => {
100 if let Ok(mouse_event) = pancurses::getmouse() {
101 match mouse_event.bstate {
102 pancurses::BUTTON1_PRESSED => Some(move_cursor_and(
103 mouse_event.x,
104 mouse_event.y,
105 vec![Event::Mouse(MouseEvent::Input {
106 state: ButtonState::Pressed,
107 button: Button::Left,
108 })],
109 )),
110 pancurses::BUTTON1_RELEASED => Some(move_cursor_and(
111 mouse_event.x,
112 mouse_event.y,
113 vec![Event::Mouse(MouseEvent::Input {
114 state: ButtonState::Released,
115 button: Button::Left,
116 })],
117 )),
118 pancurses::BUTTON1_CLICKED => Some(move_cursor_and(
119 mouse_event.x,
120 mouse_event.y,
121 vec![
122 Event::Mouse(MouseEvent::Input {
123 state: ButtonState::Pressed,
124 button: Button::Left,
125 }),
126 Event::Mouse(MouseEvent::Input {
127 state: ButtonState::Released,
128 button: Button::Left,
129 }),
130 ],
131 )),
132 _ => None,
133 }
134 } else {
135 None
136 }
137 }
138 _ => None,
139 }
140 }
141
142 pub fn draw(&mut self, primitive: Primitive) {
144 match primitive {
145 Primitive::Group(prims) => prims.into_iter().for_each(|p| self.draw(p)),
146 Primitive::Text(texts, bounds, color) => {
147 let col = crate::colors::get_closest_color(color);
148 let col_idx = self.color_registry.get_idx(PancursesColor::new(col, -1));
149 self.window
150 .attrset(pancurses::COLOR_PAIR((col_idx as u32).into()));
151 let mut y = 0;
152 texts.into_iter().for_each(|l| {
153 self.window.mv(bounds.y as i32 + y as i32, bounds.x as i32);
154 self.window.addstr(l);
155 y += 1;
156 });
157 }
158 Primitive::BoxDisplay(bounds) => {
159 let col_idx = self
160 .color_registry
161 .get_idx(PancursesColor::new(pancurses::COLOR_WHITE, -1));
162 self.window
163 .attrset(pancurses::COLOR_PAIR((col_idx as u32).into()));
164 let x = bounds.x as i32;
165 let y = bounds.y as i32;
166 let w = bounds.width as i32;
167 let h = bounds.height as i32;
168 if let Ok(sub_win) = self.window.subwin(h, w, y, x) {
169 sub_win.border(0, 0, 0, 0, 0, 0, 0, 0);
170 sub_win.delwin();
171 }
172 }
173 Primitive::Char(x, y, boxchar) => {
174 let col_idx = self
175 .color_registry
176 .get_idx(PancursesColor::new(pancurses::COLOR_WHITE, -1));
177 self.window
178 .attrset(pancurses::COLOR_PAIR((col_idx as u32).into()));
179 self.window.mv(y, x);
180 self.window.addch(boxchar);
181 }
182 _ => (),
183 }
184 }
185
186 pub fn size(&self) -> (u16, u16) {
188 let yx = self.window.get_max_yx();
189 (yx.1 as u16, yx.0 as u16)
190 }
191}
192
193pub fn move_cursor_and(x: i32, y: i32, other: Vec<Event>) -> Vec<Event> {
194 vec![Event::Mouse(MouseEvent::CursorMoved {
195 x: x as f32,
196 y: y as f32,
197 })]
198 .into_iter()
199 .chain(other.into_iter())
200 .collect()
201}