8000 feat: calculate taskbar position and display on top of it (#125) · coder/coder-desktop-windows@f566547 · GitHub
[go: up one dir, main page]

Skip to content

Commit f566547

Browse files
authored
feat: calculate taskbar position and display on top of it (#125)
Closes: #119
1 parent cf25db5 commit f566547

File tree

1 file changed

+105
-50
lines changed

1 file changed

+105
-50
lines changed

App/Views/TrayWindow.xaml.cs

Lines changed: 105 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
< 50DC span class=pl-k>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

Comments
 (0)
0