@@ -33,8 +33,6 @@ public sealed partial class TrayWindow : Window
3333 private int _lastWindowHeight ;
3434 private Storyboard ? _currentSb ;
3535
36- private NativeApi . POINT ? _lastActivatePosition ;
37-
3836 private readonly IRpcController _rpcController ;
3937 private readonly ICredentialManager _credentialManager ;
4038 private readonly ISyncSessionController _syncSessionController ;
@@ -98,18 +96,18 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
9896 WindowNative . GetWindowHandle ( this ) ) ) ;
9997 SizeProxy . SizeChanged += ( _ , e ) =>
10098 {
101- if ( _currentSb is null ) return ; // nothing running
99+ if ( _currentSb is null ) return ; // nothing running
102100
103- int newHeight = ( int ) Math . Round (
101+ var newHeight = ( int ) Math . Round (
104102 e . NewSize . Height * DisplayScale . WindowScale ( this ) ) ;
105103
106- int delta = newHeight - _lastWindowHeight ;
104+ var delta = newHeight - _lastWindowHeight ;
107105 if ( delta == 0 ) return ;
108106
109107 var pos = _aw . Position ;
110108 var size = _aw . Size ;
111109
112- pos . Y -= delta ; // grow upward
110+ pos . Y -= delta ; // grow upward
113111 size . Height = newHeight ;
114112
115113 _aw . MoveAndResize (
@@ -225,7 +223,6 @@ private void OnStoryboardCompleted(object? sender, object e)
225223
226224 private void MoveResizeAndActivate ( )
227225 {
228- SaveCursorPos ( ) ;
229226 var size = CalculateWindowSize ( RootFrame . GetContentSize ( ) . Height ) ;
230227 var pos = CalculateWindowPosition ( size ) ;
231228 var rect = new RectInt32 ( pos . X , pos . Y , size . Width , size . Height ) ;
@@ -234,18 +231,6 @@ private void MoveResizeAndActivate()
234231 ForegroundWindow . MakeForeground ( this ) ;
235232 }
236233
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-
249234 private SizeInt32 CalculateWindowSize ( double height )
250235 {
251236 if ( height <= 0 ) height = 100 ; // will be resolved next frame typically
@@ -257,41 +242,44 @@ private SizeInt32 CalculateWindowSize(double height)
257242 return new SizeInt32 ( newWidth , newHeight ) ;
258243 }
259244
260- private PointInt32 CalculateWindowPosition ( SizeInt32 size )
245+ private PointInt32 CalculateWindowPosition ( SizeInt32 panelSize )
261246 {
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 )
267267 {
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 ;
289272
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 ;
292277
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+ }
295283
296284 return new PointInt32 ( x , y ) ;
297285 }
@@ -342,4 +330,71 @@ public struct POINT
342330 public int Y ;
343331 }
344332 }
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 ) ;
345400}
0 commit comments