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
3 changes: 3 additions & 0 deletions UI/WindowingSamples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Most windowing features are currently available on desktop targets of [Uno Platf
* [**MinimizeMaximizeWindow.xaml & MinimizeMaximizeWindow.xaml.cs**](src/WindowingSamples/MinimizeMaximizeWindow.xaml.cs): Minimizing and maximizing a window.
* [**StayOnTopWindow.xaml & StayOnTopWindow.xaml.cs**](src/WindowingSamples/StayOnTopWindow.xaml.cs): Window that always stays on top of other content.
* [**WindowTitleWindow.xaml & WindowTitleWindow.xaml.cs**](src/WindowingSamples/WindowTitleWindow.xaml.cs): Customizing the Window title.
* [**ExtendContentIntoTitleBarWindow.xaml & ExtendContentIntoTitleBarWindow.xaml.cs**](src/WindowingSamples/ExtendContentIntoTitleBarWindow.xaml.cs): Demonstrating `Window.ExtendsContentIntoTitleBar` API to extend XAML UI into the titlebar area while preserving the caption buttons.
* [**CustomTitleBarWindow.xaml & CustomTitleBarWindow.xaml.cs**](src/WindowingSamples/CustomTitleBarWindow.xaml.cs): Demonstrating `SetBorderAndTitleBar(true, false)` API to completely extend the client area and hide the caption buttons, with custom caption buttons and `InputNonClientPointerSource.SetRegionRects` for the Maximize button to support Snap layouts menu.
* [**DraggableRegionWindow.xaml & DraggableRegionWindow.xaml.cs**](src/WindowingSamples/DraggableRegionWindow.xaml.cs): Demonstrating `InputNonClientPointerSource.SetRegionRects` for Caption region to create custom draggable regions in the window.

## What is the Uno Platform

Expand Down
33 changes: 17 additions & 16 deletions UI/WindowingSamples/src/WindowingSamples/App.xaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
<Application x:Class="WindowingSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wasm="http://platform.uno/wasm"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
RequestedTheme="Light"
mc:Ignorable="wasm">
<Application
x:Class="WindowingSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wasm="http://platform.uno/wasm"
RequestedTheme="Light"
mc:Ignorable="wasm">

<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Load WinUI resources -->
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Load WinUI resources -->
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

<!-- Add resources here -->
<!-- Add resources here -->

</Application>
104 changes: 104 additions & 0 deletions UI/WindowingSamples/src/WindowingSamples/CustomTitleBarWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="WindowingSamples.CustomTitleBarWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:WindowingSamples.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<!-- Custom title bar with fully custom caption buttons -->
<Grid x:Name="CustomTitleBar" Background="{ThemeResource SystemControlAcrylicElementBrush}" Height="40" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<StackPanel Orientation="Horizontal" Grid.Column="0" Margin="12,0,0,0" VerticalAlignment="Center" Spacing="8">
<FontIcon Glyph="&#xE161;" FontSize="16" />
<TextBlock Text="Custom Caption Buttons" Style="{ThemeResource CaptionTextBlockStyle}" VerticalAlignment="Center" FontWeight="SemiBold" />
</StackPanel>

<!-- Custom caption buttons -->
<StackPanel Orientation="Horizontal" Grid.Column="2" Spacing="0">
<Button x:Name="MinimizeButton" Width="48" Height="40" Background="Transparent" BorderThickness="0" Click="MinimizeClick" ToolTipService.ToolTip="Minimize">
<FontIcon Glyph="&#xE921;" FontSize="10" />
</Button>
<Button x:Name="MaximizeRestoreButton" Width="48" Height="40" Background="Transparent" BorderThickness="0" Click="MaximizeRestoreClick" ToolTipService.ToolTip="Maximize">
<FontIcon x:Name="MaximizeRestoreIcon" Glyph="&#xE922;" FontSize="10" />
</Button>
<Button x:Name="CloseButton" Width="48" Height="40" Background="Transparent" BorderThickness="0" Click="CloseClick" ToolTipService.ToolTip="Close">
<Button.Resources>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#C42B1C" />
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="#A22A1B" />
</Button.Resources>
<FontIcon Glyph="&#xE106;" FontSize="10" />
</Button>
</StackPanel>
</Grid>

<ScrollViewer Grid.Row="1" Padding="24" VerticalScrollBarVisibility="Auto">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Spacing="16" MaxWidth="700">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="18">
<Run FontWeight="Bold">SetBorderAndTitleBar(true, false) Demo</Run>
<LineBreak/>
<LineBreak/>
This window demonstrates the SetBorderAndTitleBar API with parameters (true, false) to completely extend the client area and hide the system caption buttons. The window has fully custom caption buttons that you can style as needed.
</TextBlock>

<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16">
<StackPanel Spacing="12">
<TextBlock Text="Key Features:" FontWeight="SemiBold" FontSize="16" />
<TextBlock TextWrapping="Wrap">• Full control over window appearance</TextBlock>
<TextBlock TextWrapping="Wrap">• Custom caption buttons (minimize, maximize/restore, close)</TextBlock>
<TextBlock TextWrapping="Wrap">• SetBorderAndTitleBar(true, false) hides system buttons</TextBlock>
<TextBlock TextWrapping="Wrap">• Custom button styling and behavior</TextBlock>
</StackPanel>
</Border>

<Border Background="{ThemeResource AccentFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16">
<StackPanel Spacing="12">
<TextBlock Text="InputNonClientPointerSource for Maximize:" FontWeight="SemiBold" FontSize="16" Foreground="White" />
<TextBlock TextWrapping="Wrap" Foreground="White">
The Maximize button uses InputNonClientPointerSource.SetRegionRects to support the Snap Layouts menu on Windows 11. When you hover over the maximize button, you'll see the snap layout options appear.
</TextBlock>
<TextBlock TextWrapping="Wrap" Foreground="White" FontStyle="Italic">
Note: This feature requires Windows 11 and will be a no-op on other platforms.
</TextBlock>
</StackPanel>
</Border>

<Border Background="{ThemeResource LayerFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16">
<StackPanel Spacing="8">
<TextBlock Text="Try it out:" FontWeight="SemiBold" FontSize="16" />
<TextBlock TextWrapping="Wrap">• Click the minimize button to minimize the window</TextBlock>
<TextBlock TextWrapping="Wrap">• Click the maximize button to maximize or restore the window</TextBlock>
<TextBlock TextWrapping="Wrap">• Hover over maximize button on Windows 11 to see Snap Layouts</TextBlock>
<TextBlock TextWrapping="Wrap">• Click the close button (red on hover) to close the window</TextBlock>
<TextBlock TextWrapping="Wrap">• Drag the title bar to move the window</TextBlock>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
150 changes: 150 additions & 0 deletions UI/WindowingSamples/src/WindowingSamples/CustomTitleBarWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
using Microsoft.UI.Windowing;
using Microsoft.UI.Input;
using Windows.Graphics;

namespace WindowingSamples;

/// <summary>
/// Window demonstrating SetBorderAndTitleBar(true, false) API with custom caption buttons.
/// Also demonstrates InputNonClientPointerSource.SetRegionRects for Maximize button to support Snap Layouts.
/// </summary>
public sealed partial class CustomTitleBarWindow : Window
{
private AppWindow? _appWindow;
private InputNonClientPointerSource? _nonClientPointerSource;

public CustomTitleBarWindow()
{
InitializeComponent();

Title = "Custom Title Bar with Caption Buttons";

// Get AppWindow
_appWindow = AppWindow;

if (_appWindow is not null)
{
// SetBorderAndTitleBar with (true, false) to extend client area and hide system caption buttons
var presenter = _appWindow.Presenter as OverlappedPresenter;
presenter.SetBorderAndTitleBar(hasBorder: true, hasTitleBar: false);

// Set the drag region for the custom title bar
SetTitleBar(CustomTitleBar);

// Configure InputNonClientPointerSource for Maximize button to support Snap Layouts
ConfigureMaximizeButtonSnapLayouts();

// Update maximize/restore icon based on current state
UpdateMaximizeRestoreIcon();

// Subscribe to window state changes
_appWindow.Changed += OnAppWindowChanged;
}

// Update icon when window state changes
SizeChanged += (s, e) => UpdateMaximizeRestoreIcon();
}

private void ConfigureMaximizeButtonSnapLayouts()
{
if (_appWindow is null || MaximizeRestoreButton is null)
return;

try
{
// Get the InputNonClientPointerSource for non-client area input
_nonClientPointerSource = InputNonClientPointerSource.GetForWindowId(_appWindow.Id);

if (_nonClientPointerSource is not null)
{
// Update the region when the button is loaded or size changes
_appWindow.Changed += (s, e) => UpdateMaximizeButtonRegion();
MaximizeRestoreButton.Loaded += (s, e) => UpdateMaximizeButtonRegion();
MaximizeRestoreButton.SizeChanged += (s, e) => UpdateMaximizeButtonRegion();
}
}
catch
{
// InputNonClientPointerSource may not be available on all platforms
// This is expected and we can safely ignore
}
}

private void UpdateMaximizeButtonRegion()
{
if (_nonClientPointerSource is null || MaximizeRestoreButton is null || _appWindow is null)
return;

try
{
// Get the button's position relative to the window
var transform = MaximizeRestoreButton.TransformToVisual(null);
var buttonPosition = transform.TransformPoint(new Windows.Foundation.Point(0, 0));
var scale = (float)_appWindow.Size.Width / (float)Content.ActualSize.X;

// Create a rect for the maximize button region
var rect = new RectInt32() {
X = (int)(buttonPosition.X * scale),
Y = (int)(buttonPosition.Y * scale),
Width = (int)(MaximizeRestoreButton.ActualWidth * scale),
Height = (int)(MaximizeRestoreButton.ActualHeight * scale)
};

// Set the region for the Maximize button to enable Snap Layouts
_nonClientPointerSource.SetRegionRects(NonClientRegionKind.Maximize, new[] { rect });
}
catch
{
// SetRegionRects may fail on some platforms or configurations
// This is expected and we can safely ignore
}
}

private void OnAppWindowChanged(AppWindow sender, AppWindowChangedEventArgs args)
{
if (args.DidPresenterChange)
{
UpdateMaximizeRestoreIcon();
}
}

private void UpdateMaximizeRestoreIcon()
{
if (_appWindow is null || MaximizeRestoreIcon is null)
return;

// Update icon based on window state
var isMaximized = _appWindow.Presenter.Kind == AppWindowPresenterKind.FullScreen ||
(_appWindow.Presenter is OverlappedPresenter overlapped && overlapped.State == OverlappedPresenterState.Maximized);

MaximizeRestoreIcon.Glyph = isMaximized ? "\uE923" : "\uE922"; // Restore or Maximize icon

var tooltip = isMaximized ? "Restore Down" : "Maximize";
ToolTipService.SetToolTip(MaximizeRestoreButton, tooltip);
}

private void MinimizeClick(object sender, RoutedEventArgs args)
{
if (_appWindow?.Presenter is OverlappedPresenter presenter)
{
presenter.Minimize();
}
}

private void MaximizeRestoreClick(object sender, RoutedEventArgs args)
{
if (_appWindow?.Presenter is OverlappedPresenter presenter)
{
if (presenter.State == OverlappedPresenterState.Maximized)
{
presenter.Restore();
}
else
{
presenter.Maximize();
}
}
}

public void CloseClick(object sender, RoutedEventArgs args) => Close();
}
Loading