Browse Source

More changes for non-toplevel visual tree roots. (#20732)

* Make some overlay-related types/members internal

* Make sure that TopLevel is no longer the actual root of the visual tree. This is needed for our future changes.

* API diff

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fixed incorrect test

* Make automation to target FocusRoot

* api diff

* Hide WindowBase/EmbeddableControlRoot's parents from automation

* api diff

* Separate automation root and visual root for automation purposes

* Hide ChromeOverlayLayer from public API

* Synchronize WindowBase visibility to VisualRoot

* Hide WindowBase.ArrangeSetBounds

* api diff

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
pull/20752/head
Nikita Tsukanov 7 months ago
committed by GitHub
parent
commit
2c44533707
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      Avalonia.Desktop.slnf
  2. 288
      api/Avalonia.nupkg.xml
  3. 4
      src/Avalonia.Base/Input/FocusManager.cs
  4. 7
      src/Avalonia.Base/Input/IInputRoot.cs
  5. 2
      src/Avalonia.Base/Input/KeyboardDevice.cs
  6. 2
      src/Avalonia.Base/VisualTree/IHostedVisualTreeRoot.cs
  7. 19
      src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
  8. 17
      src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
  9. 6
      src/Avalonia.Controls/Automation/Peers/EmbeddableControlRootAutomationPeer.cs
  10. 7
      src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs
  11. 2
      src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs
  12. 2
      src/Avalonia.Controls/PresentationSource/PresentationSource.Input.cs
  13. 4
      src/Avalonia.Controls/PresentationSource/PresentationSource.cs
  14. 3
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  15. 21
      src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
  16. 3
      src/Avalonia.Controls/Primitives/IPopupHost.cs
  17. 2
      src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs
  18. 2
      src/Avalonia.Controls/Primitives/OverlayLayer.cs
  19. 11
      src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
  20. 2
      src/Avalonia.Controls/Primitives/Popup.cs
  21. 2
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  22. 59
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  23. 2
      src/Avalonia.Controls/ToolTipService.cs
  24. 8
      src/Avalonia.Controls/TopLevel.cs
  25. 21
      src/Avalonia.Controls/TopLevelHost.cs
  26. 2
      src/Avalonia.Controls/Window.cs
  27. 3
      src/Avalonia.Controls/WindowBase.cs
  28. 2
      src/Avalonia.Native/AvnAutomationPeer.cs
  29. 2
      src/Avalonia.Native/TopLevelImpl.cs
  30. 3
      src/Avalonia.Themes.Fluent/Controls/Window.xaml
  31. 3
      src/Avalonia.Themes.Simple/Controls/Window.xaml
  32. 4
      src/Avalonia.X11/X11Window.cs
  33. 13
      src/Windows/Avalonia.Win32.Automation/AutomationNode.cs
  34. 10
      src/Windows/Avalonia.Win32.Automation/RootAutomationNode.cs
  35. 2
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  36. 8
      tests/Avalonia.Base.UnitTests/Rendering/CompositorInvalidationClippingTests.cs
  37. 4
      tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
  38. 4
      tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
  39. 2
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  40. 6
      tests/Avalonia.Controls.UnitTests/ToolTipTests.cs
  41. 5
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  42. 3
      tests/Avalonia.Headless.UnitTests/RenderingTests.cs
  43. 2
      tests/Avalonia.RenderTests/TestRenderRoot.cs
  44. 1
      tests/Avalonia.UnitTests/TestRoot.cs

9
Avalonia.Desktop.slnf

@ -8,11 +8,11 @@
"samples\\ControlCatalog\\ControlCatalog.csproj",
"samples\\GpuInterop\\GpuInterop.csproj",
"samples\\IntegrationTestApp\\IntegrationTestApp.csproj",
"samples\\TextTestApp\\TextTestApp.csproj",
"samples\\MiniMvvm\\MiniMvvm.csproj",
"samples\\RenderDemo\\RenderDemo.csproj",
"samples\\SampleControls\\ControlSamples.csproj",
"samples\\Sandbox\\Sandbox.csproj",
"samples\\TextTestApp\\TextTestApp.csproj",
"samples\\UnloadableAssemblyLoadContext\\UnloadableAssemblyLoadContextPlug\\UnloadableAssemblyLoadContextPlug.csproj",
"samples\\UnloadableAssemblyLoadContext\\UnloadableAssemblyLoadContext\\UnloadableAssemblyLoadContext.csproj",
"samples\\XEmbedSample\\XEmbedSample.csproj",
@ -29,13 +29,14 @@
"src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj",
"src\\Avalonia.Native\\Avalonia.Native.csproj",
"src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
"src\\Avalonia.Vulkan\\Avalonia.Vulkan.csproj",
"src\\Avalonia.Remote.Protocol\\Avalonia.Remote.Protocol.csproj",
"src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj",
"src\\Avalonia.Themes.Simple\\Avalonia.Themes.Simple.csproj",
"src\\Avalonia.Vulkan\\Avalonia.Vulkan.csproj",
"src\\Avalonia.X11\\Avalonia.X11.csproj",
"src\\HarfBuzz\\Avalonia.HarfBuzz\\Avalonia.HarfBuzz.csproj",
"src\\Headless\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
"src\\Headless\\Avalonia.Headless.XUnit\\Avalonia.Headless.XUnit.csproj",
"src\\Headless\\Avalonia.Headless\\Avalonia.Headless.csproj",
"src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj",
"src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj",
@ -58,6 +59,8 @@
"tests\\Avalonia.DesignerSupport.TestApp\\Avalonia.DesignerSupport.TestApp.csproj",
"tests\\Avalonia.DesignerSupport.Tests\\Avalonia.DesignerSupport.Tests.csproj",
"tests\\Avalonia.Generators.Tests\\Avalonia.Generators.Tests.csproj",
"tests\\Avalonia.Headless.XUnit.PerAssembly.UnitTests\\Avalonia.Headless.XUnit.PerAssembly.UnitTests.csproj",
"tests\\Avalonia.Headless.XUnit.PerTest.UnitTests\\Avalonia.Headless.XUnit.PerTest.UnitTests.csproj",
"tests\\Avalonia.IntegrationTests.Appium\\Avalonia.IntegrationTests.Appium.csproj",
"tests\\Avalonia.LeakTests\\Avalonia.LeakTests.csproj",
"tests\\Avalonia.Markup.UnitTests\\Avalonia.Markup.UnitTests.csproj",
@ -69,4 +72,4 @@
"tests\\TestFiles\\BuildTasks\\PInvoke\\PInvoke.csproj"
]
}
}
}

