Skip to content
Open
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
9 changes: 8 additions & 1 deletion components/TitleBar/samples/TitleBarFullSample.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SystemBackdrop = new MicaBackdrop()
};
newWindow.Content = new ShellPage(newWindow);

// Create the content for the window to show
// and set the FlowDirection to match the current region.
newWindow.Content = new ShellPage(newWindow)
{
FlowDirection = this.FlowDirection
};

newWindow.Activate();
#endif
}
Expand Down
36 changes: 25 additions & 11 deletions components/TitleBar/src/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@ public enum WindowMessage : int
[Flags]
public enum WindowStyle : uint
{
WS_SYSMENU = 0x80000
WS_SYSMENU = 0x80000,
}

[Flags]
public enum WindowStyleExtended : ulong
{
WS_EX_LAYOUTRTL= 0x00400000L,
}

[Flags]
public enum WindowLongIndexFlags : int
{
GWL_WNDPROC = -4,
GWL_STYLE = -16
GWL_STYLE = -16,
GWL_EXSTYLE = -20,
}

[Flags]
Expand All @@ -44,43 +51,50 @@ public enum SystemCommand
SC_KEYMENU = 0xF100
}

// TODO: Check for typing online. IntPtr, int, or long?

[DllImport("user32.dll", EntryPoint = "GetWindowLongW", SetLastError = false)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
public static extern int GetWindowLongW(IntPtr hWnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", SetLastError = false)]
public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "GetWindowLongPtrW", SetLastError = false)]
public static extern int GetWindowLongPtr(IntPtr hWnd, int nIndex);
public static extern int GetWindowLongPtrW(IntPtr hWnd, int nIndex);

public static int GetWindowLongAuto(IntPtr hWnd, int nIndex)
{
if (IntPtr.Size is 8)
{
return GetWindowLongPtr(hWnd, nIndex);
return GetWindowLongPtrW(hWnd, nIndex);
}
else
{
return GetWindowLong(hWnd, nIndex);
return GetWindowLongW(hWnd, nIndex);
}
}

[DllImport("user32.dll", EntryPoint = "FindWindowExW", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpszClass, string lpszWindow);


[DllImport("user32.dll", EntryPoint = "SetWindowLongW", SetLastError = false)]
public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
public static extern IntPtr SetWindowLongW(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW", SetLastError = false)]
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = false)]
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW", SetLastError = false)]
public static extern IntPtr SetWindowLongPtrW(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

public static IntPtr SetWindowLongAuto(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
if (IntPtr.Size is 8)
{
return SetWindowLongPtr(hWnd, nIndex, dwNewLong);
return SetWindowLongPtrW(hWnd, nIndex, dwNewLong);
}
else
{
return SetWindowLong(hWnd, nIndex, dwNewLong);
return SetWindowLongW(hWnd, nIndex, dwNewLong);
}
}

Expand Down
14 changes: 14 additions & 0 deletions components/TitleBar/src/TitleBar.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ public partial class TitleBar : Control
public static readonly DependencyProperty AutoConfigureCustomTitleBarProperty = DependencyProperty.Register(nameof(AutoConfigureCustomTitleBar), typeof(bool), typeof(TitleBar), new PropertyMetadata(true, AutoConfigureCustomTitleBarChanged));

#if WINAPPSDK
/// <summary>
/// The backing <see cref="DependencyProperty"/> for the <see cref="Window"/> property.
/// </summary>
public static readonly DependencyProperty AutoChangeWindowLayoutStyleProperty = DependencyProperty.Register(nameof(AutoChangeWindowLayoutStyle), typeof(bool), typeof(TitleBar), new PropertyMetadata(true));

/// <summary>
/// The backing <see cref="DependencyProperty"/> for the <see cref="Window"/> property.
/// </summary>
Expand Down Expand Up @@ -165,6 +170,15 @@ public bool AutoConfigureCustomTitleBar
}

#if WINAPPSDK
/// <summary>
/// Gets or sets if the TitleBar should automatically change the Window's LayoutStyle to match the FlowDirection of the TitleBar (WASDK only).
/// </summary>
public bool AutoChangeWindowLayoutStyle
{
get => (bool)GetValue(AutoChangeWindowLayoutStyleProperty);
set => SetValue(AutoChangeWindowLayoutStyleProperty, value);
}

