11// Copyright (c) 2023 Files Community
22// Licensed under the MIT License. See the LICENSE.
33
4- using System ;
5- using System . Collections . Generic ;
6- using System . Linq ;
74using Windows . Graphics ;
8- using Microsoft . UI ;
9- using Microsoft . UI . Windowing ;
5+ using Microsoft . UI . Input ;
106using Microsoft . UI . Xaml ;
11- using WinRT . Interop ;
127
138namespace Files . App . Helpers
149{
1510 public static class DragZoneHelper
1611 {
17- /// <summary>
18- /// Get Scale Adjustment
19- /// </summary>
20- /// <param name="window"></param>
21- /// <returns>scale factor percent</returns>
22- public static double GetScaleAdjustment ( Window window ) => window . Content . XamlRoot . RasterizationScale ;
12+ public delegate int SetTitleBarDragRegionDelegate ( InputNonClientPointerSource source , SizeInt32 size , double scaleFactor , Func < UIElement , RectInt32 ? , RectInt32 > getScaledRect ) ;
2313
2414 /// <summary>
25- /// Calculate dragging-zones of title bar<br/>
26- /// <strong>You MUST transform the rectangles with <see cref="GetScaleAdjustment "/> before calling <see cref="AppWindowTitleBar.SetDragRectangles "/></strong>
15+ /// Informs the bearer to refresh the drag region.
16+ /// will not set <see cref="NonClientRegionKind.LeftBorder "/>, <see cref="NonClientRegionKind.RightBorder "/>, <see cref="NonClientRegionKind.Caption"/>when titleBarHeight less than 0
2717 /// </summary>
28- /// <param name="viewportWidth"></param>
29- /// <param name="dragZoneHeight"></param>
30- /// <param name="dragZoneLeftIndent"></param>
31- /// <param name="nonDraggingZones"></param>
32- /// <returns></returns>
33- public static IEnumerable < RectInt32 > GetDragZones ( int viewportWidth , int dragZoneHeight , int dragZoneLeftIndent , IEnumerable < RectInt32 > nonDraggingZones )
18+ /// <param name="element"></param>
19+ /// <param name="window"></param>
20+ /// <param name="setTitleBarDragRegion"></param>
21+ public static void RaiseSetTitleBarDragRegion ( this Window window , SetTitleBarDragRegionDelegate setTitleBarDragRegion )
3422 {
35- var draggingZonesX = new List < Range > { new ( dragZoneLeftIndent , viewportWidth ) } ;
36- var draggingZonesY = new List < IEnumerable < Range > > { new [ ] { new Range ( 0 , dragZoneHeight ) } } ;
37-
38- foreach ( var nonDraggingZone in nonDraggingZones )
39- {
40- for ( var i = 0 ; i < draggingZonesX . Count ; ++ i )
41- {
42- var x = draggingZonesX [ i ] ;
43- var y = draggingZonesY [ i ] . ToArray ( ) ;
44- var xSubtrahend = new Range ( nonDraggingZone . X , nonDraggingZone . X + nonDraggingZone . Width ) ;
45- var ySubtrahend = new Range ( nonDraggingZone . Y , nonDraggingZone . Y + nonDraggingZone . Height ) ;
46- var xResult = ( x - xSubtrahend ) . ToArray ( ) ;
47- if ( xResult . Length is 1 && xResult [ 0 ] == x )
48- continue ;
49- var yResult = ( y - ySubtrahend ) . ToArray ( ) ;
50- switch ( xResult . Length )
51- {
52- case 0 :
53- draggingZonesY [ i ] = yResult ;
54- break ;
55- case 1 :
56- draggingZonesX . RemoveAt ( i ) ;
57- draggingZonesY . RemoveAt ( i ) ;
58- if ( xResult [ 0 ] . Lower == x . Lower )
59- {
60- draggingZonesY . InsertRange ( i , new [ ] { y , yResult } ) ;
61- draggingZonesX . InsertRange ( i , new [ ]
62- {
63- x with { Upper = xResult [ 0 ] . Upper } ,
64- x with { Lower = xSubtrahend . Lower }
65- } ) ;
66- }
67- else // xResult[0].Upper == x.Upper
68- {
69- draggingZonesY . InsertRange ( i , new [ ] { yResult , y } ) ;
70- draggingZonesX . InsertRange ( i , new [ ]
71- {
72- x with { Upper = xSubtrahend . Upper } ,
73- x with { Lower = xResult [ 0 ] . Lower }
74- } ) ;
75- }
76- ++ i ;
77- break ;
78- case 2 :
79- draggingZonesX . RemoveAt ( i ) ;
80- draggingZonesY . RemoveAt ( i ) ;
81- draggingZonesY . InsertRange ( i , new [ ] { y , yResult , y } ) ;
82- draggingZonesX . InsertRange ( i , new [ ]
83- {
84- x with { Upper = xResult [ 0 ] . Upper } ,
85- xSubtrahend ,
86- x with { Lower = xResult [ 1 ] . Lower }
87- } ) ;
88- ++ i ;
89- ++ i ;
90- break ;
91- }
92- }
93- }
94-
95- var rects = draggingZonesX
96- . SelectMany ( ( rangeX , i ) => draggingZonesY [ i ]
97- . Select ( rangeY => new RectInt32 ( rangeX . Lower , rangeY . Lower , rangeX . Distance , rangeY . Distance ) ) )
98- . OrderBy ( t => t . Y )
99- . ThenBy ( t => t . X ) . ToList ( ) ;
100- for ( var i = 0 ; i < rects . Count - 1 ; ++ i )
23+ if ( ! window . AppWindow . IsVisible )
24+ return ;
25+ // UIElement.RasterizationScale is always 1
26+ var source = InputNonClientPointerSource . GetForWindowId ( window . AppWindow . Id ) ;
27+ var uiElement = window . Content ;
28+ var scaleFactor = uiElement . XamlRoot . RasterizationScale ;
29+ var size = window . AppWindow . Size ;
30+ // If the number of regions is 0 or 1, AppWindow will automatically reset to the default region next time, but if it is >=2, it will not and need to be manually cleared
31+ source . ClearRegionRects ( NonClientRegionKind . Passthrough ) ;
32+ var titleBarHeight = setTitleBarDragRegion ( source , size , scaleFactor , GetScaledRect ) ;
33+ if ( titleBarHeight >= 0 )
10134 {
102- var now = rects [ i ] ;
103- var next = rects [ i + 1 ] ;
104- if ( now . Height == next . Height && now . X + now . Width == next . X )
105- {
106- rects . RemoveRange ( i , 2 ) ;
107- rects . Insert ( i , now with { Width = now . Width + next . Width } ) ;
108- }
35+ // region under the buttons
36+ const int borderThickness = 5 ;
37+ source . SetRegionRects ( NonClientRegionKind . LeftBorder , [ GetScaledRect ( uiElement , new ( 0 , 0 , borderThickness , titleBarHeight ) ) ] ) ;
38+ source . SetRegionRects ( NonClientRegionKind . RightBorder , [ GetScaledRect ( uiElement , new ( size . Width , 0 , borderThickness , titleBarHeight ) ) ] ) ;
39+ source . SetRegionRects ( NonClientRegionKind . Caption , [ GetScaledRect ( uiElement , new ( 0 , 0 , size . Width , titleBarHeight ) ) ] ) ;
10940 }
110-
111- return rects ;
11241 }
11342
114- /// <summary>
115- /// Set dragging-zones of title bar
116- /// </summary>
117- /// <param name="window"></param>
118- /// <param name="dragZoneHeight"></param>
119- /// <param name="dragZoneLeftIndent"></param>
120- /// <param name="nonDraggingZones"></param>
121- public static void SetDragZones ( Window window , int dragZoneHeight = 40 , int dragZoneLeftIndent = 0 , IEnumerable < RectInt32 > ? nonDraggingZones = null )
43+ private static RectInt32 GetScaledRect ( this UIElement uiElement , RectInt32 ? r = null )
12244 {
123- var hWnd = WindowNative . GetWindowHandle ( window ) ;
124- var windowId = Win32Interop . GetWindowIdFromWindow ( hWnd ) ;
125- var appWindow = AppWindow . GetFromWindowId ( windowId ) ;
126- var scaleAdjustment = GetScaleAdjustment ( window ) ;
127- var windowWidth = ( int ) ( appWindow . Size . Width / scaleAdjustment ) ;
128- nonDraggingZones ??= Array . Empty < RectInt32 > ( ) ;
129- #if DEBUG
130- // Subtract the toolbar area (center-top in window), only in DEBUG mode.
131- nonDraggingZones = nonDraggingZones . Concat ( new RectInt32 [ ] { new ( ( windowWidth - DebugToolbarWidth ) / 2 , 0 , DebugToolbarWidth , DebugToolbarHeight ) } ) ;
132- #endif
133- appWindow . TitleBar . SetDragRectangles (
134- GetDragZones ( windowWidth , dragZoneHeight , dragZoneLeftIndent , nonDraggingZones )
135- . Select ( rect => new RectInt32 (
136- ( int ) ( rect . X * scaleAdjustment ) ,
137- ( int ) ( rect . Y * scaleAdjustment ) ,
138- ( int ) ( rect . Width * scaleAdjustment ) ,
139- ( int ) ( rect . Height * scaleAdjustment ) ) )
140- . ToArray ( ) ) ;
141- }
142-
143- private const int DebugToolbarWidth = 217 ;
144- private const int DebugToolbarHeight = 25 ;
145- }
146-
147- file record Range ( int Lower , int Upper )
148- {
149- public int Distance => Upper - Lower ;
150-
151- private bool Intersects ( Range other ) => other . Lower <= Upper && other . Upper >= Lower ;
152-
153- public static IEnumerable < Range > operator - ( Range minuend , Range subtrahend )
154- {
155- if ( ! minuend . Intersects ( subtrahend ) )
45+ if ( r is { } rect )
15646 {
157- yield return minuend ;
158- yield break ;
47+ var scaleFactor = uiElement . XamlRoot . RasterizationScale ;
48+ return new ( ( int ) ( rect . X * scaleFactor ) , ( int ) ( rect . Y * scaleFactor ) , ( int ) ( rect . Width * scaleFactor ) ,
49+ ( int ) ( rect . Height * scaleFactor ) ) ;
50+ }
51+ else
52+ {
53+ var pos = uiElement . TransformToVisual ( null ) . TransformPoint ( new ( 0 , 0 ) ) ;
54+ rect = new RectInt32 ( ( int ) pos . X , ( int ) pos . Y , ( int ) uiElement . ActualSize . X , ( int ) uiElement . ActualSize . Y ) ;
55+ return GetScaledRect ( uiElement , rect ) ;
15956 }
160- if ( minuend . Lower < subtrahend . Lower )
161- yield return minuend with { Upper = subtrahend . Lower } ;
162- if ( minuend . Upper > subtrahend . Upper )
163- yield return minuend with { Lower = subtrahend . Upper } ;
16457 }
165-
166- public static IEnumerable < Range > operator - ( IEnumerable < Range > minuends , Range subtrahend )
167- => minuends . SelectMany ( minuend => minuend - subtrahend ) ;
16858 }
16959}
0 commit comments