44
55#if WINAPPSDK
66using Microsoft . UI ;
7+ using Microsoft . UI . Input ;
78using Microsoft . UI . Windowing ;
8- using static CommunityToolkit . WinUI . Controls . NativeMethods ;
9+ using Microsoft . UI . Xaml . Media ;
910
1011namespace CommunityToolkit . WinUI . Controls ;
1112
12- [ TemplatePart ( Name = nameof ( PART_ButtonsHolderColumn ) , Type = typeof ( ColumnDefinition ) ) ]
13- [ TemplatePart ( Name = nameof ( PART_IconColumn ) , Type = typeof ( ColumnDefinition ) ) ]
14- [ TemplatePart ( Name = nameof ( PART_TitleColumn ) , Type = typeof ( ColumnDefinition ) ) ]
15- [ TemplatePart ( Name = nameof ( PART_LeftDragColumn ) , Type = typeof ( ColumnDefinition ) ) ]
16- [ TemplatePart ( Name = nameof ( PART_ContentColumn ) , Type = typeof ( ColumnDefinition ) ) ]
17- [ TemplatePart ( Name = nameof ( PART_FooterColumn ) , Type = typeof ( ColumnDefinition ) ) ]
18- [ TemplatePart ( Name = nameof ( PART_RightDragColumn ) , Type = typeof ( ColumnDefinition ) ) ]
19- [ TemplatePart ( Name = nameof ( PART_TitleHolder ) , Type = typeof ( StackPanel ) ) ]
20- [ TemplatePart ( Name = nameof ( PART_RootGrid ) , Type = typeof ( Grid ) ) ]
13+ [ TemplatePart ( Name = nameof ( PART_FooterPresenter ) , Type = typeof ( ContentPresenter ) ) ]
14+ [ TemplatePart ( Name = nameof ( PART_ContentPresenter ) , Type = typeof ( ContentPresenter ) ) ]
2115
2216public partial class TitleBar : Control
2317{
24- ColumnDefinition ? PART_ButtonsHolderColumn ;
25- ColumnDefinition ? PART_IconColumn ;
26- ColumnDefinition ? PART_TitleColumn ;
27- ColumnDefinition ? PART_LeftDragColumn ;
28- ColumnDefinition ? PART_ContentColumn ;
29- ColumnDefinition ? PART_FooterColumn ;
30- ColumnDefinition ? PART_RightDragColumn ;
31- StackPanel ? PART_TitleHolder ;
32- Grid ? PART_RootGrid ;
18+ WndProcHelper WndProcHelper ;
19+ MenuFlyout MenuFlyout ;
20+ ContentPresenter ? PART_ContentPresenter ;
21+ ContentPresenter ? PART_FooterPresenter ;
3322
3423 private void SetWASDKTitleBar ( )
3524 {
@@ -42,6 +31,16 @@ private void SetWASDKTitleBar()
4231 {
4332 Window . AppWindow . TitleBar . ExtendsContentIntoTitleBar = true ;
4433
34+ if ( this . ContextFlyout != null && this . ContextFlyout is MenuFlyout menuFlyout )
35+ {
36+ this . MenuFlyout = menuFlyout ;
37+ WndProcHelper = new WndProcHelper ( this . Window ) ;
38+ WndProcHelper . RegisterWndProc ( WindowWndProc ) ;
39+ WndProcHelper . RegisterInputNonClientPointerSourceWndProc ( InputNonClientPointerSourceWndProc ) ;
40+ }
41+
42+ this . Window . SizeChanged -= Window_SizeChanged ;
43+ this . Window . SizeChanged += Window_SizeChanged ;
4544 this . Window . Activated -= Window_Activated ;
4645 this . Window . Activated += Window_Activated ;
4746
@@ -54,16 +53,8 @@ private void SetWASDKTitleBar()
5453 } ;
5554 }
5655
57- // Set the width of padding columns in the UI.
58- PART_ButtonsHolderColumn = GetTemplateChild ( nameof ( PART_ButtonsHolderColumn ) ) as ColumnDefinition ;
59- PART_IconColumn = GetTemplateChild ( nameof ( PART_IconColumn ) ) as ColumnDefinition ;
60- PART_TitleColumn = GetTemplateChild ( nameof ( PART_TitleColumn ) ) as ColumnDefinition ;
61- PART_LeftDragColumn = GetTemplateChild ( nameof ( PART_LeftDragColumn ) ) as ColumnDefinition ;
62- PART_ContentColumn = GetTemplateChild ( nameof ( PART_ContentColumn ) ) as ColumnDefinition ;
63- PART_RightDragColumn = GetTemplateChild ( nameof ( PART_RightDragColumn ) ) as ColumnDefinition ;
64- PART_FooterColumn = GetTemplateChild ( nameof ( PART_FooterColumn ) ) as ColumnDefinition ;
65- PART_TitleHolder = GetTemplateChild ( nameof ( PART_TitleHolder ) ) as StackPanel ;
66- PART_RootGrid = GetTemplateChild ( nameof ( PART_RootGrid ) ) as Grid ;
56+ PART_ContentPresenter = GetTemplateChild ( nameof ( PART_ContentPresenter ) ) as ContentPresenter ;
57+ PART_FooterPresenter = GetTemplateChild ( nameof ( PART_FooterPresenter ) ) as ContentPresenter ;
6758
6859 // Get caption button occlusion information.
6960 int CaptionButtonOcclusionWidthRight = Window . AppWindow . TitleBar . RightInset ;
@@ -87,6 +78,11 @@ private void SetWASDKTitleBar()
8778 }
8879 }
8980
81+ private void Window_SizeChanged ( object sender , WindowSizeChangedEventArgs args )
82+ {
83+ UpdateVisualStateAndDragRegion ( args . Size ) ;
84+ }
85+
9086 private void UpdateCaptionButtons ( FrameworkElement rootElement )
9187 {
9288 Window . AppWindow . TitleBar . ButtonBackgroundColor = Colors . Transparent ;
@@ -112,6 +108,7 @@ private void ResetWASDKTitleBar()
112108 }
113109
114110 Window . AppWindow . TitleBar . ExtendsContentIntoTitleBar = false ;
111+ this . Window . SizeChanged -= Window_SizeChanged ;
115112 this . Window . Activated -= Window_Activated ;
116113 SizeChanged -= this . TitleBar_SizeChanged ;
117114 Window . AppWindow . TitleBar . ResetToDefault ( ) ;
@@ -129,61 +126,119 @@ private void Window_Activated(object sender, WindowActivatedEventArgs args)
129126 }
130127 }
131128
132- private void SetDragRegionForCustomTitleBar ( )
129+ public void SetDragRegionForCustomTitleBar ( )
130+ {
131+ if ( AutoConfigureCustomTitleBar && Window != null )
132+ {
133+ ClearDragRegions ( NonClientRegionKind . Passthrough ) ;
134+ SetDragRegion ( NonClientRegionKind . Passthrough , PART_ContentPresenter , PART_FooterPresenter , PART_ButtonHolder ) ;
135+ }
136+ }
137+
138+ public double GetRasterizationScaleForElement ( UIElement element )
133139 {
134- if ( AutoConfigureCustomTitleBar && Window != null && PART_RightPaddingColumn != null && PART_LeftPaddingColumn != null )
140+ if ( element . XamlRoot != null )
141+ {
142+ return element . XamlRoot . RasterizationScale ;
143+ }
144+ return 0.0 ;
145+ }
146+
147+ public void SetDragRegion ( NonClientRegionKind nonClientRegionKind , params FrameworkElement [ ] frameworkElements )
148+ {
149+ var nonClientInputSrc = InputNonClientPointerSource . GetForWindowId ( Window . AppWindow . Id ) ;
150+ List < Windows . Graphics . RectInt32 > rects = new List < Windows . Graphics . RectInt32 > ( ) ;
151+ var scale = GetRasterizationScaleForElement ( this ) ;
152+
153+ foreach ( var frameworkElement in frameworkElements )
154+ {
155+ if ( frameworkElement == null )
156+ {
157+ continue ;
158+ }
159+ GeneralTransform transformElement = frameworkElement . TransformToVisual ( null ) ;
160+ Windows . Foundation . Rect bounds = transformElement . TransformBounds ( new Windows . Foundation . Rect ( 0 , 0 , frameworkElement . ActualWidth , frameworkElement . ActualHeight ) ) ;
161+ var transparentRect = new Windows . Graphics . RectInt32 (
162+ _X : ( int ) Math . Round ( bounds . X * scale ) ,
163+ _Y : ( int ) Math . Round ( bounds . Y * scale ) ,
164+ _Width : ( int ) Math . Round ( bounds . Width * scale ) ,
165+ _Height : ( int ) Math . Round ( bounds . Height * scale )
166+ ) ;
167+ rects . Add ( transparentRect ) ;
168+ }
169+ if ( rects . Count > 0 )
135170 {
136- double scaleAdjustment = GetScaleAdjustment ( ) ;
137-
138- PART_RightPaddingColumn . Width = new GridLength ( Window . AppWindow . TitleBar . RightInset / scaleAdjustment ) ;
139- PART_LeftPaddingColumn . Width = new GridLength ( Window . AppWindow . TitleBar . LeftInset / scaleAdjustment ) ;
140-
141- var height = ( int ) ( this . ActualHeight * scaleAdjustment ) ;
142- Windows . Graphics . RectInt32 rect1 = new ( 0 , 0 , 0 , height ) ;
143- Windows . Graphics . RectInt32 rect2 = new ( 0 , 0 , 0 , height ) ;
144- Windows . Graphics . RectInt32 rect3 = new ( 0 , 0 , 0 , height ) ;
145- Windows . Graphics . RectInt32 rect4 = new ( 0 , 0 , 0 , height ) ;
146-
147- rect1 . X = 0 ;
148- rect1 . Width = ( int ) ( ( PART_RootGrid . Padding . Left
149- + PART_LeftPaddingColumn . ActualWidth )
150- * scaleAdjustment ) ;
151-
152- rect2 . X = rect1 . X + rect1 . Width + ( int ) ( ( PART_ButtonsHolderColumn . ActualWidth ) * scaleAdjustment ) ;
153- rect2 . Width = ( int ) ( ( PART_IconColumn . ActualWidth
154- + PART_TitleColumn . ActualWidth
155- + PART_LeftDragColumn . ActualWidth )
156- * scaleAdjustment ) ;
157-
158- rect3 . X = rect2 . X + rect2 . Width + ( int ) ( PART_ContentColumn . ActualWidth * scaleAdjustment ) ;
159- rect3 . Width = ( int ) ( PART_RightDragColumn . ActualWidth * scaleAdjustment ) ;
160-
161- rect4 . X = rect3 . X + rect3 . Width + ( int ) ( ( PART_FooterColumn . ActualWidth
162- + PART_RightPaddingColumn . ActualWidth
163- + PART_RootGrid . Padding . Right )
164- * scaleAdjustment ) ;
165- rect4 . Width = ( int ) ( PART_RightPaddingColumn . ActualWidth * scaleAdjustment ) ;
166-
167- Windows . Graphics . RectInt32 [ ] dragRects = new [ ] { rect1 , rect2 , rect3 , rect4 } ;
168-
169- Window . AppWindow . TitleBar . SetDragRectangles ( dragRects ) ;
171+ nonClientInputSrc . SetRegionRects ( nonClientRegionKind , rects . ToArray ( ) ) ;
170172 }
171173 }
172174
173- private double GetScaleAdjustment ( )
175+ public void ClearDragRegions ( NonClientRegionKind nonClientRegionKind )
174176 {
175- DisplayArea displayArea = DisplayArea . GetFromWindowId ( this . Window . AppWindow . Id , DisplayAreaFallback . Primary ) ;
176- IntPtr hMonitor = Win32Interop . GetMonitorFromDisplayId ( displayArea . DisplayId ) ;
177+ var noninputsrc = InputNonClientPointerSource . GetForWindowId ( Window . AppWindow . Id ) ;
178+ noninputsrc . ClearRegionRects ( nonClientRegionKind ) ;
179+ }
177180
178- // Get DPI.
179- int result = GetDpiForMonitor ( hMonitor , Monitor_DPI_Type . MDT_Default , out uint dpiX , out uint _ ) ;
180- if ( result != 0 )
181+ private IntPtr InputNonClientPointerSourceWndProc ( IntPtr hWnd , NativeMethods . WindowMessage Msg , IntPtr wParam , IntPtr lParam )
182+ {
183+ switch ( Msg )
181184 {
182- throw new Exception ( "Could not get DPI for monitor." ) ;
185+ case NativeMethods . WindowMessage . WM_NCLBUTTONDOWN :
186+ {
187+ if ( MenuFlyout . IsOpen )
188+ {
189+ MenuFlyout . Hide ( ) ;
190+ }
191+ break ;
192+ }
193+ case NativeMethods . WindowMessage . WM_NCRBUTTONDOWN :
194+ {
195+ PointInt32 pt = new PointInt32 ( lParam . ToInt32 ( ) & 0xFFFF , lParam . ToInt32 ( ) >> 16 ) ;
196+ FlyoutShowOptions options = new FlyoutShowOptions ( ) ;
197+ options . ShowMode = FlyoutShowMode . Standard ;
198+ options . Position = InfoHelper . SystemVersion . Build >= 22000 ?
199+ new Windows . Foundation . Point ( ( pt . X - this . Window . AppWindow . Position . X - 8 ) / XamlRoot . RasterizationScale , ( pt . Y - this . Window . AppWindow . Position . Y ) / XamlRoot . RasterizationScale ) :
200+ new Windows . Foundation . Point ( pt . X - this . Window . AppWindow . Position . X - 8 , pt . Y - this . Window . AppWindow . Position . Y ) ;
201+
202+ MenuFlyout . ShowAt ( this , options ) ;
203+ return ( IntPtr ) 0 ;
204+ }
183205 }
206+ return WndProcHelper . CallInputNonClientPointerSourceWindowProc ( hWnd , Msg , wParam , lParam ) ;
207+ }
208+
209+ private IntPtr WindowWndProc ( IntPtr hWnd , NativeMethods . WindowMessage Msg , IntPtr wParam , IntPtr lParam )
210+ {
211+ switch ( Msg )
212+ {
213+ case NativeMethods . WindowMessage . WM_SYSMENU :
214+ {
215+ return ( IntPtr ) 0 ;
216+ }
217+
218+ case NativeMethods . WindowMessage . WM_SYSCOMMAND :
219+ {
220+ NativeMethods . SystemCommand sysCommand = ( NativeMethods . SystemCommand ) ( wParam . ToInt32 ( ) & 0xFFF0 ) ;
184221
185- uint scaleFactorPercent = ( uint ) ( ( ( long ) dpiX * 100 + ( 96 >> 1 ) ) / 96 ) ;
186- return scaleFactorPercent / 100.0 ;
222+ if ( sysCommand is NativeMethods . SystemCommand . SC_MOUSEMENU )
223+ {
224+ FlyoutShowOptions options = new FlyoutShowOptions ( ) ;
225+ options . Position = new Windows . Foundation . Point ( 0 , 15 ) ;
226+ options . ShowMode = FlyoutShowMode . Standard ;
227+ MenuFlyout . ShowAt ( null , options ) ;
228+ return ( IntPtr ) 0 ;
229+ }
230+ else if ( sysCommand is NativeMethods . SystemCommand . SC_KEYMENU )
231+ {
232+ FlyoutShowOptions options = new FlyoutShowOptions ( ) ;
233+ options . Position = new Windows . Foundation . Point ( 0 , 45 ) ;
234+ options . ShowMode = FlyoutShowMode . Standard ;
235+ MenuFlyout . ShowAt ( null , options ) ;
236+ return ( IntPtr ) 0 ;
237+ }
238+ break ;
239+ }
240+ }
241+ return WndProcHelper . CallWindowProc ( hWnd , Msg , wParam , lParam ) ;
187242 }
188243}
189244#endif
0 commit comments