@@ -56,8 +56,6 @@ namespace winrt::DrawingIslandComponents::implementation
56
56
57
57
DrawingIsland::~DrawingIsland ()
58
58
{
59
- m_uia.FragmentRoot = nullptr ;
60
- m_uia.FragmentFactory = nullptr ;
61
59
m_output.Compositor = nullptr ;
62
60
}
63
61
@@ -104,9 +102,18 @@ namespace winrt::DrawingIslandComponents::implementation
104
102
}
105
103
#endif
106
104
107
- m_uia.FragmentFactory = nullptr ;
108
- m_uia.FragmentRoot = nullptr ;
109
- m_uia.VisualToFragmentMap .clear ();
105
+ // Make sure automation is destroyed.
106
+ for (auto & automationPeer : m_uia.AutomationPeers )
107
+ {
108
+ automationPeer.GetAutomationProvider ()->SetCallbackHandler (nullptr );
109
+ }
110
+ m_uia.AutomationPeers .clear ();
111
+
112
+ if (nullptr != m_uia.FragmentRoot )
113
+ {
114
+ m_uia.FragmentRoot ->SetCallbackHandler (nullptr );
115
+ m_uia.FragmentRoot = nullptr ;
116
+ }
110
117
111
118
112
119
// Destroy Content:
@@ -173,9 +180,105 @@ namespace winrt::DrawingIslandComponents::implementation
173
180
}
174
181
175
182
183
+ winrt::Windows::Graphics::RectInt32
184
+ DrawingIsland::GetScreenBoundsForAutomationFragment (
185
+ _In_::IUnknown const * const sender) const
186
+ {
187
+ // Check if the automation provider that has sent this callback is the fragment root.
188
+ if (m_uia.FragmentRoot .as <::IUnknown>().get () == sender)
189
+ {
190
+ winrt::Rect logicalRect;
191
+ logicalRect.X = 0 ;
192
+ logicalRect.Y = 0 ;
193
+ logicalRect.Width = m_island.ActualSize ().x ;
194
+ logicalRect.Height = m_island.ActualSize ().y ;
195
+
196
+ // This will convert from the logical coordinate space of the ContentIsland to
197
+ // Win32 screen coordinates that are needed by Accessibility.
198
+ return m_island.CoordinateConverter ().ConvertLocalToScreen (logicalRect);
199
+ }
200
+
201
+ // Else find the matching automation peer entry for the sending fragment.
202
+ auto iterator = std::find_if (
203
+ m_uia.AutomationPeers .begin (), m_uia.AutomationPeers .end (), [&sender](auto const & automationPeer)
204
+ {
205
+ return automationPeer.Match (sender);
206
+ });
207
+
208
+ if (m_uia.AutomationPeers .end () == iterator)
209
+ {
210
+ // Did not find any match for this automation provider.
211
+ return { 0 , 0 , 0 , 0 };
212
+ }
213
+
214
+ auto const & visualPeer = iterator->GetVisual ();
215
+ winrt::Rect logicalRect;
216
+ logicalRect.X = visualPeer.Offset ().x ;
217
+ logicalRect.Y = visualPeer.Offset ().y ;
218
+ logicalRect.Width = visualPeer.Size ().x ;
219
+ logicalRect.Height = visualPeer.Size ().y ;
220
+
221
+ // This will convert from the logical coordinate space of the ContentIsland to
222
+ // Win32 screen coordinates that are needed by Accessibility.
223
+ return m_island.CoordinateConverter ().ConvertLocalToScreen (logicalRect);
224
+ }
225
+
226
+
227
+ winrt::com_ptr<IRawElementProviderFragment>
228
+ DrawingIsland::GetFragmentFromPoint (
229
+ _In_ double x,
230
+ _In_ double y) const
231
+ {
232
+ // UIA provides hit test points in screen space.
233
+ // Convert the point into a dummy empty rectangle to use with the coordinate converter.
234
+ winrt::Windows::Graphics::RectInt32 screenRect{ static_cast <int >(x + 0.5 ), static_cast <int >(y + 0.5 ), 0 , 0 };
235
+ auto logicalRect = m_island.CoordinateConverter ().ConvertScreenToLocal (screenRect);
236
+ float2 localPoint{ logicalRect.X , logicalRect.Y };
237
+ auto hitTestVisual = HitTestVisual (localPoint);
238
+
239
+ // Find the automation peer for the hit test visual if any.
240
+ if (nullptr != hitTestVisual)
241
+ {
242
+ auto iterator = std::find_if (
243
+ m_uia.AutomationPeers .begin (), m_uia.AutomationPeers .end (), [&hitTestVisual](auto const & automationPeer)
244
+ {
245
+ return automationPeer.Match (hitTestVisual);
246
+ });
247
+
248
+ if (m_uia.AutomationPeers .end () != iterator)
249
+ {
250
+ // Return the automation provider if we found an automation peer for the hit test visual.
251
+ return iterator->GetAutomationProvider ().as <IRawElementProviderFragment>();
252
+ }
253
+ }
254
+
255
+ return nullptr ;
256
+ }
257
+
258
+
259
+ winrt::com_ptr<IRawElementProviderFragment>
260
+ DrawingIsland::GetFragmentInFocus () const
261
+ {
262
+ // Find the currently selected visual's automation peer.
263
+ auto iterator = std::find_if (
264
+ m_uia.AutomationPeers .begin (), m_uia.AutomationPeers .end (), [visual = m_items.SelectedVisual ](auto const & automationPeer)
265
+ {
266
+ return automationPeer.Match (visual);
267
+ });
268
+
269
+ if (m_uia.AutomationPeers .end () != iterator)
270
+ {
271
+ // Return the automation provider if we found an automation peer for the selected visual.
272
+ return iterator->GetAutomationProvider ().as <IRawElementProviderFragment>();
273
+ }
274
+
275
+ return nullptr ;
276
+ }
277
+
278
+
176
279
winrt::Visual
177
280
DrawingIsland::HitTestVisual (
178
- float2 const point)
281
+ float2 const & point) const
179
282
{
180
283
winrt::Visual selectedVisual{ nullptr };
181
284
for (winrt::Visual visual : m_items.Visuals )
@@ -199,53 +302,51 @@ namespace winrt::DrawingIslandComponents::implementation
199
302
void
200
303
DrawingIsland::Accessibility_Initialize ()
201
304
{
202
- m_uia.FragmentRoot = winrt::make_self<IslandFragmentRoot>(m_island);
305
+ // Create an UI automation fragment root for our island's content.
306
+ m_uia.FragmentRoot = winrt::make_self<IslandFragmentRoot>();
203
307
m_uia.FragmentRoot ->SetName (L" Drawing Squares" );
204
-
205
- m_uia.FragmentFactory = winrt::make_self<NodeSimpleFragmentFactory>();
308
+ m_uia.FragmentRoot ->SetProviderOptions (
309
+ ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading | ProviderOptions_RefuseNonClientSupport);
310
+ m_uia.FragmentRoot ->SetUiaControlTypeId (UIA_WindowControlTypeId);
311
+ m_uia.FragmentRoot ->SetCallbackHandler (this );
206
312
207
313
(void )m_island.AutomationProviderRequested (
208
314
{ this , &DrawingIsland::Accessibility_OnAutomationProviderRequested });
209
315
}
210
316
211
317
212
318
void
213
- DrawingIsland::Accessibility_CreateItemFragment ()
319
+ DrawingIsland::Accessibility_CreateItemFragment (
320
+ const winrt::Visual& itemVisual)
214
321
{
215
- // Create a new fragment for the new item.
216
- winrt::com_ptr<NodeSimpleFragment> fragment = m_uia.FragmentFactory ->Create (
217
- s_colorNames[m_output.CurrentColorIndex ].c_str (), m_uia.FragmentRoot );
322
+ // Create a new automation fragment.
323
+ auto fragment = winrt::make_self<NodeSimpleFragment>();
324
+ fragment->SetName (s_colorNames[m_output.CurrentColorIndex ].c_str ());
325
+ fragment->SetCallbackHandler (this );
218
326
219
- // Store the mapping between the Visual and the Fragment:
327
+ // Add an entry to our automation peers which is a mapping between the Visual and the Fragment:
220
328
// - This is keeping the UIA objects alive.
221
329
// - Although not used yet, the lookup would be used to get back to the item Fragment for
222
330
// specific operations, such as hit-testing or tree walking. This avoids adding to more
223
331
// expensive data storage, such as the Visual.CustomProperties map.
332
+ m_uia.AutomationPeers .push_back (AutomationPeer{ itemVisual, fragment });
224
333
225
- m_uia.VisualToFragmentMap [m_items.SelectedVisual ] = fragment;
226
-
227
- // Give the fragment a backpointer to the Visual to get real-time information.
228
- fragment->SetVisual (m_items.SelectedVisual );
229
-
230
- // Add the new fragment into the UIA tree.
231
- m_uia.FragmentRoot ->AddChild (fragment);
232
-
233
- // Finally set up parent chain
234
- fragment->SetParent (m_uia.FragmentRoot );
334
+ // Connect the automation fragment to our fragment root.
335
+ m_uia.FragmentRoot ->AddChildToEnd (fragment);
235
336
}
236
337
237
338
238
339
void
239
340
DrawingIsland::Accessibility_OnAutomationProviderRequested (
240
- const winrt::ContentIsland& /* island*/ ,
341
+ const winrt::ContentIsland& island,
241
342
const winrt::ContentIslandAutomationProviderRequestedEventArgs& args)
242
343
{
243
- IInspectable providerAsIInspectable;
244
- m_uia.FragmentRoot ->QueryInterface (
245
- winrt::guid_of<IInspectable>(),
246
- winrt::put_abi (providerAsIInspectable));
344
+ // Make sure the host provider is up to date.
345
+ m_uia.FragmentRoot ->SetHostProvider (
346
+ island.GetAutomationHostProvider ().as <IRawElementProviderSimple>());
247
347
248
- args.AutomationProvider (std::move (providerAsIInspectable));
348
+ // Return the fragment root as an IInspectable.
349
+ args.AutomationProvider (m_uia.FragmentRoot .as <IInspectable>());
249
350
250
351
args.Handled (true );
251
352
}
@@ -286,8 +387,7 @@ namespace winrt::DrawingIslandComponents::implementation
286
387
Input_OnPointerReleased ();
287
388
});
288
389
289
- // Set up the keyboard source. We store this in a member variable so we can easily call
290
- // TrySetFocus() in response to left clicks in the content later on.
390
+ // Set up the keyboard source.
291
391
m_input.KeyboardSource = winrt::InputKeyboardSource::GetForIsland (m_island);
292
392
293
393
m_input.KeyboardSource .KeyDown (
@@ -407,6 +507,11 @@ namespace winrt::DrawingIslandComponents::implementation
407
507
case winrt::Windows::System::VirtualKey::Escape:
408
508
{
409
509
m_items.Visuals .RemoveAll ();
510
+
511
+ // Update accessibility.
512
+ m_uia.FragmentRoot ->RemoveAllChildren ();
513
+ m_uia.AutomationPeers .clear ();
514
+
410
515
handled = true ;
411
516
break ;
412
517
}
@@ -515,7 +620,19 @@ namespace winrt::DrawingIslandComponents::implementation
515
620
m_items.Visuals .Remove (m_items.SelectedVisual );
516
621
m_items.Visuals .InsertAtTop (m_items.SelectedVisual );
517
622
518
- // TODO: The m_uia.FragmentRoots child should be removed and added to the end as well.
623
+ // Update automation.
624
+ // First find the existing automation peer.
625
+ auto iterator = std::find_if (
626
+ m_uia.AutomationPeers .begin (), m_uia.AutomationPeers .end (), [visual = m_items.SelectedVisual ](auto const & automationPeer)
627
+ {
628
+ return automationPeer.Match (visual);
629
+ });
630
+ if (m_uia.AutomationPeers .end () != iterator)
631
+ {
632
+ // Mirror the change to the visuals above.
633
+ m_uia.FragmentRoot ->RemoveChild (iterator->GetAutomationProvider ());
634
+ m_uia.FragmentRoot ->AddChildToEnd (iterator->GetAutomationProvider ());
635
+ }
519
636
}
520
637
else
521
638
{
@@ -644,7 +761,7 @@ namespace winrt::DrawingIslandComponents::implementation
644
761
m_items.Offset .x = -BlockSize / 2 .0f ;
645
762
m_items.Offset .y = -BlockSize / 2 .0f ;
646
763
647
- Accessibility_CreateItemFragment ();
764
+ Accessibility_CreateItemFragment (visual );
648
765
}
649
766
650
767
0 commit comments