win_wrap/uia/
event.rs

1/*
2 * Copyright (c) 2024. The RigelA open source project team and
3 * its contributors reserve all rights.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and limitations under the License.
12 */
13
14use std::sync::Weak;
15
16use windows::{
17    core::{implement, Ref},
18    Win32::{
19        System::Com::SAFEARRAY,
20        UI::Accessibility::{
21            IUIAutomation6, IUIAutomationActiveTextPositionChangedEventHandler,
22            IUIAutomationActiveTextPositionChangedEventHandler_Impl,
23            IUIAutomationChangesEventHandler, IUIAutomationChangesEventHandler_Impl,
24            IUIAutomationElement, IUIAutomationEventHandler, IUIAutomationEventHandlerGroup,
25            IUIAutomationEventHandler_Impl, IUIAutomationFocusChangedEventHandler,
26            IUIAutomationFocusChangedEventHandler_Impl,
27            IUIAutomationTextEditTextChangedEventHandler,
28            IUIAutomationTextEditTextChangedEventHandler_Impl, IUIAutomationTextRange,
29            TextEditChangeType, TextEditChangeType_None, TreeScope_Subtree,
30            UIA_Text_TextSelectionChangedEventId, UiaChangeInfo, UIA_EVENT_ID,
31        },
32    },
33};
34
35use crate::{
36    common::{beep, Result},
37    uia::{element::UiAutomationElement, pattern::text::UiAutomationTextRange},
38};
39
40pub struct UiAutomationEventHandlerGroup {
41    _automation: Weak<IUIAutomation6>,
42    _group: IUIAutomationEventHandlerGroup,
43}
44
45impl UiAutomationEventHandlerGroup {
46    pub(crate) fn get_raw(&self) -> &IUIAutomationEventHandlerGroup {
47        &self._group
48    }
49    pub(crate) fn obtain(
50        automation: Weak<IUIAutomation6>,
51        group: &IUIAutomationEventHandlerGroup,
52    ) -> Self {
53        Self {
54            _automation: automation.clone(),
55            _group: group.clone(),
56        }
57    }
58
59    /**
60    注册一个活动文本位置改变时的通知函数。
61    处理函数运行在单独的子线程中。
62    `func` 用于接收事件的函数。
63    */
64    pub fn add_active_text_position_changed_listener<CB>(&self, func: CB)
65    where
66        CB: Fn(UiAutomationElement, UiAutomationTextRange) -> () + 'static,
67    {
68        let handler: IUIAutomationActiveTextPositionChangedEventHandler =
69            OnActiveTextPositionChangedCallback::new(func, self._automation.clone()).into();
70        unsafe {
71            self._group
72                .AddActiveTextPositionChangedEventHandler(TreeScope_Subtree, None, &handler)
73        }
74        .expect("Can't add the active text position changed listener.");
75    }
76
77    /**
78    注册一个文本编辑文字改变时的通知函数。
79    处理函数运行在单独的子线程中。
80    `func` 用于接收事件的函数。
81    */
82    pub fn add_text_edit_text_changed_listener<CB>(&self, func: CB)
83    where
84        CB: Fn(UiAutomationElement) -> () + 'static,
85    {
86        let handler: IUIAutomationTextEditTextChangedEventHandler =
87            OnTextEditTextChangedCallback::new(func, self._automation.clone()).into();
88        unsafe {
89            self._group.AddTextEditTextChangedEventHandler(
90                TreeScope_Subtree,
91                TextEditChangeType_None,
92                None,
93                &handler,
94            )
95        }
96        .expect("Can't add the active text position changed listener.");
97    }
98
99    /**
100    注册一个元素改变时的通知函数。
101    处理函数运行在单独的子线程中。
102    `func` 用于接收事件的函数。
103    */
104    pub fn add_changes_listener<CB>(&self, func: CB)
105    where
106        CB: Fn() -> () + 'static,
107    {
108        let handler: IUIAutomationChangesEventHandler =
109            OnChangesCallback::new(func, self._automation.clone()).into();
110        unsafe {
111            self._group
112                .AddChangesEventHandler(TreeScope_Subtree, &[0], None, &handler)
113        }
114        .expect("Can't add the changes listener.");
115    }
116
117    /**
118    注册一个文字选择区改变时的通知函数。
119    处理函数运行在单独的子线程中。
120    `func` 用于接收事件的函数。
121    */
122    pub fn add_text_selection_changed_listener<CB>(&self, func: CB)
123    where
124        CB: Fn(UiAutomationElement) -> () + 'static,
125    {
126        let handler: IUIAutomationEventHandler =
127            OnCallback::new(func, self._automation.clone()).into();
128        unsafe {
129            self._group.AddAutomationEventHandler(
130                UIA_Text_TextSelectionChangedEventId,
131                TreeScope_Subtree,
132                None,
133                &handler,
134            )
135        }
136        .expect("Can't add the text selection changed listener.");
137    }
138}
139
140unsafe impl Send for UiAutomationEventHandlerGroup {}
141
142unsafe impl Sync for UiAutomationEventHandlerGroup {}
143
144#[implement(IUIAutomationFocusChangedEventHandler)]
145pub(crate) struct OnFocusChangedCallback<CB>
146where
147    CB: Fn(UiAutomationElement) -> () + 'static,
148{
149    _automation: Weak<IUIAutomation6>,
150    _cb: Box<CB>,
151}
152
153impl<CB> OnFocusChangedCallback<CB>
154where
155    CB: Fn(UiAutomationElement) -> () + 'static,
156{
157    pub(crate) fn new(func: CB, automation: Weak<IUIAutomation6>) -> Self {
158        Self {
159            _automation: automation,
160            _cb: func.into(),
161        }
162    }
163}
164
165impl<CB> IUIAutomationFocusChangedEventHandler_Impl for OnFocusChangedCallback_Impl<CB>
166where
167    CB: Fn(UiAutomationElement) -> () + 'static,
168{
169    #[allow(non_snake_case)]
170    fn HandleFocusChangedEvent(&self, sender: Ref<'_, IUIAutomationElement>) -> Result<()> {
171        let func = &*self._cb;
172        func(UiAutomationElement::obtain(
173            self._automation.clone(),
174            sender.unwrap().clone(),
175        ));
176        Ok(())
177    }
178}
179
180#[implement(IUIAutomationActiveTextPositionChangedEventHandler)]
181struct OnActiveTextPositionChangedCallback<CB>
182where
183    CB: Fn(UiAutomationElement, UiAutomationTextRange) -> () + 'static,
184{
185    _automation: Weak<IUIAutomation6>,
186    _cb: Box<CB>,
187}
188
189impl<CB> OnActiveTextPositionChangedCallback<CB>
190where
191    CB: Fn(UiAutomationElement, UiAutomationTextRange) -> () + 'static,
192{
193    fn new(func: CB, automation: Weak<IUIAutomation6>) -> Self {
194        Self {
195            _automation: automation,
196            _cb: func.into(),
197        }
198    }
199}
200
201impl<CB> IUIAutomationActiveTextPositionChangedEventHandler_Impl
202    for OnActiveTextPositionChangedCallback_Impl<CB>
203where
204    CB: Fn(UiAutomationElement, UiAutomationTextRange) -> () + 'static,
205{
206    #[allow(non_snake_case)]
207    fn HandleActiveTextPositionChangedEvent(
208        &self,
209        sender: Ref<'_, IUIAutomationElement>,
210        range: Ref<'_, IUIAutomationTextRange>,
211    ) -> Result<()> {
212        let func = &*self._cb;
213        let element =
214            UiAutomationElement::obtain(self._automation.clone(), sender.unwrap().clone());
215        let range = UiAutomationTextRange::obtain(range.unwrap());
216        func(element, range);
217        Ok(())
218    }
219}
220
221#[implement(IUIAutomationTextEditTextChangedEventHandler)]
222struct OnTextEditTextChangedCallback<CB>
223where
224    CB: Fn(UiAutomationElement) -> () + 'static,
225{
226    _automation: Weak<IUIAutomation6>,
227    _cb: Box<CB>,
228}
229
230impl<CB> OnTextEditTextChangedCallback<CB>
231where
232    CB: Fn(UiAutomationElement) -> () + 'static,
233{
234    fn new(func: CB, automation: Weak<IUIAutomation6>) -> Self {
235        Self {
236            _automation: automation,
237            _cb: func.into(),
238        }
239    }
240}
241
242impl<CB> IUIAutomationTextEditTextChangedEventHandler_Impl
243    for OnTextEditTextChangedCallback_Impl<CB>
244where
245    CB: Fn(UiAutomationElement) -> () + 'static,
246{
247    #[allow(non_snake_case)]
248    #[allow(unused_variables)]
249    fn HandleTextEditTextChangedEvent(
250        &self,
251        sender: Ref<'_, IUIAutomationElement>,
252        text_edit_change_type: TextEditChangeType,
253        event_strings: *const SAFEARRAY,
254    ) -> Result<()> {
255        beep(400, 40);
256        Ok(())
257    }
258}
259
260//noinspection IdentifierGrammar
261#[implement(IUIAutomationChangesEventHandler)]
262struct OnChangesCallback<CB>
263where
264    CB: Fn() -> () + 'static,
265{
266    _automation: Weak<IUIAutomation6>,
267    _cb: Box<CB>,
268}
269
270impl<CB> OnChangesCallback<CB>
271where
272    CB: Fn() -> () + 'static,
273{
274    fn new(func: CB, automation: Weak<IUIAutomation6>) -> Self {
275        Self {
276            _automation: automation,
277            _cb: func.into(),
278        }
279    }
280}
281
282impl<CB> IUIAutomationChangesEventHandler_Impl for OnChangesCallback_Impl<CB>
283where
284    CB: Fn() -> () + 'static,
285{
286    //noinspection SpellCheckingInspection
287    #[allow(non_snake_case)]
288    #[allow(unused_variables)]
289    fn HandleChangesEvent(
290        &self,
291        sender: Ref<'_, IUIAutomationElement>,
292        uiachanges: *const UiaChangeInfo,
293        changescount: i32,
294    ) -> Result<()> {
295        beep(400, 40);
296        Ok(())
297    }
298}
299
300#[implement(IUIAutomationEventHandler)]
301struct OnCallback<CB>
302where
303    CB: Fn(UiAutomationElement) -> () + 'static,
304{
305    _automation: Weak<IUIAutomation6>,
306    _cb: Box<CB>,
307}
308
309impl<CB> OnCallback<CB>
310where
311    CB: Fn(UiAutomationElement) -> () + 'static,
312{
313    fn new(func: CB, automation: Weak<IUIAutomation6>) -> Self {
314        Self {
315            _automation: automation,
316            _cb: func.into(),
317        }
318    }
319}
320
321impl<CB> IUIAutomationEventHandler_Impl for OnCallback_Impl<CB>
322where
323    CB: Fn(UiAutomationElement) -> () + 'static,
324{
325    #[allow(non_snake_case)]
326    #[allow(unused_variables)]
327    fn HandleAutomationEvent(
328        &self,
329        sender: Ref<'_, IUIAutomationElement>,
330        event_id: UIA_EVENT_ID,
331    ) -> Result<()> {
332        let func = &*self._cb;
333        func(UiAutomationElement::obtain(
334            self._automation.clone(),
335            sender.unwrap().clone(),
336        ));
337        Ok(())
338    }
339}