win_wrap/uia/pattern/
text.rs1use crate::uia::{
15 element::UiAutomationElement,
16 pattern::{PatternCreator, PatternError},
17};
18use windows::{
19 core::BSTR,
20 Win32::{
21 Foundation::POINT,
22 UI::Accessibility::{
23 IUIAutomationTextPattern, IUIAutomationTextPattern2, IUIAutomationTextRange,
24 IUIAutomationTextRangeArray, TextPatternRangeEndpoint_End,
25 TextPatternRangeEndpoint_Start, TextUnit_Character, TextUnit_Document, TextUnit_Format,
26 TextUnit_Line, TextUnit_Page, TextUnit_Paragraph, TextUnit_Word, UIA_TextPattern2Id,
27 UIA_TextPatternId, UIA_PATTERN_ID,
28 },
29 },
30};
31use windows_core::Interface;
32
33pub struct UiAutomationTextPattern(IUIAutomationTextPattern);
37
38impl TryFrom<IUIAutomationTextPattern> for UiAutomationTextPattern {
39 type Error = PatternError;
40
41 fn try_from(value: IUIAutomationTextPattern) -> Result<Self, Self::Error> {
42 Ok(Self(value))
43 }
44}
45
46impl TryFrom<IUIAutomationTextPattern2> for UiAutomationTextPattern {
47 type Error = PatternError;
48
49 fn try_from(value: IUIAutomationTextPattern2) -> Result<Self, Self::Error> {
50 Ok(Self(value.cast().map_err(|e| -> PatternError {
51 format!("Can't convert type. ({})", e).into()
52 })?))
53 }
54}
55
56impl PatternCreator<IUIAutomationTextPattern> for UiAutomationTextPattern {
57 const PATTERN: UIA_PATTERN_ID = UIA_TextPatternId;
58}
59
60impl UiAutomationTextPattern {
62 pub fn document_range(&self) -> UiAutomationTextRange {
68 unsafe { UiAutomationTextRange::obtain(&self.0.DocumentRange().unwrap()) }
69 }
70
71 pub fn get_selection(&self) -> Vec<UiAutomationTextRange> {
80 if let Ok(array) = unsafe { self.0.GetSelection() } {
81 return array.to_vec();
82 }
83 vec![]
84 }
85
86 pub fn supported_text_selection(&self) -> SupportedTextSelection {
91 match unsafe { self.0.SupportedTextSelection() }.unwrap().0 {
92 1 => SupportedTextSelection::Single,
93 2 => SupportedTextSelection::Multiple,
94 _ => SupportedTextSelection::None,
95 }
96 }
97
98 pub fn get_visible_ranges(&self) -> Vec<UiAutomationTextRange> {
102 unsafe {
103 if let Ok(array) = self.0.GetVisibleRanges() {
104 return array.to_vec();
105 }
106 }
107 vec![]
108 }
109
110 pub fn range_from_child(&self, child: &UiAutomationElement) -> Option<UiAutomationTextRange> {
116 if let Ok(c) = unsafe { self.0.RangeFromChild(child.get_raw()) } {
117 return Some(UiAutomationTextRange::obtain(&c));
118 }
119 None
120 }
121
122 pub fn range_from_point(&self, x: i32, y: i32) -> Option<UiAutomationTextRange> {
132 if let Ok(x) = unsafe { self.0.RangeFromPoint(POINT { x, y }) } {
133 return Some(UiAutomationTextRange::obtain(&x));
134 }
135 None
136 }
137}
138
139pub struct UiAutomationTextPattern2(IUIAutomationTextPattern2, UiAutomationTextPattern);
141
142impl TryFrom<IUIAutomationTextPattern2> for UiAutomationTextPattern2 {
143 type Error = PatternError;
144
145 fn try_from(value: IUIAutomationTextPattern2) -> Result<Self, Self::Error> {
146 let p = value.clone().try_into()?;
147 Ok(Self(value, p))
148 }
149}
150
151impl PatternCreator<IUIAutomationTextPattern2> for UiAutomationTextPattern2 {
152 const PATTERN: UIA_PATTERN_ID = UIA_TextPattern2Id;
153}
154
155impl UiAutomationTextPattern2 {
157 pub fn get_caret_range(&self) -> Option<(bool, UiAutomationTextRange)> {
165 unsafe {
166 let mut active = std::mem::zeroed();
167 if let Ok(range) = self.0.GetCaretRange(&mut active) {
168 return Some((active.as_bool(), UiAutomationTextRange::obtain(&range)));
169 }
170 None
171 }
172 }
173
174 pub fn range_from_annotation(&self, annotation: &UiAutomationElement) -> UiAutomationTextRange {
179 let range = unsafe { self.0.RangeFromAnnotation(annotation.get_raw()).unwrap() };
180
181 UiAutomationTextRange::obtain(&range)
182 }
183}
184
185unsafe impl Send for UiAutomationTextPattern2 {}
186
187unsafe impl Sync for UiAutomationTextPattern2 {}
188
189impl std::ops::Deref for UiAutomationTextPattern2 {
190 type Target = UiAutomationTextPattern;
191
192 fn deref(&self) -> &Self::Target {
193 &self.1
194 }
195}
196
197trait TextRangeArray {
198 fn to_vec(&self) -> Vec<UiAutomationTextRange>;
199}
200
201impl TextRangeArray for IUIAutomationTextRangeArray {
202 fn to_vec(&self) -> Vec<UiAutomationTextRange> {
203 let mut v = vec![];
204 unsafe {
205 for i in 0..self.Length().unwrap() {
206 if let Ok(item) = self.GetElement(i) {
207 v.push(UiAutomationTextRange::obtain(&item));
208 }
209 }
210 }
211 v
212 }
213}
214
215#[derive(Clone, Debug)]
222pub struct UiAutomationTextRange(IUIAutomationTextRange);
223
224impl UiAutomationTextRange {
226 pub(crate) fn obtain(range: &IUIAutomationTextRange) -> Self {
230 Self(range.clone())
231 }
232
233 pub fn compare(&self, range: &UiAutomationTextRange) -> bool {
238 unsafe { self.0.Compare(&range.0) }
239 .unwrap_or(false.into())
240 .as_bool()
241 }
242
243 pub fn add_to_selection(&self) {
247 unsafe { self.0.AddToSelection() }.unwrap_or(())
248 }
249
250 pub fn compare_endpoints(
257 &self,
258 src_start_endpoint: bool,
259 range: &UiAutomationTextRange,
260 target_start_endpoint: bool,
261 ) -> i32 {
262 let src_endpoint = if src_start_endpoint {
263 TextPatternRangeEndpoint_Start
264 } else {
265 TextPatternRangeEndpoint_End
266 };
267 let target_endpoint = if target_start_endpoint {
268 TextPatternRangeEndpoint_Start
269 } else {
270 TextPatternRangeEndpoint_End
271 };
272 unsafe {
273 self.0
274 .CompareEndpoints(src_endpoint, &range.0, target_endpoint)
275 }
276 .unwrap_or(0)
277 }
278
279 pub fn expand_to_enclosing_unit(&self, text_unit: TextUnit) {
294 let unit = match text_unit {
295 TextUnit::Character => TextUnit_Character,
296 TextUnit::Format => TextUnit_Format,
297 TextUnit::Word => TextUnit_Word,
298 TextUnit::Line => TextUnit_Line,
299 TextUnit::Paragraph => TextUnit_Paragraph,
300 TextUnit::Page => TextUnit_Page,
301 TextUnit::Document => TextUnit_Document,
302 };
303 unsafe { self.0.ExpandToEnclosingUnit(unit) }.unwrap_or(())
304 }
305
306 pub fn get_text(&self, max_length: i32) -> String {
311 unsafe { self.0.GetText(max_length) }
312 .unwrap_or(BSTR::new())
313 .to_string()
314 }
315}
316
317unsafe impl Sync for UiAutomationTextRange {}
318
319unsafe impl Send for UiAutomationTextRange {}
320
321pub enum TextUnit {
325 Character,
327 Format,
329 Word,
331 Line,
333 Paragraph,
335 Page,
337 Document,
339}
340
341pub enum SupportedTextSelection {
342 None,
344 Single,
346 Multiple,
348}