@@ -56,8 +56,6 @@ namespace winrt::DrawingIslandComponents::implementation
5656
5757 DrawingIsland::~DrawingIsland ()
5858 {
59- m_uia.FragmentRoot = nullptr ;
60- m_uia.FragmentFactory = nullptr ;
6159 m_output.Compositor = nullptr ;
6260 }
6361
@@ -104,9 +102,18 @@ namespace winrt::DrawingIslandComponents::implementation
104102 }
105103#endif
106104
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+ }
110117
111118
112119 // Destroy Content:
@@ -173,9 +180,105 @@ namespace winrt::DrawingIslandComponents::implementation
173180 }
174181
175182
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+
176279 winrt::Visual
177280 DrawingIsland::HitTestVisual (
178- float2 const point)
281+ float2 const & point) const
179282 {
180283 winrt::Visual selectedVisual{ nullptr };
181284 for (winrt::Visual visual : m_items.Visuals )
@@ -199,53 +302,51 @@ namespace winrt::DrawingIslandComponents::implementation
199302 void
200303 DrawingIsland::Accessibility_Initialize ()
201304 {
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>();
203307 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 );
206312
207313 (void )m_island.AutomationProviderRequested (
208314 { this , &DrawingIsland::Accessibility_OnAutomationProviderRequested });
209315 }
210316
211317
212318 void
213- DrawingIsland::Accessibility_CreateItemFragment ()
319+ DrawingIsland::Accessibility_CreateItemFragment (
320+ const winrt::Visual& itemVisual)
214321 {
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 );
218326
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:
220328 // - This is keeping the UIA objects alive.
221329 // - Although not used yet, the lookup would be used to get back to the item Fragment for
222330 // specific operations, such as hit-testing or tree walking. This avoids adding to more
223331 // expensive data storage, such as the Visual.CustomProperties map.
332+ m_uia.AutomationPeers .push_back (AutomationPeer{ itemVisual, fragment });
224333
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);
235336 }
236337
237338
238339 void
239340 DrawingIsland::Accessibility_OnAutomationProviderRequested (
240- const winrt::ContentIsland& /* island*/ ,
341+ const winrt::ContentIsland& island,
241342 const winrt::ContentIslandAutomationProviderRequestedEventArgs& args)
242343 {
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>());
247347
248- args.AutomationProvider (std::move (providerAsIInspectable));
348+ // Return the fragment root as an IInspectable.
349+ args.AutomationProvider (m_uia.FragmentRoot .as <IInspectable>());
249350
250351 args.Handled (true );
251352 }
@@ -286,8 +387,7 @@ namespace winrt::DrawingIslandComponents::implementation
286387 Input_OnPointerReleased ();
287388 });
288389
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.
291391 m_input.KeyboardSource = winrt::InputKeyboardSource::GetForIsland (m_island);
292392
293393 m_input.KeyboardSource .KeyDown (
@@ -407,6 +507,11 @@ namespace winrt::DrawingIslandComponents::implementation
407507 case winrt::Windows::System::VirtualKey::Escape:
408508 {
409509 m_items.Visuals .RemoveAll ();
510+
511+ // Update accessibility.
512+ m_uia.FragmentRoot ->RemoveAllChildren ();
513+ m_uia.AutomationPeers .clear ();
514+
410515 handled = true ;
411516 break ;
412517 }
@@ -515,7 +620,19 @@ namespace winrt::DrawingIslandComponents::implementation
515620 m_items.Visuals .Remove (m_items.SelectedVisual );
516621 m_items.Visuals .InsertAtTop (m_items.SelectedVisual );
517622
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+ }
519636 }
520637 else
521638 {
@@ -644,7 +761,7 @@ namespace winrt::DrawingIslandComponents::implementation
644761 m_items.Offset .x = -BlockSize / 2 .0f ;
645762 m_items.Offset .y = -BlockSize / 2 .0f ;
646763
647- Accessibility_CreateItemFragment ();
764+ Accessibility_CreateItemFragment (visual );
648765 }
649766
650767
0 commit comments