/// <summary>
/// Gets or sets the window the TitleBar should configure (WASDK only).
/// </summary>
Expand Down
39 changes: 37 additions & 2 deletions components/TitleBar/src/TitleBar.WASDK.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
// See the LICENSE file in the project root for more information.

#if WINDOWS_WINAPPSDK && !HAS_UNO
using Windows.Graphics;
using Microsoft.UI;
using Microsoft.UI.Input;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml.Media;
using System.Runtime.InteropServices;
using Windows.Graphics;

namespace CommunityToolkit.WinUI.Controls;

[TemplatePart(Name = nameof(PART_FooterPresenter), Type = typeof(ContentPresenter))]
[TemplatePart(Name = nameof(PART_ContentPresenter), Type = typeof(ContentPresenter))]

public partial class TitleBar : Control
{
WndProcHelper? WndProcHelper;
Expand Down Expand Up @@ -54,12 +54,26 @@ private void SetWASDKTitleBar()
};
}

// Set the caption buttons to match the flow direction of the titlebar
if (AutoChangeWindowLayoutStyle)
{
UpdateCaptionButtonsDirection(this.FlowDirection);
}

PART_ContentPresenter = GetTemplateChild(nameof(PART_ContentPresenter)) as ContentPresenter;
PART_FooterPresenter = GetTemplateChild(nameof(PART_FooterPresenter)) as ContentPresenter;

// Get caption button occlusion information.
int CaptionButtonOcclusionWidthRight = Window.AppWindow.TitleBar.RightInset;
int CaptionButtonOcclusionWidthLeft = Window.AppWindow.TitleBar.LeftInset;

// Swap left/right if in RTL mode
if (this.FlowDirection == FlowDirection.RightToLeft)
{
(CaptionButtonOcclusionWidthRight, CaptionButtonOcclusionWidthLeft) = (CaptionButtonOcclusionWidthLeft, CaptionButtonOcclusionWidthRight);
}

// Set padding columns to match caption button occlusion.
PART_LeftPaddingColumn!.Width = new GridLength(CaptionButtonOcclusionWidthLeft);
PART_RightPaddingColumn!.Width = new GridLength(CaptionButtonOcclusionWidthRight);

Expand Down Expand Up @@ -102,6 +116,27 @@ private void UpdateCaptionButtons(FrameworkElement rootElement)
}
}

private void UpdateCaptionButtonsDirection(FlowDirection direction)
{
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this.Window);

if (hwnd != 0)
{
var exStyle = NativeMethods.GetWindowLongPtr(hwnd, (int)NativeMethods.WindowLongIndexFlags.GWL_EXSTYLE);

if (direction == FlowDirection.RightToLeft)
{
exStyle |= (nint)NativeMethods.WindowStyleExtended.WS_EX_LAYOUTRTL;
}
else
{
exStyle &= (nint)NativeMethods.WindowStyleExtended.WS_EX_LAYOUTRTL;
}

NativeMethods.SetWindowLongPtr(hwnd, (int)NativeMethods.WindowLongIndexFlags.GWL_EXSTYLE, exStyle);
}
}

private void ResetWASDKTitleBar()
{
if (this.Window == null)
Expand Down
2 changes: 1 addition & 1 deletion components/TitleBar/src/WndProcHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void RegisterInputNonClientPointerSourceWndProc(WNDPROC wndProc)

if (inputNonClientPointerSourceHandle != IntPtr.Zero)
{
int style = NativeMethods.GetWindowLongAuto(Handle, (int)NativeMethods.WindowLongIndexFlags.GWL_STYLE);
IntPtr style = NativeMethods.GetWindowLongAuto(Handle, (int)NativeMethods.WindowLongIndexFlags.GWL_STYLE);
NativeMethods.SetWindowLongAuto(Handle, (int)NativeMethods.WindowLongIndexFlags.GWL_STYLE, (IntPtr)(style & ~(int)NativeMethods.WindowStyle.WS_SYSMENU));

newInputNonClientPointerSourceWndProc = wndProc;
Expand Down
Loading