diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index cc06123032..607b39a31c 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -27,6 +27,7 @@ Actionkeyword actionrunner activatable ACTIVATEAPP +activationaction Addavirtualdesktop Addins ADDUNDORECORD @@ -196,6 +197,7 @@ bsd bsearch BSODs bstr +BText bti Btn btn @@ -297,6 +299,8 @@ codereview COINIT Colorbrush colorconv +colorhistory +colorhistorylimit colorpicker colorpickerref COLORREF @@ -510,6 +514,7 @@ DPopup DPSAPI Draggen DRAWFRAME +drawingcolor dreamsofameaningfullife drivedetectionwarning DRM @@ -827,6 +832,7 @@ gui guiddef GUITHREADINFO GValue +GText GWL gwl GWLP @@ -857,6 +863,7 @@ HDN hdrop HDS HEB +helptext hglobal HGLOBAL hh @@ -1889,6 +1896,7 @@ RStroked Rstrtmgr RTB Rtc +RText rtf Rtl RTLREADING @@ -2404,6 +2412,7 @@ VIRTUALDESKTOPCHANGE virtualization virtualized Virtualizing +visiblecolorformats Visibletrue Visio visualbrush diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 2b2f96488b..e68c840dda 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -598,7 +598,7 @@ - + diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorFormatModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorFormatModel.cs new file mode 100644 index 0000000000..46357c8dde --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorFormatModel.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation +// 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.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class ColorFormatModel : INotifyPropertyChanged + { + private string _name; + private string _example; + private bool _isShown; + + public ColorFormatModel(string name, string example, bool isShown) + { + Name = name; + Example = example; + IsShown = isShown; + } + + public string Name + { + get + { + return _name; + } + + set + { + _name = value; + OnPropertyChanged(); + } + } + + public string Example + { + get + { + return _example; + } + + set + { + _example = value; + OnPropertyChanged(); + } + } + + public bool IsShown + { + get + { + return _isShown; + } + + set + { + _isShown = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + private void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerProperties.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerProperties.cs index f5305f5960..dd4f9f90c5 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerProperties.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerProperties.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.Generic; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; @@ -14,6 +15,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library { ActivationShortcut = new HotkeySettings(true, false, false, true, 0x43); ChangeCursor = false; + ColorHistory = new List(); + ColorHistoryLimit = 20; + VisibleColorFormats = new Dictionary(); + VisibleColorFormats.Add("HEX", true); + VisibleColorFormats.Add("RGB", true); + VisibleColorFormats.Add("HSL", true); + ActivationAction = ColorPickerActivationAction.OpenColorPickerAndThenEditor; } public HotkeySettings ActivationShortcut { get; set; } @@ -25,6 +33,20 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("copiedcolorrepresentation")] public ColorRepresentationType CopiedColorRepresentation { get; set; } + [JsonPropertyName("activationaction")] + public ColorPickerActivationAction ActivationAction { get; set; } + + [JsonPropertyName("colorhistory")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Need to change this collection")] + public List ColorHistory { get; set; } + + [JsonPropertyName("colorhistorylimit")] + public int ColorHistoryLimit { get; set; } + + [JsonPropertyName("visiblecolorformats")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Need to change this collection")] + public Dictionary VisibleColorFormats { get; set; } + public override string ToString() => JsonSerializer.Serialize(this); } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/Enumerations/ColorPickerActivationAction.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/Enumerations/ColorPickerActivationAction.cs new file mode 100644 index 0000000000..e62173a53c --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/Enumerations/ColorPickerActivationAction.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.PowerToys.Settings.UI.Library.Enumerations +{ + public enum ColorPickerActivationAction + { + // Activation shortcut opens editor + OpenEditor, + + // Activation shortcut opens color picker and after picking a color color is copied into clipboard and opens editor + OpenColorPickerAndThenEditor, + + // Activation shortcut opens color picker only and picking color copies color into clipboard + OpenOnlyColorPicker, + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj b/src/core/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj index dc4cf10d0b..31dc1493ab 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj @@ -55,5 +55,4 @@ - diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/ViewModels/ColorPickerViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/ViewModels/ColorPickerViewModel.cs index 9d6dce41d6..dbafa7a4f5 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/ViewModels/ColorPickerViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/ViewModels/ColorPickerViewModel.cs @@ -4,21 +4,31 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Globalization; +using System.Linq; using System.Text.Json; +using System.Timers; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels { - public class ColorPickerViewModel : Observable + public class ColorPickerViewModel : Observable, IDisposable { + private bool disposedValue; + + // Delay saving of settings in order to avoid calling save multiple times and hitting file in use exception. If there is no other request to save settings in given interval, we proceed to save it, otherwise we schedule saving it after this interval + private const int SaveSettingsDelayInMs = 500; + private GeneralSettings GeneralSettingsConfig { get; set; } private readonly ISettingsUtils _settingsUtils; + private readonly object _delayedActionLock = new object(); private readonly ColorPickerSettings _colorPickerSettings; + private Timer _delayedTimer; private bool _isEnabled; @@ -61,6 +71,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; + + _delayedTimer = new Timer(); + _delayedTimer.Interval = SaveSettingsDelayInMs; + _delayedTimer.Elapsed += DelayedTimer_Tick; + _delayedTimer.AutoReset = false; + + InitializeColorFormats(); } /// @@ -129,6 +146,139 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels } } + public bool ActivationOpensEditor + { + get => _colorPickerSettings.Properties.ActivationAction == ColorPickerActivationAction.OpenEditor; + set + { + if (value && _colorPickerSettings.Properties.ActivationAction != ColorPickerActivationAction.OpenEditor) + { + _colorPickerSettings.Properties.ActivationAction = ColorPickerActivationAction.OpenEditor; + OnPropertyChanged(nameof(ActivationOpensEditor)); + NotifySettingsChanged(); + } + } + } + + public bool ActivationOpensColorPickerOnly + { + get => _colorPickerSettings.Properties.ActivationAction == ColorPickerActivationAction.OpenOnlyColorPicker; + set + { + if (value && _colorPickerSettings.Properties.ActivationAction != ColorPickerActivationAction.OpenOnlyColorPicker) + { + _colorPickerSettings.Properties.ActivationAction = ColorPickerActivationAction.OpenOnlyColorPicker; + OnPropertyChanged(nameof(ActivationOpensColorPickerOnly)); + NotifySettingsChanged(); + } + } + } + + public bool ActivationOpensColorPickerAndEditor + { + get => _colorPickerSettings.Properties.ActivationAction == ColorPickerActivationAction.OpenColorPickerAndThenEditor; + set + { + if (value && _colorPickerSettings.Properties.ActivationAction != ColorPickerActivationAction.OpenColorPickerAndThenEditor) + { + _colorPickerSettings.Properties.ActivationAction = ColorPickerActivationAction.OpenColorPickerAndThenEditor; + OnPropertyChanged(nameof(ActivationOpensEditor)); + NotifySettingsChanged(); + } + } + } + + public ObservableCollection ColorFormats { get; } = new ObservableCollection(); + + private void InitializeColorFormats() + { + var visibleFormats = _colorPickerSettings.Properties.VisibleColorFormats; + var formatsUnordered = new List(); + + var hexFormatName = ColorRepresentationType.HEX.ToString(); + var rgbFormatName = ColorRepresentationType.RGB.ToString(); + var hslFormatName = ColorRepresentationType.HSL.ToString(); + var hsvFormatName = ColorRepresentationType.HSV.ToString(); + var cmykFormatName = ColorRepresentationType.CMYK.ToString(); + var hsbFormatName = ColorRepresentationType.HSB.ToString(); + var hsiFormatName = ColorRepresentationType.HSI.ToString(); + var hwbFormatName = ColorRepresentationType.HWB.ToString(); + var ncolFormatName = ColorRepresentationType.NCol.ToString(); + + formatsUnordered.Add(new ColorFormatModel(hexFormatName, "#EF68FF", visibleFormats.ContainsKey(hexFormatName) && visibleFormats[hexFormatName])); + formatsUnordered.Add(new ColorFormatModel(rgbFormatName, "rgb(239, 104, 255)", visibleFormats.ContainsKey(rgbFormatName) && visibleFormats[rgbFormatName])); + formatsUnordered.Add(new ColorFormatModel(hslFormatName, "hsl(294, 100%, 70%)", visibleFormats.ContainsKey(hslFormatName) && visibleFormats[hslFormatName])); + formatsUnordered.Add(new ColorFormatModel(hsvFormatName, "hsv(294, 59%, 100%)", visibleFormats.ContainsKey(hsvFormatName) && visibleFormats[hsvFormatName])); + formatsUnordered.Add(new ColorFormatModel(cmykFormatName, "cmyk(6%, 59%, 0%, 0%)", visibleFormats.ContainsKey(cmykFormatName) && visibleFormats[cmykFormatName])); + formatsUnordered.Add(new ColorFormatModel(hsbFormatName, "hsb(100, 50%, 75%)", visibleFormats.ContainsKey(hsbFormatName) && visibleFormats[hsbFormatName])); + formatsUnordered.Add(new ColorFormatModel(hsiFormatName, "hsi(100, 50%, 75%)", visibleFormats.ContainsKey(hsiFormatName) && visibleFormats[hsiFormatName])); + formatsUnordered.Add(new ColorFormatModel(hwbFormatName, "hwb(100, 50%, 75%)", visibleFormats.ContainsKey(hwbFormatName) && visibleFormats[hwbFormatName])); + formatsUnordered.Add(new ColorFormatModel(ncolFormatName, "R10, 50%, 75%", visibleFormats.ContainsKey(ncolFormatName) && visibleFormats[ncolFormatName])); + + foreach (var storedColorFormat in _colorPickerSettings.Properties.VisibleColorFormats) + { + var predefinedFormat = formatsUnordered.FirstOrDefault(it => it.Name == storedColorFormat.Key); + if (predefinedFormat != null) + { + predefinedFormat.PropertyChanged += ColorFormat_PropertyChanged; + ColorFormats.Add(predefinedFormat); + formatsUnordered.Remove(predefinedFormat); + } + } + + // settings file might not have all formats listed, add remaining ones we support + foreach (var remainingColorFormat in formatsUnordered) + { + remainingColorFormat.PropertyChanged += ColorFormat_PropertyChanged; + ColorFormats.Add(remainingColorFormat); + } + + ColorFormats.CollectionChanged += ColorFormats_CollectionChanged; + } + + private void ColorFormats_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + UpdateColorFormats(); + ScheduleSavingOfSettings(); + } + + private void ColorFormat_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + UpdateColorFormats(); + ScheduleSavingOfSettings(); + } + + private void ScheduleSavingOfSettings() + { + lock (_delayedActionLock) + { + if (_delayedTimer.Enabled) + { + _delayedTimer.Stop(); + } + + _delayedTimer.Start(); + } + } + + private void DelayedTimer_Tick(object sender, EventArgs e) + { + lock (_delayedActionLock) + { + _delayedTimer.Stop(); + NotifySettingsChanged(); + } + } + + private void UpdateColorFormats() + { + _colorPickerSettings.Properties.VisibleColorFormats.Clear(); + foreach (var colorFormat in ColorFormats) + { + _colorPickerSettings.Properties.VisibleColorFormats.Add(colorFormat.Name, colorFormat.IsShown); + } + } + private void NotifySettingsChanged() { // Using InvariantCulture as this is an IPC message @@ -139,5 +289,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels ColorPickerSettings.ModuleName, JsonSerializer.Serialize(_colorPickerSettings))); } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _delayedTimer.Dispose(); + } + + disposedValue = true; + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs index e84929ab39..6822305541 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs @@ -39,27 +39,29 @@ namespace ViewModelTests // Act // Initialise View Model with test Config files - ColorPickerViewModel viewModel = new ColorPickerViewModel(mockSettingsUtils, generalSettingsRepository, ColorPickerIsEnabledByDefaultIPC); + using (var viewModel = new ColorPickerViewModel(mockSettingsUtils, generalSettingsRepository, ColorPickerIsEnabledByDefaultIPC)) + { + // Assert + // Verify that the old settings persisted + Assert.AreEqual(originalGeneralSettings.Enabled.ColorPicker, viewModel.IsEnabled); + Assert.AreEqual(originalSettings.Properties.ActivationShortcut.ToString(), viewModel.ActivationShortcut.ToString()); + Assert.AreEqual(originalSettings.Properties.ChangeCursor, viewModel.ChangeCursor); - // Assert - // Verify that the old settings persisted - Assert.AreEqual(originalGeneralSettings.Enabled.ColorPicker, viewModel.IsEnabled); - Assert.AreEqual(originalSettings.Properties.ActivationShortcut.ToString(), viewModel.ActivationShortcut.ToString()); - Assert.AreEqual(originalSettings.Properties.ChangeCursor, viewModel.ChangeCursor); - - // Verify that the stub file was used - var expectedCallCount = 2; // once via the view model, and once by the test (GetSettings) - BackCompatTestProperties.VerifyModuleIOProviderWasRead(mockIOProvider, ColorPickerSettings.ModuleName, expectedCallCount); - BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount); + // Verify that the stub file was used + var expectedCallCount = 2; // once via the view model, and once by the test (GetSettings) + BackCompatTestProperties.VerifyModuleIOProviderWasRead(mockIOProvider, ColorPickerSettings.ModuleName, expectedCallCount); + BackCompatTestProperties.VerifyGeneralSettingsIOProviderWasRead(mockGeneralIOProvider, expectedCallCount); + } } [TestMethod] public void ColorPickerIsEnabledByDefault() { var mockSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); - var viewModel = new ColorPickerViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SettingsRepository.GetInstance(ISettingsUtilsMocks.GetStubSettingsUtils().Object), ColorPickerIsEnabledByDefaultIPC); - - Assert.IsTrue(viewModel.IsEnabled); + using (var viewModel = new ColorPickerViewModel(ISettingsUtilsMocks.GetStubSettingsUtils().Object, SettingsRepository.GetInstance(ISettingsUtilsMocks.GetStubSettingsUtils().Object), ColorPickerIsEnabledByDefaultIPC)) + { + Assert.IsTrue(viewModel.IsEnabled); + } } private static int ColorPickerIsEnabledByDefaultIPC(string msg) diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw b/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw index c44271dd38..8f84682255 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw +++ b/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw @@ -1,839 +1,873 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - General - Navigation view item name for General - - - PowerToys Run - Product name: Navigation view item name for PowerToys Run - - - PowerRename - Product name: Navigation view item name for PowerRename - - - Shortcut Guide - Product name: Navigation view item name for Shortcut Guide - - - File Explorer - Product name: Navigation view item name for File Explorer Preview - - - FancyZones - Product name: Navigation view item name for FancyZones - - - Image Resizer - Product name: Navigation view item name for Image Resizer - - - Color Picker - Product name: Navigation view item name for Color Picker - - - Keyboard Manager - Product name: Navigation view item name for Keyboard Manager - - - Current configuration - Keyboard Manager current configuration header - - - Reconfigure your keyboard by remapping keys and shortcuts. - Keyboard Manager page description - - - Enable Keyboard Manager - - Keyboard Manager enable toggle header - do not loc the Product name. Do you want this feature on / off - - - - Select the profile to display the active key remap and shortcuts - Keyboard Manager configuration dropdown description - - - Remap a key - Keyboard Manager remap keyboard button content - - - Remap keys - Keyboard Manager remap keyboard header - - - Remap a shortcut - Keyboard Manager remap shortcuts button - - - Remap shortcuts - Keyboard Manager remap shortcuts header - - - Current Key Remappings - - - Current Shortcut Remappings - - - Key Remapping - key as in keyboard key - - - Shortcut Remapping - - - Remapped to - - - Remapped to - - - For Target Application - What computer application would this be for - - - Keyboard Manager - do not loc, product name - - - Quick and simple system-wide color picker. - - - Enable Color Picker - do not loc the Product name. Do you want this feature on / off - - - Change cursor when picking a color - - - Copied color representation - - - Open Color Picker - do not loc product name - - - A quick launcher that has additional capabilities without sacrificing performance. - - - Enable PowerToys Run - do not loc the Product name. Do you want this feature on / off - - - Search & results - - - Search result preference - - - Most recently used - - - Alphabetical order - - - Running processes/open applications - - - Search type preference - - - Application name - - - A string that is contained in the application - - - Executable name - - - Maximum number of results - - - Shortcuts - - - Open PowerToys Run - - - Open file location - - - Copy path location - - - Open console - console refers to Windows command prompt - - - Override Win+R shortcut - - - Override Win+S shortcut - - - Ignore shortcuts in fullscreen mode - - - Disable drive detection warning for the file search plugin - - - Clear the previous query on launch - - - To: - Keyboard Manager mapping keys view right header - - - About this feature - - - Appearance - - - FancyZones windows - do not loc the Product name - - - Create window layouts to help make multi-tasking easy. - windows refers to application windows - - - Keep windows in their zones when the screen resolution changes - windows refers to application windows - - - Enable FancyZones - do not loc the Product name. Do you want this feature on / off - - - Excluded apps - - - To exclude an application from snapping to zones add its name here (one per line). These apps will react only to Windows Snap. - - - Zone highlight opacity - - - Open layout editor - Shortcut to launch the FancyZones layout editor application - - - Shortcut setting - - - Information Symbol - - - Launch layout editor - launches the FancyZones layout editor application - - - Make dragged window transparent - - - Use a non-primary mouse button to toggle zone activation - - - Move windows between zones across all monitors - - - Override Windows Snap shortcut (Win + Arrow) to move windows between zones - - - Hold Shift key to activate zones while dragging - - - Show zones on all monitors while dragging a window - - - Move newly created windows to their last known zone - - - Move newly created windows to the current active monitor (EXPERIMENTAL) - - - Follow mouse cursor instead of focus when launching editor in a multi screen environment - - - Zone behavior - - - Zone highlight color - - - During zone layout changes, windows assigned to a zone will match new size/positions - - - Give feedback - - - Learn more - This label is there to point people to additional overview for how to use the product - - - Attribution - giving credit to the projects this utility was based on - - - About PowerToys - - - PowerToys Icon - - - Check for updates - - - Privacy statement - - - Report a bug - Report an issue inside powertoys - - - Request a feature - Tell our team what we should build - - - Restart as administrator - running PowerToys as a higher level user, account is typically referred to as an admin / administrator - - - Run at startup - - - A Windows Shell extension for more advanced bulk renaming using search and replace or regular expressions. - - - Shell integration - This refers to directly integrating in with Windows - - - Enable PowerRename - do not loc the Product name. Do you want this feature on / off - - - Settings theme - - - Show icon on context menu - - - Appear only in extended context menu (Shift + Right-click) - - - Maximum number of items - - - Show values from last use - - - Enable Markdown (.md) preview - Do not loc "Markdown". Do you want this feature on / off - - - Enable SVG (.svg) preview - Do you want this feature on / off - - - Enable SVG (.svg) thumbnails - Do you want this feature on / off - - - These settings allow you to manage your Windows File Explorer custom preview handlers. - - - Autocomplete - - - Open-source notice - - - Enable autocomplete for the search and replace fields - - - Zone border color - - - Zone inactive color - - - Shows a help overlay with Windows shortcuts when the Windows key is pressed. - - - Press duration before showing (ms) - pressing a key in milliseconds - - - Appearance & behavior - - - Enable Shortcut Guide - do not loc the Product name. Do you want this feature on / off - - - Opacity of background - - - Image sizes - - - Lets you resize images by right-clicking. - - - Enable Image Resizer - do not loc the Product name. Do you want this feature on / off - - - Image Size - - - Configurations - - - Configuration Name - - - Fit Property - - - Width Property - - - Height Property - - - Size Property - - - Times Symbol - - - Remove - Removes a user defined setting group for Image Resizer - - - Remove - Removes a user defined setting group for Image Resizer - - - Image Resizer - - - Add size - - - Save sizes - - - JPEG quality level - - - PNG interlacing - - - TIFF compression - - - File - as in a computer file - - - Default - - - CCITT3 - do not loc - - - CCITT4 - do not loc - - - Default - - - LZW - do not loc - - - None - - - RLE - do not loc - - - Zip - do not loc - - - BMP encoder - - - GIF encoder - - - JPEG encoder - - - PNG encoder - - - TIFF encoder - - - WMPhoto encoder - - - Fill - Refers to filling an image into a certain size. It could overflow - - - Fit - Refers to fitting an image into a certain size. It won't overflow - - - Stretch - Refers to stretching an image into a certain size. Won't overflow but could distort. - - - Centimeters - - - Inches - - - Percent - - - Pixels - - - Off - - - On - - - Learn more about administrator mode - - - Download updates automatically (Except on metered connections) - - - Currently running as administrator - - - Always run as administrator - - - Running as user - - - Running as administrator - - - About FancyZones - - - About File Explorer - - - File Explorer - - - About Image Resizer - - - About Keyboard Manager - - - About Color Picker - - - Color Picker - - - About PowerToys Run - - - PowerToys Run - - - About PowerRename - do not loc the product name - - - PowerRename - do not loc - - - About Shortcut Guide - - - Shortcut Guide - - - GitHub repository - - - Updates - - - Version: - - - Version - - - Administrator mode - - - You need to run as administrator to use this setting - - - Only shortcuts with the following hotkeys are valid: - keyboard shortcuts and - - - Restore the original size of windows when unsnapping - - - Fallback encoder - - - The following parameters can be used: - - - Filename format - - - Use original date modified - - - Encoding - - - Remap keys to other keys or shortcuts. - - - Remap shortcuts to other shortcuts or keys. Additionally, mappings can be targeted to specific applications as well. - - - Microsoft PowerToys is a set of utilities for power users to tune and streamline their Windows experience for greater productivity. - - - Allow zones to span across monitors (all monitors must have the same DPI scaling) - - - Actual height - - - Actual width - - - Original filename - - - Selected height - - - Selected width - - - Size name - - - Move windows based on their position - Windows refers to application windows - - - New update available - - - PowerToys is up-to-date. - - - Icon Preview - - - Preview Pane - - - You need to run as administrator to modify these settings - - - The settings on this page affect all users on the system - - - A reboot may be required for changes to these settings to take effect - - - Example: outlook.exe - - - Example: %1 (%2) - - - Choose a mode - - - Dark - Dark refers to color, not weight - - - Light - Light refers to color, not weight - - - Windows default - Windows refers to the Operating system - - - Windows color settings - Windows refers to the Operating system - - - Learn more about remapping limitations - This is a link that will discuss what is and is not possible for Keyboard manager to remap - - - Editor - - - Window behavior - - - Behavior - - - Use Boost library (provides extended features but may use different regex syntax) - Boost is a product name, should not be translated - - - Made with 💗 by Microsoft and the PowerToys community. - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + General + Navigation view item name for General + + + PowerToys Run + Product name: Navigation view item name for PowerToys Run + + + PowerRename + Product name: Navigation view item name for PowerRename + + + Shortcut Guide + Product name: Navigation view item name for Shortcut Guide + + + File Explorer + Product name: Navigation view item name for File Explorer Preview + + + FancyZones + Product name: Navigation view item name for FancyZones + + + Image Resizer + Product name: Navigation view item name for Image Resizer + + + Color Picker + Product name: Navigation view item name for Color Picker + + + Keyboard Manager + Product name: Navigation view item name for Keyboard Manager + + + Current configuration + Keyboard Manager current configuration header + + + Reconfigure your keyboard by remapping keys and shortcuts. + Keyboard Manager page description + + + Enable Keyboard Manager + + Keyboard Manager enable toggle header + do not loc the Product name. Do you want this feature on / off + + + + Select the profile to display the active key remap and shortcuts + Keyboard Manager configuration dropdown description + + + Remap a key + Keyboard Manager remap keyboard button content + + + Remap keys + Keyboard Manager remap keyboard header + + + Remap a shortcut + Keyboard Manager remap shortcuts button + + + Remap shortcuts + Keyboard Manager remap shortcuts header + + + Current Key Remappings + + + Current Shortcut Remappings + + + Key Remapping + key as in keyboard key + + + Shortcut Remapping + + + Remapped to + + + Remapped to + + + For Target Application + What computer application would this be for + + + Keyboard Manager + do not loc, product name + + + Quick and simple system-wide color picker. + + + Enable Color Picker + do not loc the Product name. Do you want this feature on / off + + + Change cursor when picking a color + + + Activate Color Picker + do not loc product name + + + A quick launcher that has additional capabilities without sacrificing performance. + + + Enable PowerToys Run + do not loc the Product name. Do you want this feature on / off + + + Search & results + + + Search result preference + + + Most recently used + + + Alphabetical order + + + Running processes/open applications + + + Search type preference + + + Application name + + + A string that is contained in the application + + + Executable name + + + Maximum number of results + + + Shortcuts + + + Open PowerToys Run + + + Open file location + + + Copy path location + + + Open console + console refers to Windows command prompt + + + Override Win+R shortcut + + + Override Win+S shortcut + + + Ignore shortcuts in fullscreen mode + + + Disable drive detection warning for the file search plugin + + + Clear the previous query on launch + + + To: + Keyboard Manager mapping keys view right header + + + About this feature + + + Appearance + + + FancyZones windows + do not loc the Product name + + + Create window layouts to help make multi-tasking easy. + windows refers to application windows + + + Keep windows in their zones when the screen resolution changes + windows refers to application windows + + + Enable FancyZones + do not loc the Product name. Do you want this feature on / off + + + Excluded apps + + + To exclude an application from snapping to zones add its name here (one per line). These apps will react only to Windows Snap. + + + Zone highlight opacity + + + Open layout editor + Shortcut to launch the FancyZones layout editor application + + + Shortcut setting + + + Information Symbol + + + Launch layout editor + launches the FancyZones layout editor application + + + Make dragged window transparent + + + Use a non-primary mouse button to toggle zone activation + + + Move windows between zones across all monitors + + + Override Windows Snap shortcut (Win + Arrow) to move windows between zones + + + Hold Shift key to activate zones while dragging + + + Show zones on all monitors while dragging a window + + + Move newly created windows to their last known zone + windows refers to application windows + + + Move newly created windows to the current active monitor (EXPERIMENTAL) + + + Follow mouse cursor instead of focus when launching editor in a multi screen environment + + + Zone behavior + + + Zone highlight color + + + During zone layout changes, windows assigned to a zone will match new size/positions + + + Give feedback + + + Learn more + This label is there to point people to additional overview for how to use the product + + + Attribution + giving credit to the projects this utility was based on + + + About PowerToys + + + PowerToys Icon + + + Check for updates + + + Privacy statement + + + Report a bug + Report an issue inside powertoys + + + Request a feature + Tell our team what we should build + + + Restart as administrator + running PowerToys as a higher level user, account is typically referred to as an admin / administrator + + + Run at startup + + + A Windows Shell extension for more advanced bulk renaming using search and replace or regular expressions. + + + Shell integration + This refers to directly integrating in with Windows + + + Enable PowerRename + do not loc the Product name. Do you want this feature on / off + + + Settings theme + + + Show icon on context menu + + + Appear only in extended context menu (Shift + Right-click) + + + Maximum number of items + + + Show values from last use + + + Enable Markdown (.md) preview + Do not loc "Markdown". Do you want this feature on / off + + + Enable SVG (.svg) preview + Do you want this feature on / off + + + Enable SVG (.svg) thumbnails + Do you want this feature on / off + + + These settings allow you to manage your Windows File Explorer custom preview handlers. + + + Autocomplete + + + Open-source notice + + + Enable autocomplete for the search and replace fields + + + Zone border color + + + Zone inactive color + + + Shows a help overlay with Windows shortcuts when the Windows key is pressed. + + + Press duration before showing (ms) + pressing a key in milliseconds + + + Appearance & behavior + + + Enable Shortcut Guide + do not loc the Product name. Do you want this feature on / off + + + Opacity of background + + + Image sizes + + + Lets you resize images by right-clicking. + + + Enable Image Resizer + do not loc the Product name. Do you want this feature on / off + + + Image Size + + + Configurations + + + Configuration Name + + + Fit Property + + + Width Property + + + Height Property + + + Size Property + + + Times Symbol + + + Remove + Removes a user defined setting group for Image Resizer + + + Remove + Removes a user defined setting group for Image Resizer + + + Image Resizer + + + Add size + + + Save sizes + + + JPEG quality level + + + PNG interlacing + + + TIFF compression + + + File + as in a computer file + + + Default + + + CCITT3 + do not loc + + + CCITT4 + do not loc + + + Default + + + LZW + do not loc + + + None + + + RLE + do not loc + + + Zip + do not loc + + + BMP encoder + + + GIF encoder + + + JPEG encoder + + + PNG encoder + + + TIFF encoder + + + WMPhoto encoder + + + Fill + Refers to filling an image into a certain size. It could overflow + + + Fit + Refers to fitting an image into a certain size. It won't overflow + + + Stretch + Refers to stretching an image into a certain size. Won't overflow but could distort. + + + Centimeters + + + Inches + + + Percent + + + Pixels + + + Off + + + On + + + Learn more about administrator mode + + + Download updates automatically (Except on metered connections) + + + Currently running as administrator + + + Always run as administrator + + + Running as user + + + Running as administrator + + + About FancyZones + + + About File Explorer + + + File Explorer + + + About Image Resizer + + + About Keyboard Manager + + + About Color Picker + + + Color Picker + do not loc the Product name. + + + About PowerToys Run + + + PowerToys Run + + + About PowerRename + do not loc the product name + + + PowerRename + do not loc + + + About Shortcut Guide + + + Shortcut Guide + + + GitHub repository + + + Updates + + + Version: + + + Version + + + Administrator mode + + + You need to run as administrator to use this setting + + + Only shortcuts with the following hotkeys are valid: + keyboard shortcuts and + + + Restore the original size of windows when unsnapping + + + Fallback encoder + + + The following parameters can be used: + + + Filename format + + + Use original date modified + + + Encoding + + + Remap keys to other keys or shortcuts. + + + Remap shortcuts to other shortcuts or keys. Additionally, mappings can be targeted to specific applications as well. + + + Microsoft PowerToys is a set of utilities for power users to tune and streamline their Windows experience for greater productivity. + Windows refers to the OS + + + Allow zones to span across monitors (all monitors must have the same DPI scaling) + + + Actual height + + + Actual width + + + Original filename + + + Selected height + + + Selected width + + + Size name + + + Move windows based on their position + Windows refers to application windows + + + New update available + + + PowerToys is up-to-date. + + + Icon Preview + + + Preview Pane + + + You need to run as administrator to modify these settings + + + The settings on this page affect all users on the system + + + A reboot may be required for changes to these settings to take effect + + + Example: outlook.exe + + + Example: %1 (%2) + + + Choose a mode + + + Dark + Dark refers to color, not weight + + + Light + Light refers to color, not weight + + + Windows default + Windows refers to the Operating system + + + Windows color settings + Windows refers to the Operating system + + + Color format for clipboard + + + Color Picker with editor mode enabled + do not loc the product name + + + Pick a color from the screen, copy formatted value to clipboard, then to the editor + do not loc the product name + + + Editor + + + Activation behavior + + + Color formats + + + Learn more about remapping limitations + This is a link that will discuss what is and is not possible for Keyboard manager to remap + + + Editor + refers to the FancyZone editor + + + Window behavior + + + Behavior + + + Use Boost library (provides extended features but may use different regex syntax) + Boost is a product name, should not be translated + + + Made with 💗 by Microsoft and the PowerToys community. + + + Color Picker only + do not loc the product name + + + Pick a color from the screen and copy formatted value to clipboard + + + Open directly into the editor mode + + + Editor color formats (Change the order by dragging) + + \ No newline at end of file diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml index 1c9b6333b7..2ba3412133 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml @@ -6,6 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Custom="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:Color="using:Microsoft.PowerToys.Settings.UI.Library.ViewModels" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" @@ -13,95 +14,6 @@ AutomationProperties.LandmarkType="Main"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -126,5 +38,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs index c7a17a3335..5cdea125fa 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs @@ -4,7 +4,6 @@ using System.IO.Abstractions; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.ViewModels; using Windows.UI.Xaml.Controls; diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml index f14fea5e5c..a0fcc4073f 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerLauncherPage.xaml @@ -50,7 +50,7 @@ - diff --git a/src/modules/colorPicker/ColorPickerUI/App.xaml b/src/modules/colorPicker/ColorPickerUI/App.xaml index af41aa53ae..9824ba1f50 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.xaml +++ b/src/modules/colorPicker/ColorPickerUI/App.xaml @@ -2,10 +2,13 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ColorPickerUI" + xmlns:ui="http://schemas.modernwpf.com/2019" StartupUri="MainWindow.xaml"> + + diff --git a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs index f9d47f16e8..8275072793 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs +++ b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Windows; +using ColorPicker; using ColorPicker.Helpers; using ColorPicker.Mouse; using ManagedCommon; @@ -20,6 +21,7 @@ namespace ColorPickerUI private static string[] _args; private int _powerToysPid; private bool disposedValue; + private ThemeManager _themeManager; [STAThread] public static void Main(string[] args) @@ -64,6 +66,7 @@ namespace ColorPickerUI Environment.Exit(0); }); + _themeManager = new ThemeManager(this); base.OnStartup(e); } @@ -93,6 +96,8 @@ namespace ColorPickerUI _instanceMutex?.Dispose(); } + _themeManager?.Dispose(); + // TODO: free unmanaged resources (unmanaged objects) and override finalizer // TODO: set large fields to null disposedValue = true; diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/AppearAnimationBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/AppearAnimationBehavior.cs index 6ff882e68a..8e46aaf16e 100644 --- a/src/modules/colorPicker/ColorPickerUI/Behaviors/AppearAnimationBehavior.cs +++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/AppearAnimationBehavior.cs @@ -6,7 +6,6 @@ using System; using System.Windows; using System.Windows.Interactivity; using System.Windows.Media.Animation; -using ColorPicker.Constants; namespace ColorPicker.Behaviors { @@ -42,16 +41,10 @@ namespace ColorPicker.Behaviors var opacityAppear = new DoubleAnimation(0d, 1d, duration) { - EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut }, - }; - - var resize = new DoubleAnimation(0d, WindowConstant.PickerWindowWidth, duration) - { - EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut }, + EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }, }; AssociatedObject.BeginAnimation(UIElement.OpacityProperty, opacityAppear); - AssociatedObject.BeginAnimation(FrameworkElement.WidthProperty, resize); } private void Hide() @@ -59,10 +52,8 @@ namespace ColorPicker.Behaviors var duration = new Duration(TimeSpan.FromMilliseconds(1)); var opacityAppear = new DoubleAnimation(0d, duration); - var resize = new DoubleAnimation(0d, duration); AssociatedObject.BeginAnimation(UIElement.OpacityProperty, opacityAppear); - AssociatedObject.BeginAnimation(FrameworkElement.WidthProperty, resize); } } } diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/DragAndDropReorderBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/DragAndDropReorderBehavior.cs new file mode 100644 index 0000000000..2a0ea5d907 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/DragAndDropReorderBehavior.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation +// 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.Collections.ObjectModel; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Interactivity; +using ColorPicker.Models; + +namespace ColorPicker.Behaviors +{ + public class DragAndDropReorderBehavior : Behavior + { + protected override void OnAttached() + { + base.OnAttached(); + + var style = new Style(typeof(ContentPresenter)); + style.Setters.Add( + new EventSetter( + FrameworkElement.PreviewMouseMoveEvent, + new MouseEventHandler(ItemPreviewMouseMove))); + style.Setters.Add( + new EventSetter( + FrameworkElement.DropEvent, + new DragEventHandler(ItemDrop))); + AssociatedObject.ItemContainerStyle = style; + } + + private void ItemPreviewMouseMove(object sender, MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed) + { + Task.Run( + new Action(() => + { + Dispatcher.BeginInvoke( + new Action(() => + { + if (e.LeftButton == MouseButtonState.Pressed) + { + var data = new DataObject(); + data.SetData("Source", (sender as FrameworkElement).DataContext); + DragDrop.DoDragDrop(sender as DependencyObject, data, DragDropEffects.Move); + e.Handled = true; + } + }), null); + }), CancellationToken.None); + } + } + + private void ItemDrop(object sender, DragEventArgs e) + { + var source = e.Data.GetData("Source") as ColorFormatModel; + if (source != null) + { + int newIndex = AssociatedObject.Items.IndexOf((sender as FrameworkElement).DataContext); + var list = AssociatedObject.ItemsSource as ObservableCollection; + list.RemoveAt(list.IndexOf(source)); + list.Insert(newIndex, source); + } + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/DragWindowBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/DragWindowBehavior.cs new file mode 100644 index 0000000000..0218149f43 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/DragWindowBehavior.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation +// 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.Windows; +using System.Windows.Interactivity; + +namespace ColorPicker.Behaviors +{ + public class DragWindowBehavior : Behavior + { + protected override void OnAttached() + { + base.OnAttached(); + AssociatedObject.Loaded += AssociatedObject_Loaded; + } + + private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) + { + AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown; + } + + private void AssociatedObject_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + var parentWindow = Window.GetWindow(AssociatedObject); + if (parentWindow != null) + { + parentWindow.DragMove(); + } + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/MoveWindowBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/MoveWindowBehavior.cs index 4a39f4c805..b30657e031 100644 --- a/src/modules/colorPicker/ColorPickerUI/Behaviors/MoveWindowBehavior.cs +++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/MoveWindowBehavior.cs @@ -17,7 +17,7 @@ namespace ColorPicker.Behaviors { var sender = ((MoveWindowBehavior)d).AssociatedObject; var move = new DoubleAnimation(sender.Left, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop); - move.EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut }; + move.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }; sender.BeginAnimation(Window.LeftProperty, move, HandoffBehavior.Compose); } @@ -27,7 +27,7 @@ namespace ColorPicker.Behaviors { var sender = ((MoveWindowBehavior)d).AssociatedObject; var move = new DoubleAnimation(sender.Top, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop); - move.EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut }; + move.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }; sender.BeginAnimation(Window.TopProperty, move, HandoffBehavior.Compose); } diff --git a/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml b/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml new file mode 100644 index 0000000000..f561d52ed0 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml @@ -0,0 +1,27 @@ + + + + + diff --git a/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml.cs b/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml.cs new file mode 100644 index 0000000000..ccb3910af1 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation +// 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.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace ColorPicker +{ + /// + /// Interaction logic for ColorEditorWindow.xaml + /// + public partial class ColorEditorWindow : Window + { + public ColorEditorWindow() + { + InitializeComponent(); + Closing += ColorEditorWindow_Closing; + } + + private void ColorEditorWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + e.Cancel = true; + this.Hide(); + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj index 9126afe98a..8dad2d8751 100644 --- a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj +++ b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj @@ -106,12 +106,31 @@ + + + + ColorEditorWindow.xaml + + + + ColorFormatControl.xaml + + + ColorPickerControl.xaml + + + + + + + - + + @@ -121,6 +140,12 @@ + + + + + ColorEditorView.xaml + ZoomWindow.xaml @@ -139,6 +164,18 @@ ZoomView.xaml + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -173,6 +210,34 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -205,7 +270,7 @@ True - ResXFileCodeGenerator + PublicResXFileCodeGenerator Resources.Designer.cs @@ -220,11 +285,17 @@ + + 4.4.0 + 3.3.0 runtime; build; native; contentfiles; analyzers; buildtransitive all + + 0.9.2 + 12.2.5 diff --git a/src/modules/colorPicker/ColorPickerUI/Common/RangeObservableCollection.cs b/src/modules/colorPicker/ColorPickerUI/Common/RangeObservableCollection.cs new file mode 100644 index 0000000000..599890bdac --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Common/RangeObservableCollection.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; + +namespace ColorPicker.Common +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "File name is correct, ignore generics")] + public sealed class RangeObservableCollection : ObservableCollection + { + private object _collectionChangedLock = new object(); + private bool _suppressNotification; + + protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + lock (_collectionChangedLock) + { + if (!_suppressNotification) + { + base.OnCollectionChanged(e); + } + } + } + + public void AddRange(IEnumerable list) + { + if (list == null) + { + throw new ArgumentNullException(nameof(list)); + } + + _suppressNotification = true; + + foreach (T item in list) + { + Add(item); + } + + _suppressNotification = false; + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + public void AddWithoutNotification(T item) + { + lock (_collectionChangedLock) + { + _suppressNotification = true; + Add(item); + } + } + + public void ReleaseNotification() + { + lock (_collectionChangedLock) + { + _suppressNotification = false; + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } + + public void ClearWithoutNotification() + { + lock (_collectionChangedLock) + { + _suppressNotification = true; + Clear(); + } + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Constants/WindowConstant.cs b/src/modules/colorPicker/ColorPickerUI/Constants/WindowConstant.cs deleted file mode 100644 index c6a596fe46..0000000000 --- a/src/modules/colorPicker/ColorPickerUI/Constants/WindowConstant.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace ColorPicker.Constants -{ - /// - /// This class contains all assembly wide constants for windows and views - /// - public static class WindowConstant - { - /// - /// The width of the color picker window - /// - public const double PickerWindowWidth = 240d; - } -} diff --git a/src/modules/colorPicker/ColorPickerUI/Controls/ColorFormatControl.xaml b/src/modules/colorPicker/ColorPickerUI/Controls/ColorFormatControl.xaml new file mode 100644 index 0000000000..aaee472ce5 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Controls/ColorFormatControl.xaml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/colorPicker/ColorPickerUI/Controls/ColorFormatControl.xaml.cs b/src/modules/colorPicker/ColorPickerUI/Controls/ColorFormatControl.xaml.cs new file mode 100644 index 0000000000..8d475b9fe4 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Controls/ColorFormatControl.xaml.cs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation +// 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.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using ColorPicker.Helpers; +using ColorPicker.Models; + +namespace ColorPicker.Controls +{ + /// + /// Interaction logic for ColorFormatControl.xaml + /// + public partial class ColorFormatControl : UserControl + { + public static readonly DependencyProperty ColorFormatModelProperty = DependencyProperty.Register("ColorFormatModel", typeof(ColorFormatModel), typeof(ColorFormatControl), new PropertyMetadata(ColorFormatModelPropertyChanged)); + + public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor", typeof(Color), typeof(ColorFormatControl), new PropertyMetadata(SelectedColorPropertyChanged)); + + public static readonly DependencyProperty ColorCopiedNotificationBorderProperty = DependencyProperty.Register("ColorCopiedNotificationBorder", typeof(FrameworkElement), typeof(ColorFormatControl), new PropertyMetadata(ColorCopiedBorderPropertyChanged)); + + private const int CopyIndicatorStayTimeInMs = 3000; + private IThrottledActionInvoker _actionInvoker; + private bool _copyIndicatorVisible; + + public Color SelectedColor + { + get { return (Color)GetValue(SelectedColorProperty); } + set { SetValue(SelectedColorProperty, value); } + } + + public ColorFormatModel ColorFormatModel + { + get { return (ColorFormatModel)GetValue(ColorFormatModelProperty); } + set { SetValue(ColorFormatModelProperty, value); } + } + + public FrameworkElement ColorCopiedNotificationBorder + { + get { return (FrameworkElement)GetValue(ColorCopiedNotificationBorderProperty); } + set { SetValue(ColorCopiedNotificationBorderProperty, value); } + } + + public ColorFormatControl() + { + InitializeComponent(); + _actionInvoker = Bootstrapper.Container.GetExportedValue(); + CopyToClipboardButton.Click += CopyToClipboardButton_Click; + } + + private void CopyToClipboardButton_Click(object sender, RoutedEventArgs e) + { + ClipboardHelper.CopyToClipboard(ColorTextRepresentationTextBlock.Text); + if (!_copyIndicatorVisible) + { + AppearCopiedIndicator(); + } + + _actionInvoker.ScheduleAction(() => HideCopiedIndicator(), CopyIndicatorStayTimeInMs); + } + + private static void SelectedColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ColorFormatControl)d).ColorTextRepresentationTextBlock.Text = ((ColorFormatControl)d).ColorFormatModel.Convert((Color)e.NewValue); + } + + private static void ColorFormatModelPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ColorFormatControl)d).FormatNameTextBlock.Text = ((ColorFormatModel)e.NewValue).FormatName; + } + + private static void ColorCopiedBorderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ColorFormatControl)d).ColorCopiedNotificationBorder = (FrameworkElement)e.NewValue; + } + + private void AppearCopiedIndicator() + { + _copyIndicatorVisible = true; + var opacityAppear = new DoubleAnimation(1.0, new Duration(TimeSpan.FromMilliseconds(300))); + opacityAppear.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }; + var resize = new DoubleAnimation(56, new Duration(TimeSpan.FromMilliseconds(300))); + + resize.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }; + ColorCopiedNotificationBorder.BeginAnimation(Border.OpacityProperty, opacityAppear); + ColorCopiedNotificationBorder.BeginAnimation(Border.HeightProperty, resize); + } + + private void HideCopiedIndicator() + { + _copyIndicatorVisible = false; + var opacityDisappear = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(300))); + opacityDisappear.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }; + var resize = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(300))); + + resize.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }; + ColorCopiedNotificationBorder.BeginAnimation(Border.OpacityProperty, opacityDisappear); + ColorCopiedNotificationBorder.BeginAnimation(Border.HeightProperty, resize); + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml b/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml new file mode 100644 index 0000000000..bf1af9149d --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + internal static class ColorRepresentationHelper { + /// + /// Return a representation of a given + /// + /// The for the presentation + /// The type of the representation + /// A representation of a color + internal static string GetStringRepresentationFromMediaColor(System.Windows.Media.Color color, ColorRepresentationType colorRepresentationType) + { + var drawingcolor = Color.FromArgb(color.A, color.R, color.G, color.B); + return GetStringRepresentation(drawingcolor, colorRepresentationType); + } + /// /// Return a representation of a given /// diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/CustomLibraryThemeProvider.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/CustomLibraryThemeProvider.cs new file mode 100644 index 0000000000..7929280fd0 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/CustomLibraryThemeProvider.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation +// 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.Generic; +using ControlzEx.Theming; + +namespace ColorPicker.Helpers +{ + public class CustomLibraryThemeProvider : LibraryThemeProvider + { + public static readonly CustomLibraryThemeProvider DefaultInstance = new CustomLibraryThemeProvider(); + + public CustomLibraryThemeProvider() + : base(true) + { + } + + /// + public override void FillColorSchemeValues(Dictionary values, RuntimeThemeColorValues colorValues) + { + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ThrottledActionInvoker.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ThrottledActionInvoker.cs index 4d8cb8c42f..16279bd910 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ThrottledActionInvoker.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ThrottledActionInvoker.cs @@ -11,6 +11,7 @@ namespace ColorPicker.Helpers [Export(typeof(IThrottledActionInvoker))] public sealed class ThrottledActionInvoker : IThrottledActionInvoker { + private object _invokerLock = new object(); private Action _actionToRun; private DispatcherTimer _timer; @@ -23,20 +24,27 @@ namespace ColorPicker.Helpers public void ScheduleAction(Action action, int milliseconds) { - if (_timer.IsEnabled) + lock (_invokerLock) { - _timer.Stop(); + if (_timer.IsEnabled) + { + _timer.Stop(); + } + + _actionToRun = action; + _timer.Interval = new TimeSpan(0, 0, 0, 0, milliseconds); + + _timer.Start(); } - - _actionToRun = action; - _timer.Interval = new TimeSpan(0, 0, 0, 0, milliseconds); - - _timer.Start(); } private void Timer_Tick(object sender, EventArgs e) { - _actionToRun.Invoke(); + lock (_invokerLock) + { + _timer.Stop(); + _actionToRun.Invoke(); + } } } } diff --git a/src/modules/colorPicker/ColorPickerUI/Keyboard/KeyboardMonitor.cs b/src/modules/colorPicker/ColorPickerUI/Keyboard/KeyboardMonitor.cs index db26fea938..40b3ea51a5 100644 --- a/src/modules/colorPicker/ColorPickerUI/Keyboard/KeyboardMonitor.cs +++ b/src/modules/colorPicker/ColorPickerUI/Keyboard/KeyboardMonitor.cs @@ -9,6 +9,7 @@ using System.Windows.Input; using ColorPicker.Helpers; using ColorPicker.Settings; using ColorPicker.Telemetry; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Telemetry; using static ColorPicker.NativeMethods; @@ -92,7 +93,14 @@ namespace ColorPicker.Keyboard if (ArraysAreSame(currentlyPressedKeys, _activationKeys)) { - _appStateHandler.ShowColorPicker(); + if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor) + { + _appStateHandler.ShowColorPickerEditor(); + } + else + { + _appStateHandler.ShowColorPicker(); + } } } diff --git a/src/modules/colorPicker/ColorPickerUI/MainWindow.xaml b/src/modules/colorPicker/ColorPickerUI/MainWindow.xaml index 0dd8ce3d04..f51d06a469 100644 --- a/src/modules/colorPicker/ColorPickerUI/MainWindow.xaml +++ b/src/modules/colorPicker/ColorPickerUI/MainWindow.xaml @@ -3,11 +3,18 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:constants="clr-namespace:ColorPicker.Constants" mc:Ignorable="d" xmlns:e="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:behaviors="clr-namespace:ColorPicker.Behaviors" - Title="Color Picker" Height="50" Width="{x:Static constants:WindowConstant.PickerWindowWidth}" WindowStyle="None" Opacity="0.01" ShowInTaskbar="False" ResizeMode="NoResize" Topmost="True" Background="Transparent" AllowsTransparency="True"> + Height="64" + WindowStyle="None" + Opacity="0.01" + ShowInTaskbar="False" + ResizeMode="NoResize" + Topmost="True" + Background="Transparent" + SizeToContent="Width" + AllowsTransparency="True"> diff --git a/src/modules/colorPicker/ColorPickerUI/Models/ColorFormatModel.cs b/src/modules/colorPicker/ColorPickerUI/Models/ColorFormatModel.cs new file mode 100644 index 0000000000..acefc47511 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Models/ColorFormatModel.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// 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.Windows.Media; + +namespace ColorPicker.Models +{ + public class ColorFormatModel + { + public string FormatName { get; set; } + + public Func Convert { get; set; } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Properties/Resources.Designer.cs b/src/modules/colorPicker/ColorPickerUI/Properties/Resources.Designer.cs index 3dc7f06743..cb2b1a9023 100644 --- a/src/modules/colorPicker/ColorPickerUI/Properties/Resources.Designer.cs +++ b/src/modules/colorPicker/ColorPickerUI/Properties/Resources.Designer.cs @@ -22,7 +22,7 @@ namespace ColorPicker.Properties { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { + public class Resources { private static global::System.Resources.ResourceManager resourceMan; @@ -36,7 +36,7 @@ namespace ColorPicker.Properties { /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ColorPicker.Properties.Resources", typeof(Resources).Assembly); @@ -51,7 +51,7 @@ namespace ColorPicker.Properties { /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -59,5 +59,176 @@ namespace ColorPicker.Properties { resourceCulture = value; } } + + /// + /// Looks up a localized string similar to Blue value. + /// + public static string Blue_value { + get { + return ResourceManager.GetString("Blue_value", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + public static string Cancel { + get { + return ResourceManager.GetString("Cancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copied to clipboard. + /// + public static string Copied_to_clipboard { + get { + return ResourceManager.GetString("Copied_to_clipboard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy to clipboard. + /// + public static string Copy_to_clipboard { + get { + return ResourceManager.GetString("Copy_to_clipboard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Color Picker editor. + /// + public static string cp_editor { + get { + return ResourceManager.GetString("cp_editor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Green value. + /// + public static string Green_value { + get { + return ResourceManager.GetString("Green_value", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hex value. + /// + public static string Hex_value { + get { + return ResourceManager.GetString("Hex_value", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hue slider. + /// + public static string Hue_slider { + get { + return ResourceManager.GetString("Hue_slider", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Press the Color Picker icon to capture a color from your screen.. + /// + public static string No_colors_yet { + get { + return ResourceManager.GetString("No_colors_yet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OK. + /// + public static string OK { + get { + return ResourceManager.GetString("OK", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open settings. + /// + public static string Open_settings { + get { + return ResourceManager.GetString("Open_settings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Pick color from screen. + /// + public static string Pick_color { + get { + return ResourceManager.GetString("Pick_color", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Red value. + /// + public static string Red_value { + get { + return ResourceManager.GetString("Red_value", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove. + /// + public static string Remove { + get { + return ResourceManager.GetString("Remove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saturation slider. + /// + public static string Saturation_slider { + get { + return ResourceManager.GetString("Saturation_slider", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Selected color. + /// + public static string Selected_color { + get { + return ResourceManager.GetString("Selected_color", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This color can be adjusted. + /// + public static string Selected_color_helptext { + get { + return ResourceManager.GetString("Selected_color_helptext", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adjust color. + /// + public static string Selected_color_tooltip { + get { + return ResourceManager.GetString("Selected_color_tooltip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Value slider. + /// + public static string Value_slider { + get { + return ResourceManager.GetString("Value_slider", resourceCulture); + } + } } } diff --git a/src/modules/colorPicker/ColorPickerUI/Properties/Resources.resx b/src/modules/colorPicker/ColorPickerUI/Properties/Resources.resx index af7dbebbac..61ee52abc2 100644 --- a/src/modules/colorPicker/ColorPickerUI/Properties/Resources.resx +++ b/src/modules/colorPicker/ColorPickerUI/Properties/Resources.resx @@ -46,7 +46,7 @@ mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with - : System.Serialization.Formatters.Binary.BinaryFormatter + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 @@ -60,6 +60,7 @@ : and then encoded with base64 encoding. --> + @@ -68,9 +69,10 @@ - + + @@ -85,9 +87,10 @@ - + + @@ -109,9 +112,85 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Blue value + Tool tip that appears when hovering over a textbox that represents the blue HEX value + + + Cancel + Cancel button content + + + Copied to clipboard + Message that appears when a user clicked a button that copies the value to the user's clipboard + + + Copy to clipboard + Tooltip that describes a button that will copy a value to the user's clipboard + + + Color Picker editor + Do not translate product name + + + Green value + Tool tip that appears when hovering over a textbox that represents the green HEX value + + + Hex value + Tool tip that appears when hovering over a textbox that represents the HEX value + + + Hue slider + Tool tip that appears when hovering over a slider that represents the color hue (from HSV) + + + Press the Color Picker icon to capture a color from your screen. + Message that shows when the user launches the window for the first time or when no colors have been selected + + + OK + OK button content + + + Open settings + Tooltip that shows that the user can click this button to open PowerToys settings + + + Pick color from screen + Tooltip that shows that the user can click this button to pick a new color + + + Red value + Tool tip that appears when hovering over a textbox that represents the red HEX value + + + Remove + Tooltip that shows that the user can remove an item from a list + + + Saturation slider + Tool tip that appears when hovering over a slider that represents the color saturation (from HSV) + + + Selected color + Tooltip that shows the user what the selected color is + + + This color can be adjusted + Narrator message that describes to the user that this color can be adjusted + + + Adjust color + Tooltip that shows that the user can adjust this color + + + Value slider + Tool tip that appears when hovering over a slider that represents the color value (from HSV) + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Resources/Styles.xaml b/src/modules/colorPicker/ColorPickerUI/Resources/Styles.xaml index 7aa45274bd..e0949972da 100644 --- a/src/modules/colorPicker/ColorPickerUI/Resources/Styles.xaml +++ b/src/modules/colorPicker/ColorPickerUI/Resources/Styles.xaml @@ -1,5 +1,133 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:ui="http://schemas.modernwpf.com/2019" + xmlns:converters="clr-namespace:ColorPicker.Converters"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Resources/ViewModelViewMappings.xaml b/src/modules/colorPicker/ColorPickerUI/Resources/ViewModelViewMappings.xaml index 0a70693445..a0333764cc 100644 --- a/src/modules/colorPicker/ColorPickerUI/Resources/ViewModelViewMappings.xaml +++ b/src/modules/colorPicker/ColorPickerUI/Resources/ViewModelViewMappings.xaml @@ -9,4 +9,8 @@ + + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Resources/icon.ico b/src/modules/colorPicker/ColorPickerUI/Resources/icon.ico index 29de92e103..3b21588f2f 100644 Binary files a/src/modules/colorPicker/ColorPickerUI/Resources/icon.ico and b/src/modules/colorPicker/ColorPickerUI/Resources/icon.ico differ diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs index 585b441279..d2a2e92332 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs @@ -2,6 +2,8 @@ // 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.ObjectModel; +using ColorPicker.Common; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; namespace ColorPicker.Settings @@ -13,5 +15,13 @@ namespace ColorPicker.Settings SettingItem ChangeCursor { get; } SettingItem CopiedColorRepresentation { get; set; } + + SettingItem ActivationAction { get; } + + RangeObservableCollection ColorHistory { get; } + + SettingItem ColorHistoryLimit { get; } + + ObservableCollection VisibleColorFormats { get; } } } diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index 852278f90b..419817c947 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -3,10 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.ObjectModel; using System.ComponentModel.Composition; using System.IO; using System.IO.Abstractions; +using System.Linq; using System.Threading; +using ColorPicker.Common; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Utilities; @@ -20,22 +23,40 @@ namespace ColorPicker.Settings private const string ColorPickerModuleName = "ColorPicker"; private const string DefaultActivationShortcut = "Ctrl + Break"; private const int MaxNumberOfRetry = 5; + private const int SettingsReadOnChangeDelayInMs = 300; [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Actually, call back is LoadSettingsFromJson")] private readonly IFileSystemWatcher _watcher; private readonly object _loadingSettingsLock = new object(); + private bool _loadingColorsHistory; + [ImportingConstructor] - public UserSettings() + public UserSettings(Helpers.IThrottledActionInvoker throttledActionInvoker) { _settingsUtils = new SettingsUtils(); ChangeCursor = new SettingItem(true); ActivationShortcut = new SettingItem(DefaultActivationShortcut); CopiedColorRepresentation = new SettingItem(ColorRepresentationType.HEX); + ActivationAction = new SettingItem(ColorPickerActivationAction.OpenEditor); + ColorHistoryLimit = new SettingItem(20); + ColorHistory.CollectionChanged += ColorHistory_CollectionChanged; LoadSettingsFromJson(); - _watcher = Helper.GetFileWatcher(ColorPickerModuleName, "settings.json", LoadSettingsFromJson); + + // delay loading settings on change by some time to avoid file in use exception + _watcher = Helper.GetFileWatcher(ColorPickerModuleName, "settings.json", () => throttledActionInvoker.ScheduleAction(LoadSettingsFromJson, SettingsReadOnChangeDelayInMs)); + } + + private void ColorHistory_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (!_loadingColorsHistory) + { + var settings = _settingsUtils.GetSettings(ColorPickerModuleName); + settings.Properties.ColorHistory = ColorHistory.ToList(); + settings.Save(_settingsUtils); + } } public SettingItem ActivationShortcut { get; private set; } @@ -44,6 +65,14 @@ namespace ColorPicker.Settings public SettingItem CopiedColorRepresentation { get; set; } + public SettingItem ActivationAction { get; private set; } + + public RangeObservableCollection ColorHistory { get; private set; } = new RangeObservableCollection(); + + public SettingItem ColorHistoryLimit { get; } + + public ObservableCollection VisibleColorFormats { get; private set; } = new ObservableCollection(); + private void LoadSettingsFromJson() { // TODO this IO call should by Async, update GetFileWatcher helper to support async @@ -72,6 +101,31 @@ namespace ColorPicker.Settings ChangeCursor.Value = settings.Properties.ChangeCursor; ActivationShortcut.Value = settings.Properties.ActivationShortcut.ToString(); CopiedColorRepresentation.Value = settings.Properties.CopiedColorRepresentation; + ActivationAction.Value = settings.Properties.ActivationAction; + ColorHistoryLimit.Value = settings.Properties.ColorHistoryLimit; + + if (settings.Properties.ColorHistory == null) + { + settings.Properties.ColorHistory = new System.Collections.Generic.List(); + } + + _loadingColorsHistory = true; + ColorHistory.Clear(); + foreach (var item in settings.Properties.ColorHistory) + { + ColorHistory.Add(item); + } + + _loadingColorsHistory = false; + + VisibleColorFormats.Clear(); + foreach (var item in settings.Properties.VisibleColorFormats) + { + if (item.Value) + { + VisibleColorFormats.Add(item.Key); + } + } } retry = false; diff --git a/src/modules/colorPicker/ColorPickerUI/ThemeManager.cs b/src/modules/colorPicker/ColorPickerUI/ThemeManager.cs new file mode 100644 index 0000000000..6792684dec --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/ThemeManager.cs @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation +// 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.Linq; +using System.Windows; +using ColorPicker.Helpers; +using ControlzEx.Theming; +using Microsoft.Win32; + +namespace ColorPicker +{ + public class ThemeManager : IDisposable + { + private readonly Application _app; + private const string LightTheme = "Light.Accent1"; + private const string DarkTheme = "Dark.Accent1"; + private const string HighContrastOneTheme = "HighContrast.Accent2"; + private const string HighContrastTwoTheme = "HighContrast.Accent3"; + private const string HighContrastBlackTheme = "HighContrast.Accent4"; + private const string HighContrastWhiteTheme = "HighContrast.Accent5"; + + private Theme currentTheme; + private bool _disposed; + + public event ThemeChangedHandler ThemeChanged; + + public ThemeManager(Application app) + { + _app = app; + + Uri highContrastOneThemeUri = new Uri("pack://application:,,,/Themes/HighContrast1.xaml"); + Uri highContrastTwoThemeUri = new Uri("pack://application:,,,/Themes/HighContrast2.xaml"); + Uri highContrastBlackThemeUri = new Uri("pack://application:,,,/Themes/HighContrastWhite.xaml"); + Uri highContrastWhiteThemeUri = new Uri("pack://application:,,,/Themes/HighContrastBlack.xaml"); + Uri lightThemeUri = new Uri("pack://application:,,,/Themes/Light.xaml"); + Uri darkThemeUri = new Uri("pack://application:,,,/Themes/Dark.xaml"); + + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastOneThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastTwoThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastBlackThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastWhiteThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + lightThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + darkThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + + ResetTheme(); + ControlzEx.Theming.ThemeManager.Current.ThemeSyncMode = ThemeSyncMode.SyncWithAppMode; + ControlzEx.Theming.ThemeManager.Current.ThemeChanged += Current_ThemeChanged; + SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged; + } + + private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(SystemParameters.HighContrast)) + { + ResetTheme(); + } + } + + public Theme GetCurrentTheme() + { + return currentTheme; + } + + private static Theme GetHighContrastBaseType() + { + string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes"; + string theme = (string)Registry.GetValue(registryKey, "CurrentTheme", string.Empty); + theme = theme.Split('\\').Last().Split('.').First().ToString(); + + switch (theme) + { + case "hc1": + return Theme.HighContrastOne; + case "hc2": + return Theme.HighContrastTwo; + case "hcwhite": + return Theme.HighContrastWhite; + case "hcblack": + return Theme.HighContrastBlack; + default: + return Theme.None; + } + } + + private void ResetTheme() + { + if (SystemParameters.HighContrast) + { + Theme highContrastBaseType = GetHighContrastBaseType(); + ChangeTheme(highContrastBaseType); + } + else + { + string baseColor = WindowsThemeHelper.GetWindowsBaseColor(); + ChangeTheme((Theme)Enum.Parse(typeof(Theme), baseColor)); + } + } + + private void ChangeTheme(Theme theme) + { + Theme oldTheme = currentTheme; + if (theme == currentTheme) + { + return; + } + + if (theme == Theme.HighContrastOne) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastOneTheme); + currentTheme = Theme.HighContrastOne; + } + else if (theme == Theme.HighContrastTwo) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastTwoTheme); + currentTheme = Theme.HighContrastTwo; + } + else if (theme == Theme.HighContrastWhite) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastWhiteTheme); + currentTheme = Theme.HighContrastWhite; + } + else if (theme == Theme.HighContrastBlack) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastBlackTheme); + currentTheme = Theme.HighContrastBlack; + } + else if (theme == Theme.Light) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, LightTheme); + currentTheme = Theme.Light; + } + else if (theme == Theme.Dark) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, DarkTheme); + currentTheme = Theme.Dark; + } + else + { + currentTheme = Theme.None; + } + + ThemeChanged?.Invoke(oldTheme, currentTheme); + } + + private void Current_ThemeChanged(object sender, ThemeChangedEventArgs e) + { + ResetTheme(); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + ControlzEx.Theming.ThemeManager.Current.ThemeChanged -= Current_ThemeChanged; + SystemParameters.StaticPropertyChanged -= SystemParameters_StaticPropertyChanged; + _disposed = true; + } + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + + public delegate void ThemeChangedHandler(Theme oldTheme, Theme newTheme); + + public enum Theme + { + None, + Light, + Dark, + HighContrastOne, + HighContrastTwo, + HighContrastBlack, + HighContrastWhite, + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Themes/Dark.xaml b/src/modules/colorPicker/ColorPickerUI/Themes/Dark.xaml new file mode 100644 index 0000000000..e1480cc879 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Themes/Dark.xaml @@ -0,0 +1,24 @@ + + + + Dark.Accent1 + PowerToysRun + Accent1 (Dark) + Dark + Accent1 + Black + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Themes/HighContrast1.xaml b/src/modules/colorPicker/ColorPickerUI/Themes/HighContrast1.xaml new file mode 100644 index 0000000000..9a1f7eba52 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Themes/HighContrast1.xaml @@ -0,0 +1,24 @@ + + + + HighContrast.Accent2 + PowerToysRun + Accent2 (HighContrast) + HighContrast + Accent2 + White + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Themes/HighContrast2.xaml b/src/modules/colorPicker/ColorPickerUI/Themes/HighContrast2.xaml new file mode 100644 index 0000000000..3867666bbd --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Themes/HighContrast2.xaml @@ -0,0 +1,25 @@ + + + + HighContrast.Accent3 + PowerToysRun + Accent3 (HighContrast) + HighContrast + Accent3 + White + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Themes/HighContrastBlack.xaml b/src/modules/colorPicker/ColorPickerUI/Themes/HighContrastBlack.xaml new file mode 100644 index 0000000000..fe485af1b0 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Themes/HighContrastBlack.xaml @@ -0,0 +1,25 @@ + + + + HighContrast.Accent4 + PowerToysRun + Accent4 (HighContrast) + HighContrast + Accent4 + White + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Themes/HighContrastWhite.xaml b/src/modules/colorPicker/ColorPickerUI/Themes/HighContrastWhite.xaml new file mode 100644 index 0000000000..c571ee66e9 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Themes/HighContrastWhite.xaml @@ -0,0 +1,25 @@ + + + + HighContrast.Accent5 + PowerToysRun + Accent5 (HighContrast) + HighContrast + Accent5 + White + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Themes/Light.xaml b/src/modules/colorPicker/ColorPickerUI/Themes/Light.xaml new file mode 100644 index 0000000000..b3d4a0a41d --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Themes/Light.xaml @@ -0,0 +1,25 @@ + + + + Light.Accent1 + PowerToysRun + Accent1 (Light) + Light + Accent1 + White + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModelContracts/IColorEditorViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModelContracts/IColorEditorViewModel.cs new file mode 100644 index 0000000000..6f80070482 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/ViewModelContracts/IColorEditorViewModel.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// 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.Collections.ObjectModel; +using System.Windows.Input; +using System.Windows.Media; +using ColorPicker.Models; + +namespace ColorPicker.ViewModelContracts +{ + public interface IColorEditorViewModel + { + event EventHandler OpenColorPickerRequested; + + ICommand OpenColorPickerCommand { get; } + + ICommand RemoveColorCommand { get; } + + ObservableCollection ColorRepresentations { get; } + + ObservableCollection ColorsHistory { get; } + + Color SelectedColor { get; set; } + + int SelectedColorIndex { get; set; } + + void Initialize(); + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs new file mode 100644 index 0000000000..30c59954de --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs @@ -0,0 +1,224 @@ +// Copyright (c) Microsoft Corporation +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition; +using System.Globalization; +using System.Linq; +using System.Windows.Input; +using System.Windows.Media; +using ColorPicker.Common; +using ColorPicker.Helpers; +using ColorPicker.Models; +using ColorPicker.Settings; +using ColorPicker.ViewModelContracts; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; + +namespace ColorPicker.ViewModels +{ + [Export(typeof(IColorEditorViewModel))] + public class ColorEditorViewModel : ViewModelBase, IColorEditorViewModel + { + private readonly IUserSettings _userSettings; + private readonly List _allColorRepresentations = new List(); + private Color _selectedColor; + private bool _initializing; + private int _selectedColorIndex; + + [ImportingConstructor] + public ColorEditorViewModel(IUserSettings userSettings) + { + OpenColorPickerCommand = new RelayCommand(() => OpenColorPickerRequested?.Invoke(this, EventArgs.Empty)); + RemoveColorCommand = new RelayCommand(DeleteSelectedColor); + + SelectedColorChangedCommand = new RelayCommand((newColor) => + { + ColorsHistory.Insert(0, (Color)newColor); + SelectedColorIndex = 0; + }); + ColorsHistory.CollectionChanged += ColorsHistory_CollectionChanged; + _userSettings = userSettings; + SetupAllColorRepresentations(); + SetupAvailableColorRepresentations(); + } + + public event EventHandler OpenColorPickerRequested; + + public ICommand OpenColorPickerCommand { get; } + + public ICommand RemoveColorCommand { get; } + + public ICommand SelectedColorChangedCommand { get; } + + public ICommand HideColorFormatCommand { get; } + + public ObservableCollection ColorsHistory { get; } = new ObservableCollection(); + + public ObservableCollection ColorRepresentations { get; } = new ObservableCollection(); + + public Color SelectedColor + { + get + { + return _selectedColor; + } + + set + { + _selectedColor = value; + OnPropertyChanged(); + } + } + + public int SelectedColorIndex + { + get + { + return _selectedColorIndex; + } + + set + { + _selectedColorIndex = value; + if (value >= 0) + { + SelectedColor = ColorsHistory[_selectedColorIndex]; + } + + OnPropertyChanged(); + } + } + + public void Initialize() + { + _initializing = true; + + ColorsHistory.Clear(); + + foreach (var item in _userSettings.ColorHistory) + { + var parts = item.Split('|'); + ColorsHistory.Add(new Color() + { + A = byte.Parse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture), + R = byte.Parse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture), + G = byte.Parse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture), + B = byte.Parse(parts[3], NumberStyles.Integer, CultureInfo.InvariantCulture), + }); + SelectedColorIndex = 0; + } + + _initializing = false; + } + + private void ColorsHistory_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (!_initializing) + { + _userSettings.ColorHistory.ClearWithoutNotification(); + foreach (var item in ColorsHistory) + { + _userSettings.ColorHistory.AddWithoutNotification(item.A + "|" + item.R + "|" + item.G + "|" + item.B); + } + + _userSettings.ColorHistory.ReleaseNotification(); + } + } + + private void DeleteSelectedColor() + { + // select new color on the same index if possible, otherwise the last one + var indexToSelect = SelectedColorIndex == ColorsHistory.Count - 1 ? ColorsHistory.Count - 2 : SelectedColorIndex; + ColorsHistory.RemoveAt(SelectedColorIndex); + SelectedColorIndex = indexToSelect; + } + + private void SetupAllColorRepresentations() + { + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.HEX.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HEX); }, + }); + + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.RGB.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.RGB); }, + }); + + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.HSL.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HSL); }, + }); + + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.HSV.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HSV); }, + }); + + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.CMYK.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.CMYK); }, + }); + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.HSB.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HSB); }, + }); + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.HSI.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HSI); }, + }); + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.HWB.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HWB); }, + }); + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.NCol.ToString(), + Convert = (Color color) => { return ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.NCol); }, + }); + + _userSettings.VisibleColorFormats.CollectionChanged += VisibleColorFormats_CollectionChanged; + + // Any other custom format to be added here as well that are read from settings + } + + private void VisibleColorFormats_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + SetupAvailableColorRepresentations(); + } + + private void SetupAvailableColorRepresentations() + { + ColorRepresentations.Clear(); + + foreach (var colorFormat in _userSettings.VisibleColorFormats) + { + var colorRepresentation = _allColorRepresentations.FirstOrDefault(it => it.FormatName.ToUpperInvariant() == colorFormat.ToUpperInvariant()); + if (colorRepresentation != null) + { + ColorRepresentations.Add(colorRepresentation); + } + } + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs index d747eae0a2..06f2c72320 100644 --- a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs @@ -15,6 +15,7 @@ using ColorPicker.Mouse; using ColorPicker.Settings; using ColorPicker.Telemetry; using ColorPicker.ViewModelContracts; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Telemetry; namespace ColorPicker.ViewModels @@ -22,11 +23,6 @@ namespace ColorPicker.ViewModels [Export(typeof(IMainViewModel))] public class MainViewModel : ViewModelBase, IMainViewModel { - /// - /// Defined error code for "clipboard can't open" - /// - private const uint ErrorCodeClipboardCantOpen = 0x800401D0; - private readonly ZoomWindowHelper _zoomWindowHelper; private readonly AppStateHandler _appStateHandler; private readonly IUserSettings _userSettings; @@ -107,41 +103,29 @@ namespace ColorPicker.ViewModels /// The current of the mouse cursor private void MouseInfoProvider_OnMouseDown(object sender, System.Drawing.Point p) { - CopyToClipboard(ColorText); + ClipboardHelper.CopyToClipboard(ColorText); + + _userSettings.ColorHistory.Insert(0, GetColorString()); + + if (_userSettings.ColorHistory.Count > _userSettings.ColorHistoryLimit.Value) + { + _userSettings.ColorHistory.RemoveAt(_userSettings.ColorHistory.Count - 1); + } _appStateHandler.HideColorPicker(); + + if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenColorPickerAndThenEditor || _userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor) + { + _appStateHandler.ShowColorPickerEditor(); + } + PowerToysTelemetry.Log.WriteEvent(new ColorPickerShowEvent()); } - /// - /// Copy the given text to the Windows clipboard - /// - /// The text to copy to the Windows clipboard - private static void CopyToClipboard(string text) + private string GetColorString() { - if (string.IsNullOrEmpty(text)) - { - return; - } - - // nasty hack - sometimes clipboard can be in use and it will raise and exception - for (var i = 0; i < 10; i++) - { - try - { - Clipboard.SetText(text); - break; - } - catch (COMException ex) - { - if ((uint)ex.ErrorCode != ErrorCodeClipboardCantOpen) - { - Logger.LogError("Failed to set text into clipboard", ex); - } - } - - Thread.Sleep(10); - } + var color = ((SolidColorBrush)ColorBrush).Color; + return color.A + "|" + color.R + "|" + color.G + "|" + color.B; } /// diff --git a/src/modules/colorPicker/ColorPickerUI/Views/ColorEditorView.xaml b/src/modules/colorPicker/ColorPickerUI/Views/ColorEditorView.xaml new file mode 100644 index 0000000000..6081427901 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Views/ColorEditorView.xaml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +