render.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. use dioxus_native_core::{prelude::*, tree::TreeRef};
  2. use ratatui::{layout::Rect, style::Color};
  3. use taffy::{
  4. geometry::Point,
  5. prelude::{Dimension, Layout, Size},
  6. Taffy,
  7. };
  8. use crate::{
  9. focus::Focused,
  10. layout::TaffyLayout,
  11. layout_to_screen_space,
  12. style::{RinkColor, RinkStyle},
  13. style_attributes::{BorderEdge, BorderStyle, StyleModifier},
  14. widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
  15. Config,
  16. };
  17. const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
  18. pub(crate) fn render_vnode(
  19. frame: &mut ratatui::Frame,
  20. layout: &Taffy,
  21. node: NodeRef,
  22. cfg: Config,
  23. parent_location: Point<f32>,
  24. ) {
  25. if let NodeType::Placeholder = &*node.node_type() {
  26. return;
  27. }
  28. let Layout {
  29. mut location, size, ..
  30. } = layout
  31. .layout(node.get::<TaffyLayout>().unwrap().node.unwrap())
  32. .unwrap();
  33. location.x += parent_location.x;
  34. location.y += parent_location.y;
  35. let Point { x: fx, y: fy } = location;
  36. let x = layout_to_screen_space(fx).round() as u16;
  37. let y = layout_to_screen_space(fy).round() as u16;
  38. let Size { width, height } = *size;
  39. let width = layout_to_screen_space(fx + width).round() as u16 - x;
  40. let height = layout_to_screen_space(fy + height).round() as u16 - y;
  41. match &*node.node_type() {
  42. NodeType::Text(text) => {
  43. #[derive(Default)]
  44. struct Label<'a> {
  45. text: &'a str,
  46. style: RinkStyle,
  47. }
  48. impl<'a> RinkWidget for Label<'a> {
  49. fn render(self, area: Rect, mut buf: RinkBuffer) {
  50. for (i, c) in self.text.char_indices() {
  51. let mut new_cell = RinkCell::default();
  52. new_cell.set_style(self.style);
  53. new_cell.symbol = c.to_string();
  54. buf.set(area.left() + i as u16, area.top(), new_cell);
  55. }
  56. }
  57. }
  58. let label = Label {
  59. text: &text.text,
  60. style: node.get::<StyleModifier>().unwrap().core,
  61. };
  62. let area = Rect::new(x, y, width, height);
  63. // the renderer will panic if a node is rendered out of range even if the size is zero
  64. if area.width > 0 && area.height > 0 {
  65. frame.render_widget(WidgetWithContext::new(label, cfg), area);
  66. }
  67. }
  68. NodeType::Element { .. } => {
  69. let area = Rect::new(x, y, width, height);
  70. // the renderer will panic if a node is rendered out of range even if the size is zero
  71. if area.width > 0 && area.height > 0 {
  72. frame.render_widget(WidgetWithContext::new(node, cfg), area);
  73. }
  74. let node_id = node.id();
  75. let rdom = node.real_dom();
  76. for child_id in rdom.tree_ref().children_ids_advanced(node_id, true) {
  77. let c = rdom.get(child_id).unwrap();
  78. render_vnode(frame, layout, c, cfg, location);
  79. }
  80. }
  81. NodeType::Placeholder => unreachable!(),
  82. }
  83. }
  84. impl RinkWidget for NodeRef<'_> {
  85. fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
  86. use ratatui::symbols::line::*;
  87. enum Direction {
  88. Left,
  89. Right,
  90. Up,
  91. Down,
  92. }
  93. fn draw(
  94. buf: &mut RinkBuffer,
  95. points_history: [[i32; 2]; 3],
  96. symbols: &Set,
  97. pos: [u16; 2],
  98. color: &Option<RinkColor>,
  99. ) {
  100. let [before, current, after] = points_history;
  101. let start_dir = match [before[0] - current[0], before[1] - current[1]] {
  102. [1, 0] => Direction::Right,
  103. [-1, 0] => Direction::Left,
  104. [0, 1] => Direction::Down,
  105. [0, -1] => Direction::Up,
  106. [a, b] => {
  107. panic!("draw({before:?} {current:?} {after:?}) {a}, {b} no cell adjacent")
  108. }
  109. };
  110. let end_dir = match [after[0] - current[0], after[1] - current[1]] {
  111. [1, 0] => Direction::Right,
  112. [-1, 0] => Direction::Left,
  113. [0, 1] => Direction::Down,
  114. [0, -1] => Direction::Up,
  115. [a, b] => {
  116. panic!("draw({before:?} {current:?} {after:?}) {a}, {b} no cell adjacent")
  117. }
  118. };
  119. let mut new_cell = RinkCell::default();
  120. if let Some(c) = color {
  121. new_cell.fg = *c;
  122. }
  123. new_cell.symbol = match [start_dir, end_dir] {
  124. [Direction::Down, Direction::Up] => symbols.vertical,
  125. [Direction::Down, Direction::Right] => symbols.top_left,
  126. [Direction::Down, Direction::Left] => symbols.top_right,
  127. [Direction::Up, Direction::Down] => symbols.vertical,
  128. [Direction::Up, Direction::Right] => symbols.bottom_left,
  129. [Direction::Up, Direction::Left] => symbols.bottom_right,
  130. [Direction::Right, Direction::Left] => symbols.horizontal,
  131. [Direction::Right, Direction::Up] => symbols.bottom_left,
  132. [Direction::Right, Direction::Down] => symbols.top_left,
  133. [Direction::Left, Direction::Up] => symbols.bottom_right,
  134. [Direction::Left, Direction::Right] => symbols.horizontal,
  135. [Direction::Left, Direction::Down] => symbols.top_right,
  136. _ => panic!("{before:?} {current:?} {after:?} cannont connect cell to itself"),
  137. }
  138. .to_string();
  139. buf.set(
  140. (current[0] + pos[0] as i32) as u16,
  141. (current[1] + pos[1] as i32) as u16,
  142. new_cell,
  143. );
  144. }
  145. fn draw_arc(
  146. pos: [u16; 2],
  147. starting_angle: f32,
  148. arc_angle: f32,
  149. radius: f32,
  150. symbols: &Set,
  151. buf: &mut RinkBuffer,
  152. color: &Option<RinkColor>,
  153. ) {
  154. if radius < 0.0 {
  155. return;
  156. }
  157. let num_points = (radius * arc_angle) as i32;
  158. let starting_point = [
  159. (starting_angle.cos() * (radius * RADIUS_MULTIPLIER[0])) as i32,
  160. (starting_angle.sin() * (radius * RADIUS_MULTIPLIER[1])) as i32,
  161. ];
  162. // keep track of the last 3 point to allow filling diagonals
  163. let mut points_history = [
  164. [0, 0],
  165. {
  166. // change the x or y value based on which one is changing quicker
  167. let ddx = -starting_angle.sin();
  168. let ddy = starting_angle.cos();
  169. if ddx.abs() > ddy.abs() {
  170. [starting_point[0] - ddx.signum() as i32, starting_point[1]]
  171. } else {
  172. [starting_point[0], starting_point[1] - ddy.signum() as i32]
  173. }
  174. },
  175. starting_point,
  176. ];
  177. for i in 1..=num_points {
  178. let angle = (i as f32 / num_points as f32) * arc_angle + starting_angle;
  179. let x = angle.cos() * radius * RADIUS_MULTIPLIER[0];
  180. let y = angle.sin() * radius * RADIUS_MULTIPLIER[1];
  181. let new = [x as i32, y as i32];
  182. if new != points_history[2] {
  183. points_history = [points_history[1], points_history[2], new];
  184. let dx = points_history[2][0] - points_history[1][0];
  185. let dy = points_history[2][1] - points_history[1][1];
  186. // fill diagonals
  187. if dx != 0 && dy != 0 {
  188. let connecting_point = match [dx, dy] {
  189. [1, 1] => [points_history[1][0] + 1, points_history[1][1]],
  190. [1, -1] => [points_history[1][0], points_history[1][1] - 1],
  191. [-1, 1] => [points_history[1][0], points_history[1][1] + 1],
  192. [-1, -1] => [points_history[1][0] - 1, points_history[1][1]],
  193. _ => todo!(),
  194. };
  195. draw(
  196. buf,
  197. [points_history[0], points_history[1], connecting_point],
  198. symbols,
  199. pos,
  200. color,
  201. );
  202. points_history = [points_history[1], connecting_point, points_history[2]];
  203. }
  204. draw(buf, points_history, symbols, pos, color);
  205. }
  206. }
  207. points_history = [points_history[1], points_history[2], {
  208. // change the x or y value based on which one is changing quicker
  209. let ddx = -(starting_angle + arc_angle).sin();
  210. let ddy = (starting_angle + arc_angle).cos();
  211. if ddx.abs() > ddy.abs() {
  212. [
  213. points_history[2][0] + ddx.signum() as i32,
  214. points_history[2][1],
  215. ]
  216. } else {
  217. [
  218. points_history[2][0],
  219. points_history[2][1] + ddy.signum() as i32,
  220. ]
  221. }
  222. }];
  223. draw(buf, points_history, symbols, pos, color);
  224. }
  225. fn get_radius(border: &BorderEdge, area: Rect) -> f32 {
  226. match border.style {
  227. BorderStyle::Hidden => 0.0,
  228. BorderStyle::None => 0.0,
  229. _ => match border.radius {
  230. Dimension::Percent(p) => p * area.width as f32 / 100.0,
  231. Dimension::Points(p) => p,
  232. _ => todo!(),
  233. }
  234. .abs()
  235. .min((area.width as f32 / RADIUS_MULTIPLIER[0]) / 2.0)
  236. .min((area.height as f32 / RADIUS_MULTIPLIER[1]) / 2.0),
  237. }
  238. }
  239. if area.area() == 0 {
  240. return;
  241. }
  242. // todo: only render inside borders
  243. for x in area.left()..area.right() {
  244. for y in area.top()..area.bottom() {
  245. let mut new_cell = RinkCell::default();
  246. if let Some(c) = self.get::<StyleModifier>().unwrap().core.bg {
  247. new_cell.bg = c;
  248. }
  249. if let Some(focused) = self.get::<Focused>() {
  250. if focused.0 {
  251. new_cell.bg.alpha = 100;
  252. new_cell.bg.color = new_cell.bg.blend(Color::White);
  253. }
  254. }
  255. buf.set(x, y, new_cell);
  256. }
  257. }
  258. let style = self.get::<StyleModifier>().unwrap();
  259. let borders = &style.modifier.borders;
  260. let last_edge = &borders.left;
  261. let current_edge = &borders.top;
  262. if let Some(symbols) = current_edge.style.symbol_set() {
  263. // the radius for the curve between this line and the next
  264. let r = get_radius(current_edge, area);
  265. let radius = [
  266. (r * RADIUS_MULTIPLIER[0]) as u16,
  267. (r * RADIUS_MULTIPLIER[1]) as u16,
  268. ];
  269. // the radius for the curve between this line and the last
  270. let last_r = get_radius(last_edge, area);
  271. let last_radius = [
  272. (last_r * RADIUS_MULTIPLIER[0]) as u16,
  273. (last_r * RADIUS_MULTIPLIER[1]) as u16,
  274. ];
  275. let color = current_edge.color.or(style.core.fg);
  276. let mut new_cell = RinkCell::default();
  277. if let Some(c) = color {
  278. new_cell.fg = c;
  279. }
  280. for x in (area.left() + last_radius[0] + 1)..(area.right() - radius[0]) {
  281. new_cell.symbol = symbols.horizontal.to_string();
  282. buf.set(x, area.top(), new_cell.clone());
  283. }
  284. draw_arc(
  285. [area.right() - radius[0] - 1, area.top() + radius[1]],
  286. std::f32::consts::FRAC_PI_2 * 3.0,
  287. std::f32::consts::FRAC_PI_2,
  288. r,
  289. &symbols,
  290. &mut buf,
  291. &color,
  292. );
  293. }
  294. let last_edge = &borders.top;
  295. let current_edge = &borders.right;
  296. if let Some(symbols) = current_edge.style.symbol_set() {
  297. // the radius for the curve between this line and the next
  298. let r = get_radius(current_edge, area);
  299. let radius = [
  300. (r * RADIUS_MULTIPLIER[0]) as u16,
  301. (r * RADIUS_MULTIPLIER[1]) as u16,
  302. ];
  303. // the radius for the curve between this line and the last
  304. let last_r = get_radius(last_edge, area);
  305. let last_radius = [
  306. (last_r * RADIUS_MULTIPLIER[0]) as u16,
  307. (last_r * RADIUS_MULTIPLIER[1]) as u16,
  308. ];
  309. let color = current_edge.color.or(style.core.fg);
  310. let mut new_cell = RinkCell::default();
  311. if let Some(c) = color {
  312. new_cell.fg = c;
  313. }
  314. for y in (area.top() + last_radius[1] + 1)..(area.bottom() - radius[1]) {
  315. new_cell.symbol = symbols.vertical.to_string();
  316. buf.set(area.right() - 1, y, new_cell.clone());
  317. }
  318. draw_arc(
  319. [area.right() - radius[0] - 1, area.bottom() - radius[1] - 1],
  320. 0.0,
  321. std::f32::consts::FRAC_PI_2,
  322. r,
  323. &symbols,
  324. &mut buf,
  325. &color,
  326. );
  327. }
  328. let last_edge = &borders.right;
  329. let current_edge = &borders.bottom;
  330. if let Some(symbols) = current_edge.style.symbol_set() {
  331. // the radius for the curve between this line and the next
  332. let r = get_radius(current_edge, area);
  333. let radius = [
  334. (r * RADIUS_MULTIPLIER[0]) as u16,
  335. (r * RADIUS_MULTIPLIER[1]) as u16,
  336. ];
  337. // the radius for the curve between this line and the last
  338. let last_r = get_radius(last_edge, area);
  339. let last_radius = [
  340. (last_r * RADIUS_MULTIPLIER[0]) as u16,
  341. (last_r * RADIUS_MULTIPLIER[1]) as u16,
  342. ];
  343. let color = current_edge.color.or(style.core.fg);
  344. let mut new_cell = RinkCell::default();
  345. if let Some(c) = color {
  346. new_cell.fg = c;
  347. }
  348. for x in (area.left() + radius[0])..(area.right() - last_radius[0] - 1) {
  349. new_cell.symbol = symbols.horizontal.to_string();
  350. buf.set(x, area.bottom() - 1, new_cell.clone());
  351. }
  352. draw_arc(
  353. [area.left() + radius[0], area.bottom() - radius[1] - 1],
  354. std::f32::consts::FRAC_PI_2,
  355. std::f32::consts::FRAC_PI_2,
  356. r,
  357. &symbols,
  358. &mut buf,
  359. &color,
  360. );
  361. }
  362. let last_edge = &borders.bottom;
  363. let current_edge = &borders.left;
  364. if let Some(symbols) = current_edge.style.symbol_set() {
  365. // the radius for the curve between this line and the next
  366. let r = get_radius(current_edge, area);
  367. let radius = [
  368. (r * RADIUS_MULTIPLIER[0]) as u16,
  369. (r * RADIUS_MULTIPLIER[1]) as u16,
  370. ];
  371. // the radius for the curve between this line and the last
  372. let last_r = get_radius(last_edge, area);
  373. let last_radius = [
  374. (last_r * RADIUS_MULTIPLIER[0]) as u16,
  375. (last_r * RADIUS_MULTIPLIER[1]) as u16,
  376. ];
  377. let color = current_edge.color.or(style.core.fg);
  378. let mut new_cell = RinkCell::default();
  379. if let Some(c) = color {
  380. new_cell.fg = c;
  381. }
  382. for y in (area.top() + radius[1])..(area.bottom() - last_radius[1] - 1) {
  383. new_cell.symbol = symbols.vertical.to_string();
  384. buf.set(area.left(), y, new_cell.clone());
  385. }
  386. draw_arc(
  387. [area.left() + radius[0], area.top() + radius[1]],
  388. std::f32::consts::PI,
  389. std::f32::consts::FRAC_PI_2,
  390. r,
  391. &symbols,
  392. &mut buf,
  393. &color,
  394. );
  395. }
  396. }
  397. }