9
9
using Microsoft . UI . Windowing ;
10
10
using Microsoft . UI . Xaml ;
11
11
using Microsoft . UI . Xaml . Controls ;
12
- using Microsoft . UI . Xaml . Media ;
13
12
using Microsoft . UI . Xaml . Media . Animation ;
14
13
using System ;
15
- using System . Diagnostics ;
14
+ using System . Drawing . Printing ;
16
15
using System . Runtime . InteropServices ;
17
16
using Windows . Graphics ;
18
17
using Windows . System ;
@@ -34,8 +33,6 @@ public sealed partial class TrayWindow : Window
34
33
private int _lastWindowHeight ;
35
34
private Storyboard ? _currentSb ;
36
35
37
- private NativeApi . POINT ? _lastActivatePosition ;
38
-
39
36
private readonly IRpcController _rpcController ;
40
37
private readonly ICredentialManager _credentialManager ;
41
38
private readonly ISyncSessionController _syncSessionController ;
@@ -60,7 +57,6 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
60
57
61
58
InitializeComponent ( ) ;
62
59
AppWindow . Hide ( ) ;
63
- SystemBackdrop = new DesktopAcrylicBackdrop ( ) ;
64
60
Activated += Window_Activated ;
65
61
RootFrame . SizeChanged += RootFrame_SizeChanged ;
66
62
@@ -97,18 +93,18 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
97
93
WindowNative . GetWindowHandle ( this ) ) ) ;
98
94
SizeProxy . SizeChanged += ( _ , e ) =>
99
95
{
100
- if ( _currentSb is null ) return ; // nothing running
96
+ if ( _currentSb is null ) return ; // nothing running
101
97
102
- int newHeight = ( int ) Math . Round (
98
+ var newHeight = ( int ) Math . Round (
103
99
e . NewSize . Height * DisplayScale . WindowScale ( this ) ) ;
104
100
105
- int delta = newHeight - _lastWindowHeight ;
101
+ var delta = newHeight - _lastWindowHeight ;
106
102
if ( delta == 0 ) return ;
107
103
108
104
var pos = _aw . Position ;
109
105
var size = _aw . Size ;
110
106
111
- pos . Y -= delta ; // grow upward
107
+ pos . Y -= delta ; // grow upward
112
108
size . Height = newHeight ;
113
109
114
110
_aw . MoveAndResize (
@@ -118,7 +114,6 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
118
114
} ;
119
115
}
120
116
121
-
122
117
private void SetPageByState ( RpcModel rpcModel , CredentialModel credentialModel ,
123
118
SyncSessionControllerStateModel syncSessionModel )
124
119
{
@@ -225,7 +220,6 @@ private void OnStoryboardCompleted(object? sender, object e)
225
220
226
221
private void MoveResizeAndActivate ( )
227
222
{
228
- SaveCursorPos ( ) ;
229
223
var size = CalculateWindowSize ( RootFrame . GetContentSize ( ) . Height ) ;
230
224
var pos = CalculateWindowPosition ( size ) ;
231
225
var rect = new RectInt32 ( pos . X , pos . Y , size . Width , size . Height ) ;
@@ -234,18 +228,6 @@ private void MoveResizeAndActivate()
234
228
NativeApi . SetForegroundWindow ( WindowNative . GetWindowHandle ( this ) ) ;
235
229
}
236
230
237
- private void SaveCursorPos ( )
238
- {
239
- var res = NativeApi . GetCursorPos ( out var cursorPosition ) ;
240
- if ( res )
241
- _lastActivatePosition = cursorPosition ;
242
- else
243
- // When the cursor position is null, we will spawn the window in
244
- // the bottom right corner of the primary display.
245
- // TODO: log(?) an error when this happens
246
- _lastActivatePosition = null ;
247
- }
248
-
249
231
private SizeInt32 CalculateWindowSize ( double height )
250
232
{
251
233
if ( height <= 0 ) height = 100 ; // will be resolved next frame typically
@@ -257,41 +239,38 @@ private SizeInt32 CalculateWindowSize(double height)
257
239
return new SizeInt32 ( newWidth , newHeight ) ;
258
240
}
259
241
260
- private PointInt32 CalculateWindowPosition ( SizeInt32 size )
242
+ private PointInt32 CalculateWindowPosition ( SizeInt32 panelSize )
261
243
{
262
- var width = size . Width ;
263
- var height = size . Height ;
244
+ var area = DisplayArea . GetFromWindowId ( AppWindow . Id , DisplayAreaFallback . Primary ) ;
245
+ var bounds = area . OuterBounds ; // entire monitor rect
246
+ var workArea = area . WorkArea ; // monitor minus taskbar (and other app bars)
264
247
265
- var cursorPosition = _lastActivatePosition ;
266
- if ( cursorPosition is null )
267
- {
268
- var primaryWorkArea = DisplayArea . Primary . WorkArea ;
269
- return new PointInt32 (
270
- primaryWorkArea . Width - width ,
271
- primaryWorkArea . Height - height
272
- ) ;
273
- }
274
-
275
- // Spawn the window to the top right of the cursor.
276
- var x = cursorPosition . Value . X + 10 ;
277
- var y = cursorPosition . Value . Y - 10 - height ;
248
+ var tb = GetTaskbarInfo ( area ) ;
278
249
279
- var workArea = DisplayArea . GetFromPoint (
280
- new PointInt32 ( cursorPosition . Value . X , cursorPosition . Value . Y ) ,
281
- DisplayAreaFallback . Primary
282
- ) . WorkArea ;
283
-
284
- // Adjust if the window goes off the right edge of the display.
285
- if ( x + width > workArea . X + workArea . Width ) x = workArea . X + workArea . Width - width ;
250
+ int x , y ;
251
+ switch ( tb . Position )
252
+ {
253
+ case TaskbarPosition . Left :
254
+ x = bounds . X + tb . Gap ;
255
+ y = workArea . Y + workArea . Height - panelSize . Height ;
256
+ break ;
286
257
287
- // Adjust if the window goes off the bottom edge of the display.
288
- if ( y + height > workArea . Y + workArea . Height ) y = workArea . Y + workArea . Height - height ;
258
+ case TaskbarPosition . Top :
259
+ x = workArea . X + workArea . Width - panelSize . Width ;
260
+ y = bounds . Y + tb . Gap ;
261
+ break ;
289
262
290
- // Adjust if the window goes off the left edge of the display (somehow).
291
- if ( x < workArea . X ) x = workArea . X ;
263
+ case TaskbarPosition . Bottom when tb . AutoHide :
264
+ // Auto-hide bottom bar sits under the workArea – use workArea, not bounds.
265
+ x = workArea . X + workArea . Width - panelSize . Width ;
266
+ y = workArea . Y + workArea . Height - panelSize . Height - tb . Gap ;
267
+ break ;
292
268
293
- // Adjust if the window goes off the top edge of the display (somehow).
294
- if ( y < workArea . Y ) y = workArea . Y ;
269
+ default : // right or bottom when not auto-hiding
270
+ x = workArea . X + workArea . Width - panelSize . Width ;
271
+ y = bounds . Y + bounds . Height - panelSize . Height - tb . Gap ;
272
+ break ;
273
+ }
295
274
296
275
return new PointInt32 ( x , y ) ;
297
276
}
@@ -338,4 +317,70 @@ public struct POINT
338
317
public int Y ;
339
318
}
340
319
}
320
+ internal enum TaskbarPosition { Left , Top , Right , Bottom }
321
+
322
+ internal readonly record struct TaskbarInfo ( TaskbarPosition Position , int Gap , bool AutoHide ) ;
323
+
324
+ // -----------------------------------------------------------------------------
325
+ // Taskbar helpers – ABM_GETTASKBARPOS / ABM_GETSTATE via SHAppBarMessage
326
+ // -----------------------------------------------------------------------------
327
+ private static TaskbarInfo GetTaskbarInfo ( DisplayArea area )
328
+ {
329
+ var data = new APPBARDATA
330
+ {
331
+ cbSize = ( uint ) Marshal . SizeOf < APPBARDATA > ( )
332
+ } ;
333
+
334
+ // Locate the taskbar.
335
+ if ( SHAppBarMessage ( ABM_GETTASKBARPOS , ref data ) == 0 )
336
+ return new TaskbarInfo ( TaskbarPosition . Bottom , 0 , false ) ; // failsafe
337
+
338
+ var autoHide = ( SHAppBarMessage ( ABM_GETSTATE , ref data ) & ABS_AUTOHIDE ) != 0 ;
339
+
340
+ // Use uEdge instead of guessing from the RECT.
341
+ var pos = data . uEdge switch
342
+ {
343
+ ABE_LEFT => TaskbarPosition . Left ,
344
+ ABE_TOP => TaskbarPosition . Top ,
345
+ ABE_RIGHT => TaskbarPosition . Right ,
346
+ _ => TaskbarPosition . Bottom , // ABE_BOTTOM or anything unexpected
347
+ } ;
348
+
349
+ // Thickness (gap) = shorter side of the rect.
350
+ var gap = ( pos == TaskbarPosition . Left || pos == TaskbarPosition . Right )
351
+ ? data . rc . right - data . rc . left // width
352
+ : data . rc . bottom - data . rc . top ; // height
353
+
354
+ return new TaskbarInfo ( pos , gap , autoHide ) ;
355
+ }
356
+
357
+ // ------------- P/Invoke plumbing -------------
358
+ private const uint ABM_GETTASKBARPOS = 0x0005 ;
359
+ private const uint ABM_GETSTATE = 0x0004 ;
360
+ private const int ABS_AUTOHIDE = 0x0001 ;
361
+
362
+ private const int ABE_LEFT = 0 ; // values returned in APPBARDATA.uEdge
363
+ private const int ABE_TOP = 1 ;
364
+ private const int ABE_RIGHT = 2 ;
365
+ private const int ABE_BOTTOM = 3 ;
366
+
367
+ [ StructLayout ( LayoutKind . Sequential ) ]
368
+ private struct APPBARDATA
369
+ {
370
+ public uint cbSize ;
371
+ public IntPtr hWnd ;
372
+ public uint uCallbackMessage ;
373
+ public uint uEdge ; // contains ABE_* value
374
+ public RECT rc ;
375
+ public int lParam ;
376
+ }
377
+
378
+ [ StructLayout ( LayoutKind . Sequential ) ]
379
+ private struct RECT
380
+ {
381
+ public int left , top , right , bottom ;
382
+ }
383
+
384
+ [ DllImport ( "shell32.dll" , CharSet = CharSet . Auto ) ]
385
+ private static extern uint SHAppBarMessage ( uint dwMessage , ref APPBARDATA pData ) ;
341
386
}
0 commit comments