Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@ public ShellToolbarAppearanceTracker(IShellContext shellContext)
public virtual void SetAppearance(AToolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance)
{
var foreground = appearance.ForegroundColor;
var background = appearance.BackgroundColor;
var background = !Brush.IsNullOrEmpty(appearance.Background)
? appearance.Background
: appearance.BackgroundColor is not null
? new SolidColorBrush(appearance.BackgroundColor)
: null;
var titleColor = appearance.TitleColor;

SetColors(toolbar, toolbarTracker, foreground, background, titleColor);
}

public virtual void ResetAppearance(AToolbar toolbar, IShellToolbarTracker toolbarTracker)
{
SetColors(toolbar, toolbarTracker, ShellRenderer.DefaultForegroundColor, ShellRenderer.DefaultBackgroundColor, ShellRenderer.DefaultTitleColor);
SetColors(toolbar, toolbarTracker, ShellRenderer.DefaultForegroundColor, new SolidColorBrush(ShellRenderer.DefaultBackgroundColor), ShellRenderer.DefaultTitleColor);
}

protected virtual void SetColors(AToolbar toolbar, IShellToolbarTracker toolbarTracker, Color foreground, Color background, Color title)
protected virtual void SetColors(AToolbar toolbar, IShellToolbarTracker toolbarTracker, Color foreground, Brush background, Color title)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the protected virtual signature from Color background to Brush background. It is correctly tracked in PublicAPI with a *REMOVED*/added pair and is reasonable for the net11.0 target, but it is binary/source-breaking for subclasses that override the old Color overload: after this change SetAppearance calls the Brush overload, so an existing override of the Color overload silently stops being invoked. Worth confirming during API review that dropping the old overload (vs. keeping it as an [Obsolete] overload that delegates to the new one) is the intended net11 break — this is the kind of decision that pairs with the broader Shell.Background API-design discussion on this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging this! Yes, this is an intentional change for .NET 11 — we've updated the signature from Color to Brush to enable gradient background support. It's properly tracked in PublicAPI.Unshipped.txt with the REMOVED/added pair. For anyone with a custom subclass overriding the old Color overload, the migration is straightforward — just update the parameter type to Brush. Happy to discuss further if there are concerns!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning: SetColors signature change is a binary/source-breaking change to a shipped protected virtual API (intentional — confirm API-review sign-off)

This changes the shipped protected virtual void SetColors(…, Color background, …) to take a Brush background. Verified at HEAD: the old Color-based overload is in PublicAPI.Shipped.txt (net-android line 4422), and the PR removes it via *REMOVED* and adds the Brush overload in PublicAPI.Unshipped.txt. External code that subclasses ShellToolbarAppearanceTracker and overrides SetColors will no longer compile, or its override will silently stop being called.

This is intentional and correctly tracked, targets net11.0 (where breaks are allowed), and the author explicitly chose net11.0 for this reason — so it is a heads-up for API-review sign-off, not a code defect. If avoiding the break is preferred, keep the Color overload and add a separate Brush-based virtual that SetAppearance calls, with the legacy overload delegating to it.

(found by: gemini-3.1-pro-preview, claude-opus-4.8)

