Browse Source

Streamline drawn decorations management to avoid call ordering problems (#20840)

pull/20934/head
Nikita Tsukanov 2 weeks ago
committed by GitHub
parent
commit
7520967a11
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 104
      src/Avalonia.Controls/TopLevelHost.Decorations.cs
  2. 24
      src/Avalonia.Controls/Window.cs

104
src/Avalonia.Controls/TopLevelHost.Decorations.cs

@ -48,64 +48,76 @@ internal partial class TopLevelHost
internal WindowDrawnDecorations? Decorations => _decorations; internal WindowDrawnDecorations? Decorations => _decorations;
/// <summary> /// <summary>
/// Enables drawn window decorations with the specified parts. /// Updates drawn window decorations with the specified parts and window state.
/// Creates the decorations instance, applies the template, and inserts layers into the visual tree. /// When <paramref name="parts"/> is <c>null</c>, decorations are removed entirely.
/// When non-null (including <see cref="DrawnWindowDecorationParts.None"/>), the decoration
/// infrastructure is kept alive and parts/fullscreen state are updated.
/// </summary> /// </summary>
internal void EnableDecorations(DrawnWindowDecorationParts parts) internal void UpdateDrawnDecorations(DrawnWindowDecorationParts? parts, WindowState windowState)
{ {
if (parts == null)
{
RemoveDecorations();
return;
}
var enabledParts = parts.Value;
if (_decorations != null) if (_decorations != null)
{ {
// Layers persist across part changes; pseudo-classes driven by EnabledParts // Layers persist across part changes; pseudo-classes driven by EnabledParts
// control visibility of individual decoration elements in the theme. // control visibility of individual decoration elements in the theme.
_decorations.EnabledParts = parts; _decorations.EnabledParts = enabledParts;
if (_resizeGrips != null) if (_resizeGrips != null)
_resizeGrips.IsVisible = parts.HasFlag(DrawnWindowDecorationParts.ResizeGrips); _resizeGrips.IsVisible = enabledParts.HasFlag(DrawnWindowDecorationParts.ResizeGrips);
return;
} }
else
_decorations = new WindowDrawnDecorations();
_decorations.EnabledParts = parts;
// Set up logical parenting
LogicalChildren.Add(_decorations);
// Create layer wrappers
_underlay = new LayerWrapper() { [AutomationProperties.AutomationIdProperty] = "WindowChromeUnderlay" };
_overlay = new LayerWrapper() { [AutomationProperties.AutomationIdProperty] = "WindowChromeOverlay" };
_fullscreenPopover = new LayerWrapper()
{ {
IsVisible = false, [AutomationProperties.AutomationIdProperty] = "PopoverWindowChrome" _decorations = new WindowDrawnDecorations();
}; _decorations.EnabledParts = enabledParts;
// Insert layers: underlay below TopLevel, overlay and popover above
// Visual order: underlay(0), TopLevel(1), overlay(2), fullscreenPopover(3), resizeGrips(4)
VisualChildren.Insert(0, _underlay);
VisualChildren.Add(_overlay);
VisualChildren.Add(_fullscreenPopover);
// Always create resize grips; visibility is controlled by EnabledParts
_resizeGrips = new ResizeGripLayer();
_resizeGrips.IsVisible = parts.HasFlag(DrawnWindowDecorationParts.ResizeGrips);
VisualChildren.Add(_resizeGrips);
// Attach to window if available // Set up logical parenting
if (_topLevel is Window window) LogicalChildren.Add(_decorations);
_decorations.Attach(window);
// Subscribe to template changes to re-apply and geometry changes for resize grips // Create layer wrappers
_decorations.EffectiveGeometryChanged += OnDecorationsGeometryChanged; _underlay = new LayerWrapper() { [AutomationProperties.AutomationIdProperty] = "WindowChromeUnderlay" };
_decorationsSubscriptions = _decorations.GetObservable(WindowDrawnDecorations.TemplateProperty) _overlay = new LayerWrapper() { [AutomationProperties.AutomationIdProperty] = "WindowChromeOverlay" };
.Subscribe(_ => ApplyDecorationsTemplate()); _fullscreenPopover = new LayerWrapper()
{
IsVisible = false, [AutomationProperties.AutomationIdProperty] = "PopoverWindowChrome"
};
// Insert layers: underlay below TopLevel, overlay and popover above
// Visual order: underlay(0), TopLevel(1), overlay(2), fullscreenPopover(3), resizeGrips(4)
VisualChildren.Insert(0, _underlay);
VisualChildren.Add(_overlay);
VisualChildren.Add(_fullscreenPopover);
// Always create resize grips; visibility is controlled by EnabledParts
_resizeGrips = new ResizeGripLayer();
_resizeGrips.IsVisible = enabledParts.HasFlag(DrawnWindowDecorationParts.ResizeGrips);
VisualChildren.Add(_resizeGrips);
// Attach to window if available
if (_topLevel is Window window)
_decorations.Attach(window);
// Subscribe to template changes to re-apply and geometry changes for resize grips
_decorations.EffectiveGeometryChanged += OnDecorationsGeometryChanged;
_decorationsSubscriptions = _decorations.GetObservable(WindowDrawnDecorations.TemplateProperty)
.Subscribe(_ => ApplyDecorationsTemplate());
ApplyDecorationsTemplate();
InvalidateMeasure();
}
ApplyDecorationsTemplate(); ApplyFullscreenState(windowState == WindowState.FullScreen);
InvalidateMeasure();
_decorationsOverlayPeer?.InvalidateChildren();
} }
/// <summary> /// <summary>
/// Disables drawn window decorations and removes all layers. /// Removes drawn window decorations and all associated layers.
/// </summary> /// </summary>
internal void DisableDecorations() private void RemoveDecorations()
{ {
if (_decorations == null) if (_decorations == null)
return; return;
@ -192,15 +204,15 @@ internal partial class TopLevelHost
} }
/// <summary> /// <summary>
/// Shows or hides the fullscreen popover based on the window state. /// Applies fullscreen-specific layer visibility: hides overlay/underlay and enables
/// Called by Window when window state changes. /// popover hover detection, or restores normal state.
/// </summary> /// </summary>
internal void SetFullscreenPopoverEnabled(bool enabled) private void ApplyFullscreenState(bool isFullscreen)
{ {
if (_fullscreenPopover == null) if (_fullscreenPopover == null)
return; return;
if (enabled) if (isFullscreen)
{ {
// In fullscreen mode, hide overlay and underlay, enable popover hover detection // In fullscreen mode, hide overlay and underlay, enable popover hover detection
if (_overlay != null) if (_overlay != null)

24
src/Avalonia.Controls/Window.cs

@ -626,10 +626,7 @@ namespace Avalonia.Controls
StartRendering(); StartRendering();
} }
// Update fullscreen popover visibility // Update decoration parts and fullscreen popover state for the new window state
TopLevelHost.SetFullscreenPopoverEnabled(state == WindowState.FullScreen);
// Update decoration parts for the new window state
UpdateDrawnDecorationParts(); UpdateDrawnDecorationParts();
} }
@ -643,13 +640,11 @@ namespace Avalonia.Controls
private void UpdateDrawnDecorations() private void UpdateDrawnDecorations()
{ {
var needsDrawnDecorations = PlatformImpl?.NeedsManagedDecorations ?? false; var parts = ComputeDecorationParts();
TopLevelHost.UpdateDrawnDecorations(parts, WindowState);
var parts = needsDrawnDecorations ? ComputeDecorationParts() : DrawnWindowDecorationParts.None; if (parts != null)
if (parts != DrawnWindowDecorationParts.None)
{ {
TopLevelHost.EnableDecorations(parts);
// Forward ExtendClientAreaTitleBarHeightHint to decoration TitleBarHeight // Forward ExtendClientAreaTitleBarHeightHint to decoration TitleBarHeight
var decorations = TopLevelHost.Decorations; var decorations = TopLevelHost.Decorations;
if (decorations != null) if (decorations != null)
@ -659,10 +654,6 @@ namespace Avalonia.Controls
decorations.TitleBarHeightOverride = hint; decorations.TitleBarHeightOverride = hint;
} }
} }
else
{
TopLevelHost.DisableDecorations();
}
UpdateDrawnDecorationMargins(); UpdateDrawnDecorationMargins();
} }
@ -676,11 +667,14 @@ namespace Avalonia.Controls
if (TopLevelHost.Decorations == null) if (TopLevelHost.Decorations == null)
return; return;
TopLevelHost.EnableDecorations(ComputeDecorationParts()); TopLevelHost.UpdateDrawnDecorations(ComputeDecorationParts(), WindowState);
} }
private Chrome.DrawnWindowDecorationParts ComputeDecorationParts() private Chrome.DrawnWindowDecorationParts? ComputeDecorationParts()
{ {
if (!(PlatformImpl?.NeedsManagedDecorations ?? false))
return null;
var platformNeeds = PlatformImpl?.RequestedDrawnDecorations ?? PlatformRequestedDrawnDecoration.None; var platformNeeds = PlatformImpl?.RequestedDrawnDecorations ?? PlatformRequestedDrawnDecoration.None;
var parts = Chrome.DrawnWindowDecorationParts.None; var parts = Chrome.DrawnWindowDecorationParts.None;
if (WindowDecorations != WindowDecorations.None) if (WindowDecorations != WindowDecorations.None)

Loading…
Cancel
Save