password.rs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. use crate::widgets::get_root_id;
  2. use crate::Query;
  3. use crossterm::{cursor::*, execute};
  4. use dioxus::prelude::*;
  5. use dioxus_elements::input_data::keyboard_types::Key;
  6. use dioxus_html as dioxus_elements;
  7. use dioxus_html::FormData;
  8. use dioxus_native_core::utils::cursor::{Cursor, Pos};
  9. use std::{collections::HashMap, io::stdout};
  10. use taffy::geometry::Point;
  11. #[derive(Props)]
  12. pub(crate) struct PasswordProps<'a> {
  13. #[props(!optional)]
  14. raw_oninput: Option<&'a EventHandler<'a, FormData>>,
  15. #[props(!optional)]
  16. value: Option<&'a str>,
  17. #[props(!optional)]
  18. size: Option<&'a str>,
  19. #[props(!optional)]
  20. max_length: Option<&'a str>,
  21. #[props(!optional)]
  22. width: Option<&'a str>,
  23. #[props(!optional)]
  24. height: Option<&'a str>,
  25. }
  26. #[allow(non_snake_case)]
  27. pub(crate) fn Password<'a>(cx: Scope<'a, PasswordProps>) -> Element<'a> {
  28. let tui_query: Query = cx.consume_context().unwrap();
  29. let tui_query_clone = tui_query.clone();
  30. let text_ref = use_ref(cx, || {
  31. if let Some(intial_text) = cx.props.value {
  32. intial_text.to_string()
  33. } else {
  34. String::new()
  35. }
  36. });
  37. let cursor = use_ref(cx, Cursor::default);
  38. let dragging = use_state(cx, || false);
  39. let text = text_ref.read().clone();
  40. let start_highlight = cursor.read().first().idx(&text);
  41. let end_highlight = cursor.read().last().idx(&text);
  42. let (text_before_first_cursor, text_after_first_cursor) = text.split_at(start_highlight);
  43. let (text_highlighted, text_after_second_cursor) =
  44. text_after_first_cursor.split_at(end_highlight - start_highlight);
  45. let text_before_first_cursor = ".".repeat(text_before_first_cursor.len());
  46. let text_highlighted = ".".repeat(text_highlighted.len());
  47. let text_after_second_cursor = ".".repeat(text_after_second_cursor.len());
  48. let max_len = cx
  49. .props
  50. .max_length
  51. .as_ref()
  52. .and_then(|s| s.parse().ok())
  53. .unwrap_or(usize::MAX);
  54. let width = cx
  55. .props
  56. .width
  57. .map(|s| s.to_string())
  58. // px is the same as em in tui
  59. .or_else(|| cx.props.size.map(|s| s.to_string() + "px"))
  60. .unwrap_or_else(|| "10px".to_string());
  61. let height = cx.props.height.unwrap_or("3px");
  62. // don't draw a border unless there is enough space
  63. let border = if width
  64. .strip_suffix("px")
  65. .and_then(|w| w.parse::<i32>().ok())
  66. .filter(|w| *w < 3)
  67. .is_some()
  68. || height
  69. .strip_suffix("px")
  70. .and_then(|h| h.parse::<i32>().ok())
  71. .filter(|h| *h < 3)
  72. .is_some()
  73. {
  74. "none"
  75. } else {
  76. "solid"
  77. };
  78. let onkeydown = move |k: KeyboardEvent| {
  79. if k.key() == Key::Enter {
  80. return;
  81. }
  82. let mut text = text_ref.write();
  83. cursor.write().handle_input(&k, &mut text, max_len);
  84. if let Some(input_handler) = &cx.props.raw_oninput {
  85. input_handler.call(FormData {
  86. value: text.clone(),
  87. values: HashMap::new(),
  88. files: None,
  89. });
  90. }
  91. let node = tui_query.get(get_root_id(cx).unwrap());
  92. let Point { x, y } = node.pos().unwrap();
  93. let Pos { col, row } = cursor.read().start;
  94. let (x, y) = (
  95. col as u16 + x as u16 + u16::from(border != "none"),
  96. row as u16 + y as u16 + u16::from(border != "none"),
  97. );
  98. if let Ok(pos) = crossterm::cursor::position() {
  99. if pos != (x, y) {
  100. execute!(stdout(), MoveTo(x, y)).unwrap();
  101. }
  102. } else {
  103. execute!(stdout(), MoveTo(x, y)).unwrap();
  104. }
  105. };
  106. cx.render(rsx! {
  107. div {
  108. width: "{width}",
  109. height: "{height}",
  110. border_style: "{border}",
  111. onkeydown: onkeydown,
  112. onmousemove: move |evt| {
  113. if *dragging.get() {
  114. let offset = evt.data.element_coordinates();
  115. let mut new = Pos::new(offset.x as usize, offset.y as usize);
  116. if border != "none" {
  117. new.col = new.col.saturating_sub(1);
  118. }
  119. // textboxs are only one line tall
  120. new.row = 0;
  121. if new != cursor.read().start {
  122. cursor.write().end = Some(new);
  123. }
  124. }
  125. },
  126. onmousedown: move |evt| {
  127. let offset = evt.data.element_coordinates();
  128. let mut new = Pos::new(offset.x as usize, offset.y as usize);
  129. if border != "none" {
  130. new.col = new.col.saturating_sub(1);
  131. }
  132. // textboxs are only one line tall
  133. new.row = 0;
  134. new.realize_col(&text_ref.read());
  135. cursor.set(Cursor::from_start(new));
  136. dragging.set(true);
  137. let node = tui_query_clone.get(get_root_id(cx).unwrap());
  138. let Point{ x, y } = node.pos().unwrap();
  139. let Pos { col, row } = cursor.read().start;
  140. let (x, y) = (col as u16 + x as u16 + u16::from(border != "none"), row as u16 + y as u16 + u16::from(border != "none"));
  141. if let Ok(pos) = crossterm::cursor::position() {
  142. if pos != (x, y){
  143. execute!(stdout(), MoveTo(x, y)).unwrap();
  144. }
  145. }
  146. else{
  147. execute!(stdout(), MoveTo(x, y)).unwrap();
  148. }
  149. },
  150. onmouseup: move |_| {
  151. dragging.set(false);
  152. },
  153. onmouseleave: move |_| {
  154. dragging.set(false);
  155. },
  156. onmouseenter: move |_| {
  157. dragging.set(false);
  158. },
  159. onfocusout: |_| {
  160. execute!(stdout(), MoveTo(0, 1000)).unwrap();
  161. },
  162. "{text_before_first_cursor}"
  163. span{
  164. background_color: "rgba(255, 255, 255, 50%)",
  165. "{text_highlighted}"
  166. }
  167. "{text_after_second_cursor}"
  168. }
  169. })
  170. }