render.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. use dioxus_native_core::layout_attributes::UnitSystem;
  2. use std::io::Stdout;
  3. use taffy::{
  4. geometry::Point,
  5. prelude::{Layout, Size},
  6. Taffy,
  7. };
  8. use tui::{backend::CrosstermBackend, layout::Rect, style::Color};
  9. use crate::{
  10. style::{RinkColor, RinkStyle},
  11. style_attributes::{BorderEdge, BorderStyle},
  12. widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
  13. Config, Dom, Node,
  14. };
  15. const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
  16. pub(crate) fn render_vnode(
  17. frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
  18. layout: &Taffy,
  19. rdom: &Dom,
  20. node: &Node,
  21. cfg: Config,
  22. parent_location: Point<f32>,
  23. ) {
  24. use dioxus_native_core::real_dom::NodeType;
  25. if let NodeType::Placeholder = &node.node_type {
  26. return;
  27. }
  28. let Layout {
  29. mut location, size, ..
  30. } = layout.layout(node.state.layout.node.unwrap()).unwrap();
  31. location.x += parent_location.x;
  32. location.y += parent_location.y;
  33. let Point { x, y } = location;
  34. let Size { width, height } = size;
  35. match &node.node_type {
  36. NodeType::Text { text } => {
  37. #[derive(Default)]
  38. struct Label<'a> {
  39. text: &'a str,
  40. style: RinkStyle,
  41. }
  42. impl<'a> RinkWidget for Label<'a> {
  43. fn render(self, area: Rect, mut buf: RinkBuffer) {
  44. for (i, c) in self.text.char_indices() {
  45. let mut new_cell = RinkCell::default();
  46. new_cell.set_style(self.style);
  47. new_cell.symbol = c.to_string();
  48. buf.set(area.left() + i as u16, area.top(), new_cell);
  49. }
  50. }
  51. }
  52. let label = Label {
  53. text,
  54. style: node.state.style.core,
  55. };
  56. let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16);
  57. // the renderer will panic if a node is rendered out of range even if the size is zero
  58. if area.width > 0 && area.height > 0 {
  59. frame.render_widget(WidgetWithContext::new(label, cfg), area);
  60. }
  61. }
  62. NodeType::Element { children, .. } => {
  63. let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16);
  64. // the renderer will panic if a node is rendered out of range even if the size is zero
  65. if area.width > 0 && area.height > 0 {
  66. frame.render_widget(WidgetWithContext::new(node, cfg), area);
  67. }
  68. for c in children {
  69. render_vnode(frame, layout, rdom, &rdom[*c], cfg, location);
  70. }
  71. }
  72. NodeType::Placeholder => unreachable!(),
  73. }
  74. }
  75. impl RinkWidget for &Node {
  76. fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
  77. use tui::symbols::line::*;
  78. enum Direction {
  79. Left,
  80. Right,
  81. Up,
  82. Down,
  83. }
  84. fn draw(
  85. buf: &mut RinkBuffer,
  86. points_history: [[i32; 2]; 3],
  87. symbols: &Set,
  88. pos: [u16; 2],
  89. color: &Option<RinkColor>,
  90. ) {
  91. let [before, current, after] = points_history;
  92. let start_dir = match [before[0] - current[0], before[1] - current[1]] {
  93. [1, 0] => Direction::Right,
  94. [-1, 0] => Direction::Left,
  95. [0, 1] => Direction::Down,
  96. [0, -1] => Direction::Up,
  97. [a, b] => {
  98. panic!(
  99. "draw({:?} {:?} {:?}) {}, {} no cell adjacent",
  100. before, current, after, a, b
  101. )
  102. }
  103. };
  104. let end_dir = match [after[0] - current[0], after[1] - current[1]] {
  105. [1, 0] => Direction::Right,
  106. [-1, 0] => Direction::Left,
  107. [0, 1] => Direction::Down,
  108. [0, -1] => Direction::Up,
  109. [a, b] => {
  110. panic!(
  111. "draw({:?} {:?} {:?}) {}, {} no cell adjacent",
  112. before, current, after, a, b
  113. )
  114. }
  115. };
  116. let mut new_cell = RinkCell::default();
  117. if let Some(c) = color {
  118. new_cell.fg = *c;
  119. }
  120. new_cell.symbol = match [start_dir, end_dir] {
  121. [Direction::Down, Direction::Up] => symbols.vertical,
  122. [Direction::Down, Direction::Right] => symbols.top_left,
  123. [Direction::Down, Direction::Left] => symbols.top_right,
  124. [Direction::Up, Direction::Down] => symbols.vertical,
  125. [Direction::Up, Direction::Right] => symbols.bottom_left,
  126. [Direction::Up, Direction::Left] => symbols.bottom_right,
  127. [Direction::Right, Direction::Left] => symbols.horizontal,
  128. [Direction::Right, Direction::Up] => symbols.bottom_left,
  129. [Direction::Right, Direction::Down] => symbols.top_left,
  130. [Direction::Left, Direction::Up] => symbols.bottom_right,
  131. [Direction::Left, Direction::Right] => symbols.horizontal,
  132. [Direction::Left, Direction::Down] => symbols.top_right,
  133. _ => panic!(
  134. "{:?} {:?} {:?} cannont connect cell to itself",
  135. before, current, after
  136. ),
  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. UnitSystem::Percent(p) => p * area.width as f32 / 100.0,
  231. UnitSystem::Point(p) => p,
  232. }
  233. .abs()
  234. .min((area.width as f32 / RADIUS_MULTIPLIER[0]) / 2.0)
  235. .min((area.height as f32 / RADIUS_MULTIPLIER[1]) / 2.0),
  236. }
  237. }
  238. if area.area() == 0 {
  239. return;
  240. }
  241. // todo: only render inside borders
  242. for x in area.left()..area.right() {
  243. for y in area.top()..area.bottom() {
  244. let mut new_cell = RinkCell::default();
  245. if let Some(c) = self.state.style.core.bg {
  246. new_cell.bg = c;
  247. }
  248. if self.state.focused {
  249. new_cell.bg.alpha = 100;
  250. new_cell.bg.color = new_cell.bg.blend(Color::White);
  251. }
  252. buf.set(x, y, new_cell);
  253. }
  254. }
  255. let borders = &self.state.style.modifier.borders;
  256. let last_edge = &borders.left;
  257. let current_edge = &borders.top;
  258. if let Some(symbols) = current_edge.style.symbol_set() {
  259. // the radius for the curve between this line and the next
  260. let r = get_radius(current_edge, area);
  261. let radius = [
  262. (r * RADIUS_MULTIPLIER[0]) as u16,
  263. (r * RADIUS_MULTIPLIER[1]) as u16,
  264. ];
  265. // the radius for the curve between this line and the last
  266. let last_r = get_radius(last_edge, area);
  267. let last_radius = [
  268. (last_r * RADIUS_MULTIPLIER[0]) as u16,
  269. (last_r * RADIUS_MULTIPLIER[1]) as u16,
  270. ];
  271. let color = current_edge.color.or(self.state.style.core.fg);
  272. let mut new_cell = RinkCell::default();
  273. if let Some(c) = color {
  274. new_cell.fg = c;
  275. }
  276. for x in (area.left() + last_radius[0] + 1)..(area.right() - radius[0]) {
  277. new_cell.symbol = symbols.horizontal.to_string();
  278. buf.set(x, area.top(), new_cell.clone());
  279. }
  280. draw_arc(
  281. [area.right() - radius[0] - 1, area.top() + radius[1]],
  282. std::f32::consts::FRAC_PI_2 * 3.0,
  283. std::f32::consts::FRAC_PI_2,
  284. r,
  285. &symbols,
  286. &mut buf,
  287. &color,
  288. );
  289. }
  290. let last_edge = &borders.top;
  291. let current_edge = &borders.right;
  292. if let Some(symbols) = current_edge.style.symbol_set() {
  293. // the radius for the curve between this line and the next
  294. let r = get_radius(current_edge, area);
  295. let radius = [
  296. (r * RADIUS_MULTIPLIER[0]) as u16,
  297. (r * RADIUS_MULTIPLIER[1]) as u16,
  298. ];
  299. // the radius for the curve between this line and the last
  300. let last_r = get_radius(last_edge, area);
  301. let last_radius = [
  302. (last_r * RADIUS_MULTIPLIER[0]) as u16,
  303. (last_r * RADIUS_MULTIPLIER[1]) as u16,
  304. ];
  305. let color = current_edge.color.or(self.state.style.core.fg);
  306. let mut new_cell = RinkCell::default();
  307. if let Some(c) = color {
  308. new_cell.fg = c;
  309. }
  310. for y in (area.top() + last_radius[1] + 1)..(area.bottom() - radius[1]) {
  311. new_cell.symbol = symbols.vertical.to_string();
  312. buf.set(area.right() - 1, y, new_cell.clone());
  313. }
  314. draw_arc(
  315. [area.right() - radius[0] - 1, area.bottom() - radius[1] - 1],
  316. 0.0,
  317. std::f32::consts::FRAC_PI_2,
  318. r,
  319. &symbols,
  320. &mut buf,
  321. &color,
  322. );
  323. }
  324. let last_edge = &borders.right;
  325. let current_edge = &borders.bottom;
  326. if let Some(symbols) = current_edge.style.symbol_set() {
  327. // the radius for the curve between this line and the next
  328. let r = get_radius(current_edge, area);
  329. let radius = [
  330. (r * RADIUS_MULTIPLIER[0]) as u16,
  331. (r * RADIUS_MULTIPLIER[1]) as u16,
  332. ];
  333. // the radius for the curve between this line and the last
  334. let last_r = get_radius(last_edge, area);
  335. let last_radius = [
  336. (last_r * RADIUS_MULTIPLIER[0]) as u16,
  337. (last_r * RADIUS_MULTIPLIER[1]) as u16,
  338. ];
  339. let color = current_edge.color.or(self.state.style.core.fg);
  340. let mut new_cell = RinkCell::default();
  341. if let Some(c) = color {
  342. new_cell.fg = c;
  343. }
  344. for x in (area.left() + radius[0])..(area.right() - last_radius[0] - 1) {
  345. new_cell.symbol = symbols.horizontal.to_string();
  346. buf.set(x, area.bottom() - 1, new_cell.clone());
  347. }
  348. draw_arc(
  349. [area.left() + radius[0], area.bottom() - radius[1] - 1],
  350. std::f32::consts::FRAC_PI_2,
  351. std::f32::consts::FRAC_PI_2,
  352. r,
  353. &symbols,
  354. &mut buf,
  355. &color,
  356. );
  357. }
  358. let last_edge = &borders.bottom;
  359. let current_edge = &borders.left;
  360. if let Some(symbols) = current_edge.style.symbol_set() {
  361. // the radius for the curve between this line and the next
  362. let r = get_radius(current_edge, area);
  363. let radius = [
  364. (r * RADIUS_MULTIPLIER[0]) as u16,
  365. (r * RADIUS_MULTIPLIER[1]) as u16,
  366. ];
  367. // the radius for the curve between this line and the last
  368. let last_r = get_radius(last_edge, area);
  369. let last_radius = [
  370. (last_r * RADIUS_MULTIPLIER[0]) as u16,
  371. (last_r * RADIUS_MULTIPLIER[1]) as u16,
  372. ];
  373. let color = current_edge.color.or(self.state.style.core.fg);
  374. let mut new_cell = RinkCell::default();
  375. if let Some(c) = color {
  376. new_cell.fg = c;
  377. }
  378. for y in (area.top() + radius[1])..(area.bottom() - last_radius[1] - 1) {
  379. new_cell.symbol = symbols.vertical.to_string();
  380. buf.set(area.left(), y, new_cell.clone());
  381. }
  382. draw_arc(
  383. [area.left() + radius[0], area.top() + radius[1]],
  384. std::f32::consts::PI,
  385. std::f32::consts::FRAC_PI_2,
  386. r,
  387. &symbols,
  388. &mut buf,
  389. &color,
  390. );
  391. }
  392. }
  393. }