diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33171.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33171.cs new file mode 100644 index 000000000000..27e4f8292011 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33171.cs @@ -0,0 +1,120 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 33171, "When TitleBar.IsVisible = false the caption buttons become unresponsive on Windows", PlatformAffected.UWP)] +public class Issue33171 : ContentPage +{ + TitleBar titleBar; + + public Issue33171() + { + Title = "Issue 33171"; + + // Create TitleBar + titleBar = new TitleBar + { + Title = "Maui App", + Subtitle = "Hello, World!", + ForegroundColor = Colors.Red, + HeightRequest = 48 + }; + + titleBar.LeadingContent = new Image {Source = "dotnet_bot.png", HeightRequest = 24}; + + titleBar.Content = new SearchBar + { + Placeholder = "Search", + PlaceholderColor = Colors.White, + MaximumWidthRequest = 300, + HorizontalOptions = LayoutOptions.Fill, + VerticalOptions = LayoutOptions.Center + }; + + // Set the TitleBar on the current Window when this page appears + this.Loaded += (sender, e) => + { + if (Window != null) + { + Window.TitleBar = titleBar; + } + }; + + // Create a label to display window size + var windowSizeLabel = new Label + { + AutomationId = "WindowSizeLabel", + FontSize = 16, + HorizontalOptions = LayoutOptions.Center + }; + + // Create a button to reduce window width + var reduceWidthButton = new Button + { + Text = "Reduce Window Width", + AutomationId = "ReduceWidthButton", + }; + + var changeVisibility = new Button + { + Text = "Toggle", + AutomationId = "ToggleTitleBarVisibilityButton", + }; + + var getStatus = new Button + { + Text = "Get Status", + AutomationId = "GetStatusButton", + }; + + getStatus.Clicked += (sender, e) => + { + if (Window != null) + { + windowSizeLabel.Text = Window.Width.ToString(); + } + }; + + changeVisibility.Clicked += (sender, e) => + { + if (Window != null && Window.TitleBar != null) + { + titleBar.IsVisible = !titleBar.IsVisible; + } + }; + + + reduceWidthButton.Clicked += (sender, e) => + { + if (Window != null) + { + var currentWidth = Window.Width; + var newWidth = Window.Width/2; + Window.Width = newWidth; + Window.Height = Window.Height / 2; + + windowSizeLabel.Text = Window.Width.ToString(); + } + }; + + // Create the page content with a Label + Content = new VerticalStackLayout + { + Spacing = 25, + Padding = new Thickness(30), + VerticalOptions = LayoutOptions.Center, + Children = + { + new Label + { + Text = "TitleBar Visibility Test", + AutomationId = "TitleBarAlignmentLabel", + FontSize = 32, + HorizontalOptions = LayoutOptions.Center + }, + windowSizeLabel, + changeVisibility, + getStatus, + reduceWidthButton, + } + }; + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33171.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33171.cs new file mode 100644 index 000000000000..8400cc667024 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33171.cs @@ -0,0 +1,28 @@ +#if WINDOWS //This issue is Windows specific +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue33171 : _IssuesUITest +{ + public override string Issue => "When TitleBar.IsVisible = false the caption buttons become unresponsive on Windows"; + + public Issue33171(TestDevice device) : base(device) { } + + [Test] + [Category(UITestCategories.Window)] + public void TitleBarCaptionButtonsResponsiveWhenIsVisibleFalse() + { + App.WaitForElement("ToggleTitleBarVisibilityButton"); + App.Tap("ToggleTitleBarVisibilityButton"); + App.Tap("ReduceWidthButton"); + var windowSize = App.FindElement("WindowSizeLabel").GetText(); + App.TapMaximizeButton(); + App.Tap("GetStatusButton"); + var newWindowSize = App.FindElement("WindowSizeLabel").GetText(); + Assert.That(newWindowSize, Is.Not.EqualTo(windowSize), "Window size should change after maximizing the window."); + } +} +#endif \ No newline at end of file diff --git a/src/Core/src/Platform/Windows/WindowRootView.cs b/src/Core/src/Platform/Windows/WindowRootView.cs index f0ee5e37d356..c7e9a7886efc 100644 --- a/src/Core/src/Platform/Windows/WindowRootView.cs +++ b/src/Core/src/Platform/Windows/WindowRootView.cs @@ -200,8 +200,10 @@ internal void UpdateTitleBarContentSize() if (AppTitleBarContentControl is null) return; - if (_appTitleBarHeight != AppTitleBarContentControl.ActualHeight && - AppTitleBarContentControl.Visibility == UI.Xaml.Visibility.Visible) + // Cache visibility check to avoid duplicate property access + bool isTitleBarVisible = AppTitleBarContentControl.Visibility == UI.Xaml.Visibility.Visible; + + if (_appTitleBarHeight != AppTitleBarContentControl.ActualHeight && isTitleBarVisible) { UpdateRootNavigationViewMargins(AppTitleBarContentControl.ActualHeight); @@ -214,24 +216,33 @@ internal void UpdateTitleBarContentSize() this.RefreshThemeResources(); } - var rectArray = new List(); - foreach (var child in PassthroughTitlebarElements) - { - var transform = child.TransformToVisual(null); - var bounds = transform.TransformBounds( - new FRect(0, 0, child.ActualWidth, child.ActualHeight)); - var rect = GetRect(bounds, XamlRoot.RasterizationScale); - rectArray.Add(rect); - } - if (AppWindowId.HasValue) { var nonClientInputSrc = InputNonClientPointerSource.GetForWindowId(AppWindowId.Value); - if (rectArray.Count > 0) + // Only set passthrough regions when the title bar is actually visible + // to avoid blocking caption button input when title bar is hidden + if (isTitleBarVisible) { - nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, [.. rectArray]); + var rectArray = new List(); + foreach (var child in PassthroughTitlebarElements) + { + var transform = child.TransformToVisual(null); + var bounds = transform.TransformBounds( + new FRect(0, 0, child.ActualWidth, child.ActualHeight)); + var rect = GetRect(bounds, XamlRoot.RasterizationScale); + rectArray.Add(rect); + } + + if (rectArray.Count > 0) + { + nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, [.. rectArray]); + } + else + { + nonClientInputSrc.ClearRegionRects(NonClientRegionKind.Passthrough); + } } else { diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs index 2f5c62d15e59..b06889b1ad51 100644 --- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs +++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs @@ -2,11 +2,13 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; +using OpenQA.Selenium; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Android; using OpenQA.Selenium.Appium.Android.Enums; using OpenQA.Selenium.Appium.Interfaces; using OpenQA.Selenium.Appium.iOS; +using OpenQA.Selenium.Appium.Windows; using UITest.Core; namespace UITest.Appium @@ -745,6 +747,102 @@ public static IReadOnlyCollection GetAlertText(this IUIElement alertElem return results; } + public static void TapMinimizeButton(this IApp app) + { + if (app is not AppiumWindowsApp windowsApp) + return; + + var windowsDriver = windowsApp.Driver as WindowsDriver; + if (windowsDriver == null) + return; + + try + { + var minimizeButton = FindSystemButton(windowsDriver, "Minimize", "MinimizeButton", "PART_Min"); + + if (minimizeButton != null && minimizeButton.Displayed && minimizeButton.Enabled) + { + minimizeButton.Click(); + } + + } + catch (Exception ex) + { + Console.WriteLine($"UITest: Error testing minimize button: {ex.Message}"); + } + } + + /// + /// Tests if the Windows maximize/restore button is accessible and responsive + /// + /// The Windows app instance + /// Whether to actually click the maximize button (default: false) + /// True if the maximize/restore button is accessible and responsive + public static void TapMaximizeButton(this IApp app) + { + if (app is not AppiumWindowsApp windowsApp) + return; + + var windowsDriver = windowsApp.Driver as WindowsDriver; + if (windowsDriver == null) + return; + + try + { + var maximizeButton = FindSystemButton(windowsDriver, "Maximize", "MaximizeButton", "PART_Max", "Restore"); + + if (maximizeButton != null && maximizeButton.Displayed && maximizeButton.Enabled) + { + maximizeButton.Click(); + } + + } + catch (Exception ex) + { + Console.WriteLine($"UITest: Error testing maximize button: {ex.Message}"); + } + } + + static IWebElement? FindSystemButton(WindowsDriver driver, params string[] identifiers) + { + foreach (var identifier in identifiers) + { + try + { + try + { + var element = driver.FindElement(By.XPath($"//*[@Name='{identifier}']")); + if (element != null) + return element; + } + catch { } + + try + { + var element = driver.FindElement(By.XPath($"//*[@AutomationId='{identifier}']")); + if (element != null) + return element; + } + catch { } + + //By XPath with partial name match (for localized systems) + try + { + var element = driver.FindElement(By.XPath($"//*[contains(@Name, '{identifier}')]")); + if (element != null) + return element; + } + catch { } + } + catch (Exception ex) + { + Console.WriteLine($"UITest: Exception searching for {identifier}: {ex.Message}"); + } + } + + return null; + } + /// /// Wait function that will repeatedly query the app until any matching element is found. /// Throws a TimeoutException if no element is found within the time limit.