288
api/Avalonia.nupkg.xml

@ -217,6 +217,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.VisualTree.IHostedVisualTreeRoot</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetimeOptions</Target>
@ -235,6 +241,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Diagnostics.IPopupHostProvider</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.FileDialog</Target>
@ -289,12 +301,36 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.ChromeOverlayLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.IPopupHost</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.IScrollable</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.LightDismissOverlayLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.OverlayLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.SaveFileDialog</Target>
@ -553,6 +589,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.VisualTree.IHostedVisualTreeRoot</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetimeOptions</Target>
@ -571,6 +613,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Diagnostics.IPopupHostProvider</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.FileDialog</Target>
@ -625,12 +673,36 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.ChromeOverlayLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.IPopupHost</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.IScrollable</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.LightDismissOverlayLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.OverlayLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.SaveFileDialog</Target>
@ -1231,6 +1303,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.Primitives.VisualLayerManager.ChromeOverlayLayerProperty</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.TextBlock.LetterSpacingProperty</Target>
@ -1267,6 +1345,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Automation.Peers.AutomationPeer.GetVisualRootCore</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.ContextMenu.get_PlacementMode</Target>
@ -1411,18 +1495,54 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.AdornerLayer.#ctor</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.#ctor(Avalonia.Controls.Primitives.OverlayLayer)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.ConfigurePosition(Avalonia.Visual,Avalonia.Controls.PlacementMode,Avalonia.Point,Avalonia.Controls.Primitives.PopupPositioning.PopupAnchor,Avalonia.Controls.Primitives.PopupPositioning.PopupGravity,Avalonia.Controls.Primitives.PopupPositioning.PopupPositionerConstraintAdjustment,System.Nullable{Avalonia.Rect})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.CreatePopupHost(Avalonia.Visual,Avalonia.IAvaloniaDependencyResolver,System.Boolean)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.CreatePopupHost(Avalonia.Visual,Avalonia.IAvaloniaDependencyResolver)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.SetChild(Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.TakeFocus</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.Popup.get_Host</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.Popup.get_PlacementMode</Target>
@ -1507,6 +1627,36 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_AdornerLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_ChromeOverlayLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_LightDismissOverlayLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_OverlayLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_TextSelectorLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Screens.ScreenFromWindow(Avalonia.Platform.IWindowBaseImpl)</Target>
@ -1555,6 +1705,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.WindowBase.ArrangeSetBounds(Avalonia.Size)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IWindowImpl.GetWindowsZOrder(System.Span{Avalonia.Controls.Window},System.Span{System.Int64})</Target>
@ -2251,6 +2407,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.Primitives.VisualLayerManager.ChromeOverlayLayerProperty</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Avalonia.Controls.TextBlock.LetterSpacingProperty</Target>
@ -2287,6 +2449,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Automation.Peers.AutomationPeer.GetVisualRootCore</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.ContextMenu.get_PlacementMode</Target>
@ -2431,18 +2599,54 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.AdornerLayer.#ctor</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.#ctor(Avalonia.Controls.Primitives.OverlayLayer)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.ConfigurePosition(Avalonia.Visual,Avalonia.Controls.PlacementMode,Avalonia.Point,Avalonia.Controls.Primitives.PopupPositioning.PopupAnchor,Avalonia.Controls.Primitives.PopupPositioning.PopupGravity,Avalonia.Controls.Primitives.PopupPositioning.PopupPositionerConstraintAdjustment,System.Nullable{Avalonia.Rect})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.CreatePopupHost(Avalonia.Visual,Avalonia.IAvaloniaDependencyResolver,System.Boolean)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.CreatePopupHost(Avalonia.Visual,Avalonia.IAvaloniaDependencyResolver)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.SetChild(Avalonia.Controls.Control)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.OverlayPopupHost.TakeFocus</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.Popup.get_Host</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.Popup.get_PlacementMode</Target>
@ -2527,6 +2731,36 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_AdornerLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_ChromeOverlayLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_LightDismissOverlayLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_OverlayLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.VisualLayerManager.get_TextSelectorLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Screens.ScreenFromWindow(Avalonia.Platform.IWindowBaseImpl)</Target>
@ -2575,6 +2809,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.WindowBase.ArrangeSetBounds(Avalonia.Size)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IWindowImpl.GetWindowsZOrder(System.Span{Avalonia.Controls.Window},System.Span{System.Int64})</Target>
@ -2809,6 +3049,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Input.IInputRoot.FocusRoot</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat</Target>
@ -3013,6 +3259,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Input.IInputRoot.FocusRoot</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat</Target>
@ -3493,6 +3745,24 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.AdornerLayer</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.OverlayPopupHost</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.VisualLayerManager</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Platform.Screen</Target>
@ -3511,6 +3781,24 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.AdornerLayer</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.OverlayPopupHost</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Primitives.VisualLayerManager</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Platform.Screen</Target>

