dioxus_html/events/
selection.rs1use dioxus_core::Event;
2use std::ops::Range;
3
4pub type SelectionEvent = Event<SelectionData>;
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
8#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
9#[cfg_attr(feature = "serialize", serde(rename_all = "lowercase"))]
10pub enum SelectionDirection {
11 #[default]
13 None,
14 Forward,
16 Backward,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq, Hash)]
25pub struct TextSelection {
26 range: Range<usize>,
27 direction: SelectionDirection,
28}
29
30impl TextSelection {
31 pub fn new(range: Range<usize>, direction: SelectionDirection) -> Self {
33 Self { range, direction }
34 }
35
36 pub fn range(&self) -> Range<usize> {
38 self.range.clone()
39 }
40
41 pub fn direction(&self) -> SelectionDirection {
43 self.direction
44 }
45
46 pub fn is_collapsed(&self) -> bool {
48 self.range.is_empty()
49 }
50}
51
52pub struct SelectionData {
53 inner: Box<dyn HasSelectionData>,
54}
55
56impl SelectionData {
57 pub fn new(inner: impl HasSelectionData + 'static) -> Self {
59 Self {
60 inner: Box::new(inner),
61 }
62 }
63
64 pub fn selection(&self) -> Option<TextSelection> {
72 self.inner.selection()
73 }
74
75 #[inline(always)]
77 pub fn downcast<T: 'static>(&self) -> Option<&T> {
78 self.inner.as_any().downcast_ref::<T>()
79 }
80}
81
82impl<E: HasSelectionData> From<E> for SelectionData {
83 fn from(e: E) -> Self {
84 Self { inner: Box::new(e) }
85 }
86}
87
88impl std::fmt::Debug for SelectionData {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("SelectionData")
91 .field("selection", &self.selection())
92 .finish()
93 }
94}
95
96impl PartialEq for SelectionData {
97 fn eq(&self, other: &Self) -> bool {
98 self.selection() == other.selection()
99 }
100}
101
102#[cfg(feature = "serialize")]
103#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
105pub struct SerializedSelectionData {
106 #[serde(default)]
107 pub selection_start: Option<usize>,
108 #[serde(default)]
109 pub selection_end: Option<usize>,
110 #[serde(default)]
111 pub selection_direction: Option<SelectionDirection>,
112}
113
114#[cfg(feature = "serialize")]
115impl SerializedSelectionData {
116 pub fn new(
118 selection_start: Option<usize>,
119 selection_end: Option<usize>,
120 selection_direction: Option<SelectionDirection>,
121 ) -> Self {
122 Self {
123 selection_start,
124 selection_end,
125 selection_direction,
126 }
127 }
128}
129
130#[cfg(feature = "serialize")]
131impl Default for SerializedSelectionData {
132 fn default() -> Self {
133 Self::new(None, None, None)
134 }
135}
136
137#[cfg(feature = "serialize")]
138impl From<&SelectionData> for SerializedSelectionData {
139 fn from(data: &SelectionData) -> Self {
140 let Some(selection) = data.selection() else {
141 return Self::default();
142 };
143 let range = selection.range();
144 Self::new(
145 Some(range.start),
146 Some(range.end),
147 Some(selection.direction()),
148 )
149 }
150}
151
152#[cfg(feature = "serialize")]
153impl HasSelectionData for SerializedSelectionData {
154 fn selection(&self) -> Option<TextSelection> {
155 let start = self.selection_start?;
156 let end = self.selection_end?;
157 Some(TextSelection::new(
158 start..end,
159 self.selection_direction.unwrap_or_default(),
160 ))
161 }
162
163 fn as_any(&self) -> &dyn std::any::Any {
164 self
165 }
166}
167
168#[cfg(feature = "serialize")]
169impl serde::Serialize for SelectionData {
170 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
171 SerializedSelectionData::from(self).serialize(serializer)
172 }
173}
174
175#[cfg(feature = "serialize")]
176impl<'de> serde::Deserialize<'de> for SelectionData {
177 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
178 let data = SerializedSelectionData::deserialize(deserializer)?;
179 Ok(Self {
180 inner: Box::new(data),
181 })
182 }
183}
184
185pub trait HasSelectionData: std::any::Any {
186 fn selection(&self) -> Option<TextSelection> {
192 None
193 }
194
195 fn as_any(&self) -> &dyn std::any::Any;
197}
198
199#[cfg(all(test, feature = "serialize"))]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn serialized_selection_data_deserializes_missing_fields() {
205 let data: SerializedSelectionData = serde_json::from_str("{}").unwrap();
206 assert_eq!(data, SerializedSelectionData::default());
207 }
208
209 #[test]
210 fn selection_data_exposes_serialized_fields() {
211 let event = SelectionData::new(SerializedSelectionData::new(
212 Some(1),
213 Some(4),
214 Some(SelectionDirection::Forward),
215 ));
216
217 assert_eq!(
218 event.selection(),
219 Some(TextSelection::new(1..4, SelectionDirection::Forward))
220 );
221 }
222}