From 0b4c53071e628beed0d0160d43e22604a5981aa5 Mon Sep 17 00:00:00 2001 From: DaveT1991 Date: Thu, 9 Apr 2026 17:37:45 -0400 Subject: [PATCH 1/4] Add .opus support to Peek audio previewer --- .../Previewers/MediaPreviewer/AudioPreviewer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/MediaPreviewer/AudioPreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/MediaPreviewer/AudioPreviewer.cs index a3721a04ec34..d8a7988b70f2 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/MediaPreviewer/AudioPreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/MediaPreviewer/AudioPreviewer.cs @@ -202,6 +202,7 @@ public void Unload() ".m4a", ".mp3", ".ogg", + ".opus", ".wav", ".wma", }; From db25c93b3317182589c43d4e1f5042bc0e3f48c9 Mon Sep 17 00:00:00 2001 From: DaveT1991 Date: Thu, 9 Apr 2026 21:21:15 -0400 Subject: [PATCH 2/4] =?UTF-8?q?Quick=20Accent:=20remove=20=C9=99=20and=20?= =?UTF-8?q?=E2=82=AC=20from=20Italian=20(IT)=20character=20set=20for=20'e'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The schwa (ə) is an IPA phonetic symbol, not an Italian accented letter, and is already covered by the IPA language set. The Euro sign (€) is a currency symbol and does not belong in a language-specific accent set. Italian only uses è and é as accented forms of the letter e. Fixes #46857 --- src/modules/poweraccent/PowerAccent.Core/Languages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 2a31d2481502..368591b17bf9 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -769,7 +769,7 @@ private static string[] GetDefaultLetterKeyIT(LetterKey letter) return letter switch { LetterKey.VK_A => new[] { "à" }, - LetterKey.VK_E => new[] { "è", "é", "ə", "€" }, + LetterKey.VK_E => new[] { "è", "é" }, LetterKey.VK_I => new[] { "ì", "í" }, LetterKey.VK_O => new[] { "ò", "ó" }, LetterKey.VK_U => new[] { "ù", "ú" }, From d91304b5c7142725ff1acc8b223974e476c6a67b Mon Sep 17 00:00:00 2001 From: DaveT1991 Date: Thu, 9 Apr 2026 21:28:24 -0400 Subject: [PATCH 3/4] Mouse Without Borders: deduplicate machine entries in device layout When loading the machine matrix, two scenarios could produce a duplicate machine slot that the user had no way to remove: 1. MachinePool stored the same machine name under multiple IDs (e.g. after a reconnect with a new ID). The available-machine list was built without deduplication, so both entries could end up racing for the same slot. 2. A matrix persisted from an earlier session already contained a duplicate name. The removal loop only checked availability against MachinePool, so the duplicate passed through and was preserved on every reload. Fix both by: - Deduplicating the available-machine list as it is built from MachinePool (case-insensitive, matching the rest of the codebase). - Tracking seen names in a HashSet while iterating the saved matrix and clearing any slot whose name has already appeared earlier. - Making all Contains comparisons case-insensitive for consistency. Fixes #46858 --- .../MouseWithoutBordersViewModel.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs index 447c62a6dd3e..87f4fa2a0a5d 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs @@ -589,20 +589,33 @@ private void LoadMachineMatrixString() if (!string.IsNullOrEmpty(Settings.Properties.MachinePool?.Value)) { - List availableMachines = new List(); - // Format of this field is "NAME1:ID1,NAME2:ID2,..." - // Load the available machines + // Build a deduplicated list of available machine names so that a machine + // registered under multiple IDs doesn't produce duplicate layout slots. + var seenPool = new HashSet(StringComparer.OrdinalIgnoreCase); + List availableMachines = new List(); foreach (string availableMachineIdPair in Settings.Properties.MachinePool.Value.Split(",")) { string availableMachineName = availableMachineIdPair.Split(':')[0]; - availableMachines.Add(availableMachineName); + if (seenPool.Add(availableMachineName)) + { + availableMachines.Add(availableMachineName); + } } - // Start by removing the machines from the matrix that are no longer available to pick. + // Remove machines from the matrix that are no longer available, and clear + // any duplicate entries that may have been persisted in earlier versions. + var seenInMatrix = new HashSet(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < loadMachineMatrixString.Count; i++) { - if (!availableMachines.Contains(loadMachineMatrixString[i])) + if (string.IsNullOrEmpty(loadMachineMatrixString[i])) + { + continue; + } + + bool stillAvailable = availableMachines.Contains(loadMachineMatrixString[i], StringComparer.OrdinalIgnoreCase); + bool firstOccurrence = seenInMatrix.Add(loadMachineMatrixString[i]); + if (!stillAvailable || !firstOccurrence) { editedTheMatrix = true; loadMachineMatrixString[i] = string.Empty; @@ -612,7 +625,7 @@ private void LoadMachineMatrixString() // If an available machine is not in the matrix already, fill it in the first available spot. foreach (string availableMachineName in availableMachines) { - if (!loadMachineMatrixString.Contains(availableMachineName)) + if (!loadMachineMatrixString.Contains(availableMachineName, StringComparer.OrdinalIgnoreCase)) { int availableIndex = loadMachineMatrixString.FindIndex(name => string.IsNullOrEmpty(name)); if (availableIndex >= 0) From 6954cd52edb474ecde95ac33d7fb0d17854dd8fe Mon Sep 17 00:00:00 2001 From: DaveT1991 Date: Thu, 9 Apr 2026 22:09:21 -0400 Subject: [PATCH 4/4] CmdPal Dock: fix snapped windows overlapping the dock surface The window position was expanded by SM_CXFRAME on all four sides to account for the invisible DWM resize border. This caused the visible dock surface to extend frameWidth pixels past the reserved app-bar rect (_appBarData.rc). Snap layouts respect the work-area boundary set by ABM_SETPOS, which matches _appBarData.rc, so snapped windows would partially cover the dock on the side facing the work area. Fix: only expand outward on the three sides that face away from the work area (off-screen or the screen edge). The side that faces the work area is kept exactly at _appBarData.rc so snapped windows abut it cleanly. Fixes #46849 --- .../Dock/DockWindow.xaml.cs | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs index 1adb4a6646f1..b09286a01768 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs @@ -428,12 +428,48 @@ private void UpdateWindowPosition() // PInvoke.SHAppBarMessage(ABM_SETSTATE, ref _appBarData); // PInvoke.SHAppBarMessage(PInvoke.ABM_SETAUTOHIDEBAR, ref _appBarData); - // Account for system borders when moving the window - // Adjust position to account for window frame/border - var adjustedLeft = _appBarData.rc.left - frameWidth; - var adjustedTop = _appBarData.rc.top - frameWidth; - var adjustedWidth = (_appBarData.rc.right - _appBarData.rc.left) + (2 * frameWidth); - var adjustedHeight = (_appBarData.rc.bottom - _appBarData.rc.top) + (2 * frameWidth); + // Expand the window by the invisible DWM frame on the sides that face + // away from the work area (off-screen or the screen edge). The edge + // that faces the work area must stay exactly at _appBarData.rc so that + // snapped windows do not overlap the visible dock surface. + int adjustedLeft, adjustedTop, adjustedWidth, adjustedHeight; + switch (_settings.Side) + { + case DockSide.Top: + // Bottom edge faces the work area — do not extend it. + adjustedLeft = _appBarData.rc.left - frameWidth; + adjustedTop = _appBarData.rc.top - frameWidth; + adjustedWidth = (_appBarData.rc.right - _appBarData.rc.left) + (2 * frameWidth); + adjustedHeight = (_appBarData.rc.bottom - _appBarData.rc.top) + frameWidth; + break; + case DockSide.Bottom: + // Top edge faces the work area — do not extend it. + adjustedLeft = _appBarData.rc.left - frameWidth; + adjustedTop = _appBarData.rc.top; + adjustedWidth = (_appBarData.rc.right - _appBarData.rc.left) + (2 * frameWidth); + adjustedHeight = (_appBarData.rc.bottom - _appBarData.rc.top) + frameWidth; + break; + case DockSide.Left: + // Right edge faces the work area — do not extend it. + adjustedLeft = _appBarData.rc.left - frameWidth; + adjustedTop = _appBarData.rc.top - frameWidth; + adjustedWidth = (_appBarData.rc.right - _appBarData.rc.left) + frameWidth; + adjustedHeight = (_appBarData.rc.bottom - _appBarData.rc.top) + (2 * frameWidth); + break; + case DockSide.Right: + // Left edge faces the work area — do not extend it. + adjustedLeft = _appBarData.rc.left; + adjustedTop = _appBarData.rc.top - frameWidth; + adjustedWidth = (_appBarData.rc.right - _appBarData.rc.left) + frameWidth; + adjustedHeight = (_appBarData.rc.bottom - _appBarData.rc.top) + (2 * frameWidth); + break; + default: + adjustedLeft = _appBarData.rc.left - frameWidth; + adjustedTop = _appBarData.rc.top - frameWidth; + adjustedWidth = (_appBarData.rc.right - _appBarData.rc.left) + (2 * frameWidth); + adjustedHeight = (_appBarData.rc.bottom - _appBarData.rc.top) + (2 * frameWidth); + break; + } // Move the actual window PInvoke.MoveWindow(