4
src/Avalonia.Base/Input/FocusManager.cs

@ -246,10 +246,10 @@ namespace Avalonia.Input
if (scope is not Visual v)
return null;
var root = v.VisualRoot as Visual;
var root = v.PresentationSource?.InputRoot.FocusRoot as Visual;
while (root is IHostedVisualTreeRoot hosted &&
hosted.Host?.VisualRoot is Visual parentRoot)
hosted.Host?.PresentationSource?.InputRoot.FocusRoot is {} parentRoot)
{
root = parentRoot;
}

7
src/Avalonia.Base/Input/IInputRoot.cs

@ -26,5 +26,12 @@ namespace Avalonia.Input
internal ITextInputMethodImpl? InputMethod { get; }
internal InputElement RootElement { get; }
// HACK: This is a temporary hack for "default focus" concept.
// If nothing is focused we send keyboard events to Window. Since for now we always
// control PresentationSource, we simply pass the TopLevel as a separate parameter there.
// It's also currently used by automation since we have special WindowAutomationPeer which needs to target the
// window itself
public InputElement FocusRoot { get; }
}
}

2
src/Avalonia.Base/Input/KeyboardDevice.cs

@ -225,7 +225,7 @@ namespace Avalonia.Input
if(e.Handled)
return;
var element = FocusedElement ?? e.Root.RootElement;
var element = FocusedElement ?? e.Root.FocusRoot;
if (e is RawKeyEventArgs keyInput)
{

2
src/Avalonia.Base/VisualTree/IHostedVisualTreeRoot.cs

@ -3,7 +3,7 @@ namespace Avalonia.VisualTree
/// <summary>
/// Interface for controls that are at the root of a hosted visual tree, such as popups.
/// </summary>
public interface IHostedVisualTreeRoot
internal interface IHostedVisualTreeRoot
{
/// <summary>
/// Gets the visual tree host.

19
src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Avalonia.Automation.Provider;
using Avalonia.Metadata;
namespace Avalonia.Automation.Peers
{
@ -398,13 +399,25 @@ namespace Avalonia.Automation.Peers
/// <term>Windows</term>
/// <description>No mapping, but used internally to translate coordinates.</description>
/// </item>
/// </list>
/// </remarks>
[PrivateApi]
public AutomationPeer? GetVisualRoot() => GetVisualRootCore();
/// <summary>
/// Gets the <see cref="AutomationPeer"/> that is the root of this <see cref="AutomationPeer"/>'s
/// visual tree.
/// </summary>
/// <remarks>
/// <list type="table">
/// <item>
/// <term>macOS</term>
/// <description><c>NSAccessibilityProtocol.accessibilityTopLevelUIElement</c></description>
/// </item>
/// </list>
/// </remarks>
public AutomationPeer? GetVisualRoot() => GetVisualRootCore();
[PrivateApi]
public AutomationPeer? GetAutomationRoot() => GetAutomationRootCore();
/// <summary>
/// Gets a value that indicates whether the element that is associated with this automation
@ -633,7 +646,7 @@ namespace Avalonia.Automation.Peers
return GetAutomationControlTypeCore();
}
protected virtual AutomationPeer? GetVisualRootCore()
private protected virtual AutomationPeer? GetAutomationRootCore()
{
var peer = this;
var parent = peer.GetParent();
@ -647,6 +660,8 @@ namespace Avalonia.Automation.Peers
return peer;
}
private protected virtual AutomationPeer? GetVisualRootCore() => GetAutomationRootCore();
protected virtual bool IsContentElementOverrideCore()
{

17
src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs

@ -147,10 +147,10 @@ namespace Avalonia.Automation.Peers
EnsureConnected();
return _parent;
}
protected override AutomationPeer? GetVisualRootCore()
private protected override AutomationPeer? GetVisualRootCore()
{
if (Owner.GetVisualRoot() is Control c)
if (Owner?.PresentationSource?.InputRoot?.FocusRoot is Control c)
return CreatePeerForElement(c);
return null;
}
@ -268,11 +268,13 @@ namespace Avalonia.Automation.Peers
private void VisualChildrenChanged(object? sender, EventArgs e) => InvalidateChildren();
private protected virtual Visual? GetVisualParent() => Owner.GetVisualParent();
private void OwnerPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == Visual.IsVisibleProperty)
{
var parent = Owner.GetVisualParent();
var parent = GetVisualParent();
if (parent is Control c)
(GetOrCreate(c) as ControlAutomationPeer)?.InvalidateChildren();
}
@ -303,7 +305,7 @@ namespace Avalonia.Automation.Peers
{
if (!_parentValid)
{
var parent = Owner.GetVisualParent();
var parent = GetVisualParent();
while (parent is object)
{
@ -311,6 +313,11 @@ namespace Avalonia.Automation.Peers
{
var parentPeer = GetOrCreate(c);
parentPeer.GetChildren();
if (parentPeer is ControlAutomationPeer controlPeer)
{
parent = controlPeer.GetVisualParent();
continue;
}
}
parent = parent.GetVisualParent();

6
src/Avalonia.Controls/Automation/Peers/EmbeddableControlRootAutomationPeer.cs

@ -58,8 +58,8 @@ namespace Avalonia.Controls.Automation.Peers
{
var oldFocus = _focus;
var c = focus as Control;
_focus = c?.VisualRoot == Owner ? c : null;
_focus = Owner.IsVisualAncestorOf(c) ? c : null;
if (_focus != oldFocus)
{
@ -89,5 +89,7 @@ namespace Avalonia.Controls.Automation.Peers
Owner.Closed -= OnClosed;
StopTrackingFocus();
}
private protected override Visual? GetVisualParent() => null;
}
}

7
src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs

@ -70,7 +70,7 @@ namespace Avalonia.Automation.Peers
var oldFocus = _focus;
var c = focus as Control;
_focus = c?.VisualRoot == Owner ? c : null;
_focus = Owner.IsVisualAncestorOf(c) ? c : null;
if (_focus != oldFocus)
{
@ -88,6 +88,11 @@ namespace Avalonia.Automation.Peers
OnFocusChanged(KeyboardDevice.Instance!.FocusedElement);
}
}
private protected override Visual? GetVisualParent()
{
return null;
}
}
}

