@@ -33,8 +33,6 @@ public sealed partial class TrayWindow : Window
33
33
private int _lastWindowHeight ;
34
34
private Storyboard ? _currentSb ;
35
35
36
- private NativeApi . POINT ? _lastActivatePosition ;
37
-
38
36
private readonly IRpcController _rpcController ;
39
37
private readonly ICredentialManager _credentialManager ;
40
38
private readonly ISyncSessionController _syncSessionController ;
@@ -98,18 +96,18 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
98
96
WindowNative . GetWindowHandle ( this ) ) ) ;
99
97
SizeProxy . SizeChanged += ( _ , e ) =>
100
98
{
101
- if ( _currentSb is null ) return ; // nothing running
99
+ if ( _currentSb is null ) return ; // nothing running
102
100
103
- int newHeight = ( int ) Math . Round (
101
+ var newHeight = ( int ) Math . Round (
104
102
e . NewSize . Height * DisplayScale . WindowScale ( this ) ) ;
105
103
106
- int delta = newHeight - _lastWindowHeight ;
104
+ var delta = newHeight - _lastWindowHeight ;
107
105
if ( delta == 0 ) return ;
108
106
109
107
var pos = _aw . Position ;
110
108
var size = _aw . Size ;
111
109
112
- pos . Y -= delta ; // grow upward
110
+ pos . Y -= delta ; // grow upward
113
111
size . Height = newHeight ;
114
112
115
113
_aw . MoveAndResize (
@@ -225,7 +223,6 @@ private void OnStoryboardCompleted(object? sender, object e)
225
223
226
224
private void MoveResizeAndActivate ( )
227
225
{
228
- SaveCursorPos ( ) ;
229
226
var size = CalculateWindowSize ( RootFrame . GetContentSize ( ) . Height ) ;
230
227
var pos = CalculateWindowPosition ( size ) ;
231
228
var rect = new RectInt32 ( pos . X , pos . Y , size . Width , size . Height ) ;
@@ -234,18 +231,6 @@ private void MoveResizeAndActivate()
234
231
ForegroundWindow . MakeForeground ( this ) ;
235
232
}
236
233
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
234
private SizeInt32 CalculateWindowSize ( double height )
250
235
{
251
236
if ( height <= 0 ) height = 100 ; // will be resolved next frame typically
@@ -257,41 +242,44 @@ private SizeInt32 CalculateWindowSize(double height)
257
242
return new SizeInt32 ( newWidth , newHeight ) ;
258
243
}
259
244
260
- private PointInt32 CalculateWindowPosition ( SizeInt32 size )
245
+ private PointInt32 CalculateWindowPosition ( SizeInt32 panelSize )
261
246
{
262
- var width = size . Width ;
263
- var height = size . Height ;
264
-
265
- var cursorPosition = _lastActivatePosition ;
266
- if ( cursorPosition is null )
247
+ var area = DisplayArea . GetFromWindowId ( AppWindow . Id , DisplayAreaFallback . Primary ) ;
248
+ // whole monitor
249
+ var bounds = area . OuterBounds ;
250
+ // monitor minus taskbar
251
+ var workArea = area . WorkArea ;
252
+
253
+ // get taskbar details - position, gap (size), auto-hide
254
+ var tb = GetTaskbarInfo ( area ) ;
255
+
256
+ // safe edges where tray window can touch the screen
257
+ var safeRight = workArea . X + workArea . Width ;
258
+ var safeBottom = workArea . Y + workArea . Height ;
259
+
260
+ // if the taskbar is auto-hidden at the bottom, stay clear of its reveal band
261
+ if ( tb . Position == TaskbarPosition . Bottom && tb . AutoHide )
262
+ safeBottom -= tb . Gap ; // shift everything up by its thickness
263
+
264
+ // pick corner & position the panel
265
+ int x , y ;
266
+ switch ( tb . Position )
267
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 ;
278
-
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 ;
286
-
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 ;
268
+ case TaskbarPosition . Left : // for Left we will stick to the left-bottom corner
269
+ x = bounds . X + tb . Gap ; // just right of the bar
270
+ y = safeBottom - panelSize . Height ;
271
+ break ;
289
272
290
- // Adjust if the window goes off the left edge of the display (somehow).
291
- if ( x < workArea . X ) x = workArea . X ;
273
+ case TaskbarPosition . Top : // for Top we will stick to the top-right corner
274
+ x = safeRight - panelSize . Width ;
275
+ y = bounds . Y + tb . Gap ; // just below the bar
276
+ break ;
292
277
293
- // Adjust if the window goes off the top edge of the display (somehow).
294
- if ( y < workArea . Y ) y = workArea . Y ;
278
+ default : // Bottom or Right bar we will stick to the bottom-right corner
279
+ x = safeRight - panelSize . Width ;
280
+ y = safeBottom - panelSize . Height ;
281
+ break ;
282
+ }
295
283
296
284
return new PointInt32 ( x , y ) ;
297
285
}
@@ -342,4 +330,71 @@ public struct POINT
342
330
public int Y ;
343
331
}
344
332
}
333
+
334
+ internal enum TaskbarPosition { Left , Top , Right , Bottom }
335
+
336
+ internal readonly record struct TaskbarInfo ( TaskbarPosition Position , int Gap , bool AutoHide ) ;
337
+
338
+ // -----------------------------------------------------------------------------
339
+ // Taskbar helpers – ABM_GETTASKBARPOS / ABM_GETSTATE via SHAppBarMessage
340
+ // -----------------------------------------------------------------------------
341
+ private static TaskbarInfo GetTaskbarInfo ( DisplayArea area )
342
+ {
343
+ var data = new APPBARDATA
344
+ {
345
+ cbSize = ( uint ) Marshal . SizeOf < APPBARDATA > ( )
346
+ } ;
347
+
348
+ // Locate the taskbar.
349
+ if ( SHAppBarMessage ( ABM_GETTASKBARPOS , ref data ) == 0 )
350
+ return new TaskbarInfo ( TaskbarPosition . Bottom , 0 , false ) ; // failsafe
351
+
352
+ var autoHide = ( SHAppBarMessage ( ABM_GETSTATE , ref data ) & ABS_AUTOHIDE ) != 0 ;
353
+
354
+ // Use uEdge instead of guessing from the RECT.
355
+ var pos = data . uEdge switch
356
+ {
357
+ ABE_LEFT => TaskbarPosition . Left ,
358
+ ABE_TOP => TaskbarPosition . Top ,
359
+ ABE_RIGHT => TaskbarPosition . Right ,
360
+ _=> TaskbarPosition . Bottom , // ABE_BOTTOM or anything unexpected
361
+ } ;
362
+
363
+ // Thickness (gap) = shorter side of the rect.
364
+ var gap = ( pos == TaskbarPosition . Left || pos == TaskbarPosition . Right )
365
+ ? data . rc . right - data . rc . left // width
366
+ : data . rc . bottom - data . rc . top ; // height
367
+
368
+ return new TaskbarInfo ( pos , gap , autoHide ) ;
369
+ }
370
+
371
+ // ------------- P/Invoke plumbing -------------
372
+ private const uint ABM_GETTASKBARPOS = 0x0005 ;
373
+ private const uint ABM_GETSTATE = 0x0004 ;
374
+ private const int ABS_AUTOHIDE = 0x0001 ;
375
+
376
+ private const int ABE_LEFT = 0 ; // values returned in APPBARDATA.uEdge
377
+ private const int ABE_TOP = 1 ;
378
+ private const int ABE_RIGHT = 2 ;
379
+ private const int ABE_BOTTOM = 3 ;
380
+
381
+ [ StructLayout ( LayoutKind . Sequential ) ]
382
+ private struct APPBARDATA
383
+ {
384
+ public uint cbSize ;
385
+ public IntPtr hWnd ;
386
+ public uint uCallbackMessage ;
387
+ public uint uEdge ; // contains ABE_* value
388
+ public RECT rc ;
389
+ public int lParam ;
390
+ }
391
+
392
+ [ StructLayout ( LayoutKind . Sequential ) ]
393
+ private struct RECT
394
+ {
395
+ public int left , top , right , bottom ;
396
+ }
397
+
398
+ [ DllImport ( "shell32.dll" , CharSet = CharSet . Auto ) ]
399
+ private static extern uint SHAppBarMessage ( uint dwMessage , ref APPBARDATA pData ) ;
345
400
}