99using Microsoft . UI . Windowing ;
1010using Microsoft . UI . Xaml ;
1111using Microsoft . UI . Xaml . Controls ;
12- using Microsoft . UI . Xaml . Media ;
1312using Microsoft . UI . Xaml . Media . Animation ;
1413using System ;
15- using System . Diagnostics ;
14+ using System . Drawing . Printing ;
1615using System . Runtime . InteropServices ;
1716using Windows . Graphics ;
1817using Windows . System ;
@@ -34,8 +33,6 @@ public sealed partial class TrayWindow : Window
3433 private int _lastWindowHeight ;
3534 private Storyboard ? _currentSb ;
3635
37- private NativeApi . POINT ? _lastActivatePosition ;
38-
3936 private readonly IRpcController _rpcController ;
4037 private readonly ICredentialManager _credentialManager ;
4138 private readonly ISyncSessionController _syncSessionController ;
@@ -60,7 +57,6 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
6057
6158 InitializeComponent ( ) ;
6259 AppWindow . Hide ( ) ;
63- SystemBackdrop = new DesktopAcrylicBackdrop ( ) ;
6460 Activated += Window_Activated ;
6561 RootFrame . SizeChanged += RootFrame_SizeChanged ;
6662
@@ -97,18 +93,18 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
9793 WindowNative . GetWindowHandle ( this ) ) ) ;
9894 SizeProxy . SizeChanged += ( _ , e ) =>
9995 {
100- if ( _currentSb is null ) return ; // nothing running
96+ if ( _currentSb is null ) return ; // nothing running
10197
102- int newHeight = ( int ) Math . Round (
98+ var newHeight = ( int ) Math . Round (
10399 e . NewSize . Height * DisplayScale . WindowScale ( this ) ) ;
104100
105- int delta = newHeight - _lastWindowHeight ;
101+ var delta = newHeight - _lastWindowHeight ;
106102 if ( delta == 0 ) return ;
107103
108104 var pos = _aw . Position ;
109105 var size = _aw . Size ;
110106
111- pos . Y -= delta ; // grow upward
107+ pos . Y -= delta ; // grow upward
112108 size . Height = newHeight ;
113109
114110 _aw . MoveAndResize (
@@ -118,7 +114,6 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
118114 } ;
119115 }
120116
121-
122117 private void SetPageByState ( RpcModel rpcModel , CredentialModel credentialModel ,
123118 SyncSessionControllerStateModel syncSessionModel )
124119 {
@@ -225,7 +220,6 @@ private void OnStoryboardCompleted(object? sender, object e)
225220
226221 private void MoveResizeAndActivate ( )
227222 {
228- SaveCursorPos ( ) ;
229223 var size = CalculateWindowSize ( RootFrame . GetContentSize ( ) . Height ) ;
230224 var pos = CalculateWindowPosition ( size ) ;
231225 var rect = new RectInt32 ( pos . X , pos . Y , size . Width , size . Height ) ;
@@ -234,18 +228,6 @@ private void MoveResizeAndActivate()
234228 NativeApi . SetForegroundWindow ( WindowNative . GetWindowHandle ( this ) ) ;
235229 }
236230
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-
249231 private SizeInt32 CalculateWindowSize ( double height )
250232 {
251233 if ( height <= 0 ) height = 100 ; // will be resolved next frame typically
@@ -257,41 +239,38 @@ private SizeInt32 CalculateWindowSize(double height)
257239 return new SizeInt32 ( newWidth , newHeight ) ;
258240 }
259241
260- private PointInt32 CalculateWindowPosition ( SizeInt32 size )
242+ private PointInt32 CalculateWindowPosition ( SizeInt32 panelSize )
261243 {
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)
264247
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 ) ;
278249
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 ;
286257
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 ;
289262
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 ;
292268
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+ }
295274
296275 return new PointInt32 ( x , y ) ;
297276 }
@@ -338,4 +317,70 @@ public struct POINT
338317 public int Y ;
339318 }
340319 }
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 ) ;
341386}
0 commit comments