From 2c89316ffb3fdb344b913ad231bd2634b0def6ed Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Tue, 24 Jun 2025 18:49:59 +1000 Subject: [PATCH 1/4] fix: add GH_TOKEN to release workflow (#135) --- .github/workflows/release.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c4280b6..f9fa638 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -166,6 +166,7 @@ jobs: APPCAST_GCS_URI: gs://releases.coder.com/coder-desktop/windows/appcast.xml APPCAST_SIGNATURE_GCS_URI: gs://releases.coder.com/coder-desktop/windows/appcast.xml.signature APPCAST_SIGNATURE_KEY_BASE64: ${{ secrets.APPCAST_SIGNATURE_KEY_BASE64 }} + GH_TOKEN: ${{ github.token }} GCLOUD_ACCESS_TOKEN: ${{ steps.gcloud_auth.outputs.access_token }} winget: From cf25db591f42cf43d65452780bee47e641e28ab7 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 24 Jun 2025 11:20:45 +0200 Subject: [PATCH 2/4] feat: ui improvements & style unification (#124) Fixes: #120
Dark mode
traywindow_dark traywindow_dark
traywindow_dark
Light mode
traywindow_dark traywindow_dark
traywindow_dark
--------- Co-authored-by: Dean Sheather --- App/Views/DirectoryPickerWindow.xaml | 2 +- App/Views/DirectoryPickerWindow.xaml.cs | 2 -- App/Views/FileSyncListWindow.xaml | 2 +- App/Views/FileSyncListWindow.xaml.cs | 2 -- App/Views/Pages/DirectoryPickerMainPage.xaml | 3 +-- App/Views/Pages/FileSyncListMainPage.xaml | 3 +-- App/Views/Pages/SettingsMainPage.xaml | 4 ++-- App/Views/Pages/SignInTokenPage.xaml | 3 +-- App/Views/Pages/SignInUrlPage.xaml | 3 +-- App/Views/Pages/TrayWindowMainPage.xaml | 8 ++++++-- App/Views/SettingsWindow.xaml | 2 +- App/Views/SettingsWindow.xaml.cs | 2 -- App/Views/SignInWindow.xaml | 2 +- App/Views/SignInWindow.xaml.cs | 1 - App/Views/TrayWindow.xaml.cs | 2 -- 15 files changed, 16 insertions(+), 25 deletions(-) diff --git a/App/Views/DirectoryPickerWindow.xaml b/App/Views/DirectoryPickerWindow.xaml index 8a107cb..ce1623b 100644 --- a/App/Views/DirectoryPickerWindow.xaml +++ b/App/Views/DirectoryPickerWindow.xaml @@ -13,7 +13,7 @@ MinWidth="400" MinHeight="600"> - + diff --git a/App/Views/DirectoryPickerWindow.xaml.cs b/App/Views/DirectoryPickerWindow.xaml.cs index 7af6db3..d2eb320 100644 --- a/App/Views/DirectoryPickerWindow.xaml.cs +++ b/App/Views/DirectoryPickerWindow.xaml.cs @@ -19,8 +19,6 @@ public DirectoryPickerWindow(DirectoryPickerViewModel viewModel) InitializeComponent(); TitleBarIcon.SetTitlebarIcon(this); - SystemBackdrop = new DesktopAcrylicBackdrop(); - viewModel.Initialize(this, DispatcherQueue); RootFrame.Content = new DirectoryPickerMainPage(viewModel); diff --git a/App/Views/FileSyncListWindow.xaml b/App/Views/FileSyncListWindow.xaml index 070efd2..991d02a 100644 --- a/App/Views/FileSyncListWindow.xaml +++ b/App/Views/FileSyncListWindow.xaml @@ -13,7 +13,7 @@ MinWidth="1000" MinHeight="300"> - + diff --git a/App/Views/FileSyncListWindow.xaml.cs b/App/Views/FileSyncListWindow.xaml.cs index ccd2452..9d8510b 100644 --- a/App/Views/FileSyncListWindow.xaml.cs +++ b/App/Views/FileSyncListWindow.xaml.cs @@ -16,8 +16,6 @@ public FileSyncListWindow(FileSyncListViewModel viewModel) InitializeComponent(); TitleBarIcon.SetTitlebarIcon(this); - SystemBackdrop = new DesktopAcrylicBackdrop(); - ViewModel.Initialize(this, DispatcherQueue); RootFrame.Content = new FileSyncListMainPage(ViewModel); diff --git a/App/Views/Pages/DirectoryPickerMainPage.xaml b/App/Views/Pages/DirectoryPickerMainPage.xaml index dd08c46..0fbbaea 100644 --- a/App/Views/Pages/DirectoryPickerMainPage.xaml +++ b/App/Views/Pages/DirectoryPickerMainPage.xaml @@ -9,8 +9,7 @@ xmlns:converters="using:Coder.Desktop.App.Converters" xmlns:toolkit="using:CommunityToolkit.WinUI.Controls" xmlns:viewmodels="using:Coder.Desktop.App.ViewModels" - mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + mc:Ignorable="d"> + mc:Ignorable="d"> + mc:Ignorable="d"> + 4 diff --git a/App/Views/Pages/SignInTokenPage.xaml b/App/Views/Pages/SignInTokenPage.xaml index 0ca754d..7f20b69 100644 --- a/App/Views/Pages/SignInTokenPage.xaml +++ b/App/Views/Pages/SignInTokenPage.xaml @@ -6,8 +6,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + mc:Ignorable="d"> + mc:Ignorable="d"> + + + + - + diff --git a/App/Views/SettingsWindow.xaml.cs b/App/Views/SettingsWindow.xaml.cs index 7cc9661..f2a0fdb 100644 --- a/App/Views/SettingsWindow.xaml.cs +++ b/App/Views/SettingsWindow.xaml.cs @@ -16,8 +16,6 @@ public SettingsWindow(SettingsViewModel viewModel) InitializeComponent(); TitleBarIcon.SetTitlebarIcon(this); - SystemBackdrop = new DesktopAcrylicBackdrop(); - RootFrame.Content = new SettingsMainPage(ViewModel); this.CenterOnScreen(); diff --git a/App/Views/SignInWindow.xaml b/App/Views/SignInWindow.xaml index d2c1326..6d8340c 100644 --- a/App/Views/SignInWindow.xaml +++ b/App/Views/SignInWindow.xaml @@ -11,7 +11,7 @@ Title="Sign in to Coder"> - + diff --git a/App/Views/SignInWindow.xaml.cs b/App/Views/SignInWindow.xaml.cs index 2acd0a5..da68867 100644 --- a/App/Views/SignInWindow.xaml.cs +++ b/App/Views/SignInWindow.xaml.cs @@ -24,7 +24,6 @@ public SignInWindow(SignInViewModel viewModel) { InitializeComponent(); TitleBarIcon.SetTitlebarIcon(this); - SystemBackdrop = new DesktopAcrylicBackdrop(); RootFrame.SizeChanged += RootFrame_SizeChanged; _signInUrlPage = new SignInUrlPage(this, viewModel); diff --git a/App/Views/TrayWindow.xaml.cs b/App/Views/TrayWindow.xaml.cs index e505511..a682b9b 100644 --- a/App/Views/TrayWindow.xaml.cs +++ b/App/Views/TrayWindow.xaml.cs @@ -9,7 +9,6 @@ using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Animation; using System; using System.Runtime.InteropServices; @@ -62,7 +61,6 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan InitializeComponent(); AppWindow.Hide(); - SystemBackdrop = new DesktopAcrylicBackdrop(); Activated += Window_Activated; RootFrame.SizeChanged += RootFrame_SizeChanged; From f566547f3dc1b9d48700554a96cf413cbd9bbd01 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 24 Jun 2025 13:33:16 +0200 Subject: [PATCH 3/4] feat: calculate taskbar position and display on top of it (#125) Closes: #119 --- App/Views/TrayWindow.xaml.cs | 155 ++++++++++++++++++++++++----------- 1 file changed, 105 insertions(+), 50 deletions(-) diff --git a/App/Views/TrayWindow.xaml.cs b/App/Views/TrayWindow.xaml.cs index a682b9b..6131e25 100644 --- a/App/Views/TrayWindow.xaml.cs +++ b/App/Views/TrayWindow.xaml.cs @@ -33,8 +33,6 @@ public sealed partial class TrayWindow : Window private int _lastWindowHeight; private Storyboard? _currentSb; - private NativeApi.POINT? _lastActivatePosition; - private readonly IRpcController _rpcController; private readonly ICredentialManager _credentialManager; private readonly ISyncSessionController _syncSessionController; @@ -98,18 +96,18 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan WindowNative.GetWindowHandle(this))); SizeProxy.SizeChanged += (_, e) => { - if (_currentSb is null) return; // nothing running + if (_currentSb is null) return; // nothing running - int newHeight = (int)Math.Round( + var newHeight = (int)Math.Round( e.NewSize.Height * DisplayScale.WindowScale(this)); - int delta = newHeight - _lastWindowHeight; + var delta = newHeight - _lastWindowHeight; if (delta == 0) return; var pos = _aw.Position; var size = _aw.Size; - pos.Y -= delta; // grow upward + pos.Y -= delta; // grow upward size.Height = newHeight; _aw.MoveAndResize( @@ -225,7 +223,6 @@ private void OnStoryboardCompleted(object? sender, object e) private void MoveResizeAndActivate() { - SaveCursorPos(); var size = CalculateWindowSize(RootFrame.GetContentSize().Height); var pos = CalculateWindowPosition(size); var rect = new RectInt32(pos.X, pos.Y, size.Width, size.Height); @@ -234,18 +231,6 @@ private void MoveResizeAndActivate() ForegroundWindow.MakeForeground(this); } - private void SaveCursorPos() - { - var res = NativeApi.GetCursorPos(out var cursorPosition); - if (res) - _lastActivatePosition = cursorPosition; - else - // When the cursor position is null, we will spawn the window in - // the bottom right corner of the primary display. - // TODO: log(?) an error when this happens - _lastActivatePosition = null; - } - private SizeInt32 CalculateWindowSize(double height) { if (height <= 0) height = 100; // will be resolved next frame typically @@ -257,41 +242,44 @@ private SizeInt32 CalculateWindowSize(double height) return new SizeInt32(newWidth, newHeight); } - private PointInt32 CalculateWindowPosition(SizeInt32 size) + private PointInt32 CalculateWindowPosition(SizeInt32 panelSize) { - var width = size.Width; - var height = size.Height; - - var cursorPosition = _lastActivatePosition; - if (cursorPosition is null) + var area = DisplayArea.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Primary); + // whole monitor + var bounds = area.OuterBounds; + // monitor minus taskbar + var workArea = area.WorkArea; + + // get taskbar details - position, gap (size), auto-hide + var tb = GetTaskbarInfo(area); + + // safe edges where tray window can touch the screen + var safeRight = workArea.X + workArea.Width; + var safeBottom = workArea.Y + workArea.Height; + + // if the taskbar is auto-hidden at the bottom, stay clear of its reveal band + if (tb.Position == TaskbarPosition.Bottom && tb.AutoHide) + safeBottom -= tb.Gap; // shift everything up by its thickness + + // pick corner & position the panel + int x, y; + switch (tb.Position) { - var primaryWorkArea = DisplayArea.Primary.WorkArea; - return new PointInt32( - primaryWorkArea.Width - width, - primaryWorkArea.Height - height - ); - } - - // Spawn the window to the top right of the cursor. - var x = cursorPosition.Value.X + 10; - var y = cursorPosition.Value.Y - 10 - height; - - var workArea = DisplayArea.GetFromPoint( - new PointInt32(cursorPosition.Value.X, cursorPosition.Value.Y), - DisplayAreaFallback.Primary - ).WorkArea; - - // Adjust if the window goes off the right edge of the display. - if (x + width > workArea.X + workArea.Width) x = workArea.X + workArea.Width - width; - - // Adjust if the window goes off the bottom edge of the display. - if (y + height > workArea.Y + workArea.Height) y = workArea.Y + workArea.Height - height; + case TaskbarPosition.Left: // for Left we will stick to the left-bottom corner + x = bounds.X + tb.Gap; // just right of the bar + y = safeBottom - panelSize.Height; + break; - // Adjust if the window goes off the left edge of the display (somehow). - if (x < workArea.X) x = workArea.X; + case TaskbarPosition.Top: // for Top we will stick to the top-right corner + x = safeRight - panelSize.Width; + y = bounds.Y + tb.Gap; // just below the bar + break; - // Adjust if the window goes off the top edge of the display (somehow). - if (y < workArea.Y) y = workArea.Y; + default: // Bottom or Right bar we will stick to the bottom-right corner + x = safeRight - panelSize.Width; + y = safeBottom - panelSize.Height; + break; + } return new PointInt32(x, y); } @@ -342,4 +330,71 @@ public struct POINT public int Y; } } + + internal enum TaskbarPosition { Left, Top, Right, Bottom } + + internal readonly record struct TaskbarInfo(TaskbarPosition Position, int Gap, bool AutoHide); + + // ----------------------------------------------------------------------------- + // Taskbar helpers – ABM_GETTASKBARPOS / ABM_GETSTATE via SHAppBarMessage + // ----------------------------------------------------------------------------- + private static TaskbarInfo GetTaskbarInfo(DisplayArea area) + { + var data = new APPBARDATA + { + cbSize = (uint)Marshal.SizeOf() + }; + + // Locate the taskbar. + if (SHAppBarMessage(ABM_GETTASKBARPOS, ref data) == 0) + return new TaskbarInfo(TaskbarPosition.Bottom, 0, false); // failsafe + + var autoHide = (SHAppBarMessage(ABM_GETSTATE, ref data) & ABS_AUTOHIDE) != 0; + + // Use uEdge instead of guessing from the RECT. + var pos = data.uEdge switch + { + ABE_LEFT => TaskbarPosition.Left, + ABE_TOP => TaskbarPosition.Top, + ABE_RIGHT => TaskbarPosition.Right, + _ => TaskbarPosition.Bottom, // ABE_BOTTOM or anything unexpected + }; + + // Thickness (gap) = shorter side of the rect. + var gap = (pos == TaskbarPosition.Left || pos == TaskbarPosition.Right) + ? data.rc.right - data.rc.left // width + : data.rc.bottom - data.rc.top; // height + + return new TaskbarInfo(pos, gap, autoHide); + } + + // ------------- P/Invoke plumbing ------------- + private const uint ABM_GETTASKBARPOS = 0x0005; + private const uint ABM_GETSTATE = 0x0004; + private const int ABS_AUTOHIDE = 0x0001; + + private const int ABE_LEFT = 0; // values returned in APPBARDATA.uEdge + private const int ABE_TOP = 1; + private const int ABE_RIGHT = 2; + private const int ABE_BOTTOM = 3; + + [StructLayout(LayoutKind.Sequential)] + private struct APPBARDATA + { + public uint cbSize; + public IntPtr hWnd; + public uint uCallbackMessage; + public uint uEdge; // contains ABE_* value + public RECT rc; + public int lParam; + } + + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int left, top, right, bottom; + } + + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + private static extern uint SHAppBarMessage(uint dwMessage, ref APPBARDATA pData); } From 6bc4b1eff33a6f2bdb2ebda1f1c7563a2657ff38 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Wed, 25 Jun 2025 16:07:28 +1000 Subject: [PATCH 4/4] fix: use windows-2025 in release job (#136) --- .github/workflows/release.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f9fa638..55f6da6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,7 +17,9 @@ permissions: jobs: release: - runs-on: ${{ github.repository_owner == 'coder' && 'windows-latest-16-cores' || 'windows-latest' }} + # windows-2025 is required for an up-to-date version of OpenSSL for the + # appcast generation. + runs-on: ${{ github.repository_owner == 'coder' && 'windows-2025-16-cores' || 'windows-2025' }} outputs: version: ${{ steps.version.outputs.VERSION }} timeout-minutes: 15 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy