slider.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. use std::collections::HashMap;
  2. use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData};
  3. use dioxus_native_core::{
  4. custom_element::CustomElement,
  5. node::{OwnedAttributeDiscription, OwnedAttributeValue},
  6. node_ref::AttributeMask,
  7. prelude::{ElementNode, NodeType},
  8. real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom},
  9. NodeId,
  10. };
  11. use shipyard::UniqueView;
  12. use super::{RinkWidget, WidgetContext};
  13. use crate::{query::get_layout, Event, EventData, FormData, Query};
  14. #[derive(Debug)]
  15. pub(crate) struct Slider {
  16. div_wrapper: NodeId,
  17. pre_cursor_div: NodeId,
  18. post_cursor_div: NodeId,
  19. min: f64,
  20. max: f64,
  21. step: Option<f64>,
  22. value: f64,
  23. border: bool,
  24. }
  25. impl Default for Slider {
  26. fn default() -> Self {
  27. Self {
  28. div_wrapper: Default::default(),
  29. pre_cursor_div: Default::default(),
  30. post_cursor_div: Default::default(),
  31. min: 0.0,
  32. max: 100.0,
  33. step: None,
  34. value: 0.0,
  35. border: false,
  36. }
  37. }
  38. }
  39. impl Slider {
  40. fn size(&self) -> f64 {
  41. self.max - self.min
  42. }
  43. fn step(&self) -> f64 {
  44. self.step.unwrap_or(self.size() / 10.0)
  45. }
  46. fn width(el: &ElementNodeMut) -> String {
  47. if let Some(value) = el
  48. .get_attribute(&OwnedAttributeDiscription {
  49. name: "width".to_string(),
  50. namespace: Some("style".to_string()),
  51. })
  52. .and_then(|value| value.as_text())
  53. .map(|value| value.to_string())
  54. {
  55. value
  56. } else {
  57. "1px".to_string()
  58. }
  59. }
  60. fn height(el: &ElementNodeMut) -> String {
  61. if let Some(value) = el
  62. .get_attribute(&OwnedAttributeDiscription {
  63. name: "height".to_string(),
  64. namespace: Some("style".to_string()),
  65. })
  66. .and_then(|value| value.as_text())
  67. .map(|value| value.to_string())
  68. {
  69. value
  70. } else {
  71. "1px".to_string()
  72. }
  73. }
  74. fn update_min_attr(&mut self, el: &ElementNodeMut) {
  75. if let Some(value) = el
  76. .get_attribute(&OwnedAttributeDiscription {
  77. name: "min".to_string(),
  78. namespace: None,
  79. })
  80. .and_then(|value| value.as_text())
  81. .map(|value| value.to_string())
  82. {
  83. self.min = value.parse().ok().unwrap_or(0.0);
  84. }
  85. }
  86. fn update_max_attr(&mut self, el: &ElementNodeMut) {
  87. if let Some(value) = el
  88. .get_attribute(&OwnedAttributeDiscription {
  89. name: "max".to_string(),
  90. namespace: None,
  91. })
  92. .and_then(|value| value.as_text())
  93. .map(|value| value.to_string())
  94. {
  95. self.max = value.parse().ok().unwrap_or(100.0);
  96. }
  97. }
  98. fn update_step_attr(&mut self, el: &ElementNodeMut) {
  99. if let Some(value) = el
  100. .get_attribute(&OwnedAttributeDiscription {
  101. name: "step".to_string(),
  102. namespace: None,
  103. })
  104. .and_then(|value| value.as_text())
  105. .map(|value| value.to_string())
  106. {
  107. self.step = value.parse().ok();
  108. }
  109. }
  110. fn update_size_attr(&mut self, el: &mut ElementNodeMut) {
  111. let width = Self::width(el);
  112. let height = Self::height(el);
  113. let single_char = width
  114. .strip_prefix("px")
  115. .and_then(|n| n.parse::<u32>().ok().filter(|num| *num > 3))
  116. .is_some()
  117. || height
  118. .strip_prefix("px")
  119. .and_then(|n| n.parse::<u32>().ok().filter(|num| *num > 3))
  120. .is_some();
  121. self.border = !single_char;
  122. let border_style = if self.border { "solid" } else { "none" };
  123. el.set_attribute(
  124. OwnedAttributeDiscription {
  125. name: "border-style".to_string(),
  126. namespace: Some("style".to_string()),
  127. },
  128. border_style.to_string(),
  129. );
  130. }
  131. fn update_value(&mut self, new: f64) {
  132. self.value = new.clamp(self.min, self.max);
  133. }
  134. fn update_value_attr(&mut self, el: &ElementNodeMut) {
  135. if let Some(value) = el
  136. .get_attribute(&OwnedAttributeDiscription {
  137. name: "value".to_string(),
  138. namespace: None,
  139. })
  140. .and_then(|value| value.as_text())
  141. .map(|value| value.to_string())
  142. {
  143. self.update_value(value.parse().ok().unwrap_or(0.0));
  144. }
  145. }
  146. fn write_value(&self, rdom: &mut RealDom, id: NodeId) {
  147. let value_percent = (self.value - self.min) / self.size() * 100.0;
  148. if let Some(mut div) = rdom.get_mut(self.pre_cursor_div) {
  149. let node_type = div.node_type_mut();
  150. let NodeTypeMut::Element(mut element) = node_type else {
  151. panic!("input must be an element")
  152. };
  153. element.set_attribute(
  154. OwnedAttributeDiscription {
  155. name: "width".to_string(),
  156. namespace: Some("style".to_string()),
  157. },
  158. format!("{}%", value_percent),
  159. );
  160. }
  161. if let Some(mut div) = rdom.get_mut(self.post_cursor_div) {
  162. let node_type = div.node_type_mut();
  163. let NodeTypeMut::Element(mut element) = node_type else {
  164. panic!("input must be an element")
  165. };
  166. element.set_attribute(
  167. OwnedAttributeDiscription {
  168. name: "width".to_string(),
  169. namespace: Some("style".to_string()),
  170. },
  171. format!("{}%", 100.0 - value_percent),
  172. );
  173. }
  174. // send the event
  175. let world = rdom.raw_world_mut();
  176. {
  177. let ctx: UniqueView<WidgetContext> = world.borrow().expect("expected widget context");
  178. let data = FormData {
  179. value: self.value.to_string(),
  180. values: HashMap::new(),
  181. files: None,
  182. };
  183. ctx.send(Event {
  184. id,
  185. name: "input",
  186. data: EventData::Form(data),
  187. bubbles: true,
  188. });
  189. }
  190. }
  191. fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) {
  192. let key = data.key();
  193. let step = self.step();
  194. match key {
  195. Key::ArrowDown | Key::ArrowLeft => {
  196. self.update_value(self.value - step);
  197. }
  198. Key::ArrowUp | Key::ArrowRight => {
  199. self.update_value(self.value + step);
  200. }
  201. _ => {
  202. return;
  203. }
  204. }
  205. let id = root.id();
  206. let rdom = root.real_dom_mut();
  207. self.write_value(rdom, id);
  208. }
  209. fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) {
  210. if !data.held_buttons().is_empty() {
  211. let id = root.id();
  212. let rdom = root.real_dom_mut();
  213. let world = rdom.raw_world_mut();
  214. let taffy = {
  215. let query: UniqueView<Query> = world.borrow().unwrap();
  216. query.stretch.clone()
  217. };
  218. let taffy = taffy.lock().unwrap();
  219. let layout = get_layout(rdom.get(self.div_wrapper).unwrap(), &taffy).unwrap();
  220. let width = layout.size.width as f64;
  221. let offset = data.element_coordinates();
  222. self.update_value(self.min + self.size() * offset.x / width);
  223. self.write_value(rdom, id);
  224. }
  225. }
  226. }
  227. impl CustomElement for Slider {
  228. const NAME: &'static str = "input";
  229. fn roots(&self) -> Vec<NodeId> {
  230. vec![self.div_wrapper]
  231. }
  232. fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self {
  233. let node_type = root.node_type();
  234. let NodeType::Element(el) = &*node_type else {
  235. panic!("input must be an element")
  236. };
  237. let value = el.attributes.get(&OwnedAttributeDiscription {
  238. name: "value".to_string(),
  239. namespace: None,
  240. });
  241. let value = value
  242. .and_then(|value| match value {
  243. OwnedAttributeValue::Text(text) => text.as_str().parse().ok(),
  244. OwnedAttributeValue::Float(float) => Some(*float),
  245. OwnedAttributeValue::Int(int) => Some(*int as f64),
  246. _ => None,
  247. })
  248. .unwrap_or(0.0);
  249. drop(node_type);
  250. let rdom = root.real_dom_mut();
  251. let pre_cursor_div = rdom.create_node(NodeType::Element(ElementNode {
  252. tag: "div".to_string(),
  253. attributes: [(
  254. OwnedAttributeDiscription {
  255. name: "background-color".to_string(),
  256. namespace: Some("style".to_string()),
  257. },
  258. "rgba(10,10,10,0.5)".to_string().into(),
  259. )]
  260. .into_iter()
  261. .collect(),
  262. ..Default::default()
  263. }));
  264. let pre_cursor_div_id = pre_cursor_div.id();
  265. let cursor_text = rdom.create_node("|".to_string());
  266. let cursor_text_id = cursor_text.id();
  267. let mut cursor_span = rdom.create_node(NodeType::Element(ElementNode {
  268. tag: "div".to_string(),
  269. attributes: [].into_iter().collect(),
  270. ..Default::default()
  271. }));
  272. cursor_span.add_child(cursor_text_id);
  273. let cursor_span_id = cursor_span.id();
  274. let post_cursor_div = rdom.create_node(NodeType::Element(ElementNode {
  275. tag: "span".to_string(),
  276. attributes: [
  277. (
  278. OwnedAttributeDiscription {
  279. name: "width".to_string(),
  280. namespace: Some("style".to_string()),
  281. },
  282. "100%".to_string().into(),
  283. ),
  284. (
  285. OwnedAttributeDiscription {
  286. name: "background-color".to_string(),
  287. namespace: Some("style".to_string()),
  288. },
  289. "rgba(10,10,10,0.5)".to_string().into(),
  290. ),
  291. ]
  292. .into_iter()
  293. .collect(),
  294. ..Default::default()
  295. }));
  296. let post_cursor_div_id = post_cursor_div.id();
  297. let mut div_wrapper = rdom.create_node(NodeType::Element(ElementNode {
  298. tag: "div".to_string(),
  299. attributes: [
  300. (
  301. OwnedAttributeDiscription {
  302. name: "display".to_string(),
  303. namespace: Some("style".to_string()),
  304. },
  305. "flex".to_string().into(),
  306. ),
  307. (
  308. OwnedAttributeDiscription {
  309. name: "flex-direction".to_string(),
  310. namespace: Some("style".to_string()),
  311. },
  312. "row".to_string().into(),
  313. ),
  314. (
  315. OwnedAttributeDiscription {
  316. name: "width".to_string(),
  317. namespace: Some("style".to_string()),
  318. },
  319. "100%".to_string().into(),
  320. ),
  321. (
  322. OwnedAttributeDiscription {
  323. name: "height".to_string(),
  324. namespace: Some("style".to_string()),
  325. },
  326. "100%".to_string().into(),
  327. ),
  328. ]
  329. .into_iter()
  330. .collect(),
  331. ..Default::default()
  332. }));
  333. let div_wrapper_id = div_wrapper.id();
  334. div_wrapper.add_child(pre_cursor_div_id);
  335. div_wrapper.add_child(cursor_span_id);
  336. div_wrapper.add_child(post_cursor_div_id);
  337. root.add_event_listener("mousemove");
  338. root.add_event_listener("mousedown");
  339. root.add_event_listener("keydown");
  340. Self {
  341. pre_cursor_div: pre_cursor_div_id,
  342. post_cursor_div: post_cursor_div_id,
  343. div_wrapper: div_wrapper_id,
  344. value,
  345. ..Default::default()
  346. }
  347. }
  348. fn attributes_changed(
  349. &mut self,
  350. mut root: dioxus_native_core::real_dom::NodeMut,
  351. attributes: &dioxus_native_core::node_ref::AttributeMask,
  352. ) {
  353. match attributes {
  354. AttributeMask::All => {
  355. {
  356. let node_type = root.node_type_mut();
  357. let NodeTypeMut::Element(mut el) = node_type else {
  358. panic!("input must be an element")
  359. };
  360. self.update_value_attr(&el);
  361. self.update_size_attr(&mut el);
  362. self.update_max_attr(&el);
  363. self.update_min_attr(&el);
  364. self.update_step_attr(&el);
  365. }
  366. let id = root.id();
  367. self.write_value(root.real_dom_mut(), id);
  368. }
  369. AttributeMask::Some(attrs) => {
  370. {
  371. let node_type = root.node_type_mut();
  372. let NodeTypeMut::Element(mut el) = node_type else {
  373. panic!("input must be an element")
  374. };
  375. if attrs.contains("width") || attrs.contains("height") {
  376. self.update_size_attr(&mut el);
  377. }
  378. if attrs.contains("max") {
  379. self.update_max_attr(&el);
  380. }
  381. if attrs.contains("min") {
  382. self.update_min_attr(&el);
  383. }
  384. if attrs.contains("step") {
  385. self.update_step_attr(&el);
  386. }
  387. if attrs.contains("value") {
  388. self.update_value_attr(&el);
  389. }
  390. }
  391. if attrs.contains("value") {
  392. let id = root.id();
  393. self.write_value(root.real_dom_mut(), id);
  394. }
  395. }
  396. }
  397. }
  398. }
  399. impl RinkWidget for Slider {
  400. fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) {
  401. match event.name {
  402. "keydown" => {
  403. if let EventData::Keyboard(data) = &event.data {
  404. self.handle_keydown(node, data);
  405. }
  406. }
  407. "mousemove" => {
  408. if let EventData::Mouse(data) = &event.data {
  409. self.handle_mousemove(node, data);
  410. }
  411. }
  412. "mousedown" => {
  413. if let EventData::Mouse(data) = &event.data {
  414. self.handle_mousemove(node, data);
  415. }
  416. }
  417. _ => {}
  418. }
  419. }
  420. }