2
src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs

@ -8,7 +8,7 @@ namespace Avalonia.Controls.Diagnostics
/// Diagnostics interface to retrieve an associated <see cref="IPopupHost"/>.
/// </summary>
[NotClientImplementable]
public interface IPopupHostProvider
internal interface IPopupHostProvider
{
/// <summary>
/// The popup host.

2
src/Avalonia.Controls/PresentationSource/PresentationSource.Input.cs

@ -57,4 +57,6 @@ internal partial class PresentationSource
}
return candidate;
}
public InputElement FocusRoot { get; }
}

4
src/Avalonia.Controls/PresentationSource/PresentationSource.cs

@ -19,7 +19,8 @@ internal partial class PresentationSource : IPresentationSource, IInputRoot, IDi
internal FocusManager FocusManager { get; } = new();
public PresentationSource(InputElement rootVisual, ITopLevelImpl platformImpl,
public PresentationSource(InputElement rootVisual, InputElement defaultFocusVisual,
ITopLevelImpl platformImpl,
IAvaloniaDependencyResolver dependencyResolver, Func<Size> clientSizeProvider)
{
_clientSizeProvider = clientSizeProvider;
@ -41,6 +42,7 @@ internal partial class PresentationSource : IPresentationSource, IInputRoot, IDi
LayoutManager = CreateLayoutManager();
RootVisual = rootVisual;
FocusRoot = defaultFocusVisual;
}
// In WPF it's a Visual and it's nullable. For now we have it as non-nullable InputElement since

3
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -12,7 +12,6 @@ namespace Avalonia.Controls.Primitives
/// Adorners are always on top of the adorned element and are positioned to stay relative to the adorned element.
/// </summary>
/// <remarks>
/// TODO: Need to track position of adorned elements and move the adorner if they move.
/// </remarks>
public class AdornerLayer : Canvas
{
@ -55,7 +54,7 @@ namespace Avalonia.Controls.Primitives
IsClipEnabledProperty.Changed.Subscribe(AdornerIsClipEnabledChanged);
}
public AdornerLayer()
internal AdornerLayer()
{
Children.CollectionChanged += ChildrenCollectionChanged;
_trackingHelper.SetVisual(this);

21
src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs

@ -4,27 +4,8 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives
{
public class ChromeOverlayLayer : Panel
internal class ChromeOverlayLayer : Panel
{
public static Panel? GetOverlayLayer(Visual visual)
{
foreach (var v in visual.GetVisualAncestors())
if (v is VisualLayerManager vlm)
if (vlm.OverlayLayer != null)
return vlm.ChromeOverlayLayer;
if (visual is TopLevel tl)
{
var layers = tl.GetVisualDescendants().OfType<VisualLayerManager>().FirstOrDefault();
return layers?.ChromeOverlayLayer;
}
return null;
}
public void Add(Control c)
{
base.Children.Add(c);
}
}
}

3
src/Avalonia.Controls/Primitives/IPopupHost.cs

@ -15,8 +15,7 @@ namespace Avalonia.Controls.Primitives
/// (<see cref="PopupRoot"/>) or an <see cref="OverlayPopupHost"/> which is created
/// on an <see cref="OverlayLayer"/>.
/// </remarks>
[PrivateApi]
public interface IPopupHost : IDisposable, IFocusScope
internal interface IPopupHost : IDisposable, IFocusScope
{
/// <summary>
/// Gets or sets the fixed width of the popup.

2
src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs

@ -12,7 +12,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// A layer that is used to dismiss a <see cref="Popup"/> when the user clicks outside.
/// </summary>
public class LightDismissOverlayLayer : Border, ICustomHitTest
internal class LightDismissOverlayLayer : Border, ICustomHitTest
{
public IInputElement? InputPassThroughElement { get; set; }

2
src/Avalonia.Controls/Primitives/OverlayLayer.cs

@ -4,7 +4,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives
{
public class OverlayLayer : Canvas
internal class OverlayLayer : Canvas
{
protected override bool BypassFlowDirectionPolicies => true;
public Size AvailableSize { get; private set; }

11
src/Avalonia.Controls/Primitives/OverlayPopupHost.cs

@ -31,7 +31,7 @@ namespace Avalonia.Controls.Primitives
static OverlayPopupHost()
=> KeyboardNavigation.TabNavigationProperty.OverrideDefaultValue<OverlayPopupHost>(KeyboardNavigationMode.Cycle);
public OverlayPopupHost(OverlayLayer overlayLayer)
internal OverlayPopupHost(OverlayLayer overlayLayer)
{
_overlayLayer = overlayLayer;
_positioner = new ManagedPopupPositioner(this);
@ -41,7 +41,7 @@ namespace Avalonia.Controls.Primitives
/// <inheritdoc />
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012", Justification = "Explicit set")]
public void SetChild(Control? control)
void IPopupHost.SetChild(Control? control)
{
Content = control;
}
@ -86,7 +86,7 @@ namespace Avalonia.Controls.Primitives
_overlayLayer.Children.Remove(this);
}
public void TakeFocus()
void IPopupHost.TakeFocus()
{
// Nothing to do here: overlay popups are implemented inside the window.
}
@ -150,9 +150,8 @@ namespace Avalonia.Controls.Primitives
}
double IManagedPopupPositionerPopup.Scaling => 1;
[PrivateApi]
public static IPopupHost CreatePopupHost(Visual target, IAvaloniaDependencyResolver? dependencyResolver, bool shouldUseOverlayLayer)
internal static IPopupHost CreatePopupHost(Visual target, IAvaloniaDependencyResolver? dependencyResolver, bool shouldUseOverlayLayer)
{
if (!shouldUseOverlayLayer)
{

2
src/Avalonia.Controls/Primitives/Popup.cs

@ -176,7 +176,7 @@ namespace Avalonia.Controls.Primitives
internal event EventHandler<CancelEventArgs>? Closing;
public IPopupHost? Host => _openState?.PopupHost;
internal IPopupHost? Host => _openState?.PopupHost;
/// <summary>
/// Gets or sets a hint to the window manager that a shadow should be added to the popup.

2
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -190,7 +190,7 @@ namespace Avalonia.Controls.Primitives
return new Size(width, height);
}
protected sealed override Size ArrangeSetBounds(Size size)
private protected sealed override Size ArrangeSetBounds(Size size)
{
if (_popupSize != size)
{

59
src/Avalonia.Controls/Primitives/VisualLayerManager.cs

@ -1,9 +1,10 @@
using System.Collections.Generic;
using Avalonia.Controls.Chrome;
using Avalonia.LogicalTree;
namespace Avalonia.Controls.Primitives
{
public class VisualLayerManager : Decorator
public sealed class VisualLayerManager : Decorator
{
private const int AdornerZIndex = int.MaxValue - 100;
private const int ChromeZIndex = int.MaxValue - 99;
@ -14,12 +15,9 @@ namespace Avalonia.Controls.Primitives
private ILogicalRoot? _logicalRoot;
private readonly List<Control> _layers = new();
public static readonly StyledProperty<ChromeOverlayLayer?> ChromeOverlayLayerProperty =
AvaloniaProperty.Register<VisualLayerManager, ChromeOverlayLayer?>(nameof(ChromeOverlayLayer));
public bool IsPopup { get; set; }
public AdornerLayer AdornerLayer
internal AdornerLayer AdornerLayer
{
get
{
@ -30,30 +28,43 @@ namespace Avalonia.Controls.Primitives
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031",
Justification = "A hack to make ChromeOverlayLayer lazily creatable. It is expected that GetValue(ChromeOverlayLayerProperty) alone won't work.")]
public ChromeOverlayLayer ChromeOverlayLayer
{
get
{
var current = GetValue(ChromeOverlayLayerProperty);
if (current is null)
{
var chromeOverlayLayer = new ChromeOverlayLayer();
AddLayer(chromeOverlayLayer, ChromeZIndex);
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
EnsureChromeOverlayIfNeeded();
base.OnAttachedToVisualTree(e);
}
SetValue(ChromeOverlayLayerProperty, chromeOverlayLayer);
void EnsureChromeOverlayIfNeeded()
{
// HACK: This is a replacement hack for the old set of hacks for TitleBar.
current = chromeOverlayLayer;
// Check if we are attached direclty-ish to a Window (i. e. no other VisualLayerManager in between).
// If we are, then we are the "main" VisualLayerManager and should create the ChromeOverlayLayer and add titlebar there
var parent = VisualParent;
while (parent != null)
{
if(parent is VisualLayerManager)
break;
else if (parent is Window window)
{
if (FindLayer<ChromeOverlayLayer>() == null)
{
var layer = new ChromeOverlayLayer();
AddLayer(layer, ChromeZIndex);
layer.Children.Add(new TitleBar());
}
break;
}
return current;
parent = parent.VisualParent;
}
}
public OverlayLayer? OverlayLayer
internal OverlayLayer? OverlayLayer
{
get
{
@ -66,7 +77,7 @@ namespace Avalonia.Controls.Primitives
}
}
public TextSelectorLayer? TextSelectorLayer
internal TextSelectorLayer? TextSelectorLayer
{
get
{
@ -79,7 +90,7 @@ namespace Avalonia.Controls.Primitives
}
}
public LightDismissOverlayLayer LightDismissOverlayLayer
internal LightDismissOverlayLayer LightDismissOverlayLayer
{
get
{

2
src/Avalonia.Controls/ToolTipService.cs

@ -38,7 +38,7 @@ namespace Avalonia.Controls
if (e is RawPointerEventArgs pointerEvent)
{
bool isTooltipEvent = false;
if (_tipControl?.GetValue(ToolTip.ToolTipProperty) is { } currentTip && e.Root.RootElement == currentTip.PopupHost)
if (_tipControl?.GetValue(ToolTip.ToolTipProperty) is { } currentTip && e.Root == (currentTip.PopupHost as Visual)?.GetInputRoot())
{
isTooltipEvent = true;
_lastTipEventTime = pointerEvent.Timestamp;

8
src/Avalonia.Controls/TopLevel.cs

@ -189,8 +189,12 @@ namespace Avalonia.Controls
PlatformImpl = impl ?? throw new InvalidOperationException(
"Could not create window implementation: maybe no windowing subsystem was initialized?");
dependencyResolver ??= AvaloniaLocator.Current;
_source = new PresentationSource(this, impl, dependencyResolver, () => ClientSize);
_source.RootVisual = this;
var hostVisual = new TopLevelHost(this);
((ISetLogicalParent)hostVisual).SetParent(this);
_source = new PresentationSource(hostVisual, this,
impl, dependencyResolver, () => ClientSize);
_source.Renderer.SceneInvalidated += SceneInvalidated;
_scaling = ValidateScaling(impl.RenderScaling);

21
src/Avalonia.Controls/TopLevelHost.cs

@ -0,0 +1,21 @@
using Avalonia.Input;
namespace Avalonia.Controls;
/// <summary>
/// For now this is a stub class that is needed to prevent people from assuming that TopLevel sits at the root of the
/// visual tree.
/// In future 12.x releases it will serve more roles like hosting popups and CSD.
/// </summary>
internal class TopLevelHost : Control
{
static TopLevelHost()
{
KeyboardNavigation.TabNavigationProperty.OverrideDefaultValue<TopLevelHost>(KeyboardNavigationMode.Cycle);
}
public TopLevelHost(TopLevel tl)
{
VisualChildren.Add(tl);
}
}

2
src/Avalonia.Controls/Window.cs

@ -1164,7 +1164,7 @@ namespace Avalonia.Controls
return result;
}
protected sealed override Size ArrangeSetBounds(Size size)
private protected sealed override Size ArrangeSetBounds(Size size)
{
_arrangeBounds = size;
if (_canHandleResized)

3
src/Avalonia.Controls/WindowBase.cs

@ -200,6 +200,7 @@ namespace Avalonia.Controls
if (change.Property == IsVisibleProperty)
{
VisualRoot?.IsVisible = change.GetNewValue<bool>();
IsVisibleChanged(change);
}
}
@ -311,7 +312,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="size">The requested size of the window.</param>
/// <returns>The actual size of the window.</returns>
protected virtual Size ArrangeSetBounds(Size size) => size;
private protected virtual Size ArrangeSetBounds(Size size) => size;
/// <summary>
/// Handles a window position change notification from

2
src/Avalonia.Native/AvnAutomationPeer.cs

@ -50,7 +50,7 @@ namespace Avalonia.Native
public AvnLandmarkType LandmarkType => (AvnLandmarkType?)_inner.GetLandmarkType() ?? AvnLandmarkType.LandmarkNone;
public int HeadingLevel => _inner.GetHeadingLevel();
public IAvnAutomationPeer? Parent => Wrap(_inner.GetParent());
public IAvnAutomationPeer? VisualRoot => Wrap(_inner.GetVisualRoot());
public IAvnAutomationPeer? VisualRoot => Wrap(_inner.GetAutomationRoot());
public AvnLiveSetting LiveSetting => (AvnLiveSetting)_inner.GetLiveSetting();
public int HasKeyboardFocus() => _inner.HasKeyboardFocus().AsComBool();

2
src/Avalonia.Native/TopLevelImpl.cs

@ -169,7 +169,7 @@ internal class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface
public AutomationPeer? GetAutomationPeer()
{
return _inputRoot?.RootElement is Control c ? ControlAutomationPeer.CreatePeerForElement(c) : null;
return _inputRoot?.FocusRoot is Control c ? ControlAutomationPeer.CreatePeerForElement(c) : null;
}
public bool RawTextInputEvent(ulong timeStamp, string text)

3
src/Avalonia.Themes.Fluent/Controls/Window.xaml

@ -15,9 +15,6 @@
<Border Background="{TemplateBinding Background}" IsHitTestVisible="False" />
<Panel Background="Transparent" Margin="{TemplateBinding WindowDecorationMargin}" />
<VisualLayerManager>
<VisualLayerManager.ChromeOverlayLayer>
<TitleBar />
</VisualLayerManager.ChromeOverlayLayer>
<ContentPresenter Name="PART_ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"

3
src/Avalonia.Themes.Simple/Controls/Window.xaml

@ -18,9 +18,6 @@
<Panel Margin="{TemplateBinding WindowDecorationMargin}"
Background="Transparent" />
<VisualLayerManager>
<VisualLayerManager.ChromeOverlayLayer>
<TitleBar />
</VisualLayerManager.ChromeOverlayLayer>
<ContentPresenter Name="PART_ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"

4
src/Avalonia.X11/X11Window.cs

@ -268,8 +268,8 @@ namespace Avalonia.X11
: Task.FromResult<IStorageProvider?>(null),
() => GtkSystemDialog.TryCreate(this),
// TODO: This will be incompatible with "root element is not a TopLevel" scenarios,
// we should probably have a separate API for this
() => Task.FromResult(InputRoot.RootElement is TopLevel tl
// HACK: this relies on focus root being TopLevel which currently is true
() => Task.FromResult(InputRoot.FocusRoot is TopLevel tl
? (IStorageProvider?)new ManagedStorageProvider(tl)
: null)
});

13
src/Windows/Avalonia.Win32.Automation/AutomationNode.cs

@ -11,6 +11,7 @@ using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Automation.Peers;
using Avalonia.Threading;
using Avalonia.VisualTree;
using Avalonia.Win32.Automation.Interop;
using AAP = Avalonia.Automation.Provider;
using UIA = Avalonia.Win32.Automation.Interop;
@ -83,8 +84,14 @@ namespace Avalonia.Win32.Automation
{
return InvokeSync(() =>
{
if (GetRoot() is RootAutomationNode root)
return root.ToScreen(Peer.GetBoundingRectangle());
if (Peer.GetVisualRoot() is ControlAutomationPeer root &&
root.Owner.GetPresentationSource() is not null)
{
var originalRect = Peer.GetBoundingRectangle();
return new PixelRect(root.Owner.PointToScreen(originalRect.TopLeft),
root.Owner.PointToScreen(originalRect.BottomRight)).ToRect(1);
}
return default;
});
}
@ -287,7 +294,7 @@ namespace Avalonia.Win32.Automation
private RootAutomationNode? GetRoot()
{
Dispatcher.UIThread.VerifyAccess();
return GetOrCreate(Peer.GetVisualRoot()) as RootAutomationNode;
return GetOrCreate(Peer.GetAutomationRoot()) as RootAutomationNode;
}
private void OnPeerChildrenChanged(object? sender, EventArgs e)

10
src/Windows/Avalonia.Win32.Automation/RootAutomationNode.cs

@ -48,16 +48,6 @@ namespace Avalonia.Win32.Automation
return GetOrCreate(focus);
}
public Rect ToScreen(Rect rect)
{
if (WindowImpl is null)
return default;
return new PixelRect(
WindowImpl.PointToScreen(rect.TopLeft),
WindowImpl.PointToScreen(rect.BottomRight))
.ToRect(1);
}
public override IRawElementProviderSimple? GetHostRawElementProvider()
{
var handle = WindowImpl?.Handle?.Handle ?? IntPtr.Zero;

2
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -937,7 +937,7 @@ namespace Avalonia.Win32
return IntPtr.Zero;
}
case WindowsMessage.WM_GETOBJECT:
if ((long)lParam == uiaRootObjectId && UiaCoreTypesApi.IsNetComInteropAvailable && _owner?.RootElement is Control control)
if ((long)lParam == uiaRootObjectId && UiaCoreTypesApi.IsNetComInteropAvailable && _owner?.FocusRoot is Control control)
{
var peer = ControlAutomationPeer.CreatePeerForElement(control);
var node = AutomationNode.GetOrCreate(peer);

8
tests/Avalonia.Base.UnitTests/Rendering/CompositorInvalidationClippingTests.cs

@ -14,6 +14,8 @@ public class CompositorInvalidationClippingTests : CompositorTestsBase
foreach (var child in visual.VisualChildren) count += CountVisuals(child);
return count;
}
private const int TopLevelOverhead = 2; // TopLevel + TopLevelHost
[Theory,
// If canvas itself has no background, the second render won't draw any visuals at all, since
@ -22,9 +24,9 @@ public class CompositorInvalidationClippingTests : CompositorTestsBase
InlineData(true, false, false, 1, 0),
InlineData(false, true, false, 1, 0),
// If canvas has background, the second render will draw only the canvas visual itself
InlineData(false, false, true, 5, 4),
InlineData(true, false, true,5, 4),
InlineData(false, true, true, 5, 4),
InlineData(false, false, true, 4 + TopLevelOverhead, 3 + TopLevelOverhead),
InlineData(true, false, true,4 + TopLevelOverhead, 3 + TopLevelOverhead),
InlineData(false, true, true, 4 + TopLevelOverhead, 3 + TopLevelOverhead),
]
public void Do_Not_Re_Render_Unaffected_Visual_Trees(bool clipToBounds, bool clipGeometry,
bool canvasHasContent,

4
tests/Avalonia.Controls.UnitTests/FlyoutTests.cs

@ -256,7 +256,7 @@ namespace Avalonia.Controls.UnitTests
var hitTester = new Mock<IHitTester>();
window.HitTesterOverride = hitTester.Object;
hitTester.Setup(x =>
x.HitTestFirst(new Point(90, 90), window, It.IsAny<Func<Visual, bool>>()))
x.HitTestFirst(new Point(90, 90), (Visual)window.VisualRoot!, It.IsAny<Func<Visual, bool>>()))
.Returns(button);
var e = CreatePointerPressedEventArgs(window, new Point(90, 90));
@ -299,7 +299,7 @@ namespace Avalonia.Controls.UnitTests
var hitTester = new Mock<IHitTester>();
window.HitTesterOverride = hitTester.Object;
hitTester.Setup(x =>
x.HitTestFirst(new Point(90, 90), window, It.IsAny<Func<Visual, bool>>()))
x.HitTestFirst(new Point(90, 90), (Visual)window.VisualRoot!, It.IsAny<Func<Visual, bool>>()))
.Returns(button);
var e = CreatePointerPressedEventArgs(window, new Point(90, 90));

4
tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs

@ -100,7 +100,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
[Fact]
public void PopupRoot_Should_Have_Null_VisualParent()
public void PopupRoot_Should_Have_TopLevelHost_VisualParent()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@ -108,7 +108,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Open();
Assert.Null(((Visual)target.Host!).GetVisualParent());
Assert.IsType<TopLevelHost>(((Visual)target.Host!).GetVisualParent());
}
}

2
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -607,7 +607,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
window.Content = border;
hitTester.Setup(x =>
x.HitTestFirst(new Point(10, 15), window, It.IsAny<Func<Visual, bool>>()))
x.HitTestFirst(new Point(10, 15), (Visual)window.VisualRoot!, It.IsAny<Func<Visual, bool>>()))
.Returns(border);
border.PointerPressed += (s, e) =>

6
tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

@ -26,7 +26,7 @@ namespace Avalonia.Controls.UnitTests
var toolTip = control.GetValue(ToolTip.ToolTipProperty);
Assert.NotNull(toolTip);
Assert.IsType<PopupRoot>(toolTip.PopupHost);
Assert.Same(toolTip.VisualRoot, toolTip.PopupHost);
Assert.Same(TopLevel.GetTopLevel(toolTip), toolTip.PopupHost);
}
}
@ -574,7 +574,7 @@ namespace Avalonia.Controls.UnitTests
point = new Point(id, int.MaxValue);
}
hitTesterMock.Setup(m => m.HitTestFirst(point, window, It.IsAny<Func<Visual, bool>>()))
hitTesterMock.Setup(m => m.HitTestFirst(point, It.IsAny<Visual>(), It.IsAny<Func<Visual, bool>>()))
.Returns(control);
var root = control?.GetInputRoot() ?? window.InputRoot;
@ -585,7 +585,7 @@ namespace Avalonia.Controls.UnitTests
if (lastRoot != null && lastRoot != root)
{
((TopLevel)lastRoot.RootElement)?.PlatformImpl?.Input?.Invoke(new RawPointerEventArgs(s_mouseDevice, timestamp,
((PresentationSource)lastRoot)?.PlatformImpl?.Input?.Invoke(new RawPointerEventArgs(s_mouseDevice, timestamp,
lastRoot, RawPointerEventType.LeaveWindow, new Point(-1,-1), RawInputModifiers.None));
}

5
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -222,7 +222,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Adding_Top_Level_As_Child_Should_Not_Exception()
public void Adding_Top_Level_As_Child_Should_Throw_Exception()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@ -233,8 +233,7 @@ namespace Avalonia.Controls.UnitTests
target.Template = CreateTemplate();
target.Content = child;
target.ApplyTemplate();
target.Presenter!.ApplyTemplate();
Assert.Throws<InvalidOperationException>(() => target.Presenter!.ApplyTemplate());
}
}

3
tests/Avalonia.Headless.UnitTests/RenderingTests.cs

@ -4,6 +4,7 @@ using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Rendering.Composition;
using Avalonia.Threading;
namespace Avalonia.Headless.UnitTests;
@ -159,6 +160,8 @@ public class RenderingTests
window.Show();
Dispatcher.UIThread.RunJobs();
var compositionVisual = ElementComposition.GetElementVisual(window)!;
var snapshot = await compositionVisual.Compositor.CreateCompositionVisualSnapshot(compositionVisual, 1);

2
tests/Avalonia.RenderTests/TestRenderRoot.cs

@ -75,6 +75,6 @@ namespace Avalonia.Skia.RenderTests
public IInputElement? PointerOverElement { get; set; }
public ITextInputMethodImpl? InputMethod { get; }
public InputElement RootElement => this;
public InputElement FocusRoot => this;
}
}

1
tests/Avalonia.UnitTests/TestRoot.cs

@ -80,6 +80,7 @@ namespace Avalonia.UnitTests
public IInputElement? PointerOverElement { get; set; }
public ITextInputMethodImpl? InputMethod { get; }
public InputElement RootElement => this;
public InputElement FocusRoot => this;
public bool ShowAccessKeys { get; set; }

Loading…
Cancel
Save