{
if (_disposed)
return;
Expand All @@ -43,7 +47,7 @@ protected virtual void SetColors(AToolbar toolbar, IShellToolbarTracker toolbarT
return;

shellToolbar.BarTextColor = title ?? ShellRenderer.DefaultTitleColor;
shellToolbar.BarBackground = new SolidColorBrush(background ?? ShellRenderer.DefaultBackgroundColor);
shellToolbar.BarBackground = background ?? new SolidColorBrush(ShellRenderer.DefaultBackgroundColor);
shellToolbar.IconColor = foreground ?? ShellRenderer.DefaultForegroundColor;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,26 @@ void UpdateiOS13NavigationBarAppearance(UINavigationController controller, Shell
navBar.TintColor = _defaultTint;
}

// Set BackgroundColor
var background = appearance.BackgroundColor;

if (background != null)
navigationBarAppearance.BackgroundColor = background.ToPlatform();
// Set Background (prefer Brush over Color for gradient support)
if (!Brush.IsNullOrEmpty(appearance.Background))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The translucency decision a few lines above (if (appearance.BackgroundColor?.Alpha < 1.0f)) only inspects BackgroundColor. When the toolbar background comes solely from this new appearance.Background brush, BackgroundColor is null, so null < 1.0f is false and the bar is configured with ConfigureWithOpaqueBackground() + Translucent = false. A semi-transparent brush (a SolidColorBrush, or a gradient stop with alpha < 1) set via Shell.Background therefore renders opaque, unlike the same alpha set via Shell.BackgroundColor. Consider folding the brush's effective alpha into the translucency check. Opaque gradients (the primary scenario for this PR) are unaffected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This matches the existing NavigationRenderer behavior, where translucency is also driven by color alpha and brush backgrounds are applied afterward (see NavigationRenderer.cs:881 and NavigationRenderer.cs:896).
For this PR, the main target is opaque gradients, which are unaffected. If we want to support semi-transparent brush parity with BackgroundColor, we can handle brush alpha in a follow-up

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning: iOS navbar translucency ignores the Shell.Background brush alpha — transparent brushes render opaque

In UpdateiOS13NavigationBarAppearance, translucency is decided at ShellNavBarAppearanceTracker.cs:111 from appearance.BackgroundColor?.Alpha < 1.0f, before the new brush branch here at line 135. When only Shell.Background is set, appearance.BackgroundColor is null, so null < 1.0f evaluates to false → the navbar is configured with ConfigureWithOpaqueBackground() and Translucent = false. The brush is then applied (solid → BackgroundColor, gradient → BackgroundImage) on top of an already-opaque appearance, so a semi-transparent SolidColorBrush or a gradient with transparent stops renders opaque. Opaque gradients (e.g. the test's Yellow→Green) are unaffected.

Fix: factor the brush alpha into the translucency decision (e.g. Brush.HasTransparency(appearance.Background), or inspect solidBrush.Color.Alpha / gradient stops) before choosing opaque vs. transparent.

(found by: gemini-3.1-pro-preview, claude-opus-4.8)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This matches existing NavigationRenderer behavior, where translucency is determined from BackgroundColor alpha first, and brush imagery is applied afterward (NavigationRenderer.cs:881, NavigationRenderer.cs:898, NavigationRenderer.cs:900). Opaque gradients, which are the primary scenario for this PR, are unaffected. Semi-transparent brush parity can be handled as a follow-up enhancement.

{
if (appearance.Background is SolidColorBrush solidBrush && solidBrush.Color is not null)
{
navigationBarAppearance.BackgroundColor = solidBrush.Color.ToPlatform();
}
else
{
var backgroundImage = navBar.GetBackgroundImage(appearance.Background);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Suggestion: Gradient navbar background is rendered to a fixed-size image and not refreshed on rotation/resize

For non-solid brushes the iOS path renders the gradient to a UIImage sized to the nav bar's current bounds via navBar.GetBackgroundImage(appearance.Background) (line 143) and assigns it to navigationBarAppearance.BackgroundImage. SetAppearance is only re-invoked when Shell appearance properties change, not on layout/orientation changes, so rotating the device can stretch or distort the pre-rendered gradient until appearance is re-applied. This mirrors ShellFlyoutContentRenderer.UpdateBackground, so it may be an acceptable limitation, but the toolbar width changes more visibly on rotation than the flyout. Consider a frame-change observer/override to re-render the gradient on bounds changes.

(found by: claude-opus-4.6)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a valid concern

if (backgroundImage is not null)
{
navigationBarAppearance.BackgroundImage = backgroundImage;
}
}
}
else if (appearance.BackgroundColor is not null)
{
navigationBarAppearance.BackgroundColor = appearance.BackgroundColor.ToPlatform();
}

// Set TitleColor
var titleColor = appearance.TitleColor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,9 @@ public static void UpdateBarBackground(this AToolbar nativeToolbar, Toolbar tool
}
else
{
nativeToolbar.BackgroundTintMode = null;
nativeToolbar.BackgroundTintList = null;
nativeToolbar.UpdateBackground(barBackground);

if (Brush.IsNullOrEmpty(barBackground))
nativeToolbar.BackgroundTintMode = null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,9 @@ virtual Microsoft.Maui.Controls.LongPressingEventArgs.GetPosition(Microsoft.Maui
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextProperty -> Microsoft.Maui.Controls.BindableProperty
~virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellItemRenderer.UpdateShellSectionBadge(Microsoft.Maui.Controls.ShellSection shellSection, int index) -> void
~Microsoft.Maui.Controls.ShellAppearance.Background.get -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.GetBackground(Microsoft.Maui.Controls.BindableObject obj) -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.SetBackground(Microsoft.Maui.Controls.BindableObject obj, Microsoft.Maui.Controls.Brush value) -> void
~static readonly Microsoft.Maui.Controls.Shell.BackgroundProperty -> Microsoft.Maui.Controls.BindableProperty
~virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellToolbarAppearanceTracker.SetColors(AndroidX.AppCompat.Widget.Toolbar toolbar, Microsoft.Maui.Controls.Platform.Compatibility.IShellToolbarTracker toolbarTracker, Microsoft.Maui.Graphics.Color foreground, Microsoft.Maui.Controls.Brush background, Microsoft.Maui.Graphics.Color title) -> void
*REMOVED*~virtual Microsoft.Maui.Controls.Platform.Compatibility.ShellToolbarAppearanceTracker.SetColors(AndroidX.AppCompat.Widget.Toolbar toolbar, Microsoft.Maui.Controls.Platform.Compatibility.IShellToolbarTracker toolbarTracker, Microsoft.Maui.Graphics.Color foreground, Microsoft.Maui.Graphics.Color background, Microsoft.Maui.Graphics.Color title) -> void
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
*REMOVED*~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void
*REMOVED*~static Microsoft.Maui.Controls.VisualStateManager.GetVisualStateGroups(Microsoft.Maui.Controls.VisualElement visualElement) -> System.Collections.Generic.IList<Microsoft.Maui.Controls.VisualStateGroup>
Microsoft.Maui.Controls.AppThemeBinding
Expand Down Expand Up @@ -109,3 +109,7 @@ virtual Microsoft.Maui.Controls.LongPressingEventArgs.GetPosition(Microsoft.Maui
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextProperty -> Microsoft.Maui.Controls.BindableProperty
~Microsoft.Maui.Controls.ShellAppearance.Background.get -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.GetBackground(Microsoft.Maui.Controls.BindableObject obj) -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.SetBackground(Microsoft.Maui.Controls.BindableObject obj, Microsoft.Maui.Controls.Brush value) -> void
~static readonly Microsoft.Maui.Controls.Shell.BackgroundProperty -> Microsoft.Maui.Controls.BindableProperty
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
*REMOVED*~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void
*REMOVED*~static Microsoft.Maui.Controls.VisualStateManager.GetVisualStateGroups(Microsoft.Maui.Controls.VisualElement visualElement) -> System.Collections.Generic.IList<Microsoft.Maui.Controls.VisualStateGroup>
Microsoft.Maui.Controls.AppThemeBinding
Expand Down Expand Up @@ -109,3 +109,7 @@ virtual Microsoft.Maui.Controls.LongPressingEventArgs.GetPosition(Microsoft.Maui
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextProperty -> Microsoft.Maui.Controls.BindableProperty
~Microsoft.Maui.Controls.ShellAppearance.Background.get -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.GetBackground(Microsoft.Maui.Controls.BindableObject obj) -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.SetBackground(Microsoft.Maui.Controls.BindableObject obj, Microsoft.Maui.Controls.Brush value) -> void
~static readonly Microsoft.Maui.Controls.Shell.BackgroundProperty -> Microsoft.Maui.Controls.BindableProperty
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,7 @@ virtual Microsoft.Maui.Controls.LongPressingEventArgs.GetPosition(Microsoft.Maui
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextProperty -> Microsoft.Maui.Controls.BindableProperty
~Microsoft.Maui.Controls.ShellAppearance.Background.get -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.GetBackground(Microsoft.Maui.Controls.BindableObject obj) -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.SetBackground(Microsoft.Maui.Controls.BindableObject obj, Microsoft.Maui.Controls.Brush value) -> void
~static readonly Microsoft.Maui.Controls.Shell.BackgroundProperty -> Microsoft.Maui.Controls.BindableProperty
6 changes: 5 additions & 1 deletion src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
*REMOVED*~static Microsoft.Maui.Controls.VisualStateManager.GetVisualStateGroups(Microsoft.Maui.Controls.VisualElement visualElement) -> System.Collections.Generic.IList<Microsoft.Maui.Controls.VisualStateGroup>
Microsoft.Maui.Controls.AppThemeBinding
Microsoft.Maui.Controls.AppThemeBinding.AppThemeBinding() -> void
Expand Down Expand Up @@ -99,3 +99,7 @@ virtual Microsoft.Maui.Controls.LongPressingEventArgs.GetPosition(Microsoft.Maui
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextProperty -> Microsoft.Maui.Controls.BindableProperty
~Microsoft.Maui.Controls.ShellAppearance.Background.get -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.GetBackground(Microsoft.Maui.Controls.BindableObject obj) -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.SetBackground(Microsoft.Maui.Controls.BindableObject obj, Microsoft.Maui.Controls.Brush value) -> void
~static readonly Microsoft.Maui.Controls.Shell.BackgroundProperty -> Microsoft.Maui.Controls.BindableProperty
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#nullable enable
#nullable enable
*REMOVED*~static Microsoft.Maui.Controls.VisualStateManager.GetVisualStateGroups(Microsoft.Maui.Controls.VisualElement visualElement) -> System.Collections.Generic.IList<Microsoft.Maui.Controls.VisualStateGroup>
Microsoft.Maui.Controls.BoxView.~BoxView() -> void
Microsoft.Maui.Controls.ImageSource.InvalidateStyle() -> void
Expand Down Expand Up @@ -90,3 +90,7 @@ virtual Microsoft.Maui.Controls.LongPressingEventArgs.GetPosition(Microsoft.Maui
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextColorProperty -> Microsoft.Maui.Controls.BindableProperty
~static readonly Microsoft.Maui.Controls.ToolbarItem.BadgeTextProperty -> Microsoft.Maui.Controls.BindableProperty
~Microsoft.Maui.Controls.ShellAppearance.Background.get -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.GetBackground(Microsoft.Maui.Controls.BindableObject obj) -> Microsoft.Maui.Controls.Brush
~static Microsoft.Maui.Controls.Shell.SetBackground(Microsoft.Maui.Controls.BindableObject obj, Microsoft.Maui.Controls.Brush value) -> void
~static readonly Microsoft.Maui.Controls.Shell.BackgroundProperty -> Microsoft.Maui.Controls.BindableProperty
30 changes: 29 additions & 1 deletion src/Controls/src/Core/Shell/Shell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,13 @@ static void OnFlyoutBehaviorChanged(BindableObject bindable, object oldValue, ob
BindableProperty.CreateAttached("UnselectedColor", typeof(Color), typeof(Shell), null,
propertyChanged: OnShellAppearanceValueChanged);

/// <summary>
/// Defines the background brush for the Shell toolbar. Supports gradient brushes.
/// </summary>
public static readonly new BindableProperty BackgroundProperty =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning: new Shell.BackgroundProperty shadows the inherited VisualElement.BackgroundProperty — two same-named properties with different semantics (API design — needs review)

Shell.cs:535 declares public static readonly new BindableProperty BackgroundProperty as an attached Brush property, while Shell still inherits the instance VisualElement.Background (VisualElement.cs:289/:569, also a Brush named "Background"). Only the new attached property is ingested into ShellAppearance (ShellAppearance.cs s_ingestBrushArray).

Concrete footgun verified at HEAD: Shell.SetBackground(shell, brush) and XAML Background="…" write the attached property → reaches the toolbar; shell.Background = brush in code-behind writes the inherited VisualElement property → does not reach the toolbar (it paints the Shell body). Same name, two storages, two behaviors.

This is the dominant unresolved item on the PR (already raised in the PR discussion and a prior net11 fleet-review). It is an API-design decision that should go through API review before shipping, even on net11.0. Consider honoring/reusing VisualElement.Background, or naming the new member something toolbar-specific to remove the overlap.

(found by: gpt-5.5, claude-opus-4.6, claude-opus-4.8)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This follows the existing Shell pattern — Shell.BackgroundColorProperty (line 467) also uses public static readonly new to shadow VisualElement.BackgroundColorProperty. The same attached-vs-instance split already exists for BackgroundColor. This is established Shell architecture, not new to this PR.

BindableProperty.CreateAttached("Background", typeof(Brush), typeof(Shell), Brush.Default,
propertyChanged: OnShellAppearanceValueChanged);

/// <summary>
/// The backdrop of the flyout, which is the appearance of the flyout overlay.
/// </summary>
Expand Down Expand Up @@ -713,6 +720,20 @@ static void OnFlyoutBehaviorChanged(BindableObject bindable, object oldValue, ob
/// <param name="value">The brushed used in the backdrop of the flyout.</param>
public static void SetFlyoutBackdrop(BindableObject obj, Brush value) => obj.SetValue(FlyoutBackdropProperty, value);

/// <summary>
/// Gets the background brush for the Shell toolbar.
/// </summary>
/// <param name="obj">The object from which to get the background brush.</param>
/// <returns>The background brush for the Shell toolbar.</returns>
public static Brush GetBackground(BindableObject obj) => (Brush)obj.GetValue(BackgroundProperty);

/// <summary>
/// Sets the background brush for the Shell toolbar.
/// </summary>
/// <param name="obj">The object on which to set the background brush.</param>
/// <param name="value">The brush to use as the Shell toolbar background.</param>
public static void SetBackground(BindableObject obj, Brush value) => obj.SetValue(BackgroundProperty, value);

static void OnShellAppearanceValueChanged(BindableObject bindable, object oldValue, object newValue)
{
var item = (Element)bindable;
Expand Down Expand Up @@ -816,7 +837,14 @@ void UpdateToolbarAppearanceFeatures(Element pivot, ShellAppearance appearance)
{
appearance = appearance ?? GetAppearanceForPivot(pivot);
Toolbar.BarTextColor = appearance?.TitleColor ?? DefaultTitleColor;
Toolbar.BarBackground = appearance?.BackgroundColor ?? DefaultBackgroundColor;
if (!Brush.IsNullOrEmpty(appearance?.Background))
{
Toolbar.BarBackground = appearance.Background;
}
else
{
Toolbar.BarBackground = appearance?.BackgroundColor ?? DefaultBackgroundColor;
}
Toolbar.IconColor = appearance?.ForegroundColor ?? DefaultForegroundColor;
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/Controls/src/Core/Shell/ShellAppearance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public class ShellAppearance : IShellAppearanceElement

static readonly BindableProperty[] s_ingestBrushArray = new[]
{
Shell.FlyoutBackdropProperty
Shell.FlyoutBackdropProperty,
Shell.BackgroundProperty
};

static readonly BindableProperty[] s_ingestDoubleArray = new[]
Expand Down Expand Up @@ -71,6 +72,9 @@ public class ShellAppearance : IShellAppearanceElement

/// <summary>Gets the backdrop brush for the Shell flyout.</summary>
public Brush FlyoutBackdrop => _brushArray[0];

/// <summary>Gets the background brush of the Shell.</summary>
public Brush Background => _brushArray[1];
public double FlyoutWidth => _doubleArray[0];
public double FlyoutHeight => _doubleArray[1];

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue10445.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 10445, "Shell.Background does not support gradient brushes", PlatformAffected.All)]
public class Issue10445 : TestShell
{
protected override void Init()
{
FlyoutBehavior = FlyoutBehavior.Disabled;
var gradientBrush = new LinearGradientBrush
{
StartPoint = new Point(0, 0),
EndPoint = new Point(1, 1),
GradientStops = new GradientStopCollection
{
new GradientStop(Colors.Yellow, 0.0f),
new GradientStop(Colors.Green, 1.0f)
}
};

Shell.SetBackground(this, gradientBrush);

var page = new ContentPage
{
Title = "Gradient Shell",
Content = new VerticalStackLayout
{
Padding = 20,
Spacing = 10,
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
Children =
{
new Label
{
Text = "Shell.Background should display a gradient (Yellow to Green) in the navigation bar above.",
AutomationId = "GradientInfoLabel",
HorizontalTextAlignment = TextAlignment.Center
}
}
}
};

AddContentPage(page, "Home");
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue10445 : _IssuesUITest
{
public Issue10445(TestDevice device) : base(device)
{
}

public override string Issue => "Shell.Background does not support gradient brushes";

[Test]
[Category(UITestCategories.Shell)]
public void ShellBackgroundSupportsGradientBrush()
{
// Wait for the page to be fully loaded
App.WaitForElement("GradientInfoLabel");

// Verify the Shell renders correctly with a gradient background
VerifyScreenshot();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VerifyScreenshot() fails when no baseline exists for the running platform — VisualRegressionTester.VerifyMatchesSnapshot throws VisualTestFailedException ("Baseline snapshot not yet created") when the file is missing. This fixture also runs on Mac and Windows ([TestFixture(TestDevice.Mac)] / [TestFixture(TestDevice.Windows)] in UITest.cs) and the test is not platform-scoped, but the PR only commits android + ios baselines. Since the fix explicitly targets Windows and Mac (per the PR description), this test will fail on the Mac/Windows UI legs for lack of a baseline. Note TestCases.WinUI.Tests/snapshots/windows and TestCases.Mac.Tests/snapshots/mac already hold ~1.4k baselines each, so all-platform baselines are the convention.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add the Windows and Mac baseline images once CI is triggered.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error: New VerifyScreenshot() UI test ships only android+ios baselines — it fails on the Windows and Mac Shell legs

ShellBackgroundSupportsGradientBrush (added in this PR) calls VerifyScreenshot() with [Category(UITestCategories.Shell)] and no platform exclusion (Issue10445.cs lines 15-23), so it runs on every UI-test platform. Only the android and ios snapshots were committed (TestCases.Android.Tests/snapshots/android/ShellBackgroundSupportsGradientBrush.png, TestCases.iOS.Tests/snapshots/ios/ShellBackgroundSupportsGradientBrush.png).

Verified at HEAD af58706b: there is no TestCases.WinUI.Tests/snapshots/windows/ShellBackgroundSupportsGradientBrush.png and no TestCases.Mac.Tests/snapshots/mac/ShellBackgroundSupportsGradientBrush.png, while both the snapshots/windows and snapshots/mac directories already exist (the established convention is to ship those baselines). A missing baseline makes VerifyScreenshot fail, so this test fails on the Windows and Mac Shell legs — consistent with the red maui-pr-uitests (WinUI UITests Controls Shell) leg on this build. The PR description claims the feature was tested on Windows and Mac, but no baseline backs that.

Fix: commit the windows and mac baselines, or explicitly exclude Windows/Mac from this test if the feature is not yet supported there.

(found by: claude-opus-4.8)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the Windows and Mac base images.

}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading