From 0a1759028d6dc7f87c46dfd7469b32299437e257 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Wed, 20 Sep 2023 12:46:36 +0200 Subject: [PATCH 01/39] [Hosts]Fix move up/down not being disabled when filtering (#28615) --- src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml b/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml index e889c0381ee..f62eef0a98a 100644 --- a/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml +++ b/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml @@ -199,7 +199,7 @@ + IsEnabled="{x:Bind ViewModel.Filtered, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> @@ -207,7 +207,7 @@ + IsEnabled="{x:Bind ViewModel.Filtered, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> From 5ddd8d72ddd2fb488ae517f190a967b36f681504 Mon Sep 17 00:00:00 2001 From: gokcekantarci <115616017+gokcekantarci@users.noreply.github.com> Date: Wed, 20 Sep 2023 20:30:32 +0300 Subject: [PATCH 02/39] [Peek]Always use default white background for HTML files(#28557) * [Peek] A check for color of body element in html and change it based on theme is added. * [Peek] WebView2 solution is added instead of javascript injection * [Peek] Detailed comments are added. --- .../Controls/BrowserControl.xaml.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs index 39f94af55ea..1fbe78a1660 100644 --- a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs +++ b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs @@ -24,6 +24,8 @@ public sealed partial class BrowserControl : UserControl, IDisposable /// private Uri? _navigatedUri; + private Color originalBackgroundColor; + public delegate void NavigationCompletedHandler(WebView2? sender, CoreWebView2NavigationCompletedEventArgs? args); public delegate void DOMContentLoadedHandler(CoreWebView2? sender, CoreWebView2DOMContentLoadedEventArgs? args); @@ -120,7 +122,11 @@ private async void PreviewWV2_Loaded(object sender, RoutedEventArgs e) { await PreviewBrowser.EnsureCoreWebView2Async(); - // transparent background when loading the page + // Storing the original background color so it can be reset later for specific file types like HTML. + originalBackgroundColor = PreviewBrowser.DefaultBackgroundColor; + + // Setting the background color to transparent when initially loading the WebView2 component. + // This ensures that non-HTML files are displayed with a transparent background. PreviewBrowser.DefaultBackgroundColor = Color.FromArgb(0, 0, 0, 0); PreviewBrowser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; @@ -150,6 +156,15 @@ private async void PreviewWV2_Loaded(object sender, RoutedEventArgs e) private void CoreWebView2_DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) { + // If the file being previewed is HTML or HTM, reset the background color to its original state. + // This is done to ensure that HTML and HTM files are displayed as intended, with their own background settings. + if (Source?.ToString().EndsWith(".html", StringComparison.OrdinalIgnoreCase) == true || + Source?.ToString().EndsWith(".htm", StringComparison.OrdinalIgnoreCase) == true) + { + // Reset to default behavior for HTML files + PreviewBrowser.DefaultBackgroundColor = originalBackgroundColor; + } + DOMContentLoaded?.Invoke(sender, args); } From 59f0ccebc7b360735b72651a733f9fffc7b85135 Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Wed, 20 Sep 2023 19:31:40 +0200 Subject: [PATCH 03/39] [PT Run > PluginAdditionalOptions] Refactoring and more settings types (#28601) * refactor existing code * fixes * fix combo box layout * improve layout * add more settings types * combined setting * enabled state * fix spelling * improve settings.json handling on null values * textbox improvements * rework xaml code * fix xaml style * spell fixes * spell fixes * update comment --- .../Plugins/Microsoft.Plugin.Shell/Main.cs | 6 +- .../PluginAdditionalOption.cs | 67 ++++++- .../PowerLauncherSettings.cs | 1 + .../SettingsXAML/Views/PowerLauncherPage.xaml | 168 +++++++++++++++++- .../PluginAdditionalOptionViewModel.cs | 75 +++++++- 5 files changed, 296 insertions(+), 21 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs index d458b7996a4..dfbdbc8450b 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs @@ -52,7 +52,7 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, ISavab { Key = "ShellCommandExecution", DisplayLabel = Resources.wox_shell_command_execution, - SelectionTypeValue = (int)PluginAdditionalOption.SelectionType.Combobox, + PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox, ComboBoxOptions = new List { Resources.run_command_in_command_prompt, @@ -60,7 +60,7 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, ISavab Resources.find_executable_file_and_run_it, Resources.run_command_in_windows_terminal, }, - Option = (int)_settings.Shell, + ComboBoxValue = (int)_settings.Shell, }, }; @@ -442,7 +442,7 @@ public void UpdateSettings(PowerLauncherPluginSettings settings) _settings.LeaveShellOpen = leaveShellOpen; var optionShell = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "ShellCommandExecution"); - shellOption = optionShell?.Option ?? shellOption; + shellOption = optionShell?.ComboBoxValue ?? shellOption; _settings.Shell = (ExecutionShell)shellOption; } diff --git a/src/settings-ui/Settings.UI.Library/PluginAdditionalOption.cs b/src/settings-ui/Settings.UI.Library/PluginAdditionalOption.cs index 362384c5346..54e0c7868e7 100644 --- a/src/settings-ui/Settings.UI.Library/PluginAdditionalOption.cs +++ b/src/settings-ui/Settings.UI.Library/PluginAdditionalOption.cs @@ -3,17 +3,28 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Microsoft.PowerToys.Settings.UI.Library { public class PluginAdditionalOption { - public enum SelectionType + public enum AdditionalOptionType { Checkbox = 0, Combobox = 1, + Textbox = 2, + Numberbox = 3, + CheckboxAndCombobox = 11, + CheckboxAndTextbox = 12, + CheckboxAndNumberbox = 13, } + /// + /// Gets or sets the layout type of the option in settings ui (Optional; Default is checkbox) + /// + public AdditionalOptionType PluginOptionType { get; set; } + public string Key { get; set; } public string DisplayLabel { get; set; } @@ -21,14 +32,64 @@ public enum SelectionType /// /// Gets or sets a value to show a description of this setting in the settings ui. (Optional) /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string DisplayDescription { get; set; } + /// + /// Gets or sets a value to show a label for the second setting if two combined settings are shown + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string SecondDisplayLabel { get; set; } + + /// + /// Gets or sets a value to show a description for the second setting in the settings ui if two combined settings are shown. (Optional) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string SecondDisplayDescription { get; set; } + + /// + /// Gets or sets a value indicating whether the checkbox is set or not set + /// public bool Value { get; set; } + public int ComboBoxValue { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public List ComboBoxOptions { get; set; } - public int Option { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string TextValue { get; set; } - public int SelectionTypeValue { get; set; } + /// + /// Gets or sets the value that specifies the maximum number of characters allowed for user input in the text box. (Optional; Default is 0 which means no limit.) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? TextBoxMaxLength { get; set; } + + public double NumberValue { get; set; } + + /// + /// Gets or sets a minimal value for the number box. (Optional; Default is Double.MinValue) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? NumberBoxMin { get; set; } + + /// + /// Gets or sets a maximal value for the number box. (Optional; Default is Double.MaxValue) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? NumberBoxMax { get; set; } + + /// + /// Gets or sets the value for small changes of the number box. (Optional; Default is 1) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? NumberBoxSmallChange { get; set; } + + /// + /// Gets or sets the value for large changes of the number box. (Optional; Default is 10) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? NumberBoxLargeChange { get; set; } } } diff --git a/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs b/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs index b1e4cec04d9..b85906a0010 100644 --- a/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs +++ b/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs @@ -33,6 +33,7 @@ public virtual void Save(ISettingsUtils settingsUtils) var options = new JsonSerializerOptions { WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; if (settingsUtils == null) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml index 94fd68d5bc7..9e62c422065 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml @@ -360,6 +360,7 @@ + - + CornerRadius="0" + Visibility="{x:Bind Path=ShowCheckBox, Converter={StaticResource BoolToVisibilityConverter}}"> + + + + + + + + + + + + + + + + + + IsChecked="{x:Bind Path=Value, Mode=TwoWay}" /> + + + + + + + + - - + IsChecked="{x:Bind Path=Value, Mode=TwoWay}" /> + + + + + + + + + + + + + + + _additionalOption.DisplayLabel; public string DisplayDescription => _additionalOption.DisplayDescription; + // Labels of second setting of combined types + public string SecondDisplayLabel => _additionalOption.SecondDisplayLabel; + + public string SecondDisplayDescription => _additionalOption.SecondDisplayDescription; + + // Bool checkbox setting + public bool ShowCheckBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Checkbox; + public bool Value { get => _additionalOption.Value; @@ -31,29 +40,83 @@ public bool Value { _additionalOption.Value = value; NotifyPropertyChanged(); + NotifyPropertyChanged(nameof(SecondSettingIsEnabled)); } } } + // ComboBox setting + public bool ShowComboBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Combobox && + _additionalOption.ComboBoxOptions != null && _additionalOption.ComboBoxOptions.Count > 0; + public List ComboBoxOptions => _additionalOption.ComboBoxOptions; - public int Option + public int ComboBoxValue + { + get => _additionalOption.ComboBoxValue; + set + { + if (value != _additionalOption.ComboBoxValue) + { + _additionalOption.ComboBoxValue = value; + NotifyPropertyChanged(); + } + } + } + + // TextBox setting + public bool ShowTextBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Textbox; + + public int TextBoxMaxLength => (_additionalOption.TextBoxMaxLength == null) ? 0 : _additionalOption.TextBoxMaxLength.Value; // 0 ist the default and means no limit. + + public string TextValue { - get => _additionalOption.Option; + get => _additionalOption.TextValue; set { - if (value != _additionalOption.Option) + if (value != _additionalOption.TextValue) { - _additionalOption.Option = value; + _additionalOption.TextValue = value; NotifyPropertyChanged(); } } } - public bool ShowComboBox => _additionalOption.SelectionTypeValue == (int)PluginAdditionalOption.SelectionType.Combobox && _additionalOption.ComboBoxOptions != null && _additionalOption.ComboBoxOptions.Count > 0; + // NumberBox setting + public bool ShowNumberBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Numberbox; + + public double NumberBoxMin => (_additionalOption.NumberBoxMin == null) ? double.MinValue : _additionalOption.NumberBoxMin.Value; + + public double NumberBoxMax => (_additionalOption.NumberBoxMax == null) ? double.MaxValue : _additionalOption.NumberBoxMax.Value; + + public double NumberBoxSmallChange => (_additionalOption.NumberBoxSmallChange == null) ? 1 : _additionalOption.NumberBoxSmallChange.Value; + + public double NumberBoxLargeChange => (_additionalOption.NumberBoxLargeChange == null) ? 10 : _additionalOption.NumberBoxLargeChange.Value; + + public double NumberValue + { + get => _additionalOption.NumberValue; + set + { + if (value != _additionalOption.NumberValue) + { + _additionalOption.NumberValue = value; + NotifyPropertyChanged(); + } + } + } + + // Show combined settings cards + public bool ShowCheckboxAndCombobox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.CheckboxAndCombobox; + + public bool ShowCheckboxAndTextbox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.CheckboxAndTextbox; + + public bool ShowCheckboxAndNumberbox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.CheckboxAndNumberbox; - public bool ShowCheckBox => _additionalOption.SelectionTypeValue == (int)PluginAdditionalOption.SelectionType.Checkbox; + // Enabled state of ComboBox, TextBox, NumberBox (If combined with checkbox then checkbox value decides it.) + public bool SecondSettingIsEnabled => (int)_additionalOption.PluginOptionType > 10 ? _additionalOption.Value : true; + // Handle property changes public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") From 8cd2b7cdc3fa0eeb4ae7ed175aa2d418aefb3fd5 Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Thu, 21 Sep 2023 11:11:51 +0300 Subject: [PATCH 04/39] [FancyZones] Fix handling newly created windows on Windows 11 (#28646) --- .../fancyzones/FancyZonesLib/FancyZones.cpp | 9 +-- .../FancyZonesLib/FancyZonesLib.vcxproj | 1 + .../FancyZonesLib.vcxproj.filters | 3 + .../FancyZonesWindowProcessing.cpp | 56 +++++++++++++++ .../FancyZonesWindowProcessing.h | 35 +-------- .../FancyZonesLib/WindowMouseSnap.cpp | 1 - .../fancyzones/FancyZonesLib/WindowUtils.cpp | 71 +++++++------------ .../fancyzones/FancyZonesLib/WindowUtils.h | 3 +- 8 files changed, 91 insertions(+), 88 deletions(-) create mode 100644 src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.cpp diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 76f99175d5d..7ddcd955c59 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -397,12 +398,6 @@ void FancyZones::WindowCreated(HWND window) noexcept return; } - const bool isCandidateForLastKnownZone = FancyZonesWindowUtils::IsCandidateForZoning(window); - if (!isCandidateForLastKnownZone) - { - return; - } - HMONITOR primary = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY); HMONITOR active = primary; @@ -994,7 +989,7 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept return false; } - if (FancyZonesSettings::settings().overrideSnapHotkeys && FancyZonesWindowUtils::IsCandidateForZoning(window)) + if (FancyZonesSettings::settings().overrideSnapHotkeys) { HMONITOR monitor = WorkAreaKeyFromWindow(window); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index 9bb26ccd958..a659a1839ec 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -103,6 +103,7 @@ ../pch.h + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index e8012868f2e..f97b95c5a9d 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -272,6 +272,9 @@ Source Files + + Source Files + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.cpp new file mode 100644 index 00000000000..102392ba91b --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.cpp @@ -0,0 +1,56 @@ +#include "pch.h" +#include "FancyZonesWindowProcessing.h" + +#include +#include +#include + +bool FancyZonesWindowProcessing::IsProcessable(HWND window) noexcept +{ + const bool isSplashScreen = FancyZonesWindowUtils::IsSplashScreen(window); + if (isSplashScreen) + { + return false; + } + + const bool windowMinimized = IsIconic(window); + if (windowMinimized) + { + return false; + } + + const bool standard = FancyZonesWindowUtils::IsStandardWindow(window); + if (!standard) + { + return false; + } + + // popup could be the window we don't want to snap: start menu, notification popup, tray window, etc. + // also, popup could be the windows we want to snap disregarding the "allowSnapPopupWindows" setting, e.g. Telegram + bool isPopup = FancyZonesWindowUtils::IsPopupWindow(window) && !FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(window); + if (isPopup && !FancyZonesSettings::settings().allowSnapPopupWindows) + { + return false; + } + + // allow child windows + auto hasOwner = FancyZonesWindowUtils::HasVisibleOwner(window); + if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows) + { + return false; + } + + if (FancyZonesWindowUtils::IsExcluded(window)) + { + return false; + } + + // Switch between virtual desktops results with posting same windows messages that also indicate + // creation of new window. We need to check if window being processed is on currently active desktop. + if (!VirtualDesktop::instance().IsWindowOnCurrentDesktop(window)) + { + return false; + } + + return true; +} diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.h index fc2108ffbb3..21fbcfbad2a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.h @@ -1,39 +1,6 @@ #pragma once -#include -#include - namespace FancyZonesWindowProcessing { - inline bool IsProcessable(HWND window) noexcept - { - const bool isSplashScreen = FancyZonesWindowUtils::IsSplashScreen(window); - if (isSplashScreen) - { - return false; - } - - const bool windowMinimized = IsIconic(window); - if (windowMinimized) - { - return false; - } - - // For windows that FancyZones shouldn't process (start menu, tray, popup menus) - // VirtualDesktopManager is unable to retrieve virtual desktop id and returns an error. - auto desktopId = VirtualDesktop::instance().GetDesktopId(window); - if (!desktopId.has_value()) - { - return false; - } - - // Switch between virtual desktops results with posting same windows messages that also indicate - // creation of new window. We need to check if window being processed is on currently active desktop. - if (!VirtualDesktop::instance().IsWindowOnCurrentDesktop(window)) - { - return false; - } - - return true; - } + bool IsProcessable(HWND window) noexcept; } \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp b/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp index c6b8f8ceddb..a69aaa7ef72 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp @@ -32,7 +32,6 @@ WindowMouseSnap::~WindowMouseSnap() std::unique_ptr WindowMouseSnap::Create(HWND window, const std::unordered_map>& activeWorkAreas) { if (!FancyZonesWindowProcessing::IsProcessable(window) || - !FancyZonesWindowUtils::IsCandidateForZoning(window) || FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) { return nullptr; diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp index dbad77732c3..ef895cf830f 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp @@ -166,6 +166,8 @@ bool FancyZonesWindowUtils::HasVisibleOwner(HWND window) noexcept bool FancyZonesWindowUtils::IsStandardWindow(HWND window) { + // True if from the styles the window looks like a standard window + if (GetAncestor(window, GA_ROOT) != window) { return false; @@ -181,13 +183,6 @@ bool FancyZonesWindowUtils::IsStandardWindow(HWND window) return false; } - std::array class_name; - GetClassNameA(window, class_name.data(), static_cast(class_name.size())); - if (is_system_window(window, class_name.data())) - { - return false; - } - return true; } @@ -203,44 +198,6 @@ bool FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(HWND window) return ((style & WS_THICKFRAME) == WS_THICKFRAME && (style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX && (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX); } -bool FancyZonesWindowUtils::IsCandidateForZoning(HWND window) -{ - bool isStandard = IsStandardWindow(window); - if (!isStandard) - { - return false; - } - - // popup could be the window we don't want to snap: start menu, notification popup, tray window, etc. - // also, popup could be the windows we want to snap disregarding the "allowSnapPopupWindows" setting, e.g. Telegram - bool isPopup = IsPopupWindow(window); - if (isPopup && !HasThickFrameAndMinimizeMaximizeButtons(window) && !FancyZonesSettings::settings().allowSnapPopupWindows) - { - return false; - } - - // allow child windows - auto hasOwner = HasVisibleOwner(window); - if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows) - { - return false; - } - - std::wstring processPath = get_process_path_waiting_uwp(window); - CharUpperBuffW(const_cast(processPath).data(), static_cast(processPath.length())); - if (IsExcludedByUser(window, processPath)) - { - return false; - } - - if (IsExcludedByDefault(window, processPath)) - { - return false; - } - - return true; -} - bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window) { DWORD pid = 0; @@ -268,6 +225,23 @@ bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window) return false; } +bool FancyZonesWindowUtils::IsExcluded(HWND window) +{ + std::wstring processPath = get_process_path_waiting_uwp(window); + CharUpperBuffW(const_cast(processPath).data(), static_cast(processPath.length())); + if (IsExcludedByUser(window, processPath)) + { + return true; + } + + if (IsExcludedByDefault(window, processPath)) + { + return true; + } + + return false; +} + bool FancyZonesWindowUtils::IsExcludedByUser(const HWND& hwnd, std::wstring& processPath) noexcept { return (check_excluded_app(hwnd, processPath, FancyZonesSettings::settings().excludedAppsArray)); @@ -281,6 +255,13 @@ bool FancyZonesWindowUtils::IsExcludedByDefault(const HWND& hwnd, std::wstring& return true; } + std::array class_name; + GetClassNameA(hwnd, class_name.data(), static_cast(class_name.size())); + if (is_system_window(hwnd, class_name.data())) + { + return true; + } + static std::vector defaultExcludedApps = { NonLocalizable::PowerToysAppFZEditor, NonLocalizable::CoreWindow, NonLocalizable::SearchUI }; return (check_excluded_app(hwnd, processPath, defaultExcludedApps)); } diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h index 9659d78b9cb..4ac973bacba 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h @@ -21,8 +21,9 @@ namespace FancyZonesWindowUtils bool IsStandardWindow(HWND window); bool IsPopupWindow(HWND window) noexcept; bool HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept; - bool IsCandidateForZoning(HWND window); bool IsProcessOfWindowElevated(HWND window); // If HWND is already dead, we assume it wasn't elevated + + bool IsExcluded(HWND window); bool IsExcludedByUser(const HWND& hwnd, std::wstring& processPath) noexcept; bool IsExcludedByDefault(const HWND& hwnd, std::wstring& processPath) noexcept; From 890b7f4286a95ced04d7da140b474f90fd4351ed Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Thu, 21 Sep 2023 13:47:56 +0300 Subject: [PATCH 05/39] [FancyZones]Fix for the scenario of layout reset when opening the FZEditor (#28556) * rename * moved applied layouts tests * changed work area id comparison * changed save * changed apply * changed clone * sync applied layouts * save last used vd * replace parent work area ids * proper time for sync * sync layouts considering last used virtual desktop * use ids from work areas on editor opening * update applied layouts tests * sync app zone history vd * fix test * release build fix * app zone history comparison * pass last used vd to sync * clean up unused * dpi unaware values * update GUID_NULL * use registry values only * added more tests * fix failing scenario * added replace condition to zone history * sync time * log * spellcheck * fix pch in project * fixed cloning layout --- .../FancyZonesLib/EditorParameters.cpp | 113 ++-- .../FancyZonesLib/EditorParameters.h | 5 +- .../fancyzones/FancyZonesLib/FancyZones.cpp | 82 +-- .../fancyzones/FancyZonesLib/FancyZonesData.h | 8 +- .../FancyZonesData/AppZoneHistory.cpp | 63 +-- .../FancyZonesData/AppZoneHistory.h | 12 +- .../FancyZonesData/AppliedLayouts.cpp | 147 ++---- .../FancyZonesData/AppliedLayouts.h | 10 +- .../FancyZonesData/LastUsedVirtualDesktop.cpp | 78 +++ .../FancyZonesData/LastUsedVirtualDesktop.h | 41 ++ .../FancyZonesLib/FancyZonesDataTypes.h | 11 +- .../FancyZonesLib/FancyZonesLib.vcxproj | 8 +- .../FancyZonesLib.vcxproj.filters | 10 +- .../FancyZonesLib/VirtualDesktop.cpp | 22 +- .../fancyzones/FancyZonesLib/VirtualDesktop.h | 2 - .../fancyzones/FancyZonesLib/WorkArea.cpp | 8 +- ...kAreaMap.cpp => WorkAreaConfiguration.cpp} | 36 +- ...rWorkAreaMap.h => WorkAreaConfiguration.h} | 15 +- .../UnitTests/AppZoneHistoryTests.Spec.cpp | 155 ++++++ .../UnitTests/AppliedLayoutsTests.Spec.cpp | 493 +++++++++++------- .../UnitTests/WorkAreaIdTests.Spec.cpp | 10 +- 21 files changed, 847 insertions(+), 482 deletions(-) create mode 100644 src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.cpp create mode 100644 src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.h rename src/modules/fancyzones/FancyZonesLib/{MonitorWorkAreaMap.cpp => WorkAreaConfiguration.cpp} (57%) rename src/modules/fancyzones/FancyZonesLib/{MonitorWorkAreaMap.h => WorkAreaConfiguration.h} (78%) diff --git a/src/modules/fancyzones/FancyZonesLib/EditorParameters.cpp b/src/modules/fancyzones/FancyZonesLib/EditorParameters.cpp index 26397eb1ba0..b7a7ea4523c 100644 --- a/src/modules/fancyzones/FancyZonesLib/EditorParameters.cpp +++ b/src/modules/fancyzones/FancyZonesLib/EditorParameters.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,31 @@ namespace JsonUtils int monitorHeight{}; bool isSelected = false; + bool FillFromWorkArea(const WorkArea* const workArea) + { + const auto virtualDesktopIdStr = FancyZonesUtils::GuidToString(workArea->UniqueId().virtualDesktopId); + if (!virtualDesktopIdStr) + { + return false; + } + + const auto& monitorId = workArea->UniqueId().monitorId; + monitorName = monitorId.deviceId.id; + monitorInstanceId = monitorId.deviceId.instanceId; + monitorNumber = monitorId.deviceId.number; + monitorSerialNumber = monitorId.serialNumber; + + const auto& rect = workArea->GetWorkAreaRect(); + top = rect.top(); + left = rect.left(); + workAreaWidth = rect.width(); + workAreaHeight = rect.height(); + + virtualDesktop = virtualDesktopIdStr.value(); + + return true; + } + static json::JsonObject ToJson(const MonitorInfo& monitor) { json::JsonObject result{}; @@ -84,21 +110,8 @@ namespace JsonUtils }; } -bool EditorParameters::Save() noexcept +bool EditorParameters::Save(const WorkAreaConfiguration& configuration, OnThreadExecutor& dpiUnawareThread) noexcept { - const auto virtualDesktopIdStr = FancyZonesUtils::GuidToString(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - if (!virtualDesktopIdStr) - { - Logger::error(L"Save editor params: invalid virtual desktop id"); - return false; - } - - OnThreadExecutor dpiUnawareThread; - dpiUnawareThread.submit(OnThreadExecutor::task_t{ [] { - SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); - SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED); - } }).wait(); - const bool spanZonesAcrossMonitors = FancyZonesSettings::settings().spanZonesAcrossMonitors; JsonUtils::EditorArgs argsJson; @@ -107,23 +120,31 @@ bool EditorParameters::Save() noexcept if (spanZonesAcrossMonitors) { + const auto& workArea = configuration.GetWorkArea(nullptr); + if (!workArea) + { + return false; + } + + JsonUtils::MonitorInfo monitorJson; + if (!monitorJson.FillFromWorkArea(workArea)) + { + return false; + } + RECT combinedWorkArea; - dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&]() { - combinedWorkArea = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>(); - + dpiUnawareThread.submit(OnThreadExecutor::task_t{ + [&]() { + combinedWorkArea = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>(); } }).wait(); - RECT combinedMonitorArea = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcMonitor>(); - JsonUtils::MonitorInfo monitorJson; - monitorJson.monitorName = ZonedWindowProperties::MultiMonitorName; - monitorJson.monitorInstanceId = ZonedWindowProperties::MultiMonitorInstance; - monitorJson.monitorNumber = 0; - monitorJson.virtualDesktop = virtualDesktopIdStr.value(); + // use dpi-unaware values monitorJson.top = combinedWorkArea.top; monitorJson.left = combinedWorkArea.left; monitorJson.workAreaWidth = combinedWorkArea.right - combinedWorkArea.left; monitorJson.workAreaHeight = combinedWorkArea.bottom - combinedWorkArea.top; + monitorJson.monitorWidth = combinedMonitorArea.right - combinedMonitorArea.left; monitorJson.monitorHeight = combinedMonitorArea.bottom - combinedMonitorArea.top; monitorJson.isSelected = true; @@ -133,8 +154,6 @@ bool EditorParameters::Save() noexcept } else { - auto monitors = MonitorUtils::IdentifyMonitors(); - HMONITOR targetMonitor{}; if (FancyZonesSettings::settings().use_cursorpos_editor_startupscreen) { @@ -153,31 +172,16 @@ bool EditorParameters::Save() noexcept return false; } - for (auto& monitorData : monitors) + const auto& config = configuration.GetAllWorkAreas(); + for (auto& [monitor, workArea] : config) { - HMONITOR monitor = monitorData.monitor; - - MONITORINFOEX monitorInfo{}; - dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { - monitorInfo.cbSize = sizeof(monitorInfo); - if (!GetMonitorInfo(monitor, &monitorInfo)) - { - return; - } - } }).wait(); - JsonUtils::MonitorInfo monitorJson; - - if (monitor == targetMonitor) + if (!monitorJson.FillFromWorkArea(workArea.get())) { - monitorJson.isSelected = true; /* Is monitor selected for the main editor window opening */ + continue; } - monitorJson.monitorName = monitorData.deviceId.id; - monitorJson.monitorInstanceId = monitorData.deviceId.instanceId; - monitorJson.monitorNumber = monitorData.deviceId.number; - monitorJson.monitorSerialNumber = monitorData.serialNumber; - monitorJson.virtualDesktop = virtualDesktopIdStr.value(); + monitorJson.isSelected = monitor == targetMonitor; /* Is monitor selected for the main editor window opening */ UINT dpi = 0; if (DPIAware::GetScreenDPIForMonitor(monitor, dpi) != S_OK) @@ -186,10 +190,15 @@ bool EditorParameters::Save() noexcept } monitorJson.dpi = dpi; - monitorJson.top = monitorInfo.rcWork.top; - monitorJson.left = monitorInfo.rcWork.left; - monitorJson.workAreaWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left; - monitorJson.workAreaHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + + MONITORINFOEX monitorInfo{}; + dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { + monitorInfo.cbSize = sizeof(monitorInfo); + if (!GetMonitorInfo(monitor, &monitorInfo)) + { + return; + } + } }).wait(); float width = static_cast(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left); float height = static_cast(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top); @@ -198,6 +207,12 @@ bool EditorParameters::Save() noexcept monitorJson.monitorWidth = static_cast(std::roundf(width)); monitorJson.monitorHeight = static_cast(std::roundf(height)); + // use dpi-unaware values + monitorJson.top = monitorInfo.rcWork.top; + monitorJson.left = monitorInfo.rcWork.left; + monitorJson.workAreaWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left; + monitorJson.workAreaHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + argsJson.monitors.emplace_back(std::move(monitorJson)); } } diff --git a/src/modules/fancyzones/FancyZonesLib/EditorParameters.h b/src/modules/fancyzones/FancyZonesLib/EditorParameters.h index 70124613d63..74415e9c289 100644 --- a/src/modules/fancyzones/FancyZonesLib/EditorParameters.h +++ b/src/modules/fancyzones/FancyZonesLib/EditorParameters.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + namespace NonLocalizable { namespace EditorParametersIds @@ -26,5 +29,5 @@ namespace NonLocalizable class EditorParameters { public: - static bool Save() noexcept; + static bool Save(const WorkAreaConfiguration& configuration, OnThreadExecutor& dpiUnawareThread) noexcept; }; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 7ddcd955c59..d2314d78169 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -30,6 +30,7 @@ #include #include #include +#include enum class DisplayChangeType { @@ -88,6 +89,7 @@ struct FancyZones : public winrt::implements m_windowMouseSnapper{}; WindowKeyboardSnap m_windowKeyboardSnapper{}; - MonitorWorkAreaMap m_workAreaHandler; + WorkAreaConfiguration m_workAreaConfiguration; DraggingState m_draggingState; wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on @@ -254,6 +256,7 @@ FancyZones::Run() noexcept } }); + VirtualDesktop::instance().UpdateVirtualDesktopId(); SyncVirtualDesktops(); // id format of applied-layouts and app-zone-history was changed in 0.60 @@ -268,7 +271,7 @@ FancyZones::Run() noexcept IFACEMETHODIMP_(void) FancyZones::Destroy() noexcept { - m_workAreaHandler.Clear(); + m_workAreaConfiguration.Clear(); BufferedPaintUnInit(); if (m_window) { @@ -290,7 +293,7 @@ FancyZones::VirtualDesktopChanged() noexcept void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor) { - m_windowMouseSnapper = WindowMouseSnap::Create(window, m_workAreaHandler.GetAllWorkAreas()); + m_windowMouseSnapper = WindowMouseSnap::Create(window, m_workAreaConfiguration.GetAllWorkAreas()); if (m_windowMouseSnapper) { if (FancyZonesSettings::settings().spanZonesAcrossMonitors) @@ -330,7 +333,7 @@ void FancyZones::MoveSizeEnd() bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept { - const auto& workAreas = m_workAreaHandler.GetAllWorkAreas(); + const auto& workAreas = m_workAreaConfiguration.GetAllWorkAreas(); WorkArea* workArea{ nullptr }; ZoneIndexSet indexes{}; @@ -518,7 +521,7 @@ void FancyZones::ToggleEditor() noexcept m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr)); - if (!EditorParameters::Save()) + if (!EditorParameters::Save(m_workAreaConfiguration, m_dpiUnawareThread)) { Logger::error(L"Failed to save editor startup parameters"); return; @@ -618,11 +621,11 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa // Check whether Alt is used in the shortcut key combination if (GetAsyncKeyState(VK_MENU) & 0x8000) { - m_windowKeyboardSnapper.Extend(foregroundWindow, windowRect, monitor, static_cast(lparam), m_workAreaHandler.GetAllWorkAreas()); + m_windowKeyboardSnapper.Extend(foregroundWindow, windowRect, monitor, static_cast(lparam), m_workAreaConfiguration.GetAllWorkAreas()); } else { - m_windowKeyboardSnapper.Snap(foregroundWindow, windowRect, monitor, static_cast(lparam), m_workAreaHandler.GetAllWorkAreas(), monitors); + m_windowKeyboardSnapper.Snap(foregroundWindow, windowRect, monitor, static_cast(lparam), m_workAreaConfiguration.GetAllWorkAreas(), monitors); } } else @@ -632,7 +635,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa } else { - m_windowKeyboardSnapper.Snap(foregroundWindow, monitor, static_cast(lparam), m_workAreaHandler.GetAllWorkAreas(), FancyZonesUtils::GetMonitorsOrdered()); + m_windowKeyboardSnapper.Snap(foregroundWindow, monitor, static_cast(lparam), m_workAreaConfiguration.GetAllWorkAreas(), FancyZonesUtils::GetMonitorsOrdered()); } } else if (message == WM_PRIV_INIT) @@ -727,6 +730,7 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept updateWindowsPositions = FancyZonesSettings::settings().displayChange_moveWindows; break; case DisplayChangeType::VirtualDesktop: // Switched virtual desktop + SyncVirtualDesktops(); break; case DisplayChangeType::Initialization: // Initialization updateWindowsPositions = FancyZonesSettings::settings().zoneSetChange_moveWindows; @@ -755,15 +759,18 @@ bool FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAr { rect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>(); } - - auto workArea = WorkArea::Create(m_hinstance, id, m_workAreaHandler.GetParent(monitor), rect); + + auto parentWorkAreaId = id; + parentWorkAreaId.virtualDesktopId = LastUsedVirtualDesktop::instance().GetId(); + + auto workArea = WorkArea::Create(m_hinstance, id, parentWorkAreaId, rect); if (!workArea) { Logger::error(L"Failed to create work area {}", id.toString()); return false; } - m_workAreaHandler.AddWorkArea(monitor, std::move(workArea)); + m_workAreaConfiguration.AddWorkArea(monitor, std::move(workArea)); return true; } @@ -785,8 +792,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept { Logger::debug(L"Update work areas, update windows positions: {}", updateWindowPositions); - m_workAreaHandler.SaveParentIds(); - m_workAreaHandler.Clear(); + m_workAreaConfiguration.Clear(); if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { @@ -824,7 +830,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept if (FancyZonesSettings::settings().spanZonesAcrossMonitors) // one work area across monitors { - const auto workArea = m_workAreaHandler.GetWorkArea(nullptr); + const auto workArea = m_workAreaConfiguration.GetWorkArea(nullptr); if (workArea) { for (const auto& [window, zones] : windowsToSnap) @@ -841,7 +847,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept const auto window = iter->first; const auto zones = iter->second; const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - const auto workAreaForMonitor = m_workAreaHandler.GetWorkArea(monitor); + const auto workAreaForMonitor = m_workAreaConfiguration.GetWorkArea(monitor); if (workAreaForMonitor && AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaForMonitor->UniqueId(), workAreaForMonitor->GetLayoutId()) == zones) { workAreaForMonitor->Snap(window, zones, false); @@ -856,7 +862,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept // snap rest of the windows to other work areas (in case they were moved after the monitor unplug) for (const auto& [window, zones] : windowsToSnap) { - for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) + for (const auto& [_, workArea] : m_workAreaConfiguration.GetAllWorkAreas()) { const auto savedIndexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), workArea->GetLayoutId()); if (savedIndexes == zones) @@ -869,7 +875,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept if (updateWindowPositions) { - for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) + for (const auto& [_, workArea] : m_workAreaConfiguration.GetAllWorkAreas()) { if (workArea) { @@ -884,7 +890,7 @@ void FancyZones::CycleWindows(bool reverse) noexcept auto window = GetForegroundWindow(); HMONITOR current = WorkAreaKeyFromWindow(window); - auto workArea = m_workAreaHandler.GetWorkArea(current); + auto workArea = m_workAreaConfiguration.GetWorkArea(current); if (workArea) { workArea->CycleWindows(window, reverse); @@ -893,15 +899,23 @@ void FancyZones::CycleWindows(bool reverse) noexcept void FancyZones::SyncVirtualDesktops() noexcept { + // Explorer persists current virtual desktop identifier to registry on a per session basis, + // but only after first virtual desktop switch happens. If the user hasn't switched virtual + // desktops in this session value in registry will be empty and we will use default GUID in + // that case (00000000-0000-0000-0000-000000000000). + + auto lastUsed = LastUsedVirtualDesktop::instance().GetId(); + auto current = VirtualDesktop::instance().GetCurrentVirtualDesktopId(); auto guids = VirtualDesktop::instance().GetVirtualDesktopIdsFromRegistry(); - if (guids.has_value()) + + if (current != lastUsed) { - AppZoneHistory::instance().RemoveDeletedVirtualDesktops(*guids); - AppliedLayouts::instance().RemoveDeletedVirtualDesktops(*guids); + LastUsedVirtualDesktop::instance().SetId(current); + LastUsedVirtualDesktop::instance().SaveData(); } - AppZoneHistory::instance().SyncVirtualDesktops(); - AppliedLayouts::instance().SyncVirtualDesktops(); + AppliedLayouts::instance().SyncVirtualDesktops(current, lastUsed, guids); + AppZoneHistory::instance().SyncVirtualDesktops(current, lastUsed, guids); } void FancyZones::UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept @@ -955,7 +969,7 @@ void FancyZones::SettingsUpdate(SettingId id) break; case SettingId::SpanZonesAcrossMonitors: { - m_workAreaHandler.Clear(); + m_workAreaConfiguration.Clear(); PostMessageW(m_window, WM_PRIV_INIT, NULL, NULL); } break; @@ -966,7 +980,7 @@ void FancyZones::SettingsUpdate(SettingId id) void FancyZones::RefreshLayouts() noexcept { - for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) + for (const auto& [_, workArea] : m_workAreaConfiguration.GetAllWorkAreas()) { if (workArea) { @@ -993,7 +1007,7 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept { HMONITOR monitor = WorkAreaKeyFromWindow(window); - auto workArea = m_workAreaHandler.GetWorkArea(monitor); + auto workArea = m_workAreaConfiguration.GetWorkArea(monitor); if (!workArea) { Logger::error(L"No work area for processing snap hotkey"); @@ -1038,13 +1052,15 @@ void FancyZones::ApplyQuickLayout(int key) noexcept return; } - auto workArea = m_workAreaHandler.GetWorkAreaFromCursor(); + auto workArea = m_workAreaConfiguration.GetWorkAreaFromCursor(); if (workArea) { - AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value()); - AppliedLayouts::instance().SaveData(); - RefreshLayouts(); - FlashZones(); + if (AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value())) + { + RefreshLayouts(); + FlashZones(); + AppliedLayouts::instance().SaveData(); + } } } @@ -1052,7 +1068,7 @@ void FancyZones::FlashZones() noexcept { if (FancyZonesSettings::settings().flashZonesOnQuickSwitch && !m_draggingState.IsDragging()) { - for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) + for (const auto& [_, workArea] : m_workAreaConfiguration.GetAllWorkAreas()) { if (workArea) { diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h index 565abb31944..db7fe004a96 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h @@ -26,13 +26,7 @@ class FancyZonesData return settingsFileName; } -private: #if defined(UNIT_TESTS) - friend class FancyZonesUnitTests::LayoutHotkeysUnitTests; - friend class FancyZonesUnitTests::LayoutTemplatesUnitTests; - friend class FancyZonesUnitTests::CustomLayoutsUnitTests; - friend class FancyZonesUnitTests::AppliedLayoutsUnitTests; - inline void SetSettingsModulePath(std::wstring_view moduleName) { std::wstring result = PTSettingsHelper::get_module_save_folder_location(moduleName); @@ -46,6 +40,8 @@ class FancyZonesData return result + L"\\" + std::wstring(L"zones-settings.json"); } #endif + +private: std::wstring settingsFileName; std::wstring zonesSettingsFileName; std::wstring appZoneHistoryFileName; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp index 886dec5a848..c633f0c0f7d 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp @@ -591,67 +591,43 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone return {}; } -void AppZoneHistory::SyncVirtualDesktops() +void AppZoneHistory::SyncVirtualDesktops(const GUID& currentVirtualDesktop, const GUID& lastUsedVirtualDesktop, std::optional> desktops) { - // Explorer persists current virtual desktop identifier to registry on a per session basis, - // but only after first virtual desktop switch happens. If the user hasn't switched virtual - // desktops in this session value in registry will be empty and we will use default GUID in - // that case (00000000-0000-0000-0000-000000000000). - - auto savedInRegistryVirtualDesktopID = VirtualDesktop::instance().GetCurrentVirtualDesktopIdFromRegistry(); - if (!savedInRegistryVirtualDesktopID.has_value() || savedInRegistryVirtualDesktopID.value() == GUID_NULL) - { - return; - } + TAppZoneHistoryMap history; - auto currentVirtualDesktopStr = FancyZonesUtils::GuidToString(savedInRegistryVirtualDesktopID.value()); - if (!currentVirtualDesktopStr.has_value()) + std::unordered_set activeDesktops{}; + if (desktops.has_value()) { - Logger::error(L"Failed to convert virtual desktop GUID to string"); - return; + activeDesktops = std::unordered_set(std::begin(desktops.value()), std::end(desktops.value())); } - Logger::info(L"AppZoneHistory Sync virtual desktops: current {}", currentVirtualDesktopStr.value()); - - bool dirtyFlag = false; - - for (auto& [path, perDesktopData] : m_history) - { - for (auto& data : perDesktopData) + auto findCurrentVirtualDesktopInSavedHistory = [&](const std::pair>& val) -> bool + { + for (auto& data : val.second) { - if (data.workAreaId.virtualDesktopId == GUID_NULL) + if (data.workAreaId.virtualDesktopId == currentVirtualDesktop) { - data.workAreaId.virtualDesktopId = savedInRegistryVirtualDesktopID.value(); - dirtyFlag = true; + return true; } } - } - - if (dirtyFlag) - { - Logger::info(L"Update Virtual Desktop id to {}", currentVirtualDesktopStr.value()); - SaveData(); - } -} + return false; + }; + bool replaceLastUsedWithCurrent = !desktops.has_value() || currentVirtualDesktop == GUID_NULL || lastUsedVirtualDesktop == GUID_NULL || std::find_if(m_history.begin(), m_history.end(), findCurrentVirtualDesktopInSavedHistory) == m_history.end(); -void AppZoneHistory::RemoveDeletedVirtualDesktops(const std::vector& activeDesktops) -{ - std::unordered_set active(std::begin(activeDesktops), std::end(activeDesktops)); bool dirtyFlag = false; - for (auto it = std::begin(m_history); it != std::end(m_history);) { auto& perDesktopData = it->second; for (auto desktopIt = std::begin(perDesktopData); desktopIt != std::end(perDesktopData);) { - if (desktopIt->workAreaId.virtualDesktopId != GUID_NULL && !active.contains(desktopIt->workAreaId.virtualDesktopId)) + if (replaceLastUsedWithCurrent && desktopIt->workAreaId.virtualDesktopId == lastUsedVirtualDesktop) { - auto virtualDesktopIdStr = FancyZonesUtils::GuidToString(desktopIt->workAreaId.virtualDesktopId); - if (virtualDesktopIdStr) - { - Logger::info(L"Remove Virtual Desktop id {} from app-zone-history", virtualDesktopIdStr.value()); - } + desktopIt->workAreaId.virtualDesktopId = currentVirtualDesktop; + dirtyFlag = true; + } + if (desktopIt->workAreaId.virtualDesktopId != currentVirtualDesktop && !activeDesktops.contains(desktopIt->workAreaId.virtualDesktopId)) + { desktopIt = perDesktopData.erase(desktopIt); dirtyFlag = true; } @@ -664,6 +640,7 @@ void AppZoneHistory::RemoveDeletedVirtualDesktops(const std::vector& activ if (perDesktopData.empty()) { it = m_history.erase(it); + dirtyFlag = true; } else { diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h index 18ec84ac93b..6356ab50fad 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h @@ -41,6 +41,13 @@ class AppZoneHistory #endif } +#if defined(UNIT_TESTS) + inline void SetAppZoneHistory(const TAppZoneHistoryMap& history) + { + m_history = history; + } +#endif + void LoadData(); void SaveData(); void AdjustWorkAreaIds(const std::vector& ids); @@ -56,9 +63,8 @@ class AppZoneHistory bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept; ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId) const; - void SyncVirtualDesktops(); - void RemoveDeletedVirtualDesktops(const std::vector& activeDesktops); - + void SyncVirtualDesktops(const GUID& currentVirtualDesktop, const GUID& lastUsedVirtualDesktop, std::optional> desktops); + private: AppZoneHistory(); ~AppZoneHistory() = default; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp index a6c86952d68..b05cd3da0b2 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp @@ -279,29 +279,7 @@ void AppliedLayouts::LoadData() void AppliedLayouts::SaveData() { - bool dirtyFlag = false; - TAppliedLayoutsMap updatedMap; - - for (const auto& [id, data] : m_layouts) - { - auto updatedId = id; - if (!VirtualDesktop::instance().IsVirtualDesktopIdSavedInRegistry(id.virtualDesktopId)) - { - updatedId.virtualDesktopId = GUID_NULL; - dirtyFlag = true; - } - - updatedMap.insert({ updatedId, data }); - } - - if (dirtyFlag) - { - json::to_file(AppliedLayoutsFileName(), JsonUtils::SerializeJson(updatedMap)); - } - else - { - json::to_file(AppliedLayoutsFileName(), JsonUtils::SerializeJson(m_layouts)); - } + json::to_file(AppliedLayoutsFileName(), JsonUtils::SerializeJson(m_layouts)); } void AppliedLayouts::AdjustWorkAreaIds(const std::vector& ids) @@ -344,86 +322,75 @@ void AppliedLayouts::AdjustWorkAreaIds(const std::vector> desktops) { - // Explorer persists current virtual desktop identifier to registry on a per session basis, - // but only after first virtual desktop switch happens. If the user hasn't switched virtual - // desktops in this session value in registry will be empty and we will use default GUID in - // that case (00000000-0000-0000-0000-000000000000). + TAppliedLayoutsMap layouts; - auto savedInRegistryVirtualDesktopID = VirtualDesktop::instance().GetCurrentVirtualDesktopIdFromRegistry(); - if (!savedInRegistryVirtualDesktopID.has_value() || savedInRegistryVirtualDesktopID.value() == GUID_NULL) - { - return; - } + auto findCurrentVirtualDesktopInSavedLayouts = [&](const std::pair& val) -> bool { return val.first.virtualDesktopId == currentVirtualDesktop; }; + bool replaceLastUsedWithCurrent = !desktops.has_value() || currentVirtualDesktop == GUID_NULL || + std::find_if(m_layouts.begin(), m_layouts.end(), findCurrentVirtualDesktopInSavedLayouts) == m_layouts.end(); + bool copyToOtherVirtualDesktops = lastUsedVirtualDesktop == GUID_NULL && currentVirtualDesktop != GUID_NULL && desktops.has_value(); - auto currentVirtualDesktopStr = FancyZonesUtils::GuidToString(savedInRegistryVirtualDesktopID.value()); - if (!currentVirtualDesktopStr.has_value()) + for (const auto& [workAreaId, layout] : m_layouts) { - Logger::error(L"Failed to convert virtual desktop GUID to string"); - return; - } - - Logger::info(L"AppliedLayouts Sync virtual desktops: current {}", currentVirtualDesktopStr.value()); - - bool dirtyFlag = false; + if (replaceLastUsedWithCurrent && workAreaId.virtualDesktopId == lastUsedVirtualDesktop) + { + // replace "lastUsedVirtualDesktop" with "currentVirtualDesktop" + auto updatedWorkAreaId = workAreaId; + updatedWorkAreaId.virtualDesktopId = currentVirtualDesktop; + layouts.insert({ updatedWorkAreaId, layout }); - std::vector replaceWithCurrentId{}; + if (copyToOtherVirtualDesktops) + { + // Copy to other virtual desktops on the 1st VD creation. + // If we just replace the id, we'll lose the layout on the other desktops. + // Usage scenario: + // apply the layout to the single virtual desktop with id = GUID_NULL, + // create the 2nd virtual desktop and switch to it, + // so virtual desktop id changes from GUID_NULL to a valid value of the 2nd VD. + // Then change the layout on the 2nd VD and switch back to the 1st VD. + // Layout on the initial VD will be changed too without initializing it beforehand. + for (const auto& id : desktops.value()) + { + if (id != currentVirtualDesktop) + { + auto copyWorkAreaId = workAreaId; + copyWorkAreaId.virtualDesktopId = id; + layouts.insert({ copyWorkAreaId, layout }); + } + } + } + } - for (const auto& [id, data] : m_layouts) - { - if (id.virtualDesktopId == GUID_NULL) + if (workAreaId.virtualDesktopId == currentVirtualDesktop || (desktops.has_value() && + std::find(desktops.value().begin(), desktops.value().end(), workAreaId.virtualDesktopId) != desktops.value().end())) { - replaceWithCurrentId.push_back(id); - dirtyFlag = true; + // keep only actual virtual desktop values + layouts.insert({ workAreaId, layout }); } } - for (const auto& id : replaceWithCurrentId) + if (layouts != m_layouts) { - auto mapEntry = m_layouts.extract(id); - mapEntry.key().virtualDesktopId = savedInRegistryVirtualDesktopID.value(); - m_layouts.insert(std::move(mapEntry)); - } - - if (dirtyFlag) - { - Logger::info(L"Update Virtual Desktop id to {}", currentVirtualDesktopStr.value()); + m_layouts = layouts; SaveData(); - } -} - -void AppliedLayouts::RemoveDeletedVirtualDesktops(const std::vector& activeDesktops) -{ - std::unordered_set active(std::begin(activeDesktops), std::end(activeDesktops)); - bool dirtyFlag = false; - - for (auto it = std::begin(m_layouts); it != std::end(m_layouts);) - { - GUID desktopId = it->first.virtualDesktopId; - if (desktopId != GUID_NULL) + std::wstring currentStr = FancyZonesUtils::GuidToString(currentVirtualDesktop).value_or(L"incorrect guid"); + std::wstring lastUsedStr = FancyZonesUtils::GuidToString(lastUsedVirtualDesktop).value_or(L"incorrect guid"); + std::wstring registryStr{}; + if (desktops.has_value()) { - auto foundId = active.find(desktopId); - if (foundId == std::end(active)) + for (const auto& id : desktops.value()) { - wil::unique_cotaskmem_string virtualDesktopIdStr; - if (SUCCEEDED(StringFromCLSID(desktopId, &virtualDesktopIdStr))) - { - Logger::info(L"Remove Virtual Desktop id {}", virtualDesktopIdStr.get()); - } - - it = m_layouts.erase(it); - dirtyFlag = true; - continue; + registryStr += FancyZonesUtils::GuidToString(id).value_or(L"incorrect guid") + L" "; } } - ++it; - } + else + { + registryStr = L"empty"; + } - if (dirtyFlag) - { - SaveData(); + Logger::info(L"Synced virtual desktops. Current: {}, last used: {}, registry: {}", currentStr, lastUsedStr, registryStr); } } @@ -449,9 +416,9 @@ bool AppliedLayouts::IsLayoutApplied(const FancyZonesDataTypes::WorkAreaId& id) return iter != m_layouts.end(); } -bool AppliedLayouts::ApplyLayout(const FancyZonesDataTypes::WorkAreaId& deviceId, LayoutData layout) +bool AppliedLayouts::ApplyLayout(const FancyZonesDataTypes::WorkAreaId& workAreaId, LayoutData layout) { - m_layouts[deviceId] = std::move(layout); + m_layouts[workAreaId] = layout; return true; } @@ -496,9 +463,5 @@ bool AppliedLayouts::CloneLayout(const FancyZonesDataTypes::WorkAreaId& srcId, c } Logger::info(L"Clone layout from {} to {}", dstId.toString(), srcId.toString()); - m_layouts[dstId] = m_layouts[srcId]; - - SaveData(); - - return true; + return ApplyLayout(dstId, m_layouts[srcId]); } diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h index bea37c6d92b..e4f796913b8 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h @@ -49,12 +49,18 @@ class AppliedLayouts #endif } +#if defined(UNIT_TESTS) + inline void SetAppliedLayouts(TAppliedLayoutsMap layouts) + { + m_layouts = layouts; + } +#endif + void LoadData(); void SaveData(); void AdjustWorkAreaIds(const std::vector& ids); - void SyncVirtualDesktops(); - void RemoveDeletedVirtualDesktops(const std::vector& activeDesktops); + void SyncVirtualDesktops(const GUID& currentVirtualDesktop, const GUID& lastUsedVirtualDesktop, std::optional> desktops); std::optional GetDeviceLayout(const FancyZonesDataTypes::WorkAreaId& id) const noexcept; const TAppliedLayoutsMap& GetAppliedLayoutMap() const noexcept; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.cpp new file mode 100644 index 00000000000..3c62df9e88c --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.cpp @@ -0,0 +1,78 @@ +#include "../pch.h" +#include "LastUsedVirtualDesktop.h" + +#include + +#include + +namespace JsonUtils +{ + GUID ParseJson(const json::JsonObject& json) + { + auto idStr = json.GetNamedString(NonLocalizable::LastUsedVirtualDesktop::LastUsedVirtualDesktopID); + auto idOpt = FancyZonesUtils::GuidFromString(idStr.c_str()); + + if (!idOpt.has_value()) + { + return {}; + } + + return idOpt.value(); + } + + json::JsonObject SerializeJson(const GUID& id) + { + json::JsonObject result{}; + + auto virtualDesktopStr = FancyZonesUtils::GuidToString(id); + if (virtualDesktopStr) + { + result.SetNamedValue(NonLocalizable::LastUsedVirtualDesktop::LastUsedVirtualDesktopID, json::value(virtualDesktopStr.value())); + } + + return result; + } +} + + +LastUsedVirtualDesktop& LastUsedVirtualDesktop::instance() +{ + static LastUsedVirtualDesktop self; + return self; +} + +void LastUsedVirtualDesktop::LoadData() +{ + auto data = json::from_file(LastUsedVirtualDesktopFileName()); + + try + { + if (data) + { + m_id = JsonUtils::ParseJson(data.value()); + } + else + { + m_id = GUID_NULL; + } + } + catch (const winrt::hresult_error& e) + { + Logger::error(L"Parsing last-used-virtual-desktop error: {}", e.message()); + } +} + +void LastUsedVirtualDesktop::SaveData() const +{ + json::to_file(LastUsedVirtualDesktopFileName(), JsonUtils::SerializeJson(m_id)); +} + +GUID LastUsedVirtualDesktop::GetId() const +{ + return m_id; +} + +void LastUsedVirtualDesktop::SetId(GUID id) +{ + m_id = id; +} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.h new file mode 100644 index 00000000000..da9e07eca4b --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include + +namespace NonLocalizable +{ + namespace LastUsedVirtualDesktop + { + const static wchar_t* LastUsedVirtualDesktopID = L"last-used-virtual-desktop"; + } +} + +class LastUsedVirtualDesktop +{ +public: + static LastUsedVirtualDesktop& instance(); + + inline static std::wstring LastUsedVirtualDesktopFileName() + { + std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey); +#if defined(UNIT_TESTS) + return saveFolderPath + L"\\test-last-used-virtual-desktop.json"; +#else + return saveFolderPath + L"\\last-used-virtual-desktop.json"; +#endif + } + + void LoadData(); + void SaveData() const; + + GUID GetId() const; + void SetId(GUID id); + +private: + LastUsedVirtualDesktop() = default; + ~LastUsedVirtualDesktop() = default; + + GUID m_id{}; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h index 6f61b3e125b..58673c70129 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h @@ -209,14 +209,12 @@ namespace FancyZonesDataTypes inline bool operator==(const WorkAreaId& lhs, const WorkAreaId& rhs) { - bool vdEqual = (lhs.virtualDesktopId == rhs.virtualDesktopId || lhs.virtualDesktopId == GUID_NULL || rhs.virtualDesktopId == GUID_NULL); - return vdEqual && lhs.monitorId == rhs.monitorId; + return lhs.virtualDesktopId == rhs.virtualDesktopId && lhs.monitorId == rhs.monitorId; } inline bool operator!=(const WorkAreaId& lhs, const WorkAreaId& rhs) { - bool vdEqual = (lhs.virtualDesktopId == rhs.virtualDesktopId || lhs.virtualDesktopId == GUID_NULL || rhs.virtualDesktopId == GUID_NULL); - return !vdEqual || lhs.monitorId != rhs.monitorId; + return lhs.virtualDesktopId != rhs.virtualDesktopId || lhs.monitorId != rhs.monitorId; } inline bool operator<(const WorkAreaId& lhs, const WorkAreaId& rhs) @@ -240,6 +238,11 @@ namespace FancyZonesDataTypes return lhs.monitorId.deviceId < rhs.monitorId.deviceId; } + + inline bool operator==(const AppZoneHistoryData& lhs, const AppZoneHistoryData& rhs) + { + return lhs.layoutId == rhs.layoutId && lhs.workAreaId == rhs.workAreaId && lhs.zoneIndexSet == rhs.zoneIndexSet && lhs.processIdToHandleMap == rhs.processIdToHandleMap; + } } namespace std diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index a659a1839ec..faf124b3efd 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -43,6 +43,7 @@ + @@ -59,7 +60,7 @@ - + @@ -100,6 +101,9 @@ ../pch.h + + ../pch.h + ../pch.h @@ -115,7 +119,7 @@ - + Create diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index f97b95c5a9d..220b0cc22f6 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -54,7 +54,7 @@ Header Files - + Header Files @@ -168,6 +168,9 @@ Header Files + + Header Files\FancyZonesData + @@ -200,7 +203,7 @@ Source Files - + Source Files @@ -272,6 +275,9 @@ Source Files + + Source Files\FancyZonesData + Source Files diff --git a/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.cpp b/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.cpp index 67bf726d2bc..dbd1511d5ad 100644 --- a/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.cpp +++ b/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.cpp @@ -270,30 +270,16 @@ GUID VirtualDesktop::GetCurrentVirtualDesktopId() const noexcept return m_currentVirtualDesktopId; } -GUID VirtualDesktop::GetPreviousVirtualDesktopId() const noexcept -{ - return m_previousDesktopId; -} - void VirtualDesktop::UpdateVirtualDesktopId() noexcept { - m_previousDesktopId = m_currentVirtualDesktopId; - auto currentVirtualDesktopId = GetCurrentVirtualDesktopIdFromRegistry(); - if (!currentVirtualDesktopId.has_value()) + if (currentVirtualDesktopId.has_value()) { - Logger::info("No Virtual Desktop Id found in registry"); - currentVirtualDesktopId = VirtualDesktop::instance().GetDesktopIdByTopLevelWindows(); + m_currentVirtualDesktopId = currentVirtualDesktopId.value(); } - - if (currentVirtualDesktopId.has_value()) + else { - m_currentVirtualDesktopId = *currentVirtualDesktopId; - - if (m_currentVirtualDesktopId == GUID_NULL) - { - Logger::warn("Couldn't retrieve virtual desktop id"); - } + m_currentVirtualDesktopId = GUID_NULL; } Trace::VirtualDesktopChanged(); diff --git a/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.h b/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.h index 069e1883ea3..4c25518ce8a 100644 --- a/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.h +++ b/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.h @@ -7,7 +7,6 @@ class VirtualDesktop // saved values GUID GetCurrentVirtualDesktopId() const noexcept; - GUID GetPreviousVirtualDesktopId() const noexcept; void UpdateVirtualDesktopId() noexcept; // IVirtualDesktopManager @@ -29,7 +28,6 @@ class VirtualDesktop IVirtualDesktopManager* m_vdManager{nullptr}; GUID m_currentVirtualDesktopId{}; - GUID m_previousDesktopId{}; std::optional> GetVirtualDesktopIdsFromRegistry(HKEY hKey) const; }; diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index 2d4b6adf902..638776c25ae 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -244,14 +244,12 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); if (!isLayoutAlreadyApplied) { - if (parentUniqueId.virtualDesktopId != GUID_NULL) - { - AppliedLayouts::instance().CloneLayout(parentUniqueId, m_uniqueId); - } - else + if (!AppliedLayouts::instance().CloneLayout(parentUniqueId, m_uniqueId)) { AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId); } + + AppliedLayouts::instance().SaveData(); } CalculateZoneSet(); diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp b/src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.cpp similarity index 57% rename from src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp rename to src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.cpp index 486e2739ea5..a3cfa7cf45a 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.cpp @@ -1,9 +1,9 @@ #include "pch.h" -#include "MonitorWorkAreaMap.h" +#include "WorkAreaConfiguration.h" #include -WorkArea* const MonitorWorkAreaMap::GetWorkArea(HMONITOR monitor) const +WorkArea* const WorkAreaConfiguration::GetWorkArea(HMONITOR monitor) const { auto iter = m_workAreaMap.find(monitor); if (iter != m_workAreaMap.end()) @@ -14,7 +14,7 @@ WorkArea* const MonitorWorkAreaMap::GetWorkArea(HMONITOR monitor) const return nullptr; } -WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromCursor() const +WorkArea* const WorkAreaConfiguration::GetWorkAreaFromCursor() const { const auto allMonitorsWorkArea = GetWorkArea(nullptr); if (allMonitorsWorkArea) @@ -35,7 +35,7 @@ WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromCursor() const } } -WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromWindow(HWND window) const +WorkArea* const WorkAreaConfiguration::GetWorkAreaFromWindow(HWND window) const { const auto allMonitorsWorkArea = GetWorkArea(nullptr); if (allMonitorsWorkArea) @@ -51,39 +51,17 @@ WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromWindow(HWND window) const } } -const std::unordered_map>& MonitorWorkAreaMap::GetAllWorkAreas() const noexcept +const std::unordered_map>& WorkAreaConfiguration::GetAllWorkAreas() const noexcept { return m_workAreaMap; } -void MonitorWorkAreaMap::AddWorkArea(HMONITOR monitor, std::unique_ptr workArea) +void WorkAreaConfiguration::AddWorkArea(HMONITOR monitor, std::unique_ptr workArea) { m_workAreaMap.insert({ monitor, std::move(workArea) }); } -FancyZonesDataTypes::WorkAreaId MonitorWorkAreaMap::GetParent(HMONITOR monitor) const -{ - if (m_workAreaParents.contains(monitor)) - { - return m_workAreaParents.at(monitor); - } - - return FancyZonesDataTypes::WorkAreaId{}; -} - -void MonitorWorkAreaMap::SaveParentIds() -{ - m_workAreaParents.clear(); - for (const auto& [monitor, workArea] : m_workAreaMap) - { - if (workArea) - { - m_workAreaParents.insert({ monitor, workArea->UniqueId() }); - } - } -} - -void MonitorWorkAreaMap::Clear() noexcept +void WorkAreaConfiguration::Clear() noexcept { m_workAreaMap.clear(); } diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h b/src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.h similarity index 78% rename from src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h rename to src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.h index 45fabda7091..9530dbdae3a 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h +++ b/src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.h @@ -1,15 +1,14 @@ #pragma once -#include "GuidUtils.h" #include class WorkArea; -class MonitorWorkAreaMap +class WorkAreaConfiguration { public: /** - * Get work area based on virtual desktop id and monitor handle. + * Get work area based on monitor handle. * * @param[in] monitor Monitor handle. * @@ -19,7 +18,7 @@ class MonitorWorkAreaMap WorkArea* const GetWorkArea(HMONITOR monitor) const; /** - * Get work area based on virtual desktop id and the current cursor position. + * Get work area based on the current cursor position. * * @returns Object representing single work area, interface to all actions available on work area * (e.g. moving windows through zone layout specified for that work area). @@ -49,13 +48,6 @@ class MonitorWorkAreaMap */ void AddWorkArea(HMONITOR monitor, std::unique_ptr workArea); - FancyZonesDataTypes::WorkAreaId GetParent(HMONITOR monitor) const; - - /** - * Saving current work area IDs as parents for later use. - */ - void SaveParentIds(); - /** * Clear all persisted work area related data. */ @@ -63,5 +55,4 @@ class MonitorWorkAreaMap private: std::unordered_map> m_workAreaMap; - std::unordered_map m_workAreaParents{}; }; diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp index c3e954fb00f..c3eea232365 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp @@ -339,4 +339,159 @@ namespace FancyZonesUnitTests Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(nullptr, workAreaId, layoutId)); } }; + + TEST_CLASS (AppZoneHistorySyncVirtualDesktops) + { + const GUID virtualDesktop1 = FancyZonesUtils::GuidFromString(L"{30387C86-BB15-476D-8683-AF93F6D73E99}").value(); + const GUID virtualDesktop2 = FancyZonesUtils::GuidFromString(L"{65F6343A-868F-47EE-838E-55A178A7FB7A}").value(); + const GUID deletedVirtualDesktop = FancyZonesUtils::GuidFromString(L"{2D9F3E2D-F61D-4618-B35D-85C9B8DFDFD8}").value(); + + FancyZonesDataTypes::WorkAreaId GetWorkAreaID(GUID virtualDesktop) + { + return FancyZonesDataTypes::WorkAreaId{ + .monitorId = { + .deviceId = { .id = L"id", .instanceId = L"id", .number = 1 }, + .serialNumber = L"serial-number" + }, + .virtualDesktopId = virtualDesktop + }; + } + + FancyZonesDataTypes::AppZoneHistoryData GetAppZoneHistoryData(GUID virtualDesktop, const std::wstring& layoutId, const ZoneIndexSet& zones) + { + return FancyZonesDataTypes::AppZoneHistoryData{ + .layoutId = FancyZonesUtils::GuidFromString(layoutId).value(), + .workAreaId = GetWorkAreaID(virtualDesktop), + .zoneIndexSet = zones + }; + }; + + TEST_METHOD_INITIALIZE(Init) + { + AppZoneHistory::instance().LoadData(); + } + + TEST_METHOD_CLEANUP(CleanUp) + { + std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); + } + + TEST_METHOD (SyncVirtualDesktops_SwitchVirtualDesktop) + { + AppZoneHistory::TAppZoneHistoryMap history{}; + const std::wstring app = L"app"; + history.insert({ app, std::vector{ + GetAppZoneHistoryData(virtualDesktop1, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }), + GetAppZoneHistoryData(virtualDesktop2, L"{EAC1BB3B-13D6-4839-BBF7-58C3E8AB7229}", { 1 }), + } }); + AppZoneHistory::instance().SetAppZoneHistory(history); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = virtualDesktop2; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } }; + AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(history.at(app)[0] == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop1)).value()); + Assert::IsTrue(history.at(app)[1] == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop2)).value()); + } + + TEST_METHOD (SyncVirtualDesktops_CurrentVirtualDesktopDeleted) + { + AppZoneHistory::TAppZoneHistoryMap history{}; + const std::wstring app = L"app"; + history.insert({ app, std::vector{ + GetAppZoneHistoryData(virtualDesktop1, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }), + GetAppZoneHistoryData(deletedVirtualDesktop, L"{EAC1BB3B-13D6-4839-BBF7-58C3E8AB7229}", { 1 }), + } }); + AppZoneHistory::instance().SetAppZoneHistory(history); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = deletedVirtualDesktop; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1 } }; + AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(history.at(app)[0] == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop1)).value()); + Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(deletedVirtualDesktop)).has_value()); + } + + TEST_METHOD (SyncVirtualDesktops_NotCurrentVirtualDesktopDeleted) + { + AppZoneHistory::TAppZoneHistoryMap history{}; + const std::wstring app = L"app"; + history.insert({ app, std::vector{ + GetAppZoneHistoryData(virtualDesktop1, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }), + GetAppZoneHistoryData(deletedVirtualDesktop, L"{EAC1BB3B-13D6-4839-BBF7-58C3E8AB7229}", { 1 }), + } }); + AppZoneHistory::instance().SetAppZoneHistory(history); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = virtualDesktop1; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1 } }; + AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(history.at(app)[0] == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop1)).value()); + Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(deletedVirtualDesktop)).has_value()); + } + + TEST_METHOD (SyncVirtualDesktops_AllIdsFromRegistryAreNew) + { + AppZoneHistory::TAppZoneHistoryMap history{}; + const std::wstring app = L"app"; + history.insert({ app, std::vector{ + GetAppZoneHistoryData(deletedVirtualDesktop, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }), + } }); + AppZoneHistory::instance().SetAppZoneHistory(history); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = deletedVirtualDesktop; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } }; + AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + auto expected = history.at(app)[0]; + expected.workAreaId.virtualDesktopId = currentVirtualDesktop; + Assert::IsTrue(expected == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop1)).value()); + Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop2)).has_value()); + Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(deletedVirtualDesktop)).has_value()); + } + + TEST_METHOD (SyncVirtualDesktop_NoDesktopsInRegistry) + { + AppZoneHistory::TAppZoneHistoryMap history{}; + const std::wstring app = L"app"; + history.insert({ app, std::vector{ + GetAppZoneHistoryData(deletedVirtualDesktop, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }), + } }); + AppZoneHistory::instance().SetAppZoneHistory(history); + + GUID currentVirtualDesktop = GUID_NULL; + GUID lastUsedVirtualDesktop = deletedVirtualDesktop; + std::optional> virtualDesktopsInRegistry = std::nullopt; + AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + auto expected = history.at(app)[0]; + expected.workAreaId.virtualDesktopId = currentVirtualDesktop; + Assert::IsTrue(expected == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(currentVirtualDesktop)).value()); + Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(deletedVirtualDesktop)).has_value()); + } + + TEST_METHOD (SyncVirtualDesktop_SwithVirtualDesktopFirstTime) + { + AppZoneHistory::TAppZoneHistoryMap history{}; + const std::wstring app = L"app"; + history.insert({ app, std::vector{ + GetAppZoneHistoryData(GUID_NULL, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }), + } }); + AppZoneHistory::instance().SetAppZoneHistory(history); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = GUID_NULL; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } }; + AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + auto expected = history.at(app)[0]; + expected.workAreaId.virtualDesktopId = currentVirtualDesktop; + Assert::IsTrue(expected == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(currentVirtualDesktop)).value()); + } + }; + } diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp index 87f5da5a3c6..ec9256664b2 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp @@ -14,24 +14,14 @@ namespace FancyZonesUnitTests { TEST_CLASS (AppliedLayoutsUnitTests) { - FancyZonesData& m_fzData = FancyZonesDataInstance(); - std::wstring m_testFolder = L"FancyZonesUnitTests"; - std::wstring m_testFolderPath = PTSettingsHelper::get_module_save_folder_location(m_testFolder); - TEST_METHOD_INITIALIZE(Init) { - m_fzData.SetSettingsModulePath(L"FancyZonesUnitTests"); + AppliedLayouts::instance().LoadData(); } TEST_METHOD_CLEANUP(CleanUp) { - // Move...FromZonesSettings creates all of these files, clean up - std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); - std::filesystem::remove(CustomLayouts::CustomLayoutsFileName()); - std::filesystem::remove(LayoutHotkeys::LayoutHotkeysFileName()); - std::filesystem::remove(LayoutTemplates::LayoutTemplatesFileName()); - std::filesystem::remove_all(m_testFolderPath); - AppliedLayouts::instance().LoadData(); // clean data + std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); } TEST_METHOD (AppliedLayoutsParse) @@ -75,7 +65,7 @@ namespace FancyZonesUnitTests Assert::IsTrue(AppliedLayouts::instance().IsLayoutApplied(id)); } - TEST_METHOD(AppliedLayoutsParseDataWithResolution) + TEST_METHOD (AppliedLayoutsParseDataWithResolution) { // prepare json::JsonObject root{}; @@ -242,143 +232,94 @@ namespace FancyZonesUnitTests Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty()); } - TEST_METHOD (MoveAppliedLayoutsFromZonesSettings) + TEST_METHOD (Save) { - // prepare - json::JsonObject root{}; - json::JsonArray devicesArray{}, customLayoutsArray{}, templateLayoutsArray{}, quickLayoutKeysArray{}; - - { - json::JsonObject activeZoneset{}; - activeZoneset.SetNamedValue(L"uuid", json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}")); - activeZoneset.SetNamedValue(L"type", json::value(FancyZonesDataTypes::TypeToString(FancyZonesDataTypes::ZoneSetLayoutType::Rows))); - - json::JsonObject obj{}; - obj.SetNamedValue(L"device-id", json::value(L"VSC9636#5&37ac4db&0&UID160005_3840_2160_{00000000-0000-0000-0000-000000000000}")); - obj.SetNamedValue(L"active-zoneset", activeZoneset);; - obj.SetNamedValue(L"editor-show-spacing", json::value(true)); - obj.SetNamedValue(L"editor-spacing", json::value(3)); - obj.SetNamedValue(L"editor-zone-count", json::value(4)); - obj.SetNamedValue(L"editor-sensitivity-radius", json::value(22)); - - devicesArray.Append(obj); - } - - root.SetNamedValue(L"devices", devicesArray); - root.SetNamedValue(L"custom-zone-sets", customLayoutsArray); - root.SetNamedValue(L"templates", templateLayoutsArray); - root.SetNamedValue(L"quick-layout-keys", quickLayoutKeysArray); - json::to_file(m_fzData.GetZoneSettingsPath(m_testFolder), root); - - // test - m_fzData.ReplaceZoneSettingsFileFromOlderVersions(); - AppliedLayouts::instance().LoadData(); - Assert::AreEqual((size_t)1, AppliedLayouts::instance().GetAppliedLayoutMap().size()); - - FancyZonesDataTypes::WorkAreaId id{ - .monitorId = { .deviceId = { .id = L"VSC9636", .instanceId = L"5&37ac4db&0&UID160005" }, .serialNumber = L"" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + FancyZonesDataTypes::WorkAreaId workAreaId1{ + .monitorId = { + .deviceId = { .id = L"id-1", .instanceId = L"id-1", .number = 1 }, + .serialNumber = L"serial-number-1" + }, + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{30387C86-BB15-476D-8683-AF93F6D73E99}").value() }; - Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(id).has_value()); - } - - TEST_METHOD (MoveAppliedLayoutsFromZonesSettingsNoAppliedLayoutsData) - { - // prepare - json::JsonObject root{}; - json::JsonArray customLayoutsArray{}, templateLayoutsArray{}, quickLayoutKeysArray{}; - root.SetNamedValue(L"custom-zone-sets", customLayoutsArray); - root.SetNamedValue(L"templates", templateLayoutsArray); - root.SetNamedValue(L"quick-layout-keys", quickLayoutKeysArray); - json::to_file(m_fzData.GetZoneSettingsPath(m_testFolder), root); - - // test - m_fzData.ReplaceZoneSettingsFileFromOlderVersions(); - AppliedLayouts::instance().LoadData(); - Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty()); - } - - TEST_METHOD (MoveAppliedLayoutsFromZonesSettingsNoFile) - { - // test - m_fzData.ReplaceZoneSettingsFileFromOlderVersions(); - AppliedLayouts::instance().LoadData(); - Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty()); - } - - TEST_METHOD (CloneDeviceInfo) - { - FancyZonesDataTypes::WorkAreaId deviceSrc{ - .monitorId = { .deviceId = { .id = L"Device1", .instanceId = L"" }, .serialNumber = L"" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + FancyZonesDataTypes::WorkAreaId workAreaId2{ + .monitorId = { + .deviceId = { .id = L"id-2", .instanceId = L"id-2", .number = 2 }, + .serialNumber = L"serial-number-2" }, + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{30387C86-BB15-476D-8683-AF93F6D73E99}").value() }; - FancyZonesDataTypes::WorkAreaId deviceDst{ - .monitorId = { .deviceId = { .id = L"Device2", .instanceId = L"" }, .serialNumber = L"" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + FancyZonesDataTypes::WorkAreaId workAreaId3{ + .monitorId = { + .deviceId = { .id = L"id-1", .instanceId = L"id-1", .number = 1 }, + .serialNumber = L"serial-number-1" }, + .virtualDesktopId = GUID_NULL + }; + FancyZonesDataTypes::WorkAreaId workAreaId4{ + .monitorId = { + .deviceId = { .id = L"id-2", .instanceId = L"id-2", .number = 2 }, + .serialNumber = L"serial-number-2" }, + .virtualDesktopId = GUID_NULL }; - Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceSrc)); - Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceDst)); - - AppliedLayouts::instance().CloneLayout(deviceSrc, deviceDst); + LayoutData layout1{ .uuid = FancyZonesUtils::GuidFromString(L"{D7DBECFA-23FC-4F45-9B56-51CFA9F6ABA2}").value() }; + LayoutData layout2{ .uuid = FancyZonesUtils::GuidFromString(L"{B9EDB48C-EC48-4E82-993F-A15DC1FF09D3}").value() }; + LayoutData layout3{ .uuid = FancyZonesUtils::GuidFromString(L"{94CF0000-7814-4D72-9624-794060FA269C}").value() }; + LayoutData layout4{ .uuid = FancyZonesUtils::GuidFromString(L"{13FA7ADF-1B6C-4FB6-8142-254B77C128E2}").value() }; - auto actualMap = AppliedLayouts::instance().GetAppliedLayoutMap(); - Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end()); - Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end()); + AppliedLayouts::TAppliedLayoutsMap expected{}; + expected.insert({ workAreaId1, layout1 }); + expected.insert({ workAreaId2, layout2 }); + expected.insert({ workAreaId3, layout3 }); + expected.insert({ workAreaId4, layout4 }); - auto expected = AppliedLayouts::instance().GetDeviceLayout(deviceSrc); - auto actual = AppliedLayouts::instance().GetDeviceLayout(deviceDst); + AppliedLayouts::instance().SetAppliedLayouts(expected); + AppliedLayouts::instance().SaveData(); - Assert::IsTrue(expected.has_value()); - Assert::IsTrue(actual.has_value()); - Assert::IsTrue(expected.value().uuid == actual.value().uuid); + AppliedLayouts::instance().LoadData(); + auto actual = AppliedLayouts::instance().GetAppliedLayoutMap(); + Assert::AreEqual(expected.size(), actual.size()); + Assert::IsTrue(expected.at(workAreaId1) == actual.at(workAreaId1)); + Assert::IsTrue(expected.at(workAreaId2) == actual.at(workAreaId2)); + Assert::IsTrue(expected.at(workAreaId3) == actual.at(workAreaId3)); + Assert::IsTrue(expected.at(workAreaId4) == actual.at(workAreaId4)); } - TEST_METHOD (CloneDeviceInfoIntoUnknownDevice) + TEST_METHOD (CloneDeviceInfo) { FancyZonesDataTypes::WorkAreaId deviceSrc{ .monitorId = { .deviceId = { .id = L"Device1", .instanceId = L"" }, .serialNumber = L"" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EA6B6934-D55F-49F5-A9A5-CFADE21FFFB8}").value() }; FancyZonesDataTypes::WorkAreaId deviceDst{ .monitorId = { .deviceId = { .id = L"Device2", .instanceId = L"" }, .serialNumber = L"" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EF1A8099-7D1E-4738-805A-571B31B02674}").value() }; - Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceSrc)); - + LayoutData layout { .uuid = FancyZonesUtils::GuidFromString(L"{361F96DD-FD10-4D01-ABAC-CC1C857294DD}").value() }; + Assert::IsTrue(AppliedLayouts::instance().ApplyLayout(deviceSrc, layout)); + AppliedLayouts::instance().CloneLayout(deviceSrc, deviceDst); - auto actualMap = AppliedLayouts::instance().GetAppliedLayoutMap(); - Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end()); - Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end()); - - auto expected = AppliedLayouts::instance().GetDeviceLayout(deviceSrc); - auto actual = AppliedLayouts::instance().GetDeviceLayout(deviceDst); - - Assert::IsTrue(expected.has_value()); - Assert::IsTrue(actual.has_value()); - Assert::IsTrue(expected.value().uuid == actual.value().uuid); + Assert::IsTrue(layout == AppliedLayouts::instance().GetDeviceLayout(deviceSrc)); + Assert::IsTrue(layout == AppliedLayouts::instance().GetDeviceLayout(deviceDst)); } TEST_METHOD (CloneDeviceInfoFromUnknownDevice) { FancyZonesDataTypes::WorkAreaId deviceSrc{ .monitorId = { .deviceId = { .id = L"Device1", .instanceId = L"" }, .serialNumber = L"" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EA6B6934-D55F-49F5-A9A5-CFADE21FFFB8}").value() }; FancyZonesDataTypes::WorkAreaId deviceDst{ .monitorId = { .deviceId = { .id = L"Device2", .instanceId = L"" }, .serialNumber = L"" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EF1A8099-7D1E-4738-805A-571B31B02674}").value() }; AppliedLayouts::instance().LoadData(); - Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceDst)); Assert::IsFalse(AppliedLayouts::instance().CloneLayout(deviceSrc, deviceDst)); Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(deviceSrc).has_value()); - Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(deviceDst).has_value()); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(deviceDst).has_value()); } TEST_METHOD (CloneDeviceInfoNullVirtualDesktopId) @@ -389,35 +330,25 @@ namespace FancyZonesUnitTests }; FancyZonesDataTypes::WorkAreaId deviceDst{ .monitorId = { .deviceId = { .id = L"Device2", .instanceId = L"" }, .serialNumber = L"" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EF1A8099-7D1E-4738-805A-571B31B02674}").value() }; - Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceSrc)); - Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceDst)); - + LayoutData layout{ .uuid = FancyZonesUtils::GuidFromString(L"{361F96DD-FD10-4D01-ABAC-CC1C857294DD}").value() }; + Assert::IsTrue(AppliedLayouts::instance().ApplyLayout(deviceSrc, layout)); + AppliedLayouts::instance().CloneLayout(deviceSrc, deviceDst); - auto actualMap = AppliedLayouts::instance().GetAppliedLayoutMap(); - Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end()); - Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end()); - - auto expected = AppliedLayouts::instance().GetDeviceLayout(deviceSrc); - auto actual = AppliedLayouts::instance().GetDeviceLayout(deviceDst); - - Assert::IsTrue(expected.has_value()); - Assert::IsTrue(actual.has_value()); - Assert::IsTrue(expected.value().uuid == actual.value().uuid); + Assert::IsTrue(layout == AppliedLayouts::instance().GetDeviceLayout(deviceSrc)); + Assert::IsTrue(layout == AppliedLayouts::instance().GetDeviceLayout(deviceDst)); } TEST_METHOD (ApplyLayout) { - // prepare - FancyZonesDataTypes::WorkAreaId deviceId { + FancyZonesDataTypes::WorkAreaId workAreaId { .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" }, .serialNumber = L"" }, .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}").value() }; - // test LayoutData expectedLayout { .uuid = FancyZonesUtils::GuidFromString(L"{33A2B101-06E0-437B-A61E-CDBECF502906}").value(), .type = FancyZonesDataTypes::ZoneSetLayoutType::Focus, @@ -427,47 +358,30 @@ namespace FancyZonesUnitTests .sensitivityRadius = 30 }; - AppliedLayouts::instance().ApplyLayout(deviceId, expectedLayout); - - Assert::IsFalse(AppliedLayouts::instance().GetAppliedLayoutMap().empty()); - Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(deviceId).has_value()); + AppliedLayouts::instance().ApplyLayout(workAreaId, expectedLayout); - auto actual = AppliedLayouts::instance().GetAppliedLayoutMap().find(deviceId)->second; - Assert::IsTrue(expectedLayout.type == actual.type); - Assert::AreEqual(expectedLayout.showSpacing, actual.showSpacing); - Assert::AreEqual(expectedLayout.spacing, actual.spacing); - Assert::AreEqual(expectedLayout.zoneCount, actual.zoneCount); - Assert::AreEqual(expectedLayout.sensitivityRadius, actual.sensitivityRadius); + Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(workAreaId).has_value()); + Assert::IsTrue(expectedLayout == AppliedLayouts::instance().GetAppliedLayoutMap().find(workAreaId)->second); } TEST_METHOD (ApplyLayoutReplace) { // prepare - FancyZonesDataTypes::WorkAreaId deviceId{ + FancyZonesDataTypes::WorkAreaId workAreaId{ .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" }, .serialNumber = L"" }, .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}").value() }; + + LayoutData layout{ + .uuid = FancyZonesUtils::GuidFromString(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}").value(), + .type = FancyZonesDataTypes::ZoneSetLayoutType::Rows, + .showSpacing = true, + .spacing = 3, + .zoneCount = 4, + .sensitivityRadius = 22 + }; - json::JsonObject root{}; - json::JsonArray layoutsArray{}; - { - json::JsonObject layout{}; - layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::UuidID, json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}")); - layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::TypeID, json::value(FancyZonesDataTypes::TypeToString(FancyZonesDataTypes::ZoneSetLayoutType::Rows))); - layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(true)); - layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(3)); - layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(4)); - layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(22)); - - json::JsonObject obj{}; - obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceIdID, json::value(L"DELA026#5&10a58c63&0&UID16777488_2194_1234_{61FA9FC0-26A6-4B37-A834-491C148DFC57}")); - obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout); - - layoutsArray.Append(obj); - } - root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray); - json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root); - AppliedLayouts::instance().LoadData(); + AppliedLayouts::instance().SetAppliedLayouts({ {workAreaId, layout} }); // test LayoutData expectedLayout{ @@ -479,18 +393,8 @@ namespace FancyZonesUnitTests .sensitivityRadius = 30 }; - AppliedLayouts::instance().ApplyLayout(deviceId, expectedLayout); - - Assert::AreEqual((size_t)1, AppliedLayouts::instance().GetAppliedLayoutMap().size()); - Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(deviceId).has_value()); - - auto actual = AppliedLayouts::instance().GetAppliedLayoutMap().find(deviceId)->second; - Assert::AreEqual(FancyZonesUtils::GuidToString(expectedLayout.uuid).value().c_str(), FancyZonesUtils::GuidToString(actual.uuid).value().c_str()); - Assert::IsTrue(expectedLayout.type == actual.type); - Assert::AreEqual(expectedLayout.showSpacing, actual.showSpacing); - Assert::AreEqual(expectedLayout.spacing, actual.spacing); - Assert::AreEqual(expectedLayout.zoneCount, actual.zoneCount); - Assert::AreEqual(expectedLayout.sensitivityRadius, actual.sensitivityRadius); + AppliedLayouts::instance().ApplyLayout(workAreaId, expectedLayout); + Assert::IsTrue(expectedLayout == AppliedLayouts::instance().GetDeviceLayout(workAreaId)); } TEST_METHOD (ApplyDefaultLayout) @@ -553,4 +457,245 @@ namespace FancyZonesUnitTests Assert::IsFalse(AppliedLayouts::instance().IsLayoutApplied(id2)); } }; + + TEST_CLASS (AppliedLayoutsSyncVirtualDesktops) + { + const GUID virtualDesktop1 = FancyZonesUtils::GuidFromString(L"{30387C86-BB15-476D-8683-AF93F6D73E99}").value(); + const GUID virtualDesktop2 = FancyZonesUtils::GuidFromString(L"{65F6343A-868F-47EE-838E-55A178A7FB7A}").value(); + const GUID deletedVirtualDesktop = FancyZonesUtils::GuidFromString(L"{2D9F3E2D-F61D-4618-B35D-85C9B8DFDFD8}").value(); + + LayoutData layout1{ .uuid = FancyZonesUtils::GuidFromString(L"{D7DBECFA-23FC-4F45-9B56-51CFA9F6ABA2}").value() }; + LayoutData layout2{ .uuid = FancyZonesUtils::GuidFromString(L"{B9EDB48C-EC48-4E82-993F-A15DC1FF09D3}").value() }; + LayoutData layout3{ .uuid = FancyZonesUtils::GuidFromString(L"{94CF0000-7814-4D72-9624-794060FA269C}").value() }; + LayoutData layout4{ .uuid = FancyZonesUtils::GuidFromString(L"{13FA7ADF-1B6C-4FB6-8142-254B77C128E2}").value() }; + + FancyZonesDataTypes::WorkAreaId GetWorkAreaID(int number, GUID virtualDesktop) + { + return FancyZonesDataTypes::WorkAreaId{ + .monitorId = { + .deviceId = { + .id = std::wstring(L"id-") + std::to_wstring(number), + .instanceId = std::wstring(L"id-") + std::to_wstring(number), + .number = number + }, + .serialNumber = std::wstring(L"serial-number-") + std::to_wstring(number) + }, + .virtualDesktopId = virtualDesktop + }; + } + + TEST_METHOD_INITIALIZE(Init) + { + AppliedLayouts::instance().LoadData(); + } + + TEST_METHOD_CLEANUP(CleanUp) + { + std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); + } + + TEST_METHOD(SyncVirtualDesktops_SwitchVirtualDesktop) + { + AppliedLayouts::TAppliedLayoutsMap layouts{}; + layouts.insert({ GetWorkAreaID(1, virtualDesktop1), layout1 }); + layouts.insert({ GetWorkAreaID(2, virtualDesktop1), layout2 }); + layouts.insert({ GetWorkAreaID(1, virtualDesktop2), layout3 }); + layouts.insert({ GetWorkAreaID(2, virtualDesktop2), layout4 }); + AppliedLayouts::instance().SetAppliedLayouts(layouts); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = virtualDesktop2; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } }; + AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1))); + Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1))); + Assert::IsTrue(layout3 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop2))); + Assert::IsTrue(layout4 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop2))); + } + + TEST_METHOD (SyncVirtualDesktops_CurrentVirtualDesktopDeleted) + { + AppliedLayouts::TAppliedLayoutsMap layouts{}; + layouts.insert({ GetWorkAreaID(1, virtualDesktop1), layout1 }); + layouts.insert({ GetWorkAreaID(2, virtualDesktop1), layout2 }); + layouts.insert({ GetWorkAreaID(1, deletedVirtualDesktop), layout3 }); + layouts.insert({ GetWorkAreaID(2, deletedVirtualDesktop), layout4 }); + AppliedLayouts::instance().SetAppliedLayouts(layouts); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = deletedVirtualDesktop; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1 } }; + AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1))); + Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1))); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, deletedVirtualDesktop)).has_value()); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, deletedVirtualDesktop)).has_value()); + } + + TEST_METHOD (SyncVirtualDesktops_NotCurrentVirtualDesktopDeleted) + { + AppliedLayouts::TAppliedLayoutsMap layouts{}; + layouts.insert({ GetWorkAreaID(1, virtualDesktop1), layout1 }); + layouts.insert({ GetWorkAreaID(2, virtualDesktop1), layout2 }); + layouts.insert({ GetWorkAreaID(1, deletedVirtualDesktop), layout3 }); + layouts.insert({ GetWorkAreaID(2, deletedVirtualDesktop), layout4 }); + AppliedLayouts::instance().SetAppliedLayouts(layouts); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = virtualDesktop1; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1 } }; + AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1))); + Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1))); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, deletedVirtualDesktop)).has_value()); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, deletedVirtualDesktop)).has_value()); + } + + TEST_METHOD (SyncVirtualDesktops_AllIdsFromRegistryAreNew) + { + AppliedLayouts::TAppliedLayoutsMap layouts{}; + layouts.insert({ GetWorkAreaID(1, deletedVirtualDesktop), layout1 }); + layouts.insert({ GetWorkAreaID(2, deletedVirtualDesktop), layout2 }); + AppliedLayouts::instance().SetAppliedLayouts(layouts); + + GUID currentVirtualDesktop = virtualDesktop1; + GUID lastUsedVirtualDesktop = deletedVirtualDesktop; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } }; + AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1))); + Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1))); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop2)).has_value()); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop2)).has_value()); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, deletedVirtualDesktop)).has_value()); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, deletedVirtualDesktop)).has_value()); + } + + TEST_METHOD (SyncVirtualDesktop_NoDesktopsInRegistry) + { + AppliedLayouts::TAppliedLayoutsMap layouts{}; + layouts.insert({ GetWorkAreaID(1, deletedVirtualDesktop), layout1 }); + layouts.insert({ GetWorkAreaID(2, deletedVirtualDesktop), layout2 }); + AppliedLayouts::instance().SetAppliedLayouts(layouts); + + GUID currentVirtualDesktop = GUID_NULL; + GUID lastUsedVirtualDesktop = deletedVirtualDesktop; + std::optional> virtualDesktopsInRegistry = std::nullopt; + AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, GUID_NULL))); + Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, GUID_NULL))); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, deletedVirtualDesktop)).has_value()); + Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, deletedVirtualDesktop)).has_value()); + } + + TEST_METHOD(SyncVirtualDesktops_SwithVirtualDesktopFirstTime) + { + AppliedLayouts::TAppliedLayoutsMap layouts{}; + layouts.insert({ GetWorkAreaID(1, GUID_NULL), layout1 }); + layouts.insert({ GetWorkAreaID(2, GUID_NULL), layout2 }); + AppliedLayouts::instance().SetAppliedLayouts(layouts); + + GUID currentVirtualDesktop = virtualDesktop2; + GUID lastUsedVirtualDesktop = GUID_NULL; + std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } }; + AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry); + + Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1))); + Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1))); + Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop2))); + Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop2))); + } + }; + + TEST_CLASS (AppliedLayoutsFromOutdatedFileMappingUnitTests) + { + FancyZonesData& m_fzData = FancyZonesDataInstance(); + std::wstring m_testFolder = L"FancyZonesUnitTests"; + std::wstring m_testFolderPath = PTSettingsHelper::get_module_save_folder_location(m_testFolder); + + TEST_METHOD_INITIALIZE(Init) + { + m_fzData.SetSettingsModulePath(m_testFolder); + } + + TEST_METHOD_CLEANUP(CleanUp) + { + // MoveAppliedLayoutsFromZonesSettings creates all of these files, clean up + std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); + std::filesystem::remove(CustomLayouts::CustomLayoutsFileName()); + std::filesystem::remove(LayoutHotkeys::LayoutHotkeysFileName()); + std::filesystem::remove(LayoutTemplates::LayoutTemplatesFileName()); + std::filesystem::remove_all(m_testFolderPath); + AppliedLayouts::instance().LoadData(); // clean data + } + + TEST_METHOD (MoveAppliedLayoutsFromZonesSettings) + { + // prepare + json::JsonObject root{}; + json::JsonArray devicesArray{}, customLayoutsArray{}, templateLayoutsArray{}, quickLayoutKeysArray{}; + + { + json::JsonObject activeZoneset{}; + activeZoneset.SetNamedValue(L"uuid", json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}")); + activeZoneset.SetNamedValue(L"type", json::value(FancyZonesDataTypes::TypeToString(FancyZonesDataTypes::ZoneSetLayoutType::Rows))); + + json::JsonObject obj{}; + obj.SetNamedValue(L"device-id", json::value(L"VSC9636#5&37ac4db&0&UID160005_3840_2160_{00000000-0000-0000-0000-000000000000}")); + obj.SetNamedValue(L"active-zoneset", activeZoneset); + + obj.SetNamedValue(L"editor-show-spacing", json::value(true)); + obj.SetNamedValue(L"editor-spacing", json::value(3)); + obj.SetNamedValue(L"editor-zone-count", json::value(4)); + obj.SetNamedValue(L"editor-sensitivity-radius", json::value(22)); + + devicesArray.Append(obj); + } + + root.SetNamedValue(L"devices", devicesArray); + root.SetNamedValue(L"custom-zone-sets", customLayoutsArray); + root.SetNamedValue(L"templates", templateLayoutsArray); + root.SetNamedValue(L"quick-layout-keys", quickLayoutKeysArray); + json::to_file(m_fzData.GetZoneSettingsPath(m_testFolder), root); + + // test + m_fzData.ReplaceZoneSettingsFileFromOlderVersions(); + AppliedLayouts::instance().LoadData(); + Assert::AreEqual((size_t)1, AppliedLayouts::instance().GetAppliedLayoutMap().size()); + + FancyZonesDataTypes::WorkAreaId id{ + .monitorId = { .deviceId = { .id = L"VSC9636", .instanceId = L"5&37ac4db&0&UID160005" }, .serialNumber = L"" }, + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value() + }; + Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(id).has_value()); + } + + TEST_METHOD (MoveAppliedLayoutsFromZonesSettingsNoAppliedLayoutsData) + { + // prepare + json::JsonObject root{}; + json::JsonArray customLayoutsArray{}, templateLayoutsArray{}, quickLayoutKeysArray{}; + root.SetNamedValue(L"custom-zone-sets", customLayoutsArray); + root.SetNamedValue(L"templates", templateLayoutsArray); + root.SetNamedValue(L"quick-layout-keys", quickLayoutKeysArray); + json::to_file(m_fzData.GetZoneSettingsPath(m_testFolder), root); + + // test + m_fzData.ReplaceZoneSettingsFileFromOlderVersions(); + AppliedLayouts::instance().LoadData(); + Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty()); + } + + TEST_METHOD (MoveAppliedLayoutsFromZonesSettingsNoFile) + { + // test + m_fzData.ReplaceZoneSettingsFileFromOlderVersions(); + AppliedLayouts::instance().LoadData(); + Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty()); + } + }; } \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkAreaIdTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkAreaIdTests.Spec.cpp index db5c660db0b..5b024b718d2 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkAreaIdTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkAreaIdTests.Spec.cpp @@ -43,11 +43,11 @@ namespace FancyZonesUnitTests Assert::IsFalse(id1 == id2); } - TEST_METHOD (VirtualDesktopNull) + TEST_METHOD (VirtualDesktopDifferent) { FancyZonesDataTypes::WorkAreaId id1{ .monitorId = { .deviceId = { .id = L"device", .instanceId = L"instance-id" }, .serialNumber = L"serial-number" }, - .virtualDesktopId = GUID_NULL + .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{F21F6F29-76FD-4FC1-8970-17AB8AD64847}").value() }; FancyZonesDataTypes::WorkAreaId id2{ @@ -55,14 +55,14 @@ namespace FancyZonesUnitTests .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{E21F6F29-76FD-4FC1-8970-17AB8AD64847}").value() }; - Assert::IsTrue(id1 == id2); + Assert::IsFalse(id1 == id2); } - TEST_METHOD (VirtualDesktopDifferent) + TEST_METHOD (VirtualDesktopNull) { FancyZonesDataTypes::WorkAreaId id1{ .monitorId = { .deviceId = { .id = L"device", .instanceId = L"instance-id" }, .serialNumber = L"serial-number" }, - .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{F21F6F29-76FD-4FC1-8970-17AB8AD64847}").value() + .virtualDesktopId = GUID_NULL }; FancyZonesDataTypes::WorkAreaId id2{ From 422813044d5b5caf62deed3e545111ed947ba6a7 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Thu, 21 Sep 2023 13:52:08 +0100 Subject: [PATCH 06/39] [QuickAccent]Cache computed all language characters (#28657) --- .../poweraccent/PowerAccent.Core/Languages.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 4a611f2082e..c20d9c8264e 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Concurrent; using PowerToys.PowerAccentKeyboardService; namespace PowerAccent.Core @@ -80,11 +81,15 @@ public static string[] GetDefaultLetterKey(LetterKey letter, Language lang) }; } + // Store the computed letters for each key, so that subsequent calls don't take as long. + private static ConcurrentDictionary _allLanguagesCache = new ConcurrentDictionary(); + // All private static string[] GetDefaultLetterKeyALL(LetterKey letter) { - // would be even better to loop through Languages and call these functions dynamically, but I don't know how to do that! - return GetDefaultLetterKeyCA(letter) + if (!_allLanguagesCache.ContainsKey(letter)) + { + _allLanguagesCache[letter] = GetDefaultLetterKeyCA(letter) .Union(GetDefaultLetterKeyCUR(letter)) .Union(GetDefaultLetterKeyCY(letter)) .Union(GetDefaultLetterKeyCZ(letter)) @@ -113,7 +118,10 @@ private static string[] GetDefaultLetterKeyALL(LetterKey letter) .Union(GetDefaultLetterKeySR(letter)) .Union(GetDefaultLetterKeySV(letter)) .Union(GetDefaultLetterKeyTK(letter)) - .ToArray(); + .ToArray(); + } + + return _allLanguagesCache[letter]; } // Currencies (source: https://www.eurochange.co.uk/travel-money/world-currency-abbreviations-symbols-and-codes-travel-money) From e545291461db6d7708f8f9d09adcd87bcb0001f4 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Thu, 21 Sep 2023 15:18:22 +0100 Subject: [PATCH 07/39] [Deps]Upgrade Windows App SDK to 1.4.1 (#28676) --- Directory.Packages.props | 2 +- NOTICE.md | 2 +- .../MeasureToolCore/PowerToys.MeasureToolCore.vcxproj | 8 ++++---- src/modules/MeasureTool/MeasureToolCore/packages.config | 2 +- .../powerrename/PowerRenameUILib/PowerRenameUI.vcxproj | 8 ++++---- src/modules/powerrename/PowerRenameUILib/packages.config | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ff663341762..d26fb2dbfb2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -40,7 +40,7 @@ - + diff --git a/NOTICE.md b/NOTICE.md index cbe70501233..07378214cc7 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -341,7 +341,7 @@ SOFTWARE. - Microsoft.Windows.CsWinRT 2.0.3 - Microsoft.Windows.SDK.BuildTools 10.0.22621.756 - Microsoft.Windows.SDK.Contracts 10.0.19041.1 -- Microsoft.WindowsAppSDK 1.4.230822000 +- Microsoft.WindowsAppSDK 1.4.230913002 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 - ModernWpfUI 0.9.4 diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index 4271e03a55e..ad51f8998b6 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -1,6 +1,6 @@  - + @@ -147,7 +147,7 @@ - + @@ -158,7 +158,7 @@ - - + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config index 817fac08122..ca3592e5b04 100644 --- a/src/modules/MeasureTool/MeasureToolCore/packages.config +++ b/src/modules/MeasureTool/MeasureToolCore/packages.config @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index 8287fd95976..c4051b8f035 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -1,7 +1,7 @@  - + true @@ -208,7 +208,7 @@ - + @@ -220,8 +220,8 @@ - - + + diff --git a/src/modules/powerrename/PowerRenameUILib/packages.config b/src/modules/powerrename/PowerRenameUILib/packages.config index eb4be241a80..53ee497bdd9 100644 --- a/src/modules/powerrename/PowerRenameUILib/packages.config +++ b/src/modules/powerrename/PowerRenameUILib/packages.config @@ -5,5 +5,5 @@ - + \ No newline at end of file From 1de6c7d19b2a226c9c91cf78653e7cb7174910b5 Mon Sep 17 00:00:00 2001 From: Dylan Briedis Date: Thu, 21 Sep 2023 08:04:02 -0700 Subject: [PATCH 08/39] [Registry Preview] Better preview in data grid like in Regedit (#28488) * Fix typo * Better data grid preview like regedit * Fix sorting of resource strings * Add error icons back in * Remove comments then trim whitespace * Better string detection --- .../RegistryPreviewUI/MainWindow.Utilities.cs | 124 +++++++++++++++++- .../RegistryPreviewUI/RegistryValue.xaml.cs | 2 +- .../Strings/en-US/Resources.resw | 11 +- 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs index 07f0837fdd0..bffaa7a3b66 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs @@ -8,7 +8,9 @@ using System.Diagnostics; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; +using System.Text; using System.Threading.Tasks; using Microsoft.UI.Input; using Microsoft.UI.Xaml; @@ -413,21 +415,75 @@ private bool ParseRegistryFile(string filenameText) value += registryLine; } - // Clean out any escaped characters in the value, only for the preview - value = StripEscapedCharacters(value); - // update the ListViewItem with the loaded value, based off REG value type switch (registryValue.Type) { case "ERROR": // do nothing break; + case "REG_SZ": + if (value == "\"") + { + // Value is most likely missing an end quote + registryValue.Type = "ERROR"; + value = resourceLoader.GetString("InvalidString"); + } + else + { + for (int i = 1; i < value.Length; i++) + { + if (value[i - 1] == '\\') + { + // Only allow these escape characters + if (value[i] != '"' && value[i] != '\\') + { + registryValue.Type = "ERROR"; + value = resourceLoader.GetString("InvalidString"); + break; + } + + i++; + } + + if (value[i - 1] != '\\' && value[i] == '"') + { + // Don't allow non-escaped quotes + registryValue.Type = "ERROR"; + value = resourceLoader.GetString("InvalidString"); + break; + } + } + + if (registryValue.Type != "ERROR") + { + // Clean out any escaped characters in the value, only for the preview + value = StripEscapedCharacters(value); + } + } + + registryValue.Value = value; + break; case "REG_BINARY": case "REG_NONE": if (value.Length <= 0) { value = resourceLoader.GetString("ZeroLength"); } + else + { + try + { + // Hexes are usually two characters (00), it's invalid if less or more than 2 + var bytes = value.Split(',').Select( + c => c.Length == 2 ? byte.Parse(c, NumberStyles.HexNumber, CultureInfo.InvariantCulture) : throw null); + value = string.Join(' ', bytes.Select(b => b.ToString("x2", CultureInfo.CurrentCulture))); + } + catch + { + registryValue.Type = "ERROR"; + value = resourceLoader.GetString("InvalidBinary"); + } + } registryValue.Value = value; @@ -436,6 +492,19 @@ private bool ParseRegistryFile(string filenameText) if (value.Length <= 0) { registryValue.Type = "ERROR"; + value = resourceLoader.GetString("InvalidDword"); + } + else + { + if (uint.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint dword)) + { + value = $"0x{dword:x8} ({dword})"; + } + else + { + registryValue.Type = "ERROR"; + value = resourceLoader.GetString("InvalidDword"); + } } registryValue.Value = value; @@ -444,8 +513,55 @@ private bool ParseRegistryFile(string filenameText) case "REG_QWORD": if (value.Length <= 0) { + registryValue.Type = "ERROR"; value = resourceLoader.GetString("InvalidQword"); } + else + { + try + { + // Hexes are usually two characters (00), it's invalid if less or more than 2 + var bytes = value.Split(',').Select( + c => c.Length == 2 ? byte.Parse(c, NumberStyles.HexNumber, CultureInfo.InvariantCulture) : throw null).ToArray(); + ulong qword = BitConverter.ToUInt64(bytes); + value = $"0x{qword:x8} ({qword})"; + } + catch + { + registryValue.Type = "ERROR"; + value = resourceLoader.GetString("InvalidQword"); + } + } + + registryValue.Value = value; + break; + case "REG_EXPAND_SZ": + case "REG_MULTI_SZ": + try + { + // Hexes are usually two characters (00), it's invalid if less or more than 2 + var bytes = value.Split(',').Select( + c => c.Length == 2 ? byte.Parse(c, NumberStyles.HexNumber, CultureInfo.InvariantCulture) : throw null).ToArray(); + + if (registryValue.Type == "REG_MULTI_SZ") + { + // Replace zeros (00,00) with spaces + for (int i = 0; i < bytes.Length; i += 2) + { + if (bytes[i] == 0 && bytes[i + 1] == 0) + { + bytes[i] = 0x20; + } + } + } + + value = Encoding.Unicode.GetString(bytes); + } + catch + { + registryValue.Type = "ERROR"; + value = resourceLoader.GetString("InvalidString"); + } registryValue.Value = value; break; @@ -1036,7 +1152,7 @@ private string ScanAndRemoveComments(string value) value = value.Remove(indexOf, value.Length - indexOf); } - return value; + return value.TrimEnd(); } /// diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs index a7df53f1f82..161e9cbeddb 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs @@ -33,7 +33,7 @@ public Uri ImageUri switch (Type) { case "REG_SZ": - case "REG_EXAND_SZ": + case "REG_EXPAND_SZ": case "REG_MULTI_SZ": return uriStringValue; case "ERROR": diff --git a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw index 6b766a65702..d637b2e1871 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw +++ b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw @@ -138,6 +138,12 @@ Registry files (*.reg) + + (Invalid binary value) + + + (Invalid DWORD (32-bit) value) + (Invalid QWORD (64-bit) value) @@ -147,6 +153,9 @@ File was not a Registry file + + (Invalid string value) + is larger than 10MB which is too large for this application. @@ -228,7 +237,7 @@ User Account Control - Value + Data Write to Registry From 8d5aa9fe6cc8297797367967604a0b56be46088b Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Fri, 22 Sep 2023 14:34:35 +0100 Subject: [PATCH 09/39] [Settings]Fix FancyZones switch windows shortcuts UI (#28712) --- .../Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml index de957df76cf..db1d3bb29ea 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml @@ -171,6 +171,8 @@ IsExpanded="True"> + + From b24ae12c5a6b425a7fe7c06df51770a0ef02cd58 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Fri, 22 Sep 2023 15:57:48 +0200 Subject: [PATCH 10/39] [ColorPicker][Settings]Fix duplicate name crash and cleanup (#28713) --- .../SettingsXAML/Views/ColorPickerPage.xaml | 2 +- .../Views/ColorPickerPage.xaml.cs | 25 ++++++++----------- .../ViewModels/ColorPickerViewModel.cs | 10 +++++--- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml index e847ce27cc0..8b583d31866 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml @@ -162,7 +162,7 @@ x:Uid="ColorFormatDialog" IsPrimaryButtonEnabled="{Binding IsValid, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" PrimaryButtonStyle="{ThemeResource AccentButtonStyle}" - SecondaryButtonClick="ColorFormatDialog_CancelButtonClick"> + Closed="ColorFormatDialog_Closed"> diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs index 1a408c88cd2..3d725a55fa8 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs @@ -112,7 +112,6 @@ private void Update() ColorFormatModel colorFormat = ColorFormatDialog.DataContext as ColorFormatModel; string oldName = ((KeyValuePair)ColorFormatDialog.Tag).Key; ViewModel.UpdateColorFormat(oldName, colorFormat); - ColorFormatDialog.Hide(); } private async void NewFormatClick(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) @@ -127,19 +126,6 @@ private async void NewFormatClick(object sender, Microsoft.UI.Xaml.RoutedEventAr await ColorFormatDialog.ShowAsync(); } - private void ColorFormatDialog_CancelButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) - { - if (ColorFormatDialog.Tag is KeyValuePair) - { - ColorFormatModel modifiedColorFormat = ColorFormatDialog.DataContext as ColorFormatModel; - KeyValuePair oldProperties = (KeyValuePair)ColorFormatDialog.Tag; - modifiedColorFormat.Name = oldProperties.Key; - modifiedColorFormat.Format = oldProperties.Value; - } - - ColorFormatDialog.Hide(); - } - private async void EditButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) { SettingsCard btn = sender as SettingsCard; @@ -162,5 +148,16 @@ public void RefreshEnabledState() { ViewModel.RefreshEnabledState(); } + + private void ColorFormatDialog_Closed(ContentDialog sender, ContentDialogClosedEventArgs args) + { + if (args.Result != ContentDialogResult.Primary && ColorFormatDialog.Tag is KeyValuePair) + { + ColorFormatModel modifiedColorFormat = ColorFormatDialog.DataContext as ColorFormatModel; + KeyValuePair oldProperties = (KeyValuePair)ColorFormatDialog.Tag; + modifiedColorFormat.Name = oldProperties.Key; + modifiedColorFormat.Format = oldProperties.Value; + } + } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs index c3d7be6e709..fc4b4a84b1e 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs @@ -300,13 +300,16 @@ private void ColorFormats_CollectionChanged(object sender, System.Collections.Sp UpdateColorFormats(); UpdateColorFormatPreview(); - ScheduleSavingOfSettings(); } private void ColorFormat_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - UpdateColorFormats(); - ScheduleSavingOfSettings(); + // Remaining properties are handled by the collection and by the dialog + if (e.PropertyName == nameof(ColorFormatModel.IsShown)) + { + UpdateColorFormats(); + ScheduleSavingOfSettings(); + } } private void ScheduleSavingOfSettings() @@ -437,6 +440,7 @@ internal void UpdateColorFormat(string oldName, ColorFormatModel colorFormat) SelectedColorRepresentationValue = colorFormat.Name; // name might be changed by the user } + UpdateColorFormats(); UpdateColorFormatPreview(); } From 2b0d50bcbd58d79d63d595e97e6a1e6435835431 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Fri, 22 Sep 2023 07:01:31 -0700 Subject: [PATCH 11/39] [Docs]Update for clarity and completeness (#28553) * Improve documentation style, add a few missing points * More updates --- README.md | 42 ++++++++++++------------ doc/devdocs/readme.md | 75 +++++++++++++++++++++++-------------------- 2 files changed, 61 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 3923a7c8278..e15feb37de6 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline ### Via GitHub with EXE [Recommended] -Go to [Microsoft PowerToys GitHub releases page][github-release-link], click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user. +Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user. [github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F47 @@ -79,15 +79,15 @@ There are [community driven install methods](./doc/unofficialInstallMethods.md) ## Third-Party Run Plugins -Collection of [third-party plugins](./doc/thirdPartyRunPlugins.md) created by the community that aren't distributed with PowerToys. +There is a collection of [third-party plugins](./doc/thirdPartyRunPlugins.md) created by the community that aren't distributed with PowerToys. ## Contributing -This project welcomes contributions of all types. Help spec'ing, design, documentation, finding bugs are ways everyone can help on top of coding features / bug fixes. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. +This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. -We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We will be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort. +We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We would be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort. -Most contributions require you to agree to a [Contributor License Agreement (CLA)][oss-CLA] declaring that you have the right to, and actually do, grant us the rights to use your contribution. +Most contributions require you to agree to a [Contributor License Agreement (CLA)][oss-CLA] declaring that you grant us the rights to use your contribution and that you have permission to do so. For guidance on developing for PowerToys, please read the [developer docs](/doc/devdocs) for a detailed breakdown. This includes how to setup your computer to compile. @@ -103,7 +103,7 @@ In this release, we focused on releasing new features, stability and improvement **Highlights** - - Keyboard manager now supports Numpad. Note, with previously bound hotkeys stored in settings.json would only react to non-Numpad keys now. If a user wishes to restore the previous behavior, it could be done by manually adding another binding for the Numpad variant. + - Keyboard manager now allows Numpad keys to be bound separately from the main keyboard. If using a previous configuration, only the main keyboard (not numpad) keys will be mapped. To restore the previous behavior, manually add separate bindings for numpad keys. - New utility: Crop And Lock allows you to crop a current application into a smaller window or just create a thumbnail. Focus the target window and press the shortcut to start cropping. - FancyZones code improvements and refactor. - Modernized ImageResizer UX. @@ -122,10 +122,10 @@ In this release, we focused on releasing new features, stability and improvement ### FancyZones - - Fixed issue causing canvas zones being drawn only when dragging in zone area. + - Fixed issue causing canvas zones being drawn only when dragging in the zone area. - Fixed user-defined default layout highlighting issue. - Refactored and improved code quality. - - Fixed issue causing wrong layout to be applied when duplicating non-selected layout. + - Fixed issue causing the wrong layout to be applied when duplicating a non-selected layout. ### File Locksmith @@ -148,11 +148,11 @@ In this release, we focused on releasing new features, stability and improvement ### Keyboard Manager - Rephrased labels to enhance clarity. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! - - Keyboard manager now supports Numpad. Note, with previously bound hotkeys stored in settings.json would only react to non-Numpad keys now. If a user wishes to restore the previous behavior, it could be done by manually adding another binding for the Numpad variant. + - Keyboard manager now allows Numpad keys to be bound separately from the main keyboard. If using a previous configuration, only the main keyboard (not numpad) keys will be mapped. To restore the previous behavior, manually add separate bindings for numpad keys. ### Mouse Highlighter - - Fixed highlighter being invisible issue for Always on Top windows. + - The highlighter is now visible in "Always on Top" windows. - Added settings for automatic activation on startup. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! ### Mouse Pointer Crosshairs @@ -162,26 +162,26 @@ In this release, we focused on releasing new features, stability and improvement ### Peek - Show correct file type for shortcuts. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Fixed issue causing wrong file size to be displayed. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed issue causing the wrong file size to be displayed. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Show 1 byte instead of 1 bytes file size. Thanks [@Deepak-Sangle](https://github.com/Deepak-Sangle)! - - Open URIs from developer files in default browser. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Show thumbnail and fallback to icon for unsupported files. Thanks [@pedrolamas](https://github.com/pedrolamas)! + - Open URIs from developer files in the default browser. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Show a thumbnail and fallback to an icon for unsupported files. Thanks [@pedrolamas](https://github.com/pedrolamas)! ### PowerRename - - Updated OOBE gif. Thanks [@ChaseKnowlden](https://github.com/ChaseKnowlden)! - - Localized renamed parts combo box. + - Updated the OOBE gif. Thanks [@ChaseKnowlden](https://github.com/ChaseKnowlden)! + - Localized the renamed parts combo box. - Introduced advanced counter functionality. - - Added remember last window size logic and optimized items sorting. + - Added remember last window size logic and optimized sorting of items. - Enable "Enumerate items" option by default. ### PowerToys Run - - Fixed issue causing original search to be abandoned when cycling through results. + - Fixed an issue causing original search to be abandoned when cycling through results. - Updated device and bluetooth results for Settings plugin. Thanks [@htcfreek](https://github.com/htcfreek)! - Fixed InvalidOperationException exception thrown. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Add Base64 Decoding function to the Value Generator plugin. Thanks [@LeagueOfPoro](https://github.com/LeagueOfPoro)! - - Added Keep shell open option for Shell plugin. + - Added a Keep shell open option for Shell plugin. - Added Crop And Lock to PowerToys plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! ### Registry Preview @@ -223,16 +223,16 @@ In this release, we focused on releasing new features, stability and improvement #### What is being planned for version 0.74 -For [v0.74][github-next-release-work], we'll work on below: +For [v0.74][github-next-release-work], we'll work on the items below: - Language selection - - Modernize and refresh UX of PowerToys based on WPF + - Modernize and refresh the UX of PowerToys based on WPF - Stability / bug fixes - Peek: UI improvements ## PowerToys Community -The PowerToys team is extremely grateful to have the [support of an amazing active community][community-link]. The work you do is incredibly important. PowerToys wouldn’t be nearly what it is today without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thank you and take time to recognize your work. Month over month, you directly help make PowerToys a better piece of software. +The PowerToys team is extremely grateful to have the [support of an amazing active community][community-link]. The work you do is incredibly important. PowerToys wouldn’t be nearly what it is today without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thank you and take time to recognize your work. Month by month, you directly help make PowerToys a better piece of software. ## Code of Conduct diff --git a/doc/devdocs/readme.md b/doc/devdocs/readme.md index 7c2a12061a4..69993d4596d 100644 --- a/doc/devdocs/readme.md +++ b/doc/devdocs/readme.md @@ -2,35 +2,36 @@ ## Fork, Clone, Branch and Create your PR -Once you've discussed your proposed feature/fix/etc. with a team member, and you've agreed an approach or a spec has been written and approved, it's time to start development: +Once you've discussed your proposed feature/fix/etc. with a team member, and an approach or a spec has been written and approved, it's time to start development: -1. Fork the repo if you haven't already +1. Fork the repo on GitHub if you haven't already 1. Clone your fork locally -1. Create & push a feature branch -1. Create a [Draft Pull Request (PR)](https://github.blog/2019-02-14-introducing-draft-pull-requests/) +1. Create a feature branch 1. Work on your changes +1. Create a [Draft Pull Request (PR)](https://github.blog/2019-02-14-introducing-draft-pull-requests/) +1. When ready, mark your PR as "ready for review". ## Rules - **Follow the pattern of what you already see in the code.** - [Coding style](style.md). -- Try to package new ideas/components into libraries that have nicely defined interfaces. -- Package new ideas into classes or refactor existing ideas into a class as you extend. -- When adding new classes/methods/changing existing code: add new unit tests or update the existing tests. +- Try to package new functionality/components into libraries that have nicely defined interfaces. +- Package new functionality into classes or refactor existing functionality into a class as you extend the code. +- When adding new classes/methods/changing existing code, add new unit tests or update the existing tests. ## GitHub Workflow - Before starting to work on a fix/feature, make sure there is an open issue to track the work. -- Add the `In progress` label to the issue, if not already present also add a `Cost-Small/Medium/Large` estimate and make sure all appropriate labels are set. -- If you are a community contributor, you will not be able to add labels to the issue, in that case just add a comment saying that you started to work on the issue and try to give an estimate for the delivery date. +- Add the `In progress` label to the issue, if not already present. Also add a `Cost-Small/Medium/Large` estimate and make sure all appropriate labels are set. +- If you are a community contributor, you will not be able to add labels to the issue; in that case just add a comment saying that you have started work on the issue and try to give an estimate for the delivery date. - If the work item has a medium/large cost, using the markdown task list, list each sub item and update the list with a check mark after completing each sub item. - When opening a PR, follow the PR template. -- When you'd like the team to take a look, (even if the work is not yet fully-complete), mark the PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the end result will be solid, testable, conformant code that is safe for us to merge. -- When the PR is approved, let the owner of the PR merge it. For community contributions the reviewer that approved the PR can also merge it. -- Use the `Squash and merge` option to merge a PR, if you don't want to squash it because there are logically different commits, use `Rebase and merge`. +- When you'd like the team to take a look (even if the work is not yet fully complete) mark the PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the end result will be solid, testable, conformant code that is safe for us to merge. +- When the PR is approved, let the owner of the PR merge it. For community contributions, the reviewer who approved the PR can also merge it. +- Use the `Squash and merge` option to merge a PR. If you don't want to squash it because there are logically different commits, use `Rebase and merge`. - We don't close issues automatically when referenced in a PR, so after the PR is merged: - - mark the issue(s), that the PR solved, with the `Resolution-Fix-Committed` label, remove the `In progress` label and if the issue is assigned to a project, move the item to the `Done` status. - - don't close the issue if it's a bug in the current released version since users tend to not search for closed issues, we will close the resolved issues when a new version is released. + - mark the issue(s) that the PR solved with the `Resolution-Fix-Committed` label, remove the `In progress` label and if the issue is assigned to a project, move the item to the `Done` status. + - don't close the issue if it's a bug in the current released version; since users tend to not search for closed issues, we will close the resolved issues when a new version is released. - if it's not a code fix that effects the end user, the issue can be closed (for example a fix in the build or a code refactoring and so on). ## Compiling PowerToys @@ -39,12 +40,16 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and you 1. Windows 10 April 2018 Update (version 1803) or newer 1. Visual Studio Community/Professional/Enterprise 2022 17.4 or newer -1. Git clone PowerToys repository +1. A local clone of the PowerToys repository + +### Install Visual Studio dependencies + 1. Open the `PowerToys.sln` file. 1. If you see a dialog that says `install extra components` in the solution explorer pane, click `install` ### Get Submodules to compile -We have submodules that need to be initialized before you can compile most parts of PowerToys. This should be a one time step. + +We have submodules that need to be initialized before you can compile most parts of PowerToys. This should be a one-time step. 1. Open a terminal 1. Navigate to the folder you cloned PowerToys to. @@ -52,9 +57,11 @@ We have submodules that need to be initialized before you can compile most parts ### Compiling Source Code -- Open `PowerToys.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`. -- The PowerToys binaries will be in your repo under `x64\Release\`. -- You can run `x64\Release\PowerToys.exe` directly without installing PowerToys, but some modules (i.e. PowerRename, ImageResizer, File Explorer extension etc.) will not be available unless you also build the installer and install PowerToys. +- Open `PowerToys.sln` in Visual Studio. +- In the `Solutions Configuration` drop-down menu select `Release` or `Debug`. +- From the `Build` menu choose `Build Solution`, or press Control+Shift+b on your keyboard. +- The build process may take several minutes depending on your computer's performance. Once it completes, the PowerToys binaries will be in your repo under `x64\Release\`. + - You can run `x64\Release\PowerToys.exe` directly without installing PowerToys, but some modules (i.e. PowerRename, ImageResizer, File Explorer extension etc.) will not be available unless you also build the installer and install PowerToys. ## Compile the installer @@ -62,7 +69,7 @@ Our installer is two parts, an EXE and an MSI. The EXE (Bootstrapper) contains - The EXE installs all prerequisites and installs PowerToys via the MSI. It has additional features such as the installation flags (see below). - The MSI installs the PowerToys binaries. -The installer can only be compiled in `Release` mode, step 1 and 2 must be done before the MSI will be able to be compiled. +The installer can only be compiled in `Release` mode; steps 1 and 2 must be performed before the MSI can be compiled. 1. Compile `PowerToys.sln`. Instructions are listed above. 1. Compile `BugReportTool.sln` tool. Path from root: `tools\BugReportTool\BugReportTool.sln` (details listed below) @@ -76,9 +83,11 @@ The installer can only be compiled in `Release` mode, step 1 and 2 must be done 1. Install the [WiX Toolset build tools](https://wixtoolset.org/docs/v3/releases/v3-14-0-6526/). (installer [direct link](https://wixtoolset.org/downloads/v3.14.0.6526/wix314.exe)) 1. Download [WiX binaries](https://wixtoolset.org/downloads/v3.14.0.6526/wix314-binaries.zip) and extract `wix.targets` to `C:\Program Files (x86)\WiX Toolset v3.14`. -### Locally building the installer prerequisite projects all at once from the command-line +### Building prerequisite projects + +#### From the command line -1. Open a `Developer Command Prompt for VS 2022` +1. From the start menu, open a `Developer Command Prompt for VS 2022` 1. Ensure `nuget.exe` is in your `%path%` 1. In the repo root, run these commands: @@ -93,20 +102,16 @@ nuget restore .\tools\StylesReportTool\StylesReportTool.sln msbuild -p:Platform=x64 -p:Configuration=Release .\tools\StylesReportTool\StylesReportTool.sln ``` -### Locally compiling the Bug reporting tool +#### From Visual Studio + +If you prefer, you can alternatively build prerequisite projects for the installer using the Visual Studio UI. 1. Open `tools\BugReportTool\BugReportTool.sln` 1. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` 1. From the `Build` menu, choose `Build Solution`. - -### Locally compiling the Webcam reporting tool - 1. Open `tools\WebcamReportTool\WebcamReportTool.sln` 1. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` 1. From the `Build` menu, choose `Build Solution`. - -### Locally compiling the Window styles reporting tool - 1. Open `tools\StylesReportTool\StylesReportTool.sln` 1. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` 1. From the `Build` menu, choose `Build Solution`. @@ -125,14 +130,14 @@ Head over to the wiki to see the [full list of supported installer arguments][in ## Debugging -The following configuration issue only applies if the user is a member of the Administrators group. +To debug the PowerToys application in Visual Studio, set the `runner` project as your start-up project, then start the debugger. -Some PowerToys modules require being run with the highest permission level if the current user is a member of the Administrators group. The highest permission level is required to be able to perform some actions when an elevated application (e.g. Task Manager) is in the foreground or is the target of an action. Without elevated privileges some PowerToys modules will still work but with some limitations: +Some PowerToys modules must be run with the highest permission level if the current user is a member of the Administrators group. The highest permission level is required to be able to perform some actions when an elevated application (e.g. Task Manager) is in the foreground or is the target of an action. Without elevated privileges some PowerToys modules will still work but with some limitations: -- The `FancyZones` module will be not be able to move an elevated window to a zone. +- The `FancyZones` module will not be able to move an elevated window to a zone. - The `Shortcut Guide` module will not appear if the foreground window belongs to an elevated application. -To run and debug PowerToys from Visual Studio when the user is a member of the Administrators group, Visual Studio has to be started with elevated privileges. If you want to avoid running Visual Studio with elevated privileges and don't mind the limitations described above, you can do the following: open the `runner` project properties and navigate to the `Linker -> Manifest File` settings, edit the `UAC Execution Level` property and change it from `highestAvailable (level='highestAvailable')` to `asInvoker (/level='asInvoker')`, save the changes. +Therefore, it is recommended to run Visual Studio with elevated privileges when debugging these scenarios. If you want to avoid running Visual Studio with elevated privileges and don't mind the limitations described above, you can do the following: open the `runner` project properties and navigate to the `Linker -> Manifest File` settings, edit the `UAC Execution Level` property and change it from `highestAvailable (level='highestAvailable')` to `asInvoker (/level='asInvoker'). ## How to create new PowerToys @@ -155,7 +160,7 @@ It's responsible for: ### [`Interface`](modules/interface.md) -Definition of the interface used by the [`runner`](/src/runner) to manage the PowerToys. All PowerToys must implement this interface. +The definition of the interface used by the [`runner`](/src/runner) to manage the PowerToys. All PowerToys must implement this interface. ### [`Common`](common.md) @@ -163,4 +168,4 @@ The common lib, as the name suggests, contains code shared by multiple PowerToys ### [`Settings`](settingsv2/) -Settings v2 is our current settings implementation. Please head over to the dev docs that goes into the current settings system. +Settings v2 is our current settings implementation. Please head over to the dev docs that describe the current settings system. From 1964aa6ccefec28e9e229048260d5591a7c347bd Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Mon, 25 Sep 2023 14:17:25 +0200 Subject: [PATCH 12/39] [PTRun][Settings plugin]Fix search for updates in windows 11 (#28735) --- .../WindowsSettings.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json index c7a1a8e950b..2695b7c1d25 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json @@ -1240,7 +1240,15 @@ "Name": "WindowsUpdateCheckForUpdates", "Areas": [ "AreaUpdateAndSecurity" ], "Type": "AppSettingsApp", - "Command": "ms-settings:windowsupdate-action" + "Command": "ms-settings:windowsupdate-action", + "DeprecatedInBuild": 22000 + }, + { + "Name": "WindowsUpdateCheckForUpdates", + "Areas": [ "AreaUpdateAndSecurity" ], + "Type": "AppSettingsApp", + "Command": "ms-settings:windowsupdate", + "IntroducedInBuild": 22000 }, { "Name": "WindowsUpdateAdvancedOptions", From d8ffa36bc609344b881a49ef6a9c4a908d67e0d4 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Mon, 25 Sep 2023 15:24:25 +0200 Subject: [PATCH 13/39] Escape > with > and quotes with " in style attribute (#28762) --- .../previewpane/common/Utilities/SvgPreviewHandlerHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs b/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs index 5cb346d70e0..d9623b8490c 100644 --- a/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs +++ b/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs @@ -210,7 +210,7 @@ public static string AddStyleSVG(string stringSvgData) // max-width and max-height not supported. Extra CSS is needed for it to work. string scaling = $"max-width: {width} ; max-height: {height} ;"; - scaling += $" _height:expression(this.scrollHeight > {heightR} ? \" {height}\" : \"auto\"); _width:expression(this.scrollWidth > {widthR} ? \"{width}\" : \"auto\");"; + scaling += $" _height:expression(this.scrollHeight > {heightR} ? " {height}" : "auto"); _width:expression(this.scrollWidth > {widthR} ? "{width}" : "auto");"; string newStyle = $"style=\"{scaling}{centering}{oldStyle}\""; int insertAt = stringSvgData.IndexOf(">", StringComparison.InvariantCultureIgnoreCase); From 28bd0686cc27f643287fda9780002472e57780f1 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Mon, 25 Sep 2023 16:06:55 +0100 Subject: [PATCH 14/39] [CropAndLock]Fix crash on reparent SetPosition fail (#28763) --- .../CropAndLock/CropAndLock/ReparentCropAndLockWindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/CropAndLock/CropAndLock/ReparentCropAndLockWindow.cpp b/src/modules/CropAndLock/CropAndLock/ReparentCropAndLockWindow.cpp index cdf9fb3eb06..83087502c88 100644 --- a/src/modules/CropAndLock/CropAndLock/ReparentCropAndLockWindow.cpp +++ b/src/modules/CropAndLock/CropAndLock/ReparentCropAndLockWindow.cpp @@ -131,7 +131,10 @@ void ReparentCropAndLockWindow::CropAndLock(HWND windowToCrop, RECT cropRect) SetWindowLongPtrW(m_currentTarget, GWL_STYLE, targetStyle); auto x = -cropRect.left; auto y = -cropRect.top; - winrt::check_bool(SetWindowPos(m_currentTarget, nullptr, x, y, 0, 0, SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOZORDER)); + if (0 == SetWindowPos(m_currentTarget, nullptr, x, y, 0, 0, SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOZORDER)) + { + MessageBoxW(nullptr, L"CropAndLock couldn't properly reparent the target window. It might not handle reparenting well.", L"CropAndLock", MB_ICONERROR); + } } void ReparentCropAndLockWindow::Hide() From a1e0bd51617b2bea189cb16324eecd0e13fa7b86 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Mon, 25 Sep 2023 16:23:30 +0100 Subject: [PATCH 15/39] [Peek]Fix IsDevFilePreview and white flash (#28734) --- .../Controls/BrowserControl.xaml.cs | 32 ++++++++++++++++--- .../WebBrowserPreviewer.cs | 2 ++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs index 1fbe78a1660..61efadd7fc7 100644 --- a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs +++ b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs @@ -24,7 +24,7 @@ public sealed partial class BrowserControl : UserControl, IDisposable /// private Uri? _navigatedUri; - private Color originalBackgroundColor; + private Color? _originalBackgroundColor; public delegate void NavigationCompletedHandler(WebView2? sender, CoreWebView2NavigationCompletedEventArgs? args); @@ -52,6 +52,7 @@ public Uri? Source typeof(BrowserControl), new PropertyMetadata(null, new PropertyChangedCallback((d, e) => ((BrowserControl)d).OnIsDevFilePreviewChanged()))); + // Will actually be true for Markdown files as well. public bool IsDevFilePreview { get @@ -101,6 +102,11 @@ public void Navigate() private void SourcePropertyChanged() { OpenUriDialog.Hide(); + + // Setting the background color to transparent. + // This ensures that non-HTML files are displayed with a transparent background. + PreviewBrowser.DefaultBackgroundColor = Color.FromArgb(0, 0, 0, 0); + Navigate(); } @@ -113,6 +119,10 @@ private void OnIsDevFilePreviewChanged() { PreviewBrowser.CoreWebView2.SetVirtualHostNameToFolderMapping(Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.VirtualHostName, Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.MonacoDirectory, CoreWebView2HostResourceAccessKind.Allow); } + else + { + PreviewBrowser.CoreWebView2.ClearVirtualHostNameToFolderMapping(Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.VirtualHostName); + } } } @@ -123,7 +133,10 @@ private async void PreviewWV2_Loaded(object sender, RoutedEventArgs e) await PreviewBrowser.EnsureCoreWebView2Async(); // Storing the original background color so it can be reset later for specific file types like HTML. - originalBackgroundColor = PreviewBrowser.DefaultBackgroundColor; + if (!_originalBackgroundColor.HasValue) + { + _originalBackgroundColor = PreviewBrowser.DefaultBackgroundColor; + } // Setting the background color to transparent when initially loading the WebView2 component. // This ensures that non-HTML files are displayed with a transparent background. @@ -142,6 +155,10 @@ private async void PreviewWV2_Loaded(object sender, RoutedEventArgs e) { PreviewBrowser.CoreWebView2.SetVirtualHostNameToFolderMapping(Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.VirtualHostName, Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.MonacoDirectory, CoreWebView2HostResourceAccessKind.Allow); } + else + { + PreviewBrowser.CoreWebView2.ClearVirtualHostNameToFolderMapping(Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.VirtualHostName); + } PreviewBrowser.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded; PreviewBrowser.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested; @@ -158,11 +175,16 @@ private void CoreWebView2_DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMC { // If the file being previewed is HTML or HTM, reset the background color to its original state. // This is done to ensure that HTML and HTM files are displayed as intended, with their own background settings. - if (Source?.ToString().EndsWith(".html", StringComparison.OrdinalIgnoreCase) == true || - Source?.ToString().EndsWith(".htm", StringComparison.OrdinalIgnoreCase) == true) + // This shouldn't be done for dev file previewer. + if (!IsDevFilePreview && + (Source?.ToString().EndsWith(".html", StringComparison.OrdinalIgnoreCase) == true || + Source?.ToString().EndsWith(".htm", StringComparison.OrdinalIgnoreCase) == true)) { // Reset to default behavior for HTML files - PreviewBrowser.DefaultBackgroundColor = originalBackgroundColor; + if (_originalBackgroundColor.HasValue) + { + PreviewBrowser.DefaultBackgroundColor = _originalBackgroundColor.Value; + } } DOMContentLoaded?.Invoke(sender, args); diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs index d3403b4c228..f13bad1d395 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs @@ -118,6 +118,8 @@ await Dispatcher.RunOnUiThread(async () => } else { + // Simple html file to preview. Shouldn't do things like enabling scripts or using a virtual mapped directory. + IsDevFilePreview = false; Preview = new Uri(File.Path); } }); From 77063e0767a0619abfc339db3a2a564d27310b6c Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Tue, 26 Sep 2023 22:36:50 +0100 Subject: [PATCH 16/39] 0.74 changelogs (#28775) Co-authored-by: Aaron Junker Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> Co-authored-by: Davide Giacometti Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> --- .github/actions/spell-check/expect.txt | 4 +- README.md | 192 +++++++++++++------------ 2 files changed, 106 insertions(+), 90 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index c5b83b656db..4186914fddc 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -112,7 +112,7 @@ AUTHN AUTHZ AUTOHIDE AUTOMATIONPROPERTIES -autorun +Autorun AUTOUPDATE AValid awakeness @@ -144,7 +144,7 @@ Blockquotes blogs Blt BLUEGRAY -bluetooth +Bluetooth BLURBEHIND BLURREGION bmi diff --git a/README.md b/README.md index e15feb37de6..877fa480130 100644 --- a/README.md +++ b/README.md @@ -39,19 +39,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user. -[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F47 -[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F46 -[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.73.0/PowerToysUserSetup-0.73.0-x64.exe -[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.73.0/PowerToysUserSetup-0.73.0-arm64.exe -[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.73.0/PowerToysSetup-0.73.0-x64.exe -[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.73.0/PowerToysSetup-0.73.0-arm64.exe +[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F48 +[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F47 +[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.0/PowerToysUserSetup-0.74.0-x64.exe +[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.0/PowerToysUserSetup-0.74.0-arm64.exe +[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.0/PowerToysSetup-0.74.0-x64.exe +[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.0/PowerToysSetup-0.74.0-arm64.exe | Description | Filename | sha256 hash | |----------------|----------|-------------| -| Per user - x64 | [PowerToysUserSetup-0.73.0-x64.exe][ptUserX64] | BA55D245BDD734FD6F19803DD706A3AB8E0ABC491591195534997CF2122D3B7E | -| Per user - ARM64 | [PowerToysUserSetup-0.73.0-arm64.exe][ptUserArm64] | FBFA40EA5FFA05236A7CCDD05E5142EE0C93D7485B965784196ED9B086BFEBF4 | -| Machine wide - x64 | [PowerToysSetup-0.73.0-x64.exe][ptMachineX64] | 7FDA06292C7C2E6DA5AEF88D8E9D3DE89D331E9E356A232289F9B37CE4503894 | -| Machine wide - ARM64 | [PowerToysSetup-0.73.0-arm64.exe][ptMachineArm64] | 4260AA30A1F52F194EE07E9E7ECD9E9F4CF35289267F213BC933F7A5191AC17C | +| Per user - x64 | [PowerToysUserSetup-0.74.0-x64.exe][ptUserX64] | 1C4ECE9F11488BAFFAE6B76D2B0504FA18BFFEA11EBC38BCC87F5D86AEA87C7C | +| Per user - ARM64 | [PowerToysUserSetup-0.74.0-arm64.exe][ptUserArm64] | 4F3842FAB0839A361A15A06B7720BA8A0FE7F9AF98EA94245C08DEF37678CA4A | +| Machine wide - x64 | [PowerToysSetup-0.74.0-x64.exe][ptMachineX64] | 648992E8CEA08F3C63C7CCBD554ADDF500ECBC4560187310BC12E6CB9C2F38E3 | +| Machine wide - ARM64 | [PowerToysSetup-0.74.0-arm64.exe][ptMachineArm64] | 2B6D92F1A0EA688C7EE882050AC9B030C8B3A18765163FB6D67E5E694A4D4FE3 | This is our preferred method. @@ -97,138 +97,154 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/ Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on. -### 0.73 - August 2023 Update +### 0.74 - September 2023 Update -In this release, we focused on releasing new features, stability and improvements. +In this release, we focused on stability and improvements. **Highlights** - - Keyboard manager now allows Numpad keys to be bound separately from the main keyboard. If using a previous configuration, only the main keyboard (not numpad) keys will be mapped. To restore the previous behavior, manually add separate bindings for numpad keys. - - New utility: Crop And Lock allows you to crop a current application into a smaller window or just create a thumbnail. Focus the target window and press the shortcut to start cropping. - - FancyZones code improvements and refactor. - - Modernized ImageResizer UX. - - PowerRename advanced counter functionality. + - Upgraded to Windows App SDK 1.4.1, increasing stability of WinUI3 utilities. Thanks [@dongle-the-gadget](https://github.com/dongle-the-gadget) for starting the upgrade! + - Text Extractor was upgraded to its version 2.0, with a new overlay, table mode and more Quality of Life improvements. Thanks [@TheJoeFin](https://github.com/TheJoeFin)! + - Improved FancyZones stability, fixing some layout resets and improving handling of newly created windows on Windows 11. + - Fixed many silent crashes that were reported to Watson and the user's event viewer. ### General - - Added missing CoUninitialize call in elevation logic. Thanks [@sredna](https://github.com/sredna)! - - New utility: Crop And Lock. Thanks [@robmikh](https://github.com/robmikh)! and [@kevinguo305](https://github.com/kevinguo305)! - - Added new /helped fabric bot command to GitHub repo. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! - - Fixed crashes caused by invalid settings. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Turning animations off in Windows Settings will now also turn them off in PowerToys. + - Upgraded the Windows App SDK dependency to 1.4.1. Thanks [@dongle-the-gadget](https://github.com/dongle-the-gadget) for the original 1.4.0 upgrade! + - Show in the thumbnail label and application titles when running as administrator. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Upgraded the Win UI Community Toolkit dependency to 8.0. Thanks [@niels9001](https://github.com/niels9001)! -### Always On Top +### Awake - - Added border transparency. + - Added down-sampled variants to the application's icon. Thanks [@morriscurtis](https://github.com/morriscurtis)! -### FancyZones +### Color Picker - - Fixed issue causing canvas zones being drawn only when dragging in the zone area. - - Fixed user-defined default layout highlighting issue. - - Refactored and improved code quality. - - Fixed issue causing the wrong layout to be applied when duplicating a non-selected layout. + - After adding a new color in the editor, the history will scroll the new color into view. Thanks [@peerpalo](https://github.com/peerpalo)! -### File Locksmith +### Crop and Lock + - Fixed a Crop and Lock crash that would occur when trying to reparent a window crashes the target application. An error message is shown instead. - - Icon update. Thanks [@jmaraujouy](https://github.com/jmaraujouy)! - -### File Explorer add-ons +### FancyZones + + - Set the process and main thread priority to normal. + - Fixed handling newly created windows on Windows 11. + - Fixed scenarios where opening the FancyZones Editor would reset the layouts. - - Fixed issue causing thumbnail previewers to lock files. - - Open URIs from developer files in default browser. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! +### File Explorer add-ons -### Installer + - Optimized CPU usage for generating SVG thumbnails. + - Improved handling of Gcode Thumbnails, including JPG and QOI formats. Thanks [@pedrolamas](https://github.com/pedrolamas)! + - Better handled errors when sending telemetry, which were causing reported crashes. + - Fixed some thumbnails not being shown centered like before the optimization. - - Fixed PowerToys autorun after installing as SYSTEM user. - - Removed CreateScheduledTask custom action to handle task creation only from runner code. +### File Locksmith -### Image Resizer + - Shows files opened by processes with PID greater than 65535. Thanks [@poke30744](https://github.com/poke30744)! + - Fixed a GDI object leak in the context menu which would crash Explorer. + +### Find My Mouse - - Moved from ModernWPF to WpfUI to refresh and modernize UI/UX. Thanks [@niels9001](https://github.com/niels9001)! + - Added new activation methods, including by hotkey. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -### Keyboard Manager +### Hosts File Editor - - Rephrased labels to enhance clarity. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! - - Keyboard manager now allows Numpad keys to be bound separately from the main keyboard. If using a previous configuration, only the main keyboard (not numpad) keys will be mapped. To restore the previous behavior, manually add separate bindings for numpad keys. + - Ignore the default ACME sample entries in the hosts file. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Improved save error handling and added better error messages. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Corrected a check for an error when signaling the application to start as administrator. + - Refactored the context menu. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed dialogs overlapping the title bar after the upgrade to Windows App SDK 1.4. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -### Mouse Highlighter +### Keyboard Manager - - The highlighter is now visible in "Always on Top" windows. - - Added settings for automatic activation on startup. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Distinguish between the regular minus key and the numpad minus key. -### Mouse Pointer Crosshairs +### Mouse Without Borders - - Added settings for automatic activation on startup. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed a crash when trying to restart the application. ### Peek - - Show correct file type for shortcuts. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Fixed issue causing the wrong file size to be displayed. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Show 1 byte instead of 1 bytes file size. Thanks [@Deepak-Sangle](https://github.com/Deepak-Sangle)! - - Open URIs from developer files in the default browser. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Show a thumbnail and fallback to an icon for unsupported files. Thanks [@pedrolamas](https://github.com/pedrolamas)! + - Using Peek on HTML files will show a white background by default, similar to a browser's default behavior. + - Fix a white flash on Dark theme when switching file and improved the development file preview detection and adjustments. ### PowerRename - - Updated the OOBE gif. Thanks [@ChaseKnowlden](https://github.com/ChaseKnowlden)! - - Localized the renamed parts combo box. - - Introduced advanced counter functionality. - - Added remember last window size logic and optimized sorting of items. - - Enable "Enumerate items" option by default. + - Fixed a crash caused by big counter values on the new enumeration method. ### PowerToys Run - - Fixed an issue causing original search to be abandoned when cycling through results. - - Updated device and bluetooth results for Settings plugin. Thanks [@htcfreek](https://github.com/htcfreek)! - - Fixed InvalidOperationException exception thrown. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Add Base64 Decoding function to the Value Generator plugin. Thanks [@LeagueOfPoro](https://github.com/LeagueOfPoro)! - - Added a Keep shell open option for Shell plugin. - - Added Crop And Lock to PowerToys plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - It's now possible to select which shell is used by the Shell plugin. + - A combobox option type was added to the plugin options. + - Fixed a bug in the Calculator plugin that was causing decimal numbers to be misinterpreted on locales where the dot (`.`) character isn't used as a decimal or digit separator. + - Improved the Program plugin stability when it fails to load a program's thumbnail at startup. + - The use of Pinyin for querying some plugins can now be turned on in Settings. Thanks [@ChaseKnowlden](https://github.com/ChaseKnowlden)! + - Refactored option types for plugin and added number, string and composite types to be used in the future. Thanks [@htcfreek](https://github.com/htcfreek)! + - Fixed the entry for searching for Windows updates in the Settings plugin. Thanks [@htcfreek](https://github.com/htcfreek)! + +### Quick Accent + + - The "All languages" character set is now calculated by programmatically querying the characters for every available language. Thanks [@dannysummerlin](https://github.com/dannysummerlin)! + - Added é to the Norwegian and Swedish languages. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! + - Added a runtime cache to the "All languages" character set, to only calculate accents once per key. ### Registry Preview - - Updated AppBarButtons to use an explicit AppBarButton.Icon. Thanks [@randyrants](https://github.com/randyrants)! - - Fixed crash on clicking Save As button. + - Fixed focusing issues at startup. + - Improved the data visualization to show data in a similar way to the Windows Registry Editor. Thanks [@dillydylann](https://github.com/dillydylann)! ### Runner - - Removed unneeded RegisterWindowMessage from tray icon logic. Thanks [@sredna](https://github.com/sredna)! - - Fixed startup looping issue. - - Improved old logs and installers cleanup logic. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed hanging when a bug report was generated from the flyout. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -### Screen Ruler +### Settings - - Use proper resources file. + - Improved the way the OOBE window reacts to Windows theme change. + - Fixed an issue that made it impossible to change the "Switch between windows in the current zone" "Next window" shortcut for FancyZones. + - Fixed a crash when entering a duplicate name for a color in the Color Picker page and improved clean up when cancelling a color edit. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -### Settings +### Text Extractor - - Fixed issue causing problems with modifier keys and ShortcutControl. Thanks [@sh0ckj0ckey](https://github.com/sh0ckj0ckey)! - - Fixed crash when clicking "Windows color settings" link. - - Added support for launching Settings app directly. - - Fixed issue causing DisplayDescription not showing for PowerToys Run PluginAdditionalOption. - - Fixed issue causing FileLocksmith 'Show File Locksmith in' setting not showing correct value. - - Fixed issue causing Awake on/off toggle in Settings flyout not to work when Settings Awake page is opened. + - Text Extractor 2.0, with a new overlay, table mode and more Quality of Life improvements. Thanks [@TheJoeFin](https://github.com/TheJoeFin)! ### Documentation - - Added documentation for PowerToys Run third-party plugins. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Fixed broken links in keyboardmanagerui.md. Thanks [@shubhsardana29](https://github.com/shubhsardana29)! - - Updated core team in COMMUNITY.md. - - Fixed broken links in ui-architecture.md. Thanks [@SamB](https://github.com/SamB)! - - Updated community.valuegenerator.md with Base64DecodeRequest description. + - SECURITY.md was updated from 0.0.2 to 0.0.9. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! + - Improved the README and main development document for clarity and completeness. Thanks [@codeofdusk](https://github.com/codeofdusk) and [@aprilbbrockhoeft](https://github.com/aprilbbrockhoeft)! ### Development - - Updated test packages and StyleCop. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Condense NuGet Restore into MSBuild Tasks. Thanks [@snickler](https://github.com/snickler)! + - Fixed PowerToys Run DateTime plugin tests that were failing depending on locale, so that they can be run correctly on all dev machines. + - Fixed PowerToys Run System plugin tests that were failing for certain network interfaces, so that they can be run correctly on all dev machines. Thanks [@snickler](https://github.com/snickler)! + - Fixed a markdown bug on the GitHub /helped command. + - Switched build pipelines to a new agent pool. Thanks [@DHowett](https://github.com/DHowett)! + - New .cs files created in Visual Studio get the header added automatically. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -#### What is being planned for version 0.74 +#### What is being planned for version 0.75 -For [v0.74][github-next-release-work], we'll work on the items below: +For [v0.75][github-next-release-work], we'll work on the items below: - Language selection - - Modernize and refresh the UX of PowerToys based on WPF + - .NET 8 upgrade + - Policy support for managing PowerToys Run plugins. +*Attention*: A breaking change is planned (for 0.75), in which each plugin has to declare its identifier programmatically so that it can be controlled through GPO. For third-party plugin developers, please check https://github.com/microsoft/PowerToys/pull/27468 for more details. + + - New utility: Environment Variables Editor. Here's a Work in Progress preview: + +![Environment Variables Editor WIP](https://github.com/microsoft/PowerToys/assets/26118718/f99532a8-5aae-481b-a662-19a95f4aa03d) + + - New Settings homepage. Here's a Work in Progress preview: + +![PowerToys Settings Dashboard WIP](https://github.com/microsoft/PowerToys/assets/26118718/938a5715-0a9b-4fe9-9e15-adfec92da694) + + - Modernize and refresh the UX of PowerToys based on WPF. Here's Work in Progress previews for the modules "PowerToys Run" and "Color Picker": + +![PowerToys Run UI refresh WIP](https://github.com/microsoft/PowerToys/assets/9866362/16903bcb-c18e-49fb-93ca-738b81957055) + +![ColorPicker UI refresh WIP](https://github.com/microsoft/PowerToys/assets/9866362/ceebe54b-de63-4ce7-afcb-2cd4280bf4d1) + - Stability / bug fixes - - Peek: UI improvements ## PowerToys Community From 1f86eefed129bbbaaa15c67da94806921fbe2c25 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Wed, 27 Sep 2023 16:35:43 +0100 Subject: [PATCH 17/39] [HotFix][QuickAccent]Fix missing characters (#28802) * [HotFix][QuickAccent]Fix missing characters * Remove some duplicate characters * Fix spellcheck --- .../poweraccent/PowerAccent.Core/Languages.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index c20d9c8264e..6eece7b6f2b 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -118,12 +118,55 @@ private static string[] GetDefaultLetterKeyALL(LetterKey letter) .Union(GetDefaultLetterKeySR(letter)) .Union(GetDefaultLetterKeySV(letter)) .Union(GetDefaultLetterKeyTK(letter)) + .Union(GetDefaultLetterKeyAllLanguagesOnly(letter)) .ToArray(); } return _allLanguagesCache[letter]; } + // Contains all characters that should be shown in all languages but currently don't belong to any of the single languages available for that letter. + // These characters can be removed from this list after they've been added to one of the other languages for that specific letter. + private static string[] GetDefaultLetterKeyAllLanguagesOnly(LetterKey letter) + { + return letter switch + { + LetterKey.VK_A => new[] { "α", "ȧ" }, + LetterKey.VK_B => new[] { "ḃ", "β" }, + LetterKey.VK_C => new[] { "ċ", "χ", "°C", "©", "ℂ" }, + LetterKey.VK_D => new[] { "ḍ", "ḋ", "δ" }, + LetterKey.VK_E => new[] { "ε", "η", "∈" }, + LetterKey.VK_F => new[] { "ḟ", "°F" }, + LetterKey.VK_G => new[] { "ģ", "ǧ", "ġ", "ĝ", "ǥ", "γ" }, + LetterKey.VK_H => new[] { "ḣ", "ĥ", "ħ" }, + LetterKey.VK_I => new[] { "ι" }, + LetterKey.VK_J => new[] { "ĵ" }, + LetterKey.VK_K => new[] { "ķ", "ǩ", "κ" }, + LetterKey.VK_L => new[] { "ļ", "₺", "λ" }, // ₺ is in VK_T for other languages, but not VK_L, so we add it here. + LetterKey.VK_M => new[] { "ṁ", "μ" }, + LetterKey.VK_N => new[] { "ņ", "ṅ", "ⁿ", "ν", "ℕ" }, + LetterKey.VK_O => new[] { "ȯ", "ω", "ο" }, + LetterKey.VK_P => new[] { "ṗ", "φ", "ψ", "℗" }, + LetterKey.VK_Q => new[] { "ℚ" }, + LetterKey.VK_R => new[] { "ṙ", "ρ", "®", "ℝ" }, + LetterKey.VK_S => new[] { "ṡ", "σ", "\u00A7" }, + LetterKey.VK_T => new[] { "ţ", "ṫ", "ŧ", "θ", "τ", "™" }, + LetterKey.VK_U => new[] { "ŭ", "υ" }, + LetterKey.VK_V => new[] { "V̇" }, + LetterKey.VK_W => new[] { "ẇ" }, + LetterKey.VK_X => new[] { "ẋ", "ξ", "×" }, + LetterKey.VK_Y => new[] { "ẏ" }, + LetterKey.VK_Z => new[] { "ʒ", "ǯ", "ζ", "ℤ" }, + LetterKey.VK_COMMA => new[] { "∙", "₋", "⁻", "–" }, // – is in VK_MINUS for other languages, but not VK_COMMA, so we add it here. + LetterKey.VK_PERIOD => new[] { "\u0300", "\u0301", "\u0302", "\u0303", "\u0304", "\u0308", "\u030C" }, + LetterKey.VK_MINUS => new[] { "~", "‐", "‑", "‒", "—", "―", "⁓", "−", "⸺", "⸻" }, + LetterKey.VK_SLASH_ => new[] { "÷" }, + LetterKey.VK_DIVIDE_ => new[] { "÷" }, + LetterKey.VK_MULTIPLY_ => new[] { "×", "⋅" }, + _ => Array.Empty(), + }; + } + // Currencies (source: https://www.eurochange.co.uk/travel-money/world-currency-abbreviations-symbols-and-codes-travel-money) private static string[] GetDefaultLetterKeyCUR(LetterKey letter) { From 59fb08cdd8a42967bd547ae3288054e6df20472e Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Wed, 27 Sep 2023 16:35:59 +0100 Subject: [PATCH 18/39] [HotFix][RegistryPreview]Fix out of bonds array access error (#28803) --- .../registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs index bffaa7a3b66..f3da13a340a 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs @@ -445,7 +445,7 @@ private bool ParseRegistryFile(string filenameText) i++; } - if (value[i - 1] != '\\' && value[i] == '"') + if (i < value.Length && value[i - 1] != '\\' && value[i] == '"') { // Don't allow non-escaped quotes registryValue.Type = "ERROR"; From 11f30f9d330d346d9899c15786f42ba45ad3337f Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Mon, 2 Oct 2023 23:31:22 +0300 Subject: [PATCH 19/39] [FancyZones]Don't automatically snap context menus (#28956) * check tabstop * revert * check popup thickframe * Revert "check popup thickframe" * added HasThickFrame * popup check * spellcheck --- src/modules/fancyzones/FancyZonesLib/FancyZones.cpp | 10 ++++++++++ src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp | 6 ++++++ src/modules/fancyzones/FancyZonesLib/WindowUtils.h | 1 + 3 files changed, 17 insertions(+) diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index d2314d78169..b2b470c1a4b 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -394,6 +395,15 @@ void FancyZones::WindowCreated(HWND window) noexcept return; } + // Hotfix + // Avoid automatically moving popup windows, as they can be just popup menus. + bool isPopup = FancyZonesWindowUtils::IsPopupWindow(window); + bool hasThickFrame = FancyZonesWindowUtils::HasThickFrame(window); + if (isPopup && !hasThickFrame) + { + return; + } + // Avoid already stamped (zoned) windows const bool isZoned = !FancyZonesWindowProperties::RetrieveZoneIndexProperty(window).empty(); if (isZoned) diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp index ef895cf830f..ec246180fda 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp @@ -192,6 +192,12 @@ bool FancyZonesWindowUtils::IsPopupWindow(HWND window) noexcept return ((style & WS_POPUP) == WS_POPUP); } +bool FancyZonesWindowUtils::HasThickFrame(HWND window) noexcept +{ + auto style = GetWindowLong(window, GWL_STYLE); + return ((style & WS_THICKFRAME) == WS_THICKFRAME); +} + bool FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept { auto style = GetWindowLong(window, GWL_STYLE); diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h index 4ac973bacba..80a265e0ab8 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h @@ -20,6 +20,7 @@ namespace FancyZonesWindowUtils bool HasVisibleOwner(HWND window) noexcept; bool IsStandardWindow(HWND window); bool IsPopupWindow(HWND window) noexcept; + bool HasThickFrame(HWND window) noexcept; bool HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept; bool IsProcessOfWindowElevated(HWND window); // If HWND is already dead, we assume it wasn't elevated From e14ff34b3724c87080b58e89ebc0be39f76b5e4c Mon Sep 17 00:00:00 2001 From: Andrey Nekrasov Date: Mon, 2 Oct 2023 22:42:31 +0200 Subject: [PATCH 20/39] [SVGPreview]Handle comments properly (#28863) * [SVGPreview] Handle comments properly * f: spelling * f: add tolerance to the bitmap eq test * f: remove bitmap eq testing, since it doesn't work on CI for some reason * f: parsing issue --- .github/actions/spell-check/expect.txt | 1 + .../HelperFiles/WithComments.svg | 15 +++++++++++++++ .../SvgThumbnailProviderTests.cs | 16 +++++++++++++--- .../UnitTests-SvgThumbnailProvider.csproj | 12 ++++++++---- .../common/Utilities/SvgPreviewHandlerHelper.cs | 15 ++++++++++----- 5 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/WithComments.svg diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 4186914fddc..6c848b3e3b9 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -136,6 +136,7 @@ BITMAPFILEHEADER bitmapimage BITMAPINFO BITMAPINFOHEADER +Bitmaps bitmask BITSPIXEL bla diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/WithComments.svg b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/WithComments.svg new file mode 100644 index 00000000000..3acbefe3d12 --- /dev/null +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/HelperFiles/WithComments.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs index 4e50e765c3c..4fd38cf164f 100644 --- a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/SvgThumbnailProviderTests.cs @@ -4,14 +4,12 @@ using System; using System.Drawing; +using System.Drawing.Imaging; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; using System.Text; -using Common.ComInterlop; using Microsoft.PowerToys.STATestExtension; using Microsoft.PowerToys.ThumbnailHandler.Svg; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; namespace SvgThumbnailProviderUnitTests { @@ -211,5 +209,17 @@ public void GetThumbnailValidStreamHTML() Assert.IsTrue(bitmap != null); } + + [TestMethod] + public void SvgCommentsAreHandledCorrectly() + { + var filePath = "HelperFiles/WithComments.svg"; + + SvgThumbnailProvider svgThumbnailProvider = new SvgThumbnailProvider(filePath); + + Bitmap bitmap = svgThumbnailProvider.GetThumbnail(8); + + Assert.IsTrue(bitmap != null); + } } } diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj index 100bc8ecdb5..6aec341be5e 100644 --- a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj @@ -38,15 +38,19 @@ - - + + + - + Always - + Always + \ No newline at end of file diff --git a/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs b/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs index d9623b8490c..180158152cd 100644 --- a/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs +++ b/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs @@ -119,7 +119,7 @@ private static int FindFirstXmlOpenTagIndex(string s) while ((index = s.IndexOf('<', index)) != -1) { - if (index < s.Length - 1 && s[index + 1] != '?') + if (index < s.Length - 1 && s[index + 1] != '?' && s[index + 1] != '!') { return index; } @@ -130,11 +130,11 @@ private static int FindFirstXmlOpenTagIndex(string s) return -1; } - private static int FindFirstXmlCloseTagIndex(string s) + private static int FindFirstXmlCloseTagIndex(string s, int openTagIndex) { int index = 1; - while ((index = s.IndexOf('>', index)) != -1) + while ((index = s.IndexOf('>', openTagIndex)) != -1) { if (index > 0 && s[index - 1] != '?') { @@ -160,7 +160,7 @@ public static string AddStyleSVG(string stringSvgData) return stringSvgData; } - int firstXmlCloseTagIndex = FindFirstXmlCloseTagIndex(stringSvgData); + int firstXmlCloseTagIndex = FindFirstXmlCloseTagIndex(stringSvgData, firstXmlOpenTagIndex); if (firstXmlCloseTagIndex == -1) { return stringSvgData; @@ -192,13 +192,18 @@ public static string AddStyleSVG(string stringSvgData) styleIndex -= numRemoved; } + firstXmlCloseTagIndex -= numRemoved; + stringSvgData = RemoveAttribute(stringSvgData, heightIndex, HeightAttribute, out numRemoved); if (styleIndex != -1 && styleIndex > heightIndex) { styleIndex -= numRemoved; } + firstXmlCloseTagIndex -= numRemoved; + stringSvgData = RemoveAttribute(stringSvgData, styleIndex, StyleAttribute, out numRemoved); + firstXmlCloseTagIndex -= numRemoved; width = CheckUnit(width); height = CheckUnit(height); @@ -213,7 +218,7 @@ public static string AddStyleSVG(string stringSvgData) scaling += $" _height:expression(this.scrollHeight > {heightR} ? " {height}" : "auto"); _width:expression(this.scrollWidth > {widthR} ? "{width}" : "auto");"; string newStyle = $"style=\"{scaling}{centering}{oldStyle}\""; - int insertAt = stringSvgData.IndexOf(">", StringComparison.InvariantCultureIgnoreCase); + int insertAt = firstXmlCloseTagIndex; stringSvgData = stringSvgData.Insert(insertAt, " " + newStyle); From 5e2733ece9420e7e8e52033292938bee435223f5 Mon Sep 17 00:00:00 2001 From: John Erickson Date: Tue, 3 Oct 2023 08:31:14 -0700 Subject: [PATCH 21/39] [Build]Add missing dependencies on version.h (#28619) * Fix version.h dependencies * fix for build_vcm_x86 --- src/common/GPOWrapper/GPOWrapper.vcxproj | 5 +++++ src/common/SettingsAPI/SettingsAPI.vcxproj | 5 +++++ .../UnitTests-CommonLib.vcxproj | 3 --- src/common/logger/logger.vcxproj | 3 +++ .../BackgroundActivatorDLL.vcxproj | 3 +++ src/common/updating/updating.vcxproj | 3 --- src/common/version/version.vcxproj | 19 +++++++++++++++++++ .../FileLocksmithExt/FileLocksmithExt.vcxproj | 3 --- .../PowerAccentKeyboardService.vcxproj | 3 --- .../PowerRenameUILib/PowerRenameUI.vcxproj | 3 --- .../powerrename/dll/PowerRenameExt.vcxproj | 3 --- .../VideoConferenceProxyFilter.vcxproj | 3 +++ 12 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/common/GPOWrapper/GPOWrapper.vcxproj b/src/common/GPOWrapper/GPOWrapper.vcxproj index 86c34f7ec56..7cb115d6aae 100644 --- a/src/common/GPOWrapper/GPOWrapper.vcxproj +++ b/src/common/GPOWrapper/GPOWrapper.vcxproj @@ -111,6 +111,11 @@ + + + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} + + diff --git a/src/common/SettingsAPI/SettingsAPI.vcxproj b/src/common/SettingsAPI/SettingsAPI.vcxproj index 834de6db209..306b4a8c3be 100644 --- a/src/common/SettingsAPI/SettingsAPI.vcxproj +++ b/src/common/SettingsAPI/SettingsAPI.vcxproj @@ -43,6 +43,11 @@ + + + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} + + diff --git a/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj b/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj index a2e84a9cb03..f3dd518759b 100644 --- a/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj +++ b/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj @@ -48,9 +48,6 @@ {6955446d-23f7-4023-9bb3-8657f904af99} - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - diff --git a/src/common/logger/logger.vcxproj b/src/common/logger/logger.vcxproj index a371b25109e..f4915e78e35 100644 --- a/src/common/logger/logger.vcxproj +++ b/src/common/logger/logger.vcxproj @@ -74,6 +74,9 @@ {7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f} + + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} + diff --git a/src/common/notifications/BackgroundActivatorDLL/BackgroundActivatorDLL.vcxproj b/src/common/notifications/BackgroundActivatorDLL/BackgroundActivatorDLL.vcxproj index 68fca43ea57..394fd21d9b5 100644 --- a/src/common/notifications/BackgroundActivatorDLL/BackgroundActivatorDLL.vcxproj +++ b/src/common/notifications/BackgroundActivatorDLL/BackgroundActivatorDLL.vcxproj @@ -67,6 +67,9 @@ + + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} + {031AC72E-FA28-4AB7-B690-6F7B9C28AA73} diff --git a/src/common/updating/updating.vcxproj b/src/common/updating/updating.vcxproj index 5dec7c902f2..97089b03a1f 100644 --- a/src/common/updating/updating.vcxproj +++ b/src/common/updating/updating.vcxproj @@ -51,9 +51,6 @@ {6955446d-23f7-4023-9bb3-8657f904af99} - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - diff --git a/src/common/version/version.vcxproj b/src/common/version/version.vcxproj index f91d2e4b1e7..7aaa57c5bfa 100644 --- a/src/common/version/version.vcxproj +++ b/src/common/version/version.vcxproj @@ -10,6 +10,25 @@ + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + 16.0 {CC6E41AC-8174-4E8A-8D22-85DD7F4851DF} diff --git a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj index cf3a4723194..e9186c86a53 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj +++ b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj @@ -260,9 +260,6 @@ {98537082-0fdb-40de-abd8-0dc5a4269bab} - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - diff --git a/src/modules/poweraccent/PowerAccentKeyboardService/PowerAccentKeyboardService.vcxproj b/src/modules/poweraccent/PowerAccentKeyboardService/PowerAccentKeyboardService.vcxproj index 6b216ac3c47..c8ecde85156 100644 --- a/src/modules/poweraccent/PowerAccentKeyboardService/PowerAccentKeyboardService.vcxproj +++ b/src/modules/poweraccent/PowerAccentKeyboardService/PowerAccentKeyboardService.vcxproj @@ -113,9 +113,6 @@ {6955446d-23f7-4023-9bb3-8657f904af99} - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index c4051b8f035..08e216564ea 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -193,9 +193,6 @@ {98537082-0fdb-40de-abd8-0dc5a4269bab} - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - {51920f1f-c28c-4adf-8660-4238766796c2} diff --git a/src/modules/powerrename/dll/PowerRenameExt.vcxproj b/src/modules/powerrename/dll/PowerRenameExt.vcxproj index 747f34944e4..cf58198d656 100644 --- a/src/modules/powerrename/dll/PowerRenameExt.vcxproj +++ b/src/modules/powerrename/dll/PowerRenameExt.vcxproj @@ -63,9 +63,6 @@ {98537082-0fdb-40de-abd8-0dc5a4269bab} - - {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} - {51920f1f-c28c-4adf-8660-4238766796c2} diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/VideoConferenceProxyFilter.vcxproj b/src/modules/videoconference/VideoConferenceProxyFilter/VideoConferenceProxyFilter.vcxproj index 43ff97ace1e..a2779581488 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/VideoConferenceProxyFilter.vcxproj +++ b/src/modules/videoconference/VideoConferenceProxyFilter/VideoConferenceProxyFilter.vcxproj @@ -140,6 +140,9 @@ {459e0768-7ebd-4c41-bba1-6db3b3815e0a} + + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} + From 7f8e9072c9be0afccadfe74b9225e9fd9691ced0 Mon Sep 17 00:00:00 2001 From: Deepak Sangle Date: Tue, 3 Oct 2023 21:02:34 +0530 Subject: [PATCH 22/39] [Peek]Fix file and folder sizes to be similar to Explorer (#28089) * [Peek] displaying file size in correct grammatical format * Update Directory.Packages.props * removed unnecessary file * [Peek] Added new symbols in resources.resw * [Peek] added commas separation * modified spell check * [Peek] Changed 1024 factor to 1000 factor * modified spell check * made string country specific * fix: used 1024 and fixed precision * spellcheck fixed * Update src/modules/peek/Peek.UI/Strings/en-us/Resources.resw * Update src/modules/peek/Peek.UI/Strings/en-us/Resources.resw --- .github/actions/spell-check/expect.txt | 1 - .../peek/Peek.Common/Helpers/MathHelper.cs | 8 +++ .../Helpers/ReadableStringHelper.cs | 67 ++++++++++++++----- .../peek/Peek.UI/Strings/en-us/Resources.resw | 24 ++++--- 4 files changed, 74 insertions(+), 26 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 6c848b3e3b9..f0795e524be 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1370,7 +1370,6 @@ pef PElems Pels PERCEIVEDFLAG -Percision perfmon pesi petabyte diff --git a/src/modules/peek/Peek.Common/Helpers/MathHelper.cs b/src/modules/peek/Peek.Common/Helpers/MathHelper.cs index e731d14030e..d9b8322c7d3 100644 --- a/src/modules/peek/Peek.Common/Helpers/MathHelper.cs +++ b/src/modules/peek/Peek.Common/Helpers/MathHelper.cs @@ -2,6 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Globalization; + namespace Peek.Common.Helpers { public static class MathHelper @@ -10,5 +13,10 @@ public static int Modulo(int a, int b) { return a < 0 ? ((a % b) + b) % b : a % b; } + + public static int NumberOfDigits(int num) + { + return Math.Abs(num).ToString(CultureInfo.InvariantCulture).Length; + } } } diff --git a/src/modules/peek/Peek.Common/Helpers/ReadableStringHelper.cs b/src/modules/peek/Peek.Common/Helpers/ReadableStringHelper.cs index bc2cba4547c..0ea75816e92 100644 --- a/src/modules/peek/Peek.Common/Helpers/ReadableStringHelper.cs +++ b/src/modules/peek/Peek.Common/Helpers/ReadableStringHelper.cs @@ -10,34 +10,40 @@ namespace Peek.Common.Helpers { public static class ReadableStringHelper { - private const int DecimalPercision = 10; + private const int MaxDigitsToDisplay = 3; + private const int PowerFactor = 1024; public static string BytesToReadableString(ulong bytes) { - var resourceLoader = ResourceLoaderInstance.ResourceLoader; - List format = new List - { - (bytes == 1) ? - resourceLoader.GetString("ReadableString_ByteAbbreviationFormat") : // "byte" - resourceLoader.GetString("ReadableString_BytesAbbreviationFormat"), // "bytes" - resourceLoader.GetString("ReadableString_KiloByteAbbreviationFormat"), // "KB" - resourceLoader.GetString("ReadableString_MegaByteAbbreviationFormat"), // "MB" - resourceLoader.GetString("ReadableString_GigaByteAbbreviationFormat"), // "GB" - resourceLoader.GetString("ReadableString_TeraByteAbbreviationFormat"), // "TB" - resourceLoader.GetString("ReadableString_PetaByteAbbreviationFormat"), // "PB" - resourceLoader.GetString("ReadableString_ExaByteAbbreviationFormat"), // "EB" - }; + string totalBytesDisplays = (bytes == 1) ? + ResourceLoaderInstance.ResourceLoader.GetString("ReadableString_ByteString") : + ResourceLoaderInstance.ResourceLoader.GetString("ReadableString_BytesString"); int index = 0; double number = 0.0; if (bytes > 0) { - index = (int)Math.Floor(Math.Log(bytes) / Math.Log(1024)); - number = Math.Round((bytes / Math.Pow(1024, index)) * DecimalPercision) / DecimalPercision; + index = (int)Math.Floor(Math.Log(bytes) / Math.Log(PowerFactor)); + number = bytes / Math.Pow(PowerFactor, index); + } + + if (index > 0 && number >= Math.Pow(10, MaxDigitsToDisplay)) + { + index++; + number = bytes / Math.Pow(PowerFactor, index); } - return string.Format(CultureInfo.InvariantCulture, format[index], number); + int precision = GetPrecision(index, number); + int decimalPrecision = (int)Math.Pow(10, precision); + + number = Math.Truncate(number * decimalPrecision) / decimalPrecision; + + string formatSpecifier = GetFormatSpecifierString(index, number, bytes, precision); + + return bytes == 0 + ? string.Format(CultureInfo.CurrentCulture, formatSpecifier, number) + : string.Format(CultureInfo.CurrentCulture, formatSpecifier + totalBytesDisplays, number, bytes); } public static string FormatResourceString(string resourceId, object? args) @@ -55,5 +61,32 @@ public static string FormatResourceString(string resourceId, object? args0, obje return formattedString; } + + public static int GetPrecision(int index, double number) + { + int numberOfDigits = MathHelper.NumberOfDigits((int)number); + return index == 0 ? + 0 : + MaxDigitsToDisplay - numberOfDigits; + } + + public static string GetFormatSpecifierString(int index, double number, ulong bytes, int precision) + { + var resourceLoader = ResourceLoaderInstance.ResourceLoader; + List format = new List + { + (bytes == 1) ? + resourceLoader.GetString("ReadableString_ByteAbbreviationFormat") : // "byte" + resourceLoader.GetString("ReadableString_BytesAbbreviationFormat"), // "bytes" + resourceLoader.GetString("ReadableString_KiloByteAbbreviationFormat"), // "KB" + resourceLoader.GetString("ReadableString_MegaByteAbbreviationFormat"), // "MB" + resourceLoader.GetString("ReadableString_GigaByteAbbreviationFormat"), // "GB" + resourceLoader.GetString("ReadableString_TeraByteAbbreviationFormat"), // "TB" + resourceLoader.GetString("ReadableString_PetaByteAbbreviationFormat"), // "PB" + resourceLoader.GetString("ReadableString_ExaByteAbbreviationFormat"), // "EB" + }; + + return "{0:F" + precision + "} " + format[index]; + } } } diff --git a/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw b/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw index d8df22b84a2..b711a9e26f2 100644 --- a/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw +++ b/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw @@ -154,31 +154,31 @@ Date Modified label for the unsupported files view. {0} is the date. - {0} byte + byte Abbreviation for the size unit byte. - {0} KB + KB Abbreviation for the size unit kilobyte. - {0} MB + MB Abbreviation for the size unit megabyte. - {0} GB + GB Abbreviation for the size unit gigabyte. - {0} TB + TB Abbreviation for the size unit terabyte. - {0} PB + PB Abbreviation for the size unit petabyte. - {0} EB + EB Abbreviation for the size unit exabyte. @@ -234,7 +234,7 @@ {0} is the size of the archive, {1} is the extracted size - {0} bytes + bytes Abbreviation for the size bytes @@ -253,4 +253,12 @@ Do you want Peek to open the external application? Title of the dialog showed when an URI is clicked,"Peek" is the name of the utility. + + ({1:N0} bytes) + Displays total number of bytes. Don't localize the "{1:N0}" part. + + + ({1:N0} byte) + Displays unit byte. Don't localize the "{1:N0}" part. + \ No newline at end of file From b0f814ec08dba72804d43f65e5e69c85240fcba0 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Tue, 3 Oct 2023 12:06:39 -0700 Subject: [PATCH 23/39] updating hashes and to 74.1 --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 877fa480130..d61ddeab830 100644 --- a/README.md +++ b/README.md @@ -41,17 +41,17 @@ Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and cl [github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F48 [github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F47 -[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.0/PowerToysUserSetup-0.74.0-x64.exe -[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.0/PowerToysUserSetup-0.74.0-arm64.exe -[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.0/PowerToysSetup-0.74.0-x64.exe -[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.0/PowerToysSetup-0.74.0-arm64.exe +[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.1/PowerToysUserSetup-0.74.1-x64.exe +[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.1/PowerToysUserSetup-0.74.1-arm64.exe +[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.1/PowerToysSetup-0.74.1-x64.exe +[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.74.1/PowerToysSetup-0.74.1-arm64.exe | Description | Filename | sha256 hash | |----------------|----------|-------------| -| Per user - x64 | [PowerToysUserSetup-0.74.0-x64.exe][ptUserX64] | 1C4ECE9F11488BAFFAE6B76D2B0504FA18BFFEA11EBC38BCC87F5D86AEA87C7C | -| Per user - ARM64 | [PowerToysUserSetup-0.74.0-arm64.exe][ptUserArm64] | 4F3842FAB0839A361A15A06B7720BA8A0FE7F9AF98EA94245C08DEF37678CA4A | -| Machine wide - x64 | [PowerToysSetup-0.74.0-x64.exe][ptMachineX64] | 648992E8CEA08F3C63C7CCBD554ADDF500ECBC4560187310BC12E6CB9C2F38E3 | -| Machine wide - ARM64 | [PowerToysSetup-0.74.0-arm64.exe][ptMachineArm64] | 2B6D92F1A0EA688C7EE882050AC9B030C8B3A18765163FB6D67E5E694A4D4FE3 | +| Per user - x64 | [PowerToysUserSetup-0.74.1-x64.exe][ptUserX64] | 748BF7BA33913237D36D6F48E3839D0C8035967305137A17DEFF39D775735C81 | +| Per user - ARM64 | [PowerToysUserSetup-0.74.1-arm64.exe][ptUserArm64] | F5DAA89A9CF3A2805E121085AFD056A890F241A170FAB5007AA58E2755C88C54 | +| Machine wide - x64 | [PowerToysSetup-0.74.1-x64.exe][ptMachineX64] | 298C6F4E4391BDC06E128BED86A303C3300A68EAF754B4630AF7542C78C0944A | +| Machine wide - ARM64 | [PowerToysSetup-0.74.1-arm64.exe][ptMachineArm64] | A65F3C300A48F9F81312B7FC7B306382CB87F591612D0CEC7E5C0E47E868904B | This is our preferred method. From a77a7fd936ab54d21f14e6f77425202c7ecd5cdd Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:01:30 +0100 Subject: [PATCH 24/39] [Docs]Fix typos in development docs (#28980) Fixes simple typo/grammar error. It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language Signed-off-by: brian teeman --- doc/devdocs/modules/launcher/plugins/system.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/devdocs/modules/launcher/plugins/system.md b/doc/devdocs/modules/launcher/plugins/system.md index dcf6c3d9f8d..72f2cf38fc3 100644 --- a/doc/devdocs/modules/launcher/plugins/system.md +++ b/doc/devdocs/modules/launcher/plugins/system.md @@ -15,7 +15,7 @@ Available commands: * Hibernate * Open / Empty Recycle Bin * UEFI Firmware Settings (Only available on systems, that boot in UEFI mode.) -* IP / MAC / Address => Show informations about network connections. +* IP / MAC / Address => Show information about network connections. ## Optional plugin settings @@ -46,7 +46,7 @@ Available commands: ### [`NetworkConnectionProperties.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs) - The [`NetworkConnectionProperties`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs) class contains methods to get the properties of a network interface/connection. -- An instance of this class collects/provides all required informations about one connection/adapter. +- An instance of this class collects/provides all required information about one connection/adapter. ### [`SystemPluginContext.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs) - An instance of the class [`SystemPluginContext`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs) contains/defines the context data of a system plugin result. We select the context menu based on the defined properties. From 98e2a62c21f7d80918ccc0a6936eedf97e501843 Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:02:37 +0100 Subject: [PATCH 25/39] [Hosts]Fix typo (#28984) Fixes simple typo/grammar error. It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language. Signed-off-by: brian teeman --- src/modules/Hosts/Hosts/Strings/en-us/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Hosts/Hosts/Strings/en-us/Resources.resw b/src/modules/Hosts/Hosts/Strings/en-us/Resources.resw index c40e36aaafa..75303506e68 100644 --- a/src/modules/Hosts/Hosts/Strings/en-us/Resources.resw +++ b/src/modules/Hosts/Hosts/Strings/en-us/Resources.resw @@ -239,7 +239,7 @@ "Hosts" refers to the system hosts file, do not loc - Seperate multiple hosts by space (e.g. server server.local). Maximum 9 hosts per entry. + Separate multiple hosts by space (e.g. server server.local). Maximum 9 hosts per entry. Do not localize "server" and "server.local" From 0f8a4ae6f45172a5e9e074ca505240062eafef7e Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:05:06 +0100 Subject: [PATCH 26/39] [ImageResizer]Fix comment typos (#28985) Fixes simple typo/grammar error which otherwise could be read as height It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language. Signed-off-by: brian teeman --- .../imageresizer/codeAnalysis/GlobalSuppressions.cs | 8 ++++---- .../imageresizer/ui/Extensions/BitmapMetadataExtension.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/imageresizer/codeAnalysis/GlobalSuppressions.cs b/src/modules/imageresizer/codeAnalysis/GlobalSuppressions.cs index 421fd16329f..0c41e798397 100644 --- a/src/modules/imageresizer/codeAnalysis/GlobalSuppressions.cs +++ b/src/modules/imageresizer/codeAnalysis/GlobalSuppressions.cs @@ -13,10 +13,10 @@ [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "We follow the C# Core Coding Style which avoids using `this` unless absolutely necessary.")] [assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:UsingDirectivesMustBePlacedWithinNamespace", Justification = "We follow the C# Core Coding Style which puts using statements outside the namespace.")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "It is not a priority and have hight impact in code changes.")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "It is not a priority and have hight impact in code changes.")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:ConstantsMustAppearBeforeFields", Justification = "It is not a priority and have hight impact in code changes.")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "It is not a priority and have hight impact in code changes.")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "It is not a priority and has a high impact in code changes.")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "It is not a priority and has a high impact in code changes.")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:ConstantsMustAppearBeforeFields", Justification = "It is not a priority and has a high impact in code changes.")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "It is not a priority and has a high impact in code changes.")] [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:FieldNamesMustNotBeginWithUnderscore", Justification = "We follow the C# Core Coding Style which uses underscores as prefixes rather than using `this.`.")] diff --git a/src/modules/imageresizer/ui/Extensions/BitmapMetadataExtension.cs b/src/modules/imageresizer/ui/Extensions/BitmapMetadataExtension.cs index 6ed051d4f36..f5f74498344 100644 --- a/src/modules/imageresizer/ui/Extensions/BitmapMetadataExtension.cs +++ b/src/modules/imageresizer/ui/Extensions/BitmapMetadataExtension.cs @@ -183,7 +183,7 @@ object GetQueryWithPreCheck(BitmapMetadata metadata, string query) /// Prints all metadata to debug console /// /// - /// Intented for debug only!!! + /// Intended for debug only!!! /// public static void PrintsAllMetadataToDebugOutput(this BitmapMetadata metadata) { @@ -205,7 +205,7 @@ public static void PrintsAllMetadataToDebugOutput(this BitmapMetadata metadata) /// Iterates recursively through all metadata /// /// - /// Intented for debug only!!! + /// Intended for debug only!!! /// public static List<(string MetadataPath, object Value)> GetListOfMetadataForDebug(this BitmapMetadata metadata) { From 20fa3740bdb4404abe52208d77f61e93b22c5842 Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:07:40 +0100 Subject: [PATCH 27/39] [PTRun] Fix comments typos (#28986) Fixes simple typo/grammar errors It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language. Signed-off-by: brian teeman --- .../Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs | 2 +- .../Properties/Resources.Designer.cs | 2 +- .../Helper/ContextMenuHelper.cs | 2 +- .../Components/NetworkConnectionProperties.cs | 2 +- .../Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs | 2 +- .../Components/AvailableResultsList.cs | 2 +- .../Components/TimeAndDateHelper.cs | 2 +- .../Helper/ContextMenuHelper.cs | 2 +- .../Helper/ResultHelper.cs | 2 +- src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs | 2 +- src/modules/launcher/PowerLauncher/Plugin/PluginConfig.cs | 2 +- .../Wox.Plugin/Common/VirtualDesktop/VirtualDesktopHelper.cs | 2 +- src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs index d9ee193e8aa..b7b4484dfc9 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs @@ -43,7 +43,7 @@ internal static List GetResultList(List searchControllerRe return true; }, - // For debugging you can set the second parameter to true to see more informations. + // For debugging you can set the second parameter to true to see more information. ToolTipData = GetToolTip(x.Result, false), }); } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Properties/Resources.Designer.cs index d7ff3a4c36b..024368b1584 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Properties/Resources.Designer.cs @@ -79,7 +79,7 @@ public static string wox_plugin_windowwalker_Desktop { } /// - /// Looks up a localized string similar to Folder windows doesn't run in separate processes. (Klick to open Explorer properties.). + /// Looks up a localized string similar to Folder windows doesn't run in separate processes. (Click to open Explorer properties.). /// public static string wox_plugin_windowwalker_ExplorerInfoSubTitle { get { diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ContextMenuHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ContextMenuHelper.cs index ad959441e10..39858ecea1b 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ContextMenuHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ContextMenuHelper.cs @@ -22,7 +22,7 @@ internal static class ContextMenuHelper /// Return a list with all context menu entries for the given /// Symbols taken from /// - /// The result for the context menu entires + /// The result for the context menu entries /// The name of the this assembly /// A list with context menu entries internal static List GetContextMenu(Result result, string assemblyName) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs index d806efdb7a0..aaba05682ef 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components { /// - /// This class represents the informations for a network connection/interface + /// This class represents the information for a network connection/interface /// internal sealed class NetworkConnectionProperties { diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs index 93c67587666..0591f4d1a13 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs @@ -124,7 +124,7 @@ public List Query(Query query) // { // results.Add(new Result() // { - // Title = "Getting network informations. Please wait ...", + // Title = "Getting network information. Please wait ...", // IcoPath = $"Images\\networkAdapter.{IconTheme}.png", // Score = StringMatcher.FuzzySearch("address", "ip address").Score, // }); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs index e051d895687..2fc553f8220 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs @@ -59,7 +59,7 @@ internal static List GetList(bool isKeywordSearch, bool? timeLo if (isKeywordSearch || !TimeDateSettings.Instance.OnlyDateTimeNowGlobal) { - // We use long instead of int for unix time stamp because int ist to small after 03:14:07 UTC 2038-01-19 + // We use long instead of int for unix time stamp because int is too small after 03:14:07 UTC 2038-01-19 long unixTimestamp = (long)dateTimeNowUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; int weekOfYear = calendar.GetWeekOfYear(dateTimeNow, DateTimeFormatInfo.CurrentInfo.CalendarWeekRule, DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek); string era = DateTimeFormatInfo.CurrentInfo.GetEraName(calendar.GetEra(dateTimeNow)); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs index 86e8ca2274a..86294474c6c 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs @@ -99,7 +99,7 @@ internal static bool ParseStringAsDateTime(in string input, out DateTime timesta else if (Regex.IsMatch(input, @"^u\d+") && input.Length <= 12 && long.TryParse(input.TrimStart('u'), out long secondsInt)) { // unix time stamp - // we use long instead of int because int ist to small after 03:14:07 UTC 2038-01-19 + // we use long instead of int because int is too small after 03:14:07 UTC 2038-01-19 timestamp = new DateTime(1970, 1, 1).AddSeconds(secondsInt).ToLocalTime(); return true; } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ContextMenuHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ContextMenuHelper.cs index 735199c0def..e25d3314247 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ContextMenuHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ContextMenuHelper.cs @@ -21,7 +21,7 @@ internal static class ContextMenuHelper /// Return a list with all context menu entries for the given /// Symbols taken from /// - /// The result for the context menu entires + /// The result for the context menu entries /// The name of the this assembly /// A list with context menu entries internal static List GetContextMenu(in Result result, in string assemblyName) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs index 7240ee0fdc6..53cf403f60d 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs @@ -67,7 +67,7 @@ internal static List GetResultList( /// /// Add a tool-tip to the given , based o the given . /// - /// The that contain informations for the tool-tip. + /// The that contains information for the tool-tip. /// The that need a tool-tip. private static void AddOptionalToolTip(WindowsSetting entry, Result result) { diff --git a/src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs b/src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs index cbb98683e82..fd9a8e2ef67 100644 --- a/src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs +++ b/src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs @@ -214,7 +214,7 @@ private static void GetMergedMachineAndUserVariables(Dictionary } /// - /// Returns the variables for the specified target. Errors that occurs will be catched and logged. + /// Returns the variables for the specified target. Errors that occurs will be caught and logged. /// /// The target variable source of the type /// A dictionary with the variable or an empty dictionary on errors. diff --git a/src/modules/launcher/PowerLauncher/Plugin/PluginConfig.cs b/src/modules/launcher/PowerLauncher/Plugin/PluginConfig.cs index 5c160db40d5..fa445f135b1 100644 --- a/src/modules/launcher/PowerLauncher/Plugin/PluginConfig.cs +++ b/src/modules/launcher/PowerLauncher/Plugin/PluginConfig.cs @@ -39,7 +39,7 @@ public static List Parse(string[] pluginDirectories) private static void ParsePluginConfigs(IEnumerable directories) { - // todo use linq when diable plugin is implemented since parallel.foreach + list is not thread saft + // todo use linq when disable plugin is implemented since parallel.foreach + list is not thread saft foreach (var directory in directories) { if (File.Exists(Path.Combine(directory, "NeedDelete.txt"))) diff --git a/src/modules/launcher/Wox.Plugin/Common/VirtualDesktop/VirtualDesktopHelper.cs b/src/modules/launcher/Wox.Plugin/Common/VirtualDesktop/VirtualDesktopHelper.cs index 602bf05efee..e7039b1c964 100644 --- a/src/modules/launcher/Wox.Plugin/Common/VirtualDesktop/VirtualDesktopHelper.cs +++ b/src/modules/launcher/Wox.Plugin/Common/VirtualDesktop/VirtualDesktopHelper.cs @@ -16,7 +16,7 @@ namespace Wox.Plugin.Common.VirtualDesktop.Helper { /// /// Helper class to work with Virtual Desktops. - /// This helper uses only public available and documented COM-Interfaces or informations from registry. + /// This helper uses only public available and documented COM-Interfaces or information from registry. /// /// /// To use this helper you have to create an instance of it and access the method via the helper instance. diff --git a/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs b/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs index 8f02de16c5c..140a6c37b8b 100644 --- a/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs +++ b/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs @@ -1077,7 +1077,7 @@ public enum ExtendedWindowStyles : uint /// /// The window itself contains child windows that should take part in dialog box, navigation. If this /// style is specified, the dialog manager recurses into children of this window when performing - /// navigation operations such as handling tha TAB key, an arrow key, or a keyboard mnemonic. + /// navigation operations such as handling the TAB key, an arrow key, or a keyboard mnemonic. /// WS_EX_CONTROLPARENT = 0x10000, From 8a376225ce76ae3af7da83d57f55330ee52b33aa Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:08:33 +0100 Subject: [PATCH 28/39] [Peek] Fix comments typo (#28987) Fixes simple typo/grammar errors It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language. Signed-off-by: brian teeman --- src/modules/peek/Peek.Common/Helpers/PropertyStoreHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/peek/Peek.Common/Helpers/PropertyStoreHelper.cs b/src/modules/peek/Peek.Common/Helpers/PropertyStoreHelper.cs index 6c5e7524169..a1c5b6678e0 100644 --- a/src/modules/peek/Peek.Common/Helpers/PropertyStoreHelper.cs +++ b/src/modules/peek/Peek.Common/Helpers/PropertyStoreHelper.cs @@ -65,7 +65,7 @@ public static partial class PropertyStoreHelper /// /// The file/folder path /// The property store flags - /// an IPropertyStroe interface + /// an IPropertyStore interface private static DisposablePropertyStore GetPropertyStoreFromPath(string path, GETPROPERTYSTOREFLAGS flags = GETPROPERTYSTOREFLAGS.GPS_EXTRINSICPROPERTIES | GETPROPERTYSTOREFLAGS.GPS_BESTEFFORT) { IShellItem2? shellItem2 = null; From 88e8696b4d36fce9c70acdf81f44aa83bed66d4f Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:09:23 +0100 Subject: [PATCH 29/39] [File Explorer add-ons]Fix typo (#28989) Fixes simple typo/grammar errors It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language. Signed-off-by: brian teeman --- .../MonacoPreviewHandler/MonacoPreviewHandlerControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs index ca16bcc8353..7480acd1dca 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs @@ -165,7 +165,7 @@ public override void DoPreview(T dataSource) } catch (NullReferenceException e) { - Logger.LogError("NullReferenceException catched. Skipping exception.", e); + Logger.LogError("NullReferenceException caught. Skipping exception.", e); } } catch (WebView2RuntimeNotFoundException e) From fd8d4b93878cf7c487254fd79788a9fb562a2693 Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:10:58 +0100 Subject: [PATCH 30/39] [ShortcutGuide]Fix typo (#28990) Fixes simple typo/grammar errors It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language. Signed-off-by: brian teeman --- src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp index f1c843d6155..719f713e793 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp @@ -73,7 +73,7 @@ namespace result.hwnd = active_window; // In reality, Windows Snap works if even one of those styles is set // for a window, it is just limited. If there is no WS_MAXIMIZEBOX using - // WinKey + Up just won't maximize the window. Similary, without + // WinKey + Up just won't maximize the window. Similarly, without // WS_MINIMIZEBOX the window will not get minimized. A "Save As..." dialog // is a example of such window - it can be snapped to both sides and to // all screen corners, but will not get maximized nor minimized. From 74dd40e203f80efdf1945c75568048c082d23199 Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:11:40 +0100 Subject: [PATCH 31/39] [Settings]Fix UI typos (#28991) Fixes simple typo/grammar errors It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language. Signed-off-by: brian teeman --- src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs | 2 +- .../Settings.UI.Library/Enumerations/ColorRepresentationType.cs | 2 +- src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs | 2 +- .../Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs b/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs index 09dde9318ca..6f974faba71 100644 --- a/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs +++ b/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs @@ -51,7 +51,7 @@ public ColorPickerProperties() [JsonPropertyName("activationaction")] public ColorPickerActivationAction ActivationAction { get; set; } - // Property ColorHistory is not used, the color history is saved separatedly in the colorHistory.json file + // Property ColorHistory is not used, the color history is saved separately in the colorHistory.json file [JsonPropertyName("colorhistory")] public List ColorHistory { get; set; } diff --git a/src/settings-ui/Settings.UI.Library/Enumerations/ColorRepresentationType.cs b/src/settings-ui/Settings.UI.Library/Enumerations/ColorRepresentationType.cs index 5bebe799a87..09ef003e883 100644 --- a/src/settings-ui/Settings.UI.Library/Enumerations/ColorRepresentationType.cs +++ b/src/settings-ui/Settings.UI.Library/Enumerations/ColorRepresentationType.cs @@ -4,7 +4,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Enumerations { - // NOTE: don't change the order (numbers) of the enumeration entires + // NOTE: don't change the order (numbers) of the enumeration entries /// /// The type of the color representation diff --git a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs index f16584c6c89..afee54379d6 100644 --- a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs @@ -703,7 +703,7 @@ public void NotifyPropertyChanged([CallerMemberName] string propertyName = null, } /// - /// Method SelectSettingBackupDir opens folder browser to select a backup and retore location. + /// Method SelectSettingBackupDir opens folder browser to select a backup and restore location. /// private async void SelectSettingBackupDir() { diff --git a/src/settings-ui/Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs index c54d217906f..4463b1287b8 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs @@ -67,7 +67,7 @@ public int ComboBoxValue // TextBox setting public bool ShowTextBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Textbox; - public int TextBoxMaxLength => (_additionalOption.TextBoxMaxLength == null) ? 0 : _additionalOption.TextBoxMaxLength.Value; // 0 ist the default and means no limit. + public int TextBoxMaxLength => (_additionalOption.TextBoxMaxLength == null) ? 0 : _additionalOption.TextBoxMaxLength.Value; // 0 is the default and means no limit. public string TextValue { From 5c1e4ac471a519e0503aae24d5081f3b6389bbed Mon Sep 17 00:00:00 2001 From: Brian Teeman Date: Wed, 4 Oct 2023 11:13:01 +0100 Subject: [PATCH 32/39] [Settings]Fix restore typo in code (#28992) Fixes simple typo/grammar errors It's easier to understand if it is spelled correctly. This is especially important for those for whom english may not be their first language. Signed-off-by: brian teeman --- .../SettingsBackupAndRestoreUtils.cs | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs index 759e86d7515..8315c52bdb6 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs @@ -292,9 +292,9 @@ public static string GetRegSettingsBackupAndRestoreRegItem(string itemName) } // get data needed for process - var backupRetoreSettings = JsonNode.Parse(GetBackupRestoreSettingsJson()); - var currentSettingsFiles = GetSettingsFiles(backupRetoreSettings, appBasePath).ToList().ToDictionary(x => x.Substring(appBasePath.Length)); - var backupSettingsFiles = GetSettingsFiles(backupRetoreSettings, latestSettingsFolder).ToList().ToDictionary(x => x.Substring(latestSettingsFolder.Length)); + var backupRestoreSettings = JsonNode.Parse(GetBackupRestoreSettingsJson()); + var currentSettingsFiles = GetSettingsFiles(backupRestoreSettings, appBasePath).ToList().ToDictionary(x => x.Substring(appBasePath.Length)); + var backupSettingsFiles = GetSettingsFiles(backupRestoreSettings, latestSettingsFolder).ToList().ToDictionary(x => x.Substring(latestSettingsFolder.Length)); if (backupSettingsFiles.Count == 0) { @@ -306,13 +306,13 @@ public static string GetRegSettingsBackupAndRestoreRegItem(string itemName) foreach (var currentFile in backupSettingsFiles) { var relativePath = currentFile.Value.Substring(latestSettingsFolder.Length + 1); - var retoreFullPath = Path.Combine(appBasePath, relativePath); - var settingsToRestoreJson = GetExportVersion(backupRetoreSettings, currentFile.Key, currentFile.Value); + var restoreFullPath = Path.Combine(appBasePath, relativePath); + var settingsToRestoreJson = GetExportVersion(backupRestoreSettings, currentFile.Key, currentFile.Value); if (currentSettingsFiles.TryGetValue(currentFile.Key, out string value)) { // we have a setting file to restore to - var currentSettingsFileJson = GetExportVersion(backupRetoreSettings, currentFile.Key, value); + var currentSettingsFileJson = GetExportVersion(backupRestoreSettings, currentFile.Key, value); if (JsonNormalizer.Normalize(settingsToRestoreJson) != JsonNormalizer.Normalize(currentSettingsFileJson)) { @@ -339,7 +339,7 @@ public static string GetRegSettingsBackupAndRestoreRegItem(string itemName) if (anyFilesUpdated) { // something was changed do we need to return true to indicate a restart is needed. - var restartAfterRestore = (bool?)backupRetoreSettings!["RestartAfterRestore"]; + var restartAfterRestore = (bool?)backupRestoreSettings!["RestartAfterRestore"]; if (!restartAfterRestore.HasValue || restartAfterRestore.Value) { return (true, $"RESTART APP", "Success"); @@ -639,11 +639,11 @@ private static string[] GetSettingsFiles(JsonNode settings, string path) } // get data needed for process - var backupRetoreSettings = JsonNode.Parse(GetBackupRestoreSettingsJson()); - var currentSettingsFiles = GetSettingsFiles(backupRetoreSettings, appBasePath).ToList().ToDictionary(x => x.Substring(appBasePath.Length)); + var backupRestoreSettings = JsonNode.Parse(GetBackupRestoreSettingsJson()); + var currentSettingsFiles = GetSettingsFiles(backupRestoreSettings, appBasePath).ToList().ToDictionary(x => x.Substring(appBasePath.Length)); var fullBackupDir = Path.Combine(Path.GetTempPath(), $"settings_{DateTime.UtcNow.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture)}"); var latestSettingsFolder = GetLatestSettingsFolder(); - var lastBackupSettingsFiles = GetSettingsFiles(backupRetoreSettings, latestSettingsFolder).ToList().ToDictionary(x => x.Substring(latestSettingsFolder.Length)); + var lastBackupSettingsFiles = GetSettingsFiles(backupRestoreSettings, latestSettingsFolder).ToList().ToDictionary(x => x.Substring(latestSettingsFolder.Length)); lastBackupExists = lastBackupSettingsFiles.Count > 0; @@ -661,13 +661,13 @@ private static string[] GetSettingsFiles(JsonNode settings, string path) tempFile = currentFile; // need to check and back this up; - var currentSettingsFileToBackup = GetExportVersion(backupRetoreSettings, currentFile.Key, currentFile.Value); + var currentSettingsFileToBackup = GetExportVersion(backupRestoreSettings, currentFile.Key, currentFile.Value); var doBackup = false; if (lastBackupSettingsFiles.TryGetValue(currentFile.Key, out string value)) { // there is a previous backup for this, get an export version of it. - var lastSettingsFileDoc = GetExportVersion(backupRetoreSettings, currentFile.Key, value); + var lastSettingsFileDoc = GetExportVersion(backupRestoreSettings, currentFile.Key, value); // check to see if the new export version would be same as last export version. if (JsonNormalizer.Normalize(currentSettingsFileToBackup) != JsonNormalizer.Normalize(lastSettingsFileDoc)) @@ -804,9 +804,9 @@ private static string WildCardToRegular(string value) /// Method GetExportVersion gets the version of the settings file that we want to backup. /// It will be formatted and all problematic settings removed from it. /// - public static string GetExportVersion(JsonNode backupRetoreSettings, string settingFileKey, string settingsFileName) + public static string GetExportVersion(JsonNode backupRestoreSettings, string settingFileKey, string settingsFileName) { - var ignoredSettings = GetIgnoredSettings(backupRetoreSettings, settingFileKey); + var ignoredSettings = GetIgnoredSettings(backupRestoreSettings, settingFileKey); var settingsFile = JsonDocument.Parse(File.ReadAllText(settingsFileName)); var outputBuffer = new ArrayBufferWriter(); @@ -828,7 +828,7 @@ public static string GetExportVersion(JsonNode backupRetoreSettings, string sett if (settingFileKey.Equals("\\PowerToys Run\\settings.json", StringComparison.OrdinalIgnoreCase)) { // PowerToys Run hack fix-up - var ptRunIgnoredSettings = GetPTRunIgnoredSettings(backupRetoreSettings); + var ptRunIgnoredSettings = GetPTRunIgnoredSettings(backupRestoreSettings); var ptrSettings = JsonNode.Parse(Encoding.UTF8.GetString(outputBuffer.WrittenSpan)); foreach (JsonObject pluginToChange in ptRunIgnoredSettings) @@ -856,16 +856,16 @@ public static string GetExportVersion(JsonNode backupRetoreSettings, string sett /// /// Method GetPTRunIgnoredSettings gets the 'Run-Plugin-level' settings we should ignore because they are problematic to backup/restore. /// - private static JsonArray GetPTRunIgnoredSettings(JsonNode backupRetoreSettings) + private static JsonArray GetPTRunIgnoredSettings(JsonNode backupRestoreSettings) { - if (backupRetoreSettings == null) + if (backupRestoreSettings == null) { - throw new ArgumentNullException(nameof(backupRetoreSettings)); + throw new ArgumentNullException(nameof(backupRestoreSettings)); } - if (backupRetoreSettings["IgnoredPTRunSettings"] != null) + if (backupRestoreSettings["IgnoredPTRunSettings"] != null) { - return (JsonArray)backupRetoreSettings["IgnoredPTRunSettings"]; + return (JsonArray)backupRestoreSettings["IgnoredPTRunSettings"]; } return new JsonArray(); @@ -874,11 +874,11 @@ private static JsonArray GetPTRunIgnoredSettings(JsonNode backupRetoreSettings) /// /// Method GetIgnoredSettings gets the 'top-level' settings we should ignore because they are problematic to backup/restore. /// - private static string[] GetIgnoredSettings(JsonNode backupRetoreSettings, string settingFileKey) + private static string[] GetIgnoredSettings(JsonNode backupRestoreSettings, string settingFileKey) { - if (backupRetoreSettings == null) + if (backupRestoreSettings == null) { - throw new ArgumentNullException(nameof(backupRetoreSettings)); + throw new ArgumentNullException(nameof(backupRestoreSettings)); } if (settingFileKey.StartsWith("\\", StringComparison.OrdinalIgnoreCase)) @@ -886,11 +886,11 @@ private static string[] GetIgnoredSettings(JsonNode backupRetoreSettings, string settingFileKey = settingFileKey.Substring(1); } - if (backupRetoreSettings["IgnoredSettings"] != null) + if (backupRestoreSettings["IgnoredSettings"] != null) { - if (backupRetoreSettings["IgnoredSettings"][settingFileKey] != null) + if (backupRestoreSettings["IgnoredSettings"][settingFileKey] != null) { - var settingsArray = (JsonArray)backupRetoreSettings["IgnoredSettings"][settingFileKey]; + var settingsArray = (JsonArray)backupRestoreSettings["IgnoredSettings"][settingFileKey]; Console.WriteLine("settingsArray " + settingsArray.GetType().FullName); From f03f838692f969fd477359458a30913b89f5282c Mon Sep 17 00:00:00 2001 From: Blake <66808440+blakestack@users.noreply.github.com> Date: Wed, 4 Oct 2023 06:53:07 -0400 Subject: [PATCH 33/39] [QuickAccent]Add Asturian characters to the Spanish charset (#28966) --- src/modules/poweraccent/PowerAccent.Core/Languages.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 6eece7b6f2b..0695b27f06d 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -260,7 +260,9 @@ private static string[] GetDefaultLetterKeySP(LetterKey letter) { LetterKey.VK_A => new[] { "á" }, LetterKey.VK_E => new[] { "é", "€" }, + LetterKey.VK_H => new[] { "ḥ" }, LetterKey.VK_I => new[] { "í" }, + LetterKey.VK_L => new[] { "ḷ" }, LetterKey.VK_N => new[] { "ñ" }, LetterKey.VK_O => new[] { "ó" }, LetterKey.VK_U => new[] { "ú", "ü" }, From 1ba9ffbfbf51f1f141f07facf0cfc1b544114d49 Mon Sep 17 00:00:00 2001 From: Andrey Nekrasov Date: Wed, 4 Oct 2023 13:39:32 +0200 Subject: [PATCH 34/39] [KBMEditor]Visually distinguish between decimal dot and period characters (#28867) * [KBM] Visually distinguish between decimal dot and period characters * f: naming --- src/common/interop/keyboard_layout.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/interop/keyboard_layout.cpp b/src/common/interop/keyboard_layout.cpp index afac8fe1410..7e0a13e848e 100644 --- a/src/common/interop/keyboard_layout.cpp +++ b/src/common/interop/keyboard_layout.cpp @@ -238,6 +238,7 @@ void LayoutMap::LayoutMapImpl::UpdateLayout() keyboardLayoutMap[VK_NONCONVERT] = L"IME Non-Convert"; keyboardLayoutMap[VK_ACCEPT] = L"IME Kana"; keyboardLayoutMap[VK_MODECHANGE] = L"IME Mode Change"; + keyboardLayoutMap[VK_DECIMAL] = L". (Numpad)"; keyboardLayoutMap[CommonSharedConstants::VK_DISABLED] = L"Disable"; } From 53ec08ebc036ed13c0f94b887b82afca94832d2f Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Wed, 4 Oct 2023 13:47:46 +0200 Subject: [PATCH 35/39] [PTRun]Fix clear query on launch for action invoked from keyboard (#28822) --- src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs b/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs index b03269698a8..098e16a9ba8 100644 --- a/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs +++ b/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs @@ -1065,7 +1065,6 @@ public void HandleContextMenu(Key acceleratorKey, ModifierKeys acceleratorModifi { if (contextMenuItems.AcceleratorKey == acceleratorKey && contextMenuItems.AcceleratorModifiers == acceleratorModifiers) { - MainWindowVisibility = Visibility.Collapsed; contextMenuItems.Command.Execute(null); } } From d3edd6acc2ac6cef8630d089980b493713bd47ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:07:10 +0100 Subject: [PATCH 36/39] [Deps]Bump check-spelling/check-spelling from 0.0.21 to 0.0.22 (#28953) Bumps [check-spelling/check-spelling](https://github.com/check-spelling/check-spelling) from 0.0.21 to 0.0.22. - [Release notes](https://github.com/check-spelling/check-spelling/releases) - [Commits](https://github.com/check-spelling/check-spelling/compare/v0.0.21...v0.0.22) --- updated-dependencies: - dependency-name: check-spelling/check-spelling dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/spelling2.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/spelling2.yml b/.github/workflows/spelling2.yml index 0cbb2ce08c7..e38784d1484 100644 --- a/.github/workflows/spelling2.yml +++ b/.github/workflows/spelling2.yml @@ -68,7 +68,7 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@v0.0.21 + uses: check-spelling/check-spelling@v0.0.22 with: config: .github/actions/spell-check suppress_push_for_open_pull_request: 1 @@ -97,7 +97,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' steps: - name: comment - uses: check-spelling/check-spelling@v0.0.21 + uses: check-spelling/check-spelling@v0.0.22 with: config: .github/actions/spell-check checkout: true @@ -114,7 +114,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@v0.0.21 + uses: check-spelling/check-spelling@v0.0.22 with: config: .github/actions/spell-check checkout: true From a92443bdbfa4ae33b0b3ce5b05d35a4ebbc95b96 Mon Sep 17 00:00:00 2001 From: Deepak Sangle Date: Wed, 4 Oct 2023 17:37:53 +0530 Subject: [PATCH 37/39] [Settings]Fix typo in Hosts File Editor page (#28927) --- src/settings-ui/Settings.UI/Strings/en-us/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 50dfcccfb65..12d937e0d09 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -3635,7 +3635,7 @@ Activate by holding the key for the character you want to add an accent to, then Security key - Chose the encoding of the hosts file + Choose the encoding of the hosts file "Hosts" refers to the system hosts file, do not loc From 4eeba2629c7b4cee9bc44b4b86ac359b15f155d4 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 4 Oct 2023 09:40:01 -0500 Subject: [PATCH 38/39] [Build]Enable Control Flow Guard; required for MS binaries (#28910) * Enable Control Flow Guard; it is a requirement for any MS binaries As it says on the tin. Go go gadget VC! * Disable CFG for CLR projects (boo) * Disable E&C... (cherry picked from commit f0c3f58468c2b5d5eb7ebd37e60b82e6f14fc5e8) * okay, stop being surgical and just whack it * Revert "okay, stop being surgical and just whack it" This reverts commit e4e10c6b8ae0a5b407bb9d3d11b34b82a14934ed. * Oh CLRSupport is a **property** --- Cpp.Build.props | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Cpp.Build.props b/Cpp.Build.props index 2da75a85876..5eb275a5076 100644 --- a/Cpp.Build.props +++ b/Cpp.Build.props @@ -42,6 +42,9 @@ arm64 false $(MSBuildThisFileFullPath)\..\deps\;$(MSBuildThisFileFullPath)\..\packages\;$(ExternalIncludePath) + + + Guard @@ -53,9 +56,12 @@ false true stdcpplatest - false + false /await %(AdditionalOptions) _UNICODE;UNICODE;%(PreprocessorDefinitions) + + Guard + ProgramDatabase Windows From 45150067b3d5c2211f01560bd8d139f95aa94f9c Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Wed, 4 Oct 2023 17:25:28 +0100 Subject: [PATCH 39/39] Revert "[Deps]Bump check-spelling/check-spelling from 0.0.21 to 0.0.22 (#28953)" (#29016) This reverts commit d3edd6acc2ac6cef8630d089980b493713bd47ff. --- .github/workflows/spelling2.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/spelling2.yml b/.github/workflows/spelling2.yml index e38784d1484..0cbb2ce08c7 100644 --- a/.github/workflows/spelling2.yml +++ b/.github/workflows/spelling2.yml @@ -68,7 +68,7 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@v0.0.22 + uses: check-spelling/check-spelling@v0.0.21 with: config: .github/actions/spell-check suppress_push_for_open_pull_request: 1 @@ -97,7 +97,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' steps: - name: comment - uses: check-spelling/check-spelling@v0.0.22 + uses: check-spelling/check-spelling@v0.0.21 with: config: .github/actions/spell-check checkout: true @@ -114,7 +114,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@v0.0.22 + uses: check-spelling/check-spelling@v0.0.21 with: config: .github/actions/spell-check checkout: true