├── InlineColorPicker ├── License.txt ├── extension.vsixmanifest ├── Properties │ └── AssemblyInfo.cs ├── source.extension.vsixmanifest └── InlineColorPicker.csproj ├── InlineColorPicker_VS2022 ├── License.txt ├── Properties │ └── AssemblyInfo.cs ├── source.extension.vsixmanifest └── InlineColorPicker_VS2022.csproj ├── InlineColorPickerShared ├── ColorInfo.cs ├── Integration │ ├── ColorTag.cs │ ├── ColorTaggerProvider.cs │ ├── ColorAdornmentTagger.cs │ ├── ColorAdornmentTaggerProvider.cs │ └── ColorTagger.cs ├── ColorPopup.xaml.cs ├── ColorPopup.xaml ├── InlineColorPickerShared.shproj ├── ColorAdorner.xaml ├── app.config ├── Support │ ├── IntraTextAdornmentTagTransformer.cs │ ├── RegexTagger.cs │ └── IntraTextAdornmentTagger.cs ├── AlphaSelectControl.xaml.cs ├── InlineColorPickerShared.projitems ├── AlphaSelectControl.xaml ├── ColorAdorner.xaml.cs ├── ColorPicker.xaml ├── ColorSpace.cs └── ColorPicker.xaml.cs ├── README.md ├── LICENSE ├── .gitattributes ├── InlineColorPicker.sln └── .gitignore /InlineColorPicker/License.txt: -------------------------------------------------------------------------------- 1 | MIT -------------------------------------------------------------------------------- /InlineColorPicker_VS2022/License.txt: -------------------------------------------------------------------------------- 1 | MIT -------------------------------------------------------------------------------- /InlineColorPickerShared/ColorInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Media; 7 | 8 | namespace InlineColorPicker 9 | { 10 | public class ColorInfo 11 | { 12 | public Color? Color; 13 | public bool WasSpecifiedWithAlpha; 14 | 15 | public static ColorInfo White = new ColorInfo() { Color = Colors.White, WasSpecifiedWithAlpha = true }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inline Color Picker Extension for Visual Studio 2 | Lightweight color picker extension for CSS, XAML, CS, VB, etc for Visual Studio. Write and update colors faster. 3 | 4 | Install from Visual Studio Marketplace: 5 | * [Inline Color Picker](https://marketplace.visualstudio.com/items?itemName=NikolaMSFT.InlineColorPicker) for Visual Studio 2017, 2019 6 | * [Inline Color Picker - x64 Preview](https://marketplace.visualstudio.com/items?itemName=NikolaMSFT.InlineColorPickerX64Preview) for Visual Studio 2022 7 | -------------------------------------------------------------------------------- /InlineColorPickerShared/Integration/ColorTag.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text; 2 | using Microsoft.VisualStudio.Text.Tagging; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | 6 | namespace InlineColorPicker 7 | { 8 | public class ColorTag : ITag { 9 | internal ColorTag(ColorInfo colorInfo, ITrackingSpan span) { 10 | this.ColorInfo = colorInfo; 11 | this.TrackingSpan = span; 12 | } 13 | 14 | internal ITrackingSpan TrackingSpan; 15 | internal ColorInfo ColorInfo; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /InlineColorPickerShared/ColorPopup.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Navigation; 13 | using System.Windows.Shapes; 14 | using System.Windows.Controls.Primitives; 15 | 16 | namespace InlineColorPicker { 17 | /// 18 | /// Interaction logic for ColorPopup.xaml 19 | /// 20 | public partial class ColorPopup : Popup { 21 | public ColorPopup() { 22 | InitializeComponent(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /InlineColorPickerShared/Integration/ColorTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace InlineColorPicker 8 | { 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("text")] 11 | [TagType(typeof(ColorTag))] 12 | internal sealed class ColorTaggerProvider : ITaggerProvider 13 | { 14 | public ITagger CreateTagger(ITextBuffer buffer) where T : ITag 15 | { 16 | if (buffer == null) 17 | throw new ArgumentNullException("buffer"); 18 | 19 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorTagger(buffer)) as ITagger; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /InlineColorPickerShared/ColorPopup.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /InlineColorPickerShared/InlineColorPickerShared.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6821d28a-0114-4e3b-9f95-3790cb3fa07c 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /InlineColorPickerShared/ColorAdorner.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nokola 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /InlineColorPickerShared/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /InlineColorPicker/extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inline Color Picker 6 | Color picker extension for Visual Studio. 7 | License.txt 8 | fill_color-512.png 9 | fill_color-512.png 10 | color picker, xaml, css, html, color 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /InlineColorPicker_VS2022/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("InlineColorPicker_VS2022")] 9 | [assembly: AssemblyDescription("Displays and edits colors in XAML, HTML, C# and other files")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("InlineColorPicker_VS2022")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /InlineColorPicker_VS2022/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inline Color Picker - x64 Preview 6 | Color picker extension for Visual Studio. 7 | License.txt 8 | color picker, xaml, css, html, color 9 | 10 | 11 | 12 | amd64 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /InlineColorPicker/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("InlineColorPicker")] 9 | [assembly: AssemblyDescription("Displays and edits colors in XAML, HTML, C# and other files. Created by Nikola Mihaylov")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("InlineColorPicker")] 13 | [assembly: AssemblyCopyright("Microsoft")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /InlineColorPickerShared/Integration/ColorAdornmentTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using System.Text.RegularExpressions; 6 | using System.Windows.Media; 7 | using Microsoft.VisualStudio.Text.Editor; 8 | 9 | namespace InlineColorPicker { 10 | /// 11 | /// Provides color swatch adornments in place of color constants. 12 | /// 13 | internal sealed class ColorAdornmentTagger : IntraTextAdornmentTagTransformer { 14 | internal static ITagger GetTagger(IWpfTextView view, Lazy> colorTagger) { 15 | return view.Properties.GetOrCreateSingletonProperty( 16 | () => new ColorAdornmentTagger(view, colorTagger.Value)); 17 | } 18 | 19 | private ColorAdornmentTagger(IWpfTextView view, ITagAggregator colorTagger) 20 | : base(view, colorTagger) { } 21 | 22 | protected override ColorAdorner CreateAdornment(ColorTag dataTag) { 23 | return new ColorAdorner(dataTag); 24 | } 25 | 26 | protected override void UpdateAdornment(ColorAdorner adornment, ColorTag dataTag) { 27 | adornment.Update(dataTag); 28 | } 29 | 30 | public override void Dispose() { 31 | base.Dispose(); 32 | _view.Properties.RemoveProperty(typeof(ColorAdornmentTagger)); 33 | } 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /InlineColorPicker/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inline Color Picker 6 | Color picker extension for Visual Studio. 7 | License.txt 8 | color picker, xaml, css, html, color 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /InlineColorPickerShared/Integration/ColorAdornmentTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | using Microsoft.VisualStudio.Utilities; 7 | 8 | namespace InlineColorPicker 9 | { 10 | [Export(typeof(IViewTaggerProvider))] 11 | [ContentType("text")] 12 | [ContentType("projection")] 13 | [TagType(typeof(IntraTextAdornmentTag))] 14 | internal sealed class ColorAdornmentTaggerProvider : IViewTaggerProvider 15 | { 16 | #pragma warning disable 649 // "field never assigned to" -- field is set by MEF. 17 | [Import] 18 | internal IBufferTagAggregatorFactoryService BufferTagAggregatorFactoryService; 19 | #pragma warning restore 649 20 | 21 | public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where T : ITag 22 | { 23 | if (textView == null) 24 | throw new ArgumentNullException("textView"); 25 | 26 | if (buffer == null) 27 | throw new ArgumentNullException("buffer"); 28 | 29 | if (buffer != textView.TextBuffer) 30 | return null; 31 | 32 | return ColorAdornmentTagger.GetTagger( 33 | (IWpfTextView)textView, 34 | new Lazy>( 35 | () => BufferTagAggregatorFactoryService.CreateTagAggregator(textView.TextBuffer))) 36 | as ITagger; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /InlineColorPickerShared/Support/IntraTextAdornmentTagTransformer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using Microsoft.VisualStudio.Text; 5 | using Microsoft.VisualStudio.Text.Editor; 6 | using Microsoft.VisualStudio.Text.Tagging; 7 | 8 | namespace InlineColorPicker 9 | { 10 | /// 11 | /// Helper class for translating given tags into intra-text adornments. 12 | /// 13 | internal abstract class IntraTextAdornmentTagTransformer 14 | : IntraTextAdornmentTagger, IDisposable 15 | where TDataTag : ITag 16 | where TAdornment : UIElement 17 | { 18 | protected readonly ITagAggregator _dataTagger; 19 | 20 | protected IntraTextAdornmentTagTransformer(IWpfTextView view, ITagAggregator dataTagger) 21 | : base(view) 22 | { 23 | _dataTagger = dataTagger; 24 | 25 | _dataTagger.TagsChanged += HandleDataTagsChanged; 26 | } 27 | 28 | protected override IEnumerable> GetAdorenmentData(NormalizedSnapshotSpanCollection spans) 29 | { 30 | if (spans.Count == 0) 31 | yield break; 32 | 33 | ITextSnapshot snapshot = spans[0].Snapshot; 34 | 35 | foreach (IMappingTagSpan dataTagSpan in _dataTagger.GetTags(spans)) 36 | { 37 | NormalizedSnapshotSpanCollection dataTagSpans = dataTagSpan.Span.GetSpans(snapshot); 38 | 39 | // Ignore data tags that are split by projection. 40 | // This is theoretically possible but unlikely in current scenarios. 41 | if (dataTagSpans.Count != 1) 42 | continue; 43 | 44 | yield return Tuple.Create(new SnapshotSpan(dataTagSpans[0].Start, 0), dataTagSpan.Tag); 45 | } 46 | } 47 | 48 | private void HandleDataTagsChanged(object sender, TagsChangedEventArgs args) 49 | { 50 | var changedSpans = args.Span.GetSpans(_view.TextBuffer.CurrentSnapshot); 51 | InvalidateSpans(changedSpans); 52 | } 53 | 54 | public virtual void Dispose() 55 | { 56 | _dataTagger.Dispose(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /InlineColorPickerShared/AlphaSelectControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Documents; 8 | using System.Windows.Input; 9 | using System.Windows.Media; 10 | using System.Windows.Media.Animation; 11 | using System.Windows.Shapes; 12 | 13 | namespace EasyPainter.Imaging.Silverlight { 14 | public partial class AlphaSelectControl : UserControl { 15 | 16 | public delegate void AlphaChangedHandler(byte newAlpha); 17 | public event AlphaChangedHandler AlphaChanged; 18 | 19 | public AlphaSelectControl() { 20 | InitializeComponent(); 21 | } 22 | 23 | public Color DisplayColor { 24 | get { return displayColor.Color; } 25 | set { 26 | Color color = value; 27 | color.A = 255; 28 | displayColor.Color = color; 29 | UpdateSelectionForAlpha(value.A); 30 | } 31 | } 32 | 33 | private UIElement _mouseCapture = null; 34 | 35 | private void rectMonitor_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { 36 | rectMonitor.CaptureMouse(); 37 | _mouseCapture = rectMonitor; 38 | UpdateSelection(e.GetPosition((UIElement)sender).Y); 39 | } 40 | 41 | private void rectMonitor_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { 42 | rectMonitor.ReleaseMouseCapture(); 43 | } 44 | 45 | private void rectMonitor_MouseMove(object sender, MouseEventArgs e) { 46 | if (_mouseCapture != rectMonitor) return; 47 | double yPos = e.GetPosition((UIElement)sender).Y; 48 | if (yPos < 0) yPos = 0; 49 | if (yPos > rectMonitor.ActualHeight) yPos = rectMonitor.ActualHeight; 50 | UpdateSelection(yPos); 51 | } 52 | 53 | private void rectMonitor_LostMouseCapture(object sender, MouseEventArgs e) { 54 | _mouseCapture = null; 55 | } 56 | 57 | private void UpdateSelectionForAlpha(int alpha) { 58 | Canvas.SetTop(rectMarker, ((255-alpha) * rectMonitor.ActualHeight)/255.0); 59 | } 60 | 61 | private void UpdateSelection(double yPos) { 62 | byte alpha = (byte)(255-(yPos * 255 / rectMonitor.ActualHeight)); 63 | Canvas.SetTop(rectMarker, yPos); 64 | if (AlphaChanged != null) AlphaChanged(alpha); 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /InlineColorPickerShared/InlineColorPickerShared.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | 6821d28a-0114-4e3b-9f95-3790cb3fa07c 7 | 8 | 9 | InlineColorPickerShared 10 | 11 | 12 | 13 | AlphaSelectControl.xaml 14 | 15 | 16 | ColorAdorner.xaml 17 | 18 | 19 | 20 | ColorPicker.xaml 21 | 22 | 23 | ColorPopup.xaml 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Designer 38 | MSBuild:Compile 39 | 40 | 41 | Designer 42 | MSBuild:Compile 43 | 44 | 45 | Designer 46 | MSBuild:Compile 47 | 48 | 49 | Designer 50 | MSBuild:Compile 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /InlineColorPicker.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31521.260 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InlineColorPicker", "InlineColorPicker\InlineColorPicker.csproj", "{F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}" 7 | EndProject 8 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "InlineColorPickerShared", "InlineColorPickerShared\InlineColorPickerShared.shproj", "{6821D28A-0114-4E3B-9F95-3790CB3FA07C}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InlineColorPicker_VS2022", "InlineColorPicker_VS2022\InlineColorPicker_VS2022.csproj", "{575CBF20-A557-4926-80E9-53DB70C6708A}" 11 | EndProject 12 | Global 13 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 14 | InlineColorPickerShared\InlineColorPickerShared.projitems*{575cbf20-a557-4926-80e9-53db70c6708a}*SharedItemsImports = 4 15 | InlineColorPickerShared\InlineColorPickerShared.projitems*{6821d28a-0114-4e3b-9f95-3790cb3fa07c}*SharedItemsImports = 13 16 | InlineColorPickerShared\InlineColorPickerShared.projitems*{f5e96c0f-4208-4f27-b0f0-6cab30d1292f}*SharedItemsImports = 4 17 | EndGlobalSection 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Debug|x86 = Debug|x86 21 | Release|Any CPU = Release|Any CPU 22 | Release|x86 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}.Debug|x86.Build.0 = Debug|Any CPU 29 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}.Release|x86.ActiveCfg = Release|Any CPU 32 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F}.Release|x86.Build.0 = Release|Any CPU 33 | {575CBF20-A557-4926-80E9-53DB70C6708A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {575CBF20-A557-4926-80E9-53DB70C6708A}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {575CBF20-A557-4926-80E9-53DB70C6708A}.Debug|x86.ActiveCfg = Debug|x86 36 | {575CBF20-A557-4926-80E9-53DB70C6708A}.Debug|x86.Build.0 = Debug|x86 37 | {575CBF20-A557-4926-80E9-53DB70C6708A}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {575CBF20-A557-4926-80E9-53DB70C6708A}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {575CBF20-A557-4926-80E9-53DB70C6708A}.Release|x86.ActiveCfg = Release|x86 40 | {575CBF20-A557-4926-80E9-53DB70C6708A}.Release|x86.Build.0 = Release|x86 41 | EndGlobalSection 42 | GlobalSection(SolutionProperties) = preSolution 43 | HideSolutionNode = FALSE 44 | EndGlobalSection 45 | GlobalSection(ExtensibilityGlobals) = postSolution 46 | SolutionGuid = {6A9064F8-5368-47BD-B976-C2E1AA6EFFBE} 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /InlineColorPickerShared/AlphaSelectControl.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /InlineColorPickerShared/Integration/ColorTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | using Microsoft.VisualStudio.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Windows.Media; 9 | using EasyPainter.Imaging.Silverlight; 10 | 11 | namespace InlineColorPicker 12 | { 13 | internal sealed class ColorTagger : ITagger 14 | { 15 | private readonly ITextBuffer _buffer; 16 | private readonly bool shouldAllowFreeFloatingColorNames = false; // true for CSS, false otherwise. Used to mark "Red" vs red (no quotes) as tag 17 | 18 | readonly Regex _regex = new Regex(@"\#[\dA-F]{3,8}\b", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 19 | 20 | internal ColorTagger(ITextBuffer buffer) 21 | { 22 | _buffer = buffer; 23 | shouldAllowFreeFloatingColorNames = _buffer.ContentType.TypeName == "css"; 24 | } 25 | 26 | public event EventHandler TagsChanged; 27 | 28 | public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 29 | { 30 | foreach (SnapshotSpan span in spans) 31 | { 32 | string text = span.GetText(); 33 | foreach (Match match in ColorSpace.MatchNamedColor(text, shouldAllowFreeFloatingColorNames)) 34 | { 35 | object color; 36 | try 37 | { 38 | color = ColorConverter.ConvertFromString(ColorSpace.HexFromColorUpperCase[match.ToString().ToUpperInvariant().Substring(1)]); 39 | } 40 | catch (Exception) 41 | { 42 | color = null; 43 | } 44 | int matchStart = span.Start + match.Index + 1; 45 | int matchLength = match.Length - 1; 46 | 47 | ITrackingSpan trackingSpan = span.Snapshot.CreateTrackingSpan(matchStart, matchLength, SpanTrackingMode.EdgeInclusive, TrackingFidelityMode.Forward); 48 | 49 | ColorTag tag; 50 | ColorInfo colorInfo = new ColorInfo() { WasSpecifiedWithAlpha = false }; 51 | if (color == null) 52 | { 53 | colorInfo.Color = null; 54 | } 55 | else 56 | { 57 | colorInfo.Color = (Color)color; 58 | } 59 | 60 | tag = new ColorTag(colorInfo, trackingSpan); 61 | 62 | yield return new TagSpan(new SnapshotSpan(span.Snapshot, matchStart, matchLength), tag); 63 | } 64 | 65 | foreach (Match match in _regex.Matches(text)) 66 | { 67 | 68 | object color; 69 | try 70 | { 71 | color = ColorConverter.ConvertFromString(match.ToString()); 72 | } 73 | catch (Exception) 74 | { 75 | color = null; 76 | } 77 | int matchStart = span.Start + match.Index; 78 | int matchLength = match.Length; 79 | 80 | ITrackingSpan trackingSpan = span.Snapshot.CreateTrackingSpan(matchStart, matchLength, SpanTrackingMode.EdgeInclusive, TrackingFidelityMode.Forward); 81 | 82 | ColorTag tag; 83 | ColorInfo colorInfo = new ColorInfo() { WasSpecifiedWithAlpha = matchLength > 7 }; 84 | if (color == null) 85 | { 86 | colorInfo.Color = null; 87 | } 88 | else 89 | { 90 | colorInfo.Color = (Color)color; 91 | } 92 | 93 | tag = new ColorTag(colorInfo, trackingSpan); 94 | 95 | yield return new TagSpan(new SnapshotSpan(span.Snapshot, matchStart, matchLength), tag); 96 | } 97 | } 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /InlineColorPickerShared/Support/RegexTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.VisualStudio.Text.Tagging; 7 | 8 | namespace InlineColorPicker 9 | { 10 | internal abstract class RegexTagger : ITagger where T : ITag 11 | { 12 | private readonly IEnumerable _matchExpressions; 13 | 14 | public RegexTagger(ITextBuffer buffer, IEnumerable matchExpressions) 15 | { 16 | _matchExpressions = matchExpressions; 17 | 18 | buffer.Changed += (sender, args) => HandleBufferChanged(args); 19 | } 20 | 21 | #region ITagger implementation 22 | 23 | public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 24 | { 25 | foreach (var line in GetIntersectingLines(spans)) 26 | { 27 | string text = line.GetText(); 28 | 29 | foreach (var regex in _matchExpressions) 30 | { 31 | foreach (var match in regex.Matches(text).Cast()) 32 | { 33 | T tag = TryCreateTagForMatch(match); 34 | if (tag != null) 35 | { 36 | SnapshotSpan span = new SnapshotSpan(line.Start + match.Index, match.Length); 37 | yield return new TagSpan(span, tag); 38 | } 39 | } 40 | } 41 | } 42 | } 43 | public event EventHandler TagsChanged; 44 | 45 | #endregion 46 | 47 | IEnumerable GetIntersectingLines(NormalizedSnapshotSpanCollection spans) 48 | { 49 | if (spans.Count == 0) 50 | yield break; 51 | int lastVisitedLineNumber = -1; 52 | ITextSnapshot snapshot = spans[0].Snapshot; 53 | foreach (var span in spans) 54 | { 55 | int firstLine = snapshot.GetLineNumberFromPosition(span.Start); 56 | int lastLine = snapshot.GetLineNumberFromPosition(span.End); 57 | 58 | for (int i = Math.Max(lastVisitedLineNumber, firstLine); i <= lastLine; i++) 59 | { 60 | yield return snapshot.GetLineFromLineNumber(i); 61 | } 62 | 63 | lastVisitedLineNumber = lastLine; 64 | } 65 | } 66 | 67 | /// 68 | /// Overridden in the derived implementation to provide a tag for each regular expression match. 69 | /// If the return value is null, this match will be skipped. 70 | /// 71 | /// The match to create a tag for. 72 | /// The tag to return from , if non-null. 73 | protected abstract T TryCreateTagForMatch(Match match); 74 | 75 | /// 76 | /// Handle buffer changes. The default implementation expands changes to full lines and sends out 77 | /// a event for these lines. 78 | /// 79 | /// The buffer change arguments. 80 | protected virtual void HandleBufferChanged(TextContentChangedEventArgs args) 81 | { 82 | if (args.Changes.Count == 0) 83 | return; 84 | 85 | var temp = TagsChanged; 86 | if (temp == null) 87 | return; 88 | 89 | // Combine all changes into a single span so that 90 | // the ITagger<>.TagsChanged event can be raised just once for a compound edit 91 | // with many parts. 92 | 93 | ITextSnapshot snapshot = args.After; 94 | 95 | int start = args.Changes[0].NewPosition; 96 | int end = args.Changes[args.Changes.Count - 1].NewEnd; 97 | 98 | SnapshotSpan totalAffectedSpan = new SnapshotSpan( 99 | snapshot.GetLineFromPosition(start).Start, 100 | snapshot.GetLineFromPosition(end).End); 101 | 102 | temp(this, new SnapshotSpanEventArgs(totalAffectedSpan)); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /InlineColorPicker_VS2022/InlineColorPicker_VS2022.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | Debug 10 | AnyCPU 11 | 2.0 12 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 13 | {575CBF20-A557-4926-80E9-53DB70C6708A} 14 | Library 15 | Properties 16 | InlineColorPicker_VS2022 17 | InlineColorPicker_VS2022 18 | v4.7.2 19 | false 20 | true 21 | false 22 | false 23 | false 24 | false 25 | Program 26 | $(DevEnvDir)devenv.exe 27 | /rootsuffix Exp 28 | 29 | 30 | true 31 | full 32 | false 33 | bin\Debug\ 34 | DEBUG;TRACE 35 | prompt 36 | 4 37 | 38 | 39 | pdbonly 40 | true 41 | bin\Release\ 42 | TRACE 43 | prompt 44 | 4 45 | 46 | 47 | 48 | 49 | 50 | 51 | Designer 52 | 53 | 54 | 55 | 56 | compile; build; native; contentfiles; analyzers; buildtransitive 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Always 72 | true 73 | 74 | 75 | 76 | 77 | 78 | 85 | -------------------------------------------------------------------------------- /InlineColorPickerShared/ColorAdorner.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Navigation; 13 | using System.Windows.Shapes; 14 | using Microsoft.VisualStudio.Text; 15 | using EasyPainter.Imaging.Silverlight; 16 | using System.Diagnostics; 17 | using System.Windows.Threading; 18 | 19 | namespace InlineColorPicker { 20 | /// 21 | /// Interaction logic for ColorAdorner.xaml 22 | /// 23 | public partial class ColorAdorner : UserControl { 24 | ColorTag _tag = null; 25 | DispatcherTimer _timer = new DispatcherTimer(); 26 | public ColorAdorner(ColorTag tag) { 27 | InitializeComponent(); 28 | Update(tag); 29 | _timer.Interval = _updateColorSpan; 30 | _timer.Tick += new EventHandler(_timer_Tick); 31 | } 32 | 33 | TimeSpan _updateColorSpan = TimeSpan.FromMilliseconds(200); 34 | void _timer_Tick(object sender, EventArgs e) { 35 | if ((DateTime.Now - _lastColorUpdateTime) <= _updateColorSpan) return; 36 | ApplyColorIfNeeded(); 37 | } 38 | 39 | private ColorInfo _currentColor = new ColorInfo() { Color = Colors.Red, WasSpecifiedWithAlpha = true }; 40 | 41 | /// 42 | /// 43 | /// 44 | internal ColorInfo ColorInfo { 45 | get { return _currentColor; } 46 | set { 47 | _currentColor = value; 48 | if (_currentColor.Color == null) { 49 | rectColor.Fill = null; 50 | line1.Visibility = Visibility.Collapsed; 51 | line2.Visibility = Visibility.Collapsed; 52 | } 53 | else 54 | { 55 | rectColor.Fill = new SolidColorBrush(_currentColor.Color.Value); 56 | line1.Visibility = Visibility.Visible; 57 | line2.Visibility = Visibility.Visible; 58 | } 59 | } 60 | } 61 | 62 | public void Update(ColorTag tag) { 63 | _tag = tag; 64 | this.ColorInfo = _tag.ColorInfo; 65 | } 66 | 67 | static ColorPopup _popup = null; 68 | 69 | private void InitPopupIfNeeded() { 70 | if (_popup != null) return; 71 | _popup = new ColorPopup(); 72 | _popup.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint; 73 | _popup.HorizontalOffset = 12; 74 | _popup.VerticalOffset = 12; 75 | } 76 | 77 | private void Button_Click(object sender, RoutedEventArgs e) { 78 | InitPopupIfNeeded(); 79 | 80 | _popup.PlacementTarget = this; 81 | if (_currentColor == null) { 82 | _popup.picker.ColorInfo = ColorInfo.White; 83 | } 84 | else { 85 | _popup.picker.ColorInfo = _currentColor; 86 | } 87 | _mustApplyColor = false; 88 | _popup.picker.ColorSelected += picker_ColorSelected; 89 | _popup.Closed += _popup_Closed; 90 | 91 | _popup.IsOpen = true; 92 | _popup.picker.HexValue.Focus(); 93 | _timer.Start(); 94 | } 95 | 96 | bool _mustApplyColor = false; 97 | DateTime _lastColorUpdateTime = DateTime.MinValue; 98 | 99 | void ApplyColorIfNeeded() { 100 | if (!_mustApplyColor) return; 101 | _mustApplyColor = false; 102 | ITextBuffer buffer = _tag.TrackingSpan.TextBuffer; 103 | SnapshotSpan span = _tag.TrackingSpan.GetSpan(buffer.CurrentSnapshot); 104 | buffer.Replace(span, ColorSpace.GetHexCodeOrName(_tag.ColorInfo)); 105 | 106 | 107 | } 108 | 109 | void picker_ColorSelected(ColorInfo info) { 110 | _tag.ColorInfo = info; 111 | _mustApplyColor = true; 112 | if ((DateTime.Now - _lastColorUpdateTime) > _updateColorSpan) { 113 | ApplyColorIfNeeded(); 114 | } 115 | _lastColorUpdateTime = DateTime.Now; 116 | //Debug.WriteLine("Span: " + span.ToString()); 117 | //_tag.TrackingSpan = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeExclusive, TrackingFidelityMode.Forward); 118 | //Debug.WriteLine("Tracking: " + span.ToString()); 119 | } 120 | 121 | void _popup_Closed(object sender, EventArgs e) { 122 | _timer.Stop(); 123 | _popup.Closed -= _popup_Closed; 124 | _popup.picker.ColorSelected -= picker_ColorSelected; 125 | ApplyColorIfNeeded(); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /InlineColorPicker/InlineColorPicker.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 10.0.20305 8 | 2.0 9 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | {F5E96C0F-4208-4F27-B0F0-6CAB30D1292F} 11 | Library 12 | Properties 13 | InlineColorPicker 14 | InlineColorPicker 15 | v4.7.2 16 | 512 17 | false 18 | 15.0 19 | 20 | 21 | 22 | 23 | 12.0 24 | 25 | false 26 | publish\ 27 | true 28 | Disk 29 | false 30 | Foreground 31 | 7 32 | Days 33 | false 34 | false 35 | true 36 | 0 37 | 1.0.0.%2a 38 | false 39 | true 40 | 41 | 42 | 43 | 44 | true 45 | full 46 | false 47 | bin\Debug\ 48 | DEBUG;TRACE 49 | prompt 50 | 4 51 | false 52 | False 53 | \\wfserver\files\nikolami\InlineColorPicker 54 | 55 | 56 | pdbonly 57 | true 58 | bin\Release\ 59 | TRACE 60 | prompt 61 | 4 62 | false 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Designer 83 | 84 | 85 | 86 | 87 | False 88 | Microsoft .NET Framework 4 %28x86 and x64%29 89 | true 90 | 91 | 92 | False 93 | .NET Framework 3.5 SP1 Client Profile 94 | false 95 | 96 | 97 | False 98 | .NET Framework 3.5 SP1 99 | false 100 | 101 | 102 | False 103 | Windows Installer 4.5 104 | true 105 | 106 | 107 | 108 | 109 | 16.10.31321.278 110 | 111 | 112 | 16.10.1055 113 | runtime; build; native; contentfiles; analyzers; buildtransitive 114 | all 115 | 116 | 117 | 118 | 119 | Always 120 | true 121 | 122 | 123 | 124 | 125 | 10.0 126 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 127 | 128 | 129 | 130 | 131 | 138 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /InlineColorPickerShared/ColorPicker.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 130 | A: 131 | 143 | R: 144 | 156 | G: 157 | 169 | B: 170 | 182 | 183 | 185 | 186 | -------------------------------------------------------------------------------- /InlineColorPickerShared/Support/IntraTextAdornmentTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.VisualStudio.Text.Editor; 7 | using Microsoft.VisualStudio.Text.Tagging; 8 | using System.Diagnostics; 9 | 10 | namespace InlineColorPicker 11 | { 12 | /// 13 | /// Helper class for interspersing adornments into text. 14 | /// 15 | /// 16 | /// To avoid an issue around intra-text adornment support and its interaction with text buffer changes, 17 | /// this tagger reacts to text and color tag changes with a delay. It waits to send out its own TagsChanged 18 | /// event until the WPF Dispatcher is running again and it takes care to report adornments 19 | /// that are consistent with the latest sent TagsChanged event by storing that particular snapshot 20 | /// and using it to query for the data tags. 21 | /// 22 | internal abstract class IntraTextAdornmentTagger 23 | : ITagger 24 | where TData : ITag 25 | where TAdornment : UIElement 26 | { 27 | protected readonly IWpfTextView _view; 28 | private Dictionary _adornmentCache = new Dictionary(); 29 | protected ITextSnapshot _snapshot { get; private set; } 30 | private readonly List _invalidatedSpans = new List(); 31 | 32 | protected IntraTextAdornmentTagger(IWpfTextView view) 33 | { 34 | _view = view; 35 | _snapshot = view.TextBuffer.CurrentSnapshot; 36 | 37 | _view.LayoutChanged += HandleLayoutChanged; 38 | _view.TextBuffer.Changed += HandleBufferChanged; 39 | } 40 | 41 | protected abstract TAdornment CreateAdornment(TData dataTag); 42 | protected abstract void UpdateAdornment(TAdornment adornment, TData dataTag); 43 | protected abstract IEnumerable> GetAdorenmentData(NormalizedSnapshotSpanCollection spans); 44 | 45 | private void HandleBufferChanged(object sender, TextContentChangedEventArgs args) 46 | { 47 | var editedSpans = args.Changes.Select(change => new SnapshotSpan(args.After, change.NewSpan)).ToList(); 48 | InvalidateSpans(editedSpans); 49 | } 50 | 51 | protected void InvalidateSpans(IList spans) 52 | { 53 | lock (_invalidatedSpans) 54 | { 55 | bool wasEmpty = _invalidatedSpans.Count == 0; 56 | _invalidatedSpans.AddRange(spans); 57 | 58 | if (wasEmpty && _invalidatedSpans.Count > 0) 59 | { 60 | _ = _view.VisualElement.Dispatcher.BeginInvoke(new Action(AsyncUpdate)); 61 | } 62 | } 63 | } 64 | 65 | private void AsyncUpdate() 66 | { 67 | // Store the snapshot that we're now current with and send an event 68 | // for the text that has changed. 69 | if (_snapshot != _view.TextBuffer.CurrentSnapshot) 70 | { 71 | _snapshot = _view.TextBuffer.CurrentSnapshot; 72 | 73 | Dictionary translatedAdornmentCache = new Dictionary(); 74 | 75 | foreach (var keyValuePair in _adornmentCache) 76 | { 77 | translatedAdornmentCache[keyValuePair.Key.TranslateTo(_snapshot, SpanTrackingMode.EdgeExclusive)] = keyValuePair.Value; 78 | } 79 | 80 | _adornmentCache = translatedAdornmentCache; 81 | } 82 | 83 | List translatedSpans; 84 | lock (_invalidatedSpans) 85 | { 86 | translatedSpans = _invalidatedSpans.Select(s => s.TranslateTo(_snapshot, SpanTrackingMode.EdgeInclusive)).ToList(); 87 | _invalidatedSpans.Clear(); 88 | } 89 | 90 | if (translatedSpans.Count == 0) 91 | return; 92 | 93 | var start = translatedSpans.Select(span => span.Start).Min(); 94 | var end = translatedSpans.Select(span => span.End).Max(); 95 | 96 | 97 | RaiseTagsChanged(new SnapshotSpan(start, end)); 98 | } 99 | 100 | protected void RaiseTagsChanged(SnapshotSpan span) 101 | { 102 | var handler = this.TagsChanged; 103 | if (handler != null) 104 | handler(this, new SnapshotSpanEventArgs(span)); 105 | } 106 | 107 | private void HandleLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) 108 | { 109 | SnapshotSpan visibleSpan = _view.TextViewLines.FormattedSpan; 110 | 111 | // Filter out the adornments that are no longer visible. 112 | List toRemove = new List( 113 | from keyValuePair 114 | in _adornmentCache 115 | where !keyValuePair.Key.TranslateTo(visibleSpan.Snapshot, SpanTrackingMode.EdgeExclusive).IntersectsWith(visibleSpan) 116 | select keyValuePair.Key); 117 | 118 | foreach (var span in toRemove) 119 | _adornmentCache.Remove(span); 120 | } 121 | 122 | 123 | // Produces tags on the snapshot that the tag consumer asked for. 124 | public virtual IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 125 | { 126 | if (spans == null || spans.Count == 0) 127 | yield break; 128 | 129 | // Translate the request to the snapshot that this tagger is current with. 130 | 131 | ITextSnapshot requestedSnapshot = spans[0].Snapshot; 132 | 133 | var translatedSpans = new NormalizedSnapshotSpanCollection(spans.Select(span => span.TranslateTo(_snapshot, SpanTrackingMode.EdgeExclusive))); 134 | 135 | // Grab the adornments. 136 | foreach (var tagSpan in GetAdornmentTagsOnSnapshot(translatedSpans)) 137 | { 138 | // Translate each adornment to the snapshot that the tagger was asked about. 139 | SnapshotSpan span = tagSpan.Span.TranslateTo(requestedSnapshot, SpanTrackingMode.EdgeExclusive); 140 | PositionAffinity? affinity = span.Length == 0 ? (PositionAffinity?)PositionAffinity.Successor : null; // Affinity is needed only for zero-length adornments. 141 | 142 | IntraTextAdornmentTag tag = new IntraTextAdornmentTag(tagSpan.Tag.Adornment, tagSpan.Tag.RemovalCallback, affinity); 143 | yield return new TagSpan(span, tag); 144 | } 145 | } 146 | 147 | // Produces tags on the snapshot that this tagger is current with. 148 | private IEnumerable> GetAdornmentTagsOnSnapshot(NormalizedSnapshotSpanCollection spans) 149 | { 150 | if (spans.Count == 0) 151 | { 152 | yield break; 153 | } 154 | 155 | ITextSnapshot snapshot = spans[0].Snapshot; 156 | 157 | System.Diagnostics.Debug.Assert(snapshot == _snapshot); 158 | 159 | // Since WPF UI objects have state (like mouse hover or animation) and are relatively expensive to create and lay out, 160 | // this code tries to reuse controls as much as possible. 161 | // The controls are stored in _adornmentCache between the calls. 162 | 163 | // Mark which adornments fall inside the requested spans with Keep=false 164 | // so that they can be removed from the cache if they no longer correspond to data tags. 165 | HashSet toRemove = new HashSet(); 166 | foreach (var ar in _adornmentCache) 167 | if (spans.IntersectsWith(new NormalizedSnapshotSpanCollection(ar.Key))) 168 | toRemove.Add(ar.Key); 169 | 170 | foreach (var spanDataPair in GetAdorenmentData(spans)) 171 | { 172 | // Look up the corresponding adornment or create one if it's new. 173 | TAdornment adornment; 174 | SnapshotSpan snapshotSpan = spanDataPair.Item1; 175 | TData adornmentData = spanDataPair.Item2; 176 | if (_adornmentCache.TryGetValue(snapshotSpan, out adornment)) 177 | { 178 | UpdateAdornment(adornment, adornmentData); 179 | toRemove.Remove(snapshotSpan); 180 | } 181 | else 182 | { 183 | adornment = CreateAdornment(adornmentData); 184 | 185 | // Get the adornment to measure itself. Its DesiredSize property is used to determine 186 | // how much space to leave between text for this adornment. 187 | // Note: If the size of the adornment changes, the line will be reformatted to accommodate it. 188 | // Note: Some adornments may change size when added to the view's visual tree due to inherited 189 | // dependency properties that affect layout. Such options can include SnapsToDevicePixels, 190 | // UseLayoutRounding, TextRenderingMode, TextHintingMode, and TextFormattingMode. Making sure 191 | // that these properties on the adornment match the view's values before calling Measure here 192 | // can help avoid the size change and the resulting unnecessary re-format. 193 | adornment.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 194 | 195 | _adornmentCache.Add(snapshotSpan, adornment); 196 | } 197 | 198 | yield return new TagSpan(snapshotSpan, new IntraTextAdornmentTag(adornment, null, null)); 199 | } 200 | 201 | foreach (var snapshotSpan in toRemove) 202 | _adornmentCache.Remove(snapshotSpan); 203 | } 204 | 205 | public event EventHandler TagsChanged; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /InlineColorPickerShared/ColorSpace.cs: -------------------------------------------------------------------------------- 1 | // Based on code from Page Brooks, Website: http://www.pagebrooks.com, RSS Feed: http://feeds.pagebrooks.com/pagebrooks 2 | // Modified by nokola (http://nokola.com) to include rgv to hsv color space translation 3 | 4 | using System; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Documents; 8 | using System.Windows.Ink; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Animation; 12 | using System.Windows.Shapes; 13 | using System.Collections.Generic; 14 | using InlineColorPicker; 15 | using System.Text.RegularExpressions; 16 | using System.Text; 17 | 18 | namespace EasyPainter.Imaging.Silverlight 19 | { 20 | internal class ColorSpace 21 | { 22 | 23 | internal static string[] NamedColors = new string[] { 24 | "AliceBlue", "#FFF0F8FF", "AntiqueWhite", "#FFFAEBD7", "Aqua", "#FF00FFFF", "Aquamarine", "#FF7FFFD4", 25 | "Azure", "#FFF0FFFF", "Beige", "#FFF5F5DC", "Bisque", "#FFFFE4C4", "Black", "#FF000000", 26 | "BlanchedAlmond", "#FFFFEBCD", "Blue", "#FF0000FF", "BlueViolet", "#FF8A2BE2", "Brown", "#FFA52A2A", 27 | "BurlyWood", "#FFDEB887", "CadetBlue", "#FF5F9EA0", "Chartreuse", "#FF7FFF00", "Chocolate", "#FFD2691E", 28 | "Coral", "#FFFF7F50", "CornflowerBlue", "#FF6495ED", "Cornsilk", "#FFFFF8DC", "Crimson", "#FFDC143C", 29 | "Cyan", "#FF00FFFF", "DarkBlue", "#FF00008B", "DarkCyan", "#FF008B8B", "DarkGoldenrod", "#FFB8860B", 30 | "DarkGray", "#FFA9A9A9", "DarkGreen", "#FF006400", "DarkKhaki", "#FFBDB76B", "DarkMagenta", "#FF8B008B", 31 | "DarkOliveGreen", "#FF556B2F", "DarkOrange", "#FFFF8C00", "DarkOrchid", "#FF9932CC", "DarkRed", "#FF8B0000", 32 | "DarkSalmon", "#FFE9967A", "DarkSeaGreen", "#FF8FBC8F", "DarkSlateBlue", "#FF483D8B", "DarkSlateGray", "#FF2F4F4F", 33 | "DarkTurquoise", "#FF00CED1", "DarkViolet", "#FF9400D3", "DeepPink", "#FFFF1493", "DeepSkyBlue", "#FF00BFFF", 34 | "DimGray", "#FF696969", "DodgerBlue", "#FF1E90FF", "Firebrick", "#FFB22222", "FloralWhite", "#FFFFFAF0", 35 | "ForestGreen", "#FF228B22", "Fuchsia", "#FFFF00FF", "Gainsboro", "#FFDCDCDC", "GhostWhite", "#FFF8F8FF", 36 | "Gold", "#FFFFD700", "Goldenrod", "#FFDAA520", "Gray", "#FF808080", "Green", "#FF008000", 37 | "GreenYellow", "#FFADFF2F", "Honeydew", "#FFF0FFF0", "HotPink", "#FFFF69B4", "IndianRed", "#FFCD5C5C", 38 | "Indigo", "#FF4B0082", "Ivory", "#FFFFFFF0", "Khaki", "#FFF0E68C", "Lavender", "#FFE6E6FA", 39 | "LavenderBlush", "#FFFFF0F5", "LawnGreen", "#FF7CFC00", "LemonChiffon", "#FFFFFACD", "LightBlue", "#FFADD8E6", 40 | "LightCoral", "#FFF08080", "LightCyan", "#FFE0FFFF", "LightGoldenrodYellow", "#FFFAFAD2", "LightGray", "#FFD3D3D3", 41 | "LightGreen", "#FF90EE90", "LightPink", "#FFFFB6C1", "LightSalmon", "#FFFFA07A", "LightSeaGreen", "#FF20B2AA", 42 | "LightSkyBlue", "#FF87CEFA", "LightSlateGray", "#FF778899", "LightSteelBlue", "#FFB0C4DE", "LightYellow", "#FFFFFFE0", 43 | "Lime", "#FF00FF00", "LimeGreen", "#FF32CD32", "Linen", "#FFFAF0E6", "Magenta", "#FFFF00FF", 44 | "Maroon", "#FF800000", "MediumAquamarine", "#FF66CDAA", "MediumBlue", "#FF0000CD", "MediumOrchid", "#FFBA55D3", 45 | "MediumPurple", "#FF9370DB", "MediumSeaGreen", "#FF3CB371", "MediumSlateBlue", "#FF7B68EE", "MediumSpringGreen", "#FF00FA9A", 46 | "MediumTurquoise", "#FF48D1CC", "MediumVioletRed", "#FFC71585", "MidnightBlue", "#FF191970", "MintCream", "#FFF5FFFA", 47 | "MistyRose", "#FFFFE4E1", "Moccasin", "#FFFFE4B5", "NavajoWhite", "#FFFFDEAD", "Navy", "#FF000080", 48 | "OldLace", "#FFFDF5E6", "Olive", "#FF808000", "OliveDrab", "#FF6B8E23", "Orange", "#FFFFA500", 49 | "OrangeRed", "#FFFF4500", "Orchid", "#FFDA70D6", "PaleGoldenrod", "#FFEEE8AA", "PaleGreen", "#FF98FB98", 50 | "PaleTurquoise", "#FFAFEEEE", "PaleVioletRed", "#FFDB7093", "PapayaWhip", "#FFFFEFD5", "PeachPuff", "#FFFFDAB9", 51 | "Peru", "#FFCD853F", "Pink", "#FFFFC0CB", "Plum", "#FFDDA0DD", "PowderBlue", "#FFB0E0E6", 52 | "Purple", "#FF800080", "Red", "#FFFF0000", "RosyBrown", "#FFBC8F8F", "RoyalBlue", "#FF4169E1", 53 | "SaddleBrown", "#FF8B4513", "Salmon", "#FFFA8072", "SandyBrown", "#FFF4A460", "SeaGreen", "#FF2E8B57", 54 | "SeaShell", "#FFFFF5EE", "Sienna", "#FFA0522D", "Silver", "#FFC0C0C0", "SkyBlue", "#FF87CEEB", 55 | "SlateBlue", "#FF6A5ACD", "SlateGray", "#FF708090", "Snow", "#FFFFFAFA", "SpringGreen", "#FF00FF7F", 56 | "SteelBlue", "#FF4682B4", "Tan", "#FFD2B48C", "Teal", "#FF008080", "Thistle", "#FFD8BFD8", 57 | "Tomato", "#FFFF6347", "Transparent", "#00FFFFFF", "Turquoise", "#FF40E0D0", "Violet", "#FFEE82EE", 58 | "Wheat", "#FFF5DEB3", "White", "#FFFFFFFF", "WhiteSmoke", "#FFF5F5F5", "Yellow", "#FFFFFF00", 59 | "YellowGreen", "#FF9ACD32" }; 60 | 61 | private const byte MIN = 0; 62 | private const byte MAX = 255; 63 | 64 | internal static Dictionary HexFromColorUpperCase = new Dictionary(); 65 | internal static Dictionary ColorFromHex = new Dictionary(); 66 | private static Regex MatchNamedColorRegex; 67 | //private static Regex MatchNamedColorFreeFloatingRegex; 68 | 69 | static ColorSpace() 70 | { 71 | StringBuilder stringBuilder = new StringBuilder("[\"']\\b("); 72 | int num = NamedColors.Length / 2; 73 | for (int i = 0; i < num; i++) 74 | { 75 | HexFromColorUpperCase[NamedColors[i * 2].ToUpperInvariant()] = NamedColors[i * 2 + 1]; 76 | ColorFromHex[NamedColors[i * 2 + 1]] = NamedColors[i * 2]; 77 | stringBuilder.Append(NamedColors[i * 2]); 78 | if (i < num - 1) 79 | { 80 | stringBuilder.Append("|"); 81 | } 82 | } 83 | stringBuilder.Append(")\\b"); 84 | MatchNamedColorRegex = new Regex(stringBuilder.ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant); 85 | //MatchNamedColorFreeFloatingRegex = new Regex(stringBuilder.Remove(0, 4).ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant); 86 | } 87 | 88 | internal static MatchCollection MatchNamedColor(string text, bool shouldAllowFreeFloatingColorNames) 89 | { 90 | //if (shouldAllowFreeFloatingColorNames) 91 | //{ 92 | // return MatchNamedColorFreeFloatingRegex.Matches(text); 93 | //} 94 | 95 | return MatchNamedColorRegex.Matches(text); 96 | } 97 | 98 | public static Color GetColorFromPosition(int position) 99 | { 100 | byte mod = (byte)(position % MAX); 101 | byte diff = (byte)(MAX - mod); 102 | byte alpha = 255; 103 | 104 | switch (position / MAX) 105 | { 106 | case 0: return Color.FromArgb(alpha, MAX, mod, MIN); 107 | case 1: return Color.FromArgb(alpha, diff, MAX, MIN); 108 | case 2: return Color.FromArgb(alpha, MIN, MAX, mod); 109 | case 3: return Color.FromArgb(alpha, MIN, diff, MAX); 110 | case 4: return Color.FromArgb(alpha, mod, MIN, MAX); 111 | case 5: return Color.FromArgb(alpha, MAX, MIN, diff); 112 | default: return Colors.Black; 113 | } 114 | } 115 | 116 | internal static string GetHexCodeOrName(ColorInfo colorInfo) 117 | { 118 | Color c = colorInfo.Color.Value; 119 | 120 | string text; 121 | string alphaText = string.Format("#{0}{1}{2}{3}", 122 | c.A.ToString("X2"), 123 | c.R.ToString("X2"), 124 | c.G.ToString("X2"), 125 | c.B.ToString("X2")); 126 | 127 | if (colorInfo.WasSpecifiedWithAlpha || (c.A < 255)) 128 | { 129 | text = alphaText; 130 | } 131 | else 132 | { 133 | text = string.Format("#{0}{1}{2}", 134 | c.R.ToString("X2"), 135 | c.G.ToString("X2"), 136 | c.B.ToString("X2")); 137 | } 138 | 139 | string result; 140 | if (ColorFromHex.TryGetValue(alphaText, out result)) 141 | { 142 | return result; 143 | } 144 | 145 | return text; 146 | } 147 | 148 | public static void ConvertRgbToHsv(double r, double g, double b, out double h, out double s, out double v) 149 | { 150 | double colorMax = Math.Max(Math.Max(r, g), b); 151 | 152 | v = colorMax; 153 | if (v == 0) 154 | { 155 | h = 0; 156 | s = 0; 157 | return; 158 | } 159 | 160 | // normalize 161 | r /= v; 162 | g /= v; 163 | b /= v; 164 | 165 | double colorMin = Math.Min(Math.Min(r, g), b); 166 | colorMax = Math.Max(Math.Max(r, g), b); 167 | 168 | s = colorMax - colorMin; 169 | if (s == 0) 170 | { 171 | h = 0; 172 | return; 173 | } 174 | 175 | // normalize saturation 176 | r = (r - colorMin) / s; 177 | g = (g - colorMin) / s; 178 | b = (b - colorMin) / s; 179 | colorMax = Math.Max(Math.Max(r, g), b); 180 | 181 | // calculate hue 182 | if (colorMax == r) 183 | { 184 | h = 0.0 + 60.0 * (g - b); 185 | if (h < 0.0) 186 | { 187 | h += 360.0; 188 | } 189 | } 190 | else if (colorMax == g) 191 | { 192 | h = 120.0 + 60.0 * (b - r); 193 | } 194 | else // colorMax == b 195 | { 196 | h = 240.0 + 60.0 * (r - g); 197 | } 198 | 199 | } 200 | 201 | // Algorithm ported from: http://www.colorjack.com/software/dhtml+color+picker.html 202 | public static Color ConvertHsvToRgb(float h, float s, float v) 203 | { 204 | h = h / 360; 205 | if (s > 0) 206 | { 207 | if (h >= 1) 208 | h = 0; 209 | h = 6 * h; 210 | int hueFloor = (int)Math.Floor(h); 211 | byte a = (byte)Math.Round(MAX * v * (1.0 - s)); 212 | byte b = (byte)Math.Round(MAX * v * (1.0 - (s * (h - hueFloor)))); 213 | byte c = (byte)Math.Round(MAX * v * (1.0 - (s * (1.0 - (h - hueFloor))))); 214 | byte d = (byte)Math.Round(MAX * v); 215 | 216 | switch (hueFloor) 217 | { 218 | case 0: return Color.FromArgb(MAX, d, c, a); 219 | case 1: return Color.FromArgb(MAX, b, d, a); 220 | case 2: return Color.FromArgb(MAX, a, d, c); 221 | case 3: return Color.FromArgb(MAX, a, b, d); 222 | case 4: return Color.FromArgb(MAX, c, a, d); 223 | case 5: return Color.FromArgb(MAX, d, a, b); 224 | default: return Color.FromArgb(0, 0, 0, 0); 225 | } 226 | } 227 | else 228 | { 229 | byte d = (byte)(v * MAX); 230 | return Color.FromArgb(255, d, d, d); 231 | } 232 | } 233 | } 234 | } 235 | 236 | -------------------------------------------------------------------------------- /InlineColorPickerShared/ColorPicker.xaml.cs: -------------------------------------------------------------------------------- 1 | // 1. added mouse capture capability 2 | // 2. fixed bugs around edge cases (e.g. 0000000 and FFFFFFF colors) 3 | // 3. added Alpha picker 4 | // 4. Added ability to type in hex color 5 | // 5. improved speed and layout 6 | 7 | using System; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Documents; 11 | using System.Windows.Ink; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Animation; 15 | using System.Windows.Shapes; 16 | using System.Windows.Media.Imaging; 17 | using System.ComponentModel; 18 | using InlineColorPicker; 19 | using System.Globalization; 20 | 21 | namespace EasyPainter.Imaging.Silverlight 22 | { 23 | public partial class ColorPicker : UserControl 24 | { 25 | float m_selectedHue; 26 | double m_sampleX; 27 | double m_sampleY; 28 | private ColorInfo m_selectedColor = new ColorInfo() { Color = Colors.White, WasSpecifiedWithAlpha = true }; 29 | public delegate void ColorSelectedHandler(ColorInfo info); 30 | public event ColorSelectedHandler ColorSelected; 31 | private UIElement _mouseCapture = null; 32 | 33 | internal ColorInfo ColorInfo 34 | { 35 | get { return m_selectedColor; } 36 | set 37 | { 38 | m_selectedColor = value; 39 | Color c = m_selectedColor.Color.Value; 40 | UpdateOnColorChanged(c.A, c.R, c.G, c.B); 41 | SetTexts(ColorSpace.GetHexCodeOrName(m_selectedColor)); 42 | } 43 | } 44 | 45 | private void SetTexts(string text) 46 | { 47 | HexValue.Text = text; 48 | byte a, r, g, b; 49 | if (GetArgb(text, out a, out r, out g, out b)) 50 | { 51 | AlphaValue.Text = a.ToString(); 52 | RedValue.Text = r.ToString(); 53 | GreenValue.Text = g.ToString(); 54 | BlueValue.Text = b.ToString(); 55 | } 56 | 57 | if (text.StartsWith("#")) 58 | { 59 | HexValue.Select(1, text.Length - 1); 60 | } 61 | else 62 | { 63 | HexValue.SelectAll(); 64 | } 65 | } 66 | 67 | public ColorPicker() 68 | { 69 | InitializeComponent(); 70 | rectHueMonitor.MouseLeftButtonDown += new MouseButtonEventHandler(rectHueMonitor_MouseLeftButtonDown); 71 | rectHueMonitor.MouseLeftButtonUp += new MouseButtonEventHandler(rectHueMonitor_MouseLeftButtonUp); 72 | rectHueMonitor.LostMouseCapture += new MouseEventHandler(rectHueMonitor_LostMouseCapture); 73 | rectHueMonitor.MouseMove += new MouseEventHandler(rectHueMonitor_MouseMove); 74 | rectHueMonitor.MouseWheel += rectHueMonitor_MouseWheel; 75 | 76 | rectLuminosityMonitor.MouseLeftButtonDown += new MouseButtonEventHandler(rectLuminosityMonitor_MouseLeftButtonDown); 77 | rectLuminosityMonitor.MouseLeftButtonUp += new MouseButtonEventHandler(rectLuminosityMonitor_MouseLeftButtonUp); 78 | rectLuminosityMonitor.LostMouseCapture += new MouseEventHandler(rectLuminosityMonitor_LostMouseCapture); 79 | rectLuminosityMonitor.MouseMove += new MouseEventHandler(rectLuminosityMonitor_MouseMove); 80 | rectLuminosityMonitor.MouseWheel += rectLuminosityMonitor_MouseWheel; 81 | 82 | rectSaturationMonitor.MouseLeftButtonDown += new MouseButtonEventHandler(rectSaturationMonitor_MouseLeftButtonDown); 83 | rectSaturationMonitor.MouseLeftButtonUp += new MouseButtonEventHandler(rectSaturationMonitor_MouseLeftButtonUp); 84 | rectSaturationMonitor.LostMouseCapture += new MouseEventHandler(rectSaturationMonitor_LostMouseCapture); 85 | rectSaturationMonitor.MouseMove += new MouseEventHandler(rectSaturationMonitor_MouseMove); 86 | rectSaturationMonitor.MouseWheel += rectSaturationMonitor_MouseWheel; 87 | 88 | rectSampleMonitor.MouseLeftButtonDown += new MouseButtonEventHandler(rectSampleMonitor_MouseLeftButtonDown); 89 | rectSampleMonitor.MouseLeftButtonUp += new MouseButtonEventHandler(rectSampleMonitor_MouseLeftButtonUp); 90 | rectSampleMonitor.LostMouseCapture += new MouseEventHandler(rectSampleMonitor_LostMouseCapture); 91 | rectSampleMonitor.MouseMove += new MouseEventHandler(rectSampleMonitor_MouseMove); 92 | 93 | ctlAlphaSelect.AlphaChanged += new AlphaSelectControl.AlphaChangedHandler(ctlAlphaSelect_AlphaChanged); 94 | m_selectedHue = 0; 95 | m_sampleX = 0; 96 | m_sampleY = 0; 97 | this.LayoutUpdated += new EventHandler(ColorPicker_LayoutUpdated); 98 | } 99 | 100 | void rectHueMonitor_MouseWheel(object sender, MouseWheelEventArgs e) 101 | { 102 | int increment = 1; 103 | if (Keyboard.Modifiers == ModifierKeys.Control) increment = 5; 104 | GeneralTransform trans = rectHueMonitorParent.TransformToDescendant(HueSelector); 105 | Point p = trans.Transform(new Point(0, -4)); 106 | int yPos = (int)Math.Abs(p.Y); 107 | if (yPos < 0) yPos = 0; 108 | if (yPos >= rectHueMonitor.ActualHeight) yPos = (int)rectHueMonitor.ActualHeight - 1; 109 | if (e.Delta > 0) 110 | { 111 | yPos -= increment; 112 | } 113 | else if (e.Delta < 0) 114 | { 115 | yPos += increment; 116 | } 117 | UpdateHueSelection(yPos); 118 | } 119 | 120 | void rectLuminosityMonitor_MouseWheel(object sender, MouseWheelEventArgs e) 121 | { 122 | int increment = 1; 123 | if (Keyboard.Modifiers == ModifierKeys.Control) increment = 5; 124 | GeneralTransform trans = rectLuminosityMonitorParent.TransformToDescendant(LuminositySelector); 125 | Point p = trans.Transform(new Point(0, -4)); 126 | int yPos = (int)Math.Abs(p.Y); 127 | if (yPos < 0) yPos = 0; 128 | if (yPos >= rectLuminosityMonitor.ActualHeight) yPos = (int)rectLuminosityMonitor.ActualHeight - 1; 129 | if (e.Delta > 0) 130 | { 131 | yPos -= increment; 132 | } 133 | else if (e.Delta < 0) 134 | { 135 | yPos += increment; 136 | } 137 | UpdateLuminositySelection(yPos); 138 | } 139 | 140 | void rectSaturationMonitor_MouseWheel(object sender, MouseWheelEventArgs e) 141 | { 142 | int increment = 1; 143 | if (Keyboard.Modifiers == ModifierKeys.Control) increment = 5; 144 | GeneralTransform trans = rectSaturationMonitorParent.TransformToDescendant(SaturationSelector); 145 | Point p = trans.Transform(new Point(0, 0)); 146 | int xPos = (int)Math.Abs(p.X); 147 | if (xPos < 0) xPos = 0; 148 | if (xPos >= rectSaturationMonitor.ActualWidth) xPos = (int)rectSaturationMonitor.ActualWidth - 1; 149 | if (e.Delta > 0) 150 | { 151 | xPos += increment; 152 | } 153 | else if (e.Delta < 0) 154 | { 155 | xPos -= increment; 156 | } 157 | UpdateSaturationSelection(xPos); 158 | } 159 | 160 | bool _firstTime = true; 161 | void ColorPicker_LayoutUpdated(object sender, EventArgs e) 162 | { 163 | if (_firstTime) 164 | { 165 | _firstTime = false; 166 | Color c = m_selectedColor.Color.Value; 167 | UpdateOnColorChanged(c.A, c.R, c.G, c.B); 168 | } 169 | } 170 | 171 | void rectSampleMonitor_LostMouseCapture(object sender, MouseEventArgs e) 172 | { 173 | _mouseCapture = null; 174 | } 175 | 176 | void rectHueMonitor_LostMouseCapture(object sender, MouseEventArgs e) 177 | { 178 | _mouseCapture = null; 179 | } 180 | 181 | void rectLuminosityMonitor_LostMouseCapture(object sender, MouseEventArgs e) 182 | { 183 | _mouseCapture = null; 184 | } 185 | 186 | void rectSaturationMonitor_LostMouseCapture(object sender, MouseEventArgs e) 187 | { 188 | _mouseCapture = null; 189 | } 190 | 191 | void rectHueMonitor_MouseLeftButtonDown(object sender, MouseEventArgs e) 192 | { 193 | rectHueMonitor.CaptureMouse(); 194 | _mouseCapture = rectHueMonitor; 195 | int yPos = (int)e.GetPosition((UIElement)sender).Y; 196 | UpdateHueSelection(yPos); 197 | } 198 | 199 | void rectLuminosityMonitor_MouseLeftButtonDown(object sender, MouseEventArgs e) 200 | { 201 | rectLuminosityMonitor.CaptureMouse(); 202 | _mouseCapture = rectLuminosityMonitor; 203 | int yPos = (int)e.GetPosition((UIElement)sender).Y; 204 | UpdateLuminositySelection(yPos); 205 | } 206 | 207 | void rectSaturationMonitor_MouseLeftButtonDown(object sender, MouseEventArgs e) 208 | { 209 | rectSaturationMonitor.CaptureMouse(); 210 | _mouseCapture = rectSaturationMonitor; 211 | int xPos = (int)e.GetPosition((UIElement)sender).X; 212 | UpdateSaturationSelection(xPos); 213 | } 214 | 215 | void rectHueMonitor_MouseLeftButtonUp(object sender, MouseEventArgs e) 216 | { 217 | rectHueMonitor.ReleaseMouseCapture(); 218 | } 219 | 220 | void rectLuminosityMonitor_MouseLeftButtonUp(object sender, MouseEventArgs e) 221 | { 222 | rectLuminosityMonitor.ReleaseMouseCapture(); 223 | } 224 | 225 | void rectSaturationMonitor_MouseLeftButtonUp(object sender, MouseEventArgs e) 226 | { 227 | rectSaturationMonitor.ReleaseMouseCapture(); 228 | } 229 | 230 | void rectHueMonitor_MouseMove(object sender, MouseEventArgs e) 231 | { 232 | if (_mouseCapture != rectHueMonitor) return; 233 | int yPos = (int)e.GetPosition((UIElement)sender).Y; 234 | if (yPos < 0) yPos = 0; 235 | if (yPos >= rectHueMonitor.ActualHeight) yPos = (int)rectHueMonitor.ActualHeight - 1; 236 | UpdateHueSelection(yPos); 237 | } 238 | 239 | void rectLuminosityMonitor_MouseMove(object sender, MouseEventArgs e) 240 | { 241 | if (_mouseCapture != rectLuminosityMonitor) return; 242 | int yPos = (int)e.GetPosition((UIElement)sender).Y; 243 | if (yPos < 0) yPos = 0; 244 | if (yPos >= rectLuminosityMonitor.ActualHeight) yPos = (int)rectLuminosityMonitor.ActualHeight - 1; 245 | UpdateLuminositySelection(yPos); 246 | } 247 | 248 | void rectSaturationMonitor_MouseMove(object sender, MouseEventArgs e) 249 | { 250 | if (_mouseCapture != rectSaturationMonitor) return; 251 | int xPos = (int)e.GetPosition((UIElement)sender).X; 252 | if (xPos < 0) xPos = 0; 253 | if (xPos >= rectSaturationMonitor.ActualWidth) xPos = (int)rectSaturationMonitor.ActualWidth - 1; 254 | UpdateSaturationSelection(xPos); 255 | } 256 | 257 | void rectSampleMonitor_MouseLeftButtonDown(object sender, MouseEventArgs e) 258 | { 259 | rectSampleMonitor.CaptureMouse(); 260 | _mouseCapture = rectSampleMonitor; 261 | Point pos = e.GetPosition((UIElement)sender); 262 | m_sampleX = (int)pos.X; 263 | m_sampleY = (int)pos.Y; 264 | UpdateSample(m_sampleX, m_sampleY); 265 | } 266 | 267 | void rectSampleMonitor_MouseLeftButtonUp(object sender, MouseEventArgs e) 268 | { 269 | rectSampleMonitor.ReleaseMouseCapture(); 270 | } 271 | 272 | void rectSampleMonitor_MouseMove(object sender, MouseEventArgs e) 273 | { 274 | if (_mouseCapture != rectSampleMonitor) return; 275 | Point pos = e.GetPosition((UIElement)sender); 276 | m_sampleX = (int)pos.X; 277 | m_sampleY = (int)pos.Y; 278 | if (m_sampleY < 0) m_sampleY = 0; 279 | if (m_sampleY > rectSampleMonitor.ActualHeight) m_sampleY = (int)rectSampleMonitor.ActualHeight; 280 | if (m_sampleX < 0) m_sampleX = 0; 281 | if (m_sampleX > rectSampleMonitor.ActualWidth) m_sampleX = (int)rectSampleMonitor.ActualWidth; 282 | UpdateSample(m_sampleX, m_sampleY); 283 | } 284 | 285 | private void UpdateSample(double xPos, double yPos) 286 | { 287 | SampleSelector.Margin = new Thickness(xPos - (SampleSelector.Height / 2), yPos - (SampleSelector.Height / 2), 0, 0); 288 | 289 | float yComponent = 1 - (float)(yPos / rectSample.ActualHeight); 290 | float xComponent = (float)(xPos / rectSample.ActualWidth); 291 | 292 | byte a = m_selectedColor.Color.Value.A; 293 | Color c = ColorSpace.ConvertHsvToRgb((float)m_selectedHue, xComponent, yComponent); 294 | c.A = a; 295 | Color maxLum = ColorSpace.ConvertHsvToRgb((float)m_selectedHue, xComponent, 1); 296 | maxLum.A = a; 297 | Color maxSat = ColorSpace.ConvertHsvToRgb((float)m_selectedHue, 1, yComponent); 298 | maxSat.A = a; 299 | m_selectedColor.Color = c; 300 | SelectedColor.Fill = new SolidColorBrush(m_selectedColor.Color.Value); 301 | SetTexts(ColorSpace.GetHexCodeOrName(m_selectedColor)); 302 | 303 | ctlAlphaSelect.DisplayColor = m_selectedColor.Color.Value; 304 | rectLuminosityMonitor.Fill = new LinearGradientBrush(Color.FromArgb(255, maxLum.R, maxLum.G, maxLum.B), Color.FromArgb(255, 0, 0, 0), 90); 305 | rectSaturationMonitor.Fill = new LinearGradientBrush(Color.FromArgb(255, 255, 255, 255), Color.FromArgb(255, maxSat.R, maxSat.G, maxSat.B), 0); 306 | LuminositySelector.Margin = new Thickness(0, yPos - (LuminositySelector.ActualHeight / 2), 0, 0); 307 | SaturationSelector.Margin = new Thickness(xPos + (SaturationSelector.ActualWidth / 2), 0, 0, 0); 308 | ColorSelected?.Invoke(m_selectedColor); 309 | } 310 | 311 | private void UpdateHueSelection(int yPos) 312 | { 313 | int huePos = (int)(yPos / rectHueMonitor.ActualHeight * 255); 314 | int gradientStops = 6;//what is this for? 315 | Color c = ColorSpace.GetColorFromPosition(huePos * gradientStops); 316 | rectSample.Fill = new SolidColorBrush(c); 317 | HueSelector.Margin = new Thickness(0, yPos - (HueSelector.ActualHeight / 2), 0, 0); 318 | m_selectedHue = (float)(yPos / rectHueMonitor.ActualHeight) * 360; 319 | UpdateSample(m_sampleX, m_sampleY); 320 | } 321 | 322 | private void UpdateLuminositySelection(int yPos) 323 | { 324 | UpdateSample(m_sampleX, yPos); 325 | } 326 | 327 | private void UpdateSaturationSelection(int xPos) 328 | { 329 | UpdateSample(xPos, m_sampleY); 330 | } 331 | 332 | private void ctlAlphaSelect_AlphaChanged(byte newAlpha) 333 | { 334 | Color c = m_selectedColor.Color.Value; 335 | c.A = newAlpha; 336 | m_selectedColor.Color = c; 337 | SetTexts(ColorSpace.GetHexCodeOrName(m_selectedColor)); 338 | SelectedColor.Fill = new SolidColorBrush(m_selectedColor.Color.Value); 339 | 340 | ColorSelected?.Invoke(m_selectedColor); 341 | } 342 | 343 | private void Byte_Value_GotFocus(object sender, MouseButtonEventArgs e) 344 | { 345 | if (sender != null) ((TextBox)sender).SelectAll(); 346 | } 347 | 348 | private void HexValue_TextChanged(object sender, TextChangedEventArgs e) 349 | { 350 | string text = HexValue.Text; 351 | if (text == ColorSpace.GetHexCodeOrName(m_selectedColor)) return; 352 | byte a, r, g, b; 353 | if (!GetArgb(text, out a, out r, out g, out b)) return; // invalid color 354 | 355 | UpdateOnColorChanged(a, r, g, b); 356 | } 357 | 358 | private void ByteValue_TextChanged(object sender, TextChangedEventArgs e) 359 | { 360 | try 361 | { 362 | if (AlphaValue == null || RedValue == null || GreenValue == null || BlueValue == null) return; 363 | byte a, r, g, b; 364 | if (byte.TryParse(AlphaValue.Text, out a)) 365 | if (byte.TryParse(RedValue.Text, out r)) 366 | if (byte.TryParse(GreenValue.Text, out g)) 367 | if (byte.TryParse(BlueValue.Text, out b)) 368 | { 369 | byte[] data = { a, r, g, b }; 370 | string hex = BitConverter.ToString(data).Replace("-", string.Empty); 371 | HexValue.Text = "#" + hex; 372 | //changing HexValue will trigger update. 373 | //UpdateOnColorChanged(a, r, g, b); 374 | } 375 | } 376 | catch { }; 377 | } 378 | 379 | private void ByteValue_MouseWheel(object sender, MouseWheelEventArgs e) 380 | { 381 | TextBox txt = sender as TextBox; 382 | byte value; 383 | if (e.Delta > 0) 384 | { 385 | if (byte.TryParse(txt.Text, out value)) 386 | { 387 | value += 1; 388 | if (value > 255) value = 255; 389 | txt.Text = value.ToString(); 390 | } 391 | } 392 | else if (e.Delta < 0) 393 | { 394 | if (byte.TryParse(txt.Text, out value)) 395 | { 396 | value -= 1; 397 | if (value < 0) value = 0; 398 | txt.Text = value.ToString(); 399 | } 400 | } 401 | } 402 | 403 | private void UpdateOnColorChanged(byte a, byte r, byte g, byte b) 404 | { 405 | m_selectedColor.Color = Color.FromArgb(a, r, g, b); 406 | 407 | double h, s, v; 408 | ColorSpace.ConvertRgbToHsv(r / 255.0, g / 255.0, b / 255.0, out h, out s, out v); 409 | 410 | // update selected color 411 | SelectedColor.Fill = new SolidColorBrush(m_selectedColor.Color.Value); 412 | 413 | // update Saturation and Value locator 414 | double xPos = s * rectSample.ActualWidth; 415 | double yPos = (1 - v) * rectSample.ActualHeight; 416 | m_sampleX = xPos; 417 | m_sampleY = yPos; 418 | SampleSelector.Margin = new Thickness(xPos - (SampleSelector.Height / 2), yPos - (SampleSelector.Height / 2), 0, 0); 419 | 420 | m_selectedHue = (float)h; 421 | h /= 360; 422 | const int gradientStops = 6; 423 | rectSample.Fill = new SolidColorBrush(ColorSpace.GetColorFromPosition(((int)(h * 255)) * gradientStops)); 424 | 425 | LuminositySelector.Margin = new Thickness(0, yPos - (LuminositySelector.ActualHeight / 2), 0, 0); 426 | SaturationSelector.Margin = new Thickness(xPos + (SaturationSelector.ActualWidth / 2), 0, 0, 0); 427 | 428 | // Update Hue locator 429 | HueSelector.Margin = new Thickness(0, (h * rectHueMonitor.ActualHeight) - (HueSelector.ActualHeight / 2), 0, 0); 430 | 431 | // update alpha selector 432 | if (ctlAlphaSelect != null) 433 | { 434 | // TODO: fix - when null should be assigned later 435 | ctlAlphaSelect.DisplayColor = m_selectedColor.Color.Value; 436 | } 437 | 438 | // update alpha byte 439 | if (AlphaValue != null) 440 | { 441 | AlphaValue.Text = a.ToString(); 442 | } 443 | 444 | // update alpha byte 445 | if (RedValue != null) 446 | { 447 | RedValue.Text = r.ToString(); 448 | } 449 | 450 | // update alpha byte 451 | if (GreenValue != null) 452 | { 453 | GreenValue.Text = g.ToString(); 454 | } 455 | 456 | // update alpha byte 457 | if (BlueValue != null) 458 | { 459 | BlueValue.Text = b.ToString(); 460 | } 461 | 462 | float yComponent = 1 - (float)(yPos / rectSample.ActualHeight); 463 | float xComponent = (float)(xPos / rectSample.ActualWidth); 464 | Color c = ColorSpace.ConvertHsvToRgb((float)m_selectedHue, (float)xComponent, (float)yComponent); 465 | c.A = a; 466 | Color maxLum = ColorSpace.ConvertHsvToRgb((float)m_selectedHue, (float)xComponent, 1.0f); 467 | maxLum.A = a; 468 | Color maxSat = ColorSpace.ConvertHsvToRgb((float)m_selectedHue, 1.0f, (float)yComponent); 469 | maxSat.A = a; 470 | rectLuminosityMonitor.Fill = new LinearGradientBrush(Color.FromArgb(255, maxLum.R, maxLum.G, maxLum.B), Color.FromArgb(255, 0, 0, 0), 90); 471 | rectSaturationMonitor.Fill = new LinearGradientBrush(Color.FromArgb(255, 255, 255, 255), Color.FromArgb(255, maxSat.R, maxSat.G, maxSat.B), 0); 472 | 473 | ColorSelected?.Invoke(m_selectedColor); 474 | } 475 | 476 | private bool GetArgb(string hexColorOrName, out byte a, out byte r, out byte g, out byte b) 477 | { 478 | string text; 479 | if (!ColorSpace.HexFromColorUpperCase.TryGetValue(hexColorOrName.ToUpperInvariant(), out text)) 480 | { 481 | text = hexColorOrName; 482 | } 483 | 484 | a = r = b = g = 0; 485 | string strA, strR, strG, strB; 486 | if (text.Length == 9) 487 | { 488 | strA = text.Substring(1, 2); 489 | strR = text.Substring(3, 2); 490 | strG = text.Substring(5, 2); 491 | strB = text.Substring(7, 2); 492 | } 493 | else if (text.Length == 7) 494 | { 495 | strA = "ff"; 496 | strR = text.Substring(1, 2); 497 | strG = text.Substring(3, 2); 498 | strB = text.Substring(5, 2); 499 | } 500 | else 501 | { 502 | return false; 503 | } 504 | 505 | if (!Byte.TryParse(strA, NumberStyles.HexNumber, null, out a)) return false; 506 | if (!Byte.TryParse(strR, NumberStyles.HexNumber, null, out r)) return false; 507 | if (!Byte.TryParse(strG, NumberStyles.HexNumber, null, out g)) return false; 508 | if (!Byte.TryParse(strB, NumberStyles.HexNumber, null, out b)) return false; 509 | return true; 510 | } 511 | } 512 | public class SelectAllFocusBehavior 513 | { 514 | public static bool GetEnable(DependencyObject obj) 515 | { 516 | return (bool)obj.GetValue(EnableProperty); 517 | } 518 | public static void SetEnable(DependencyObject obj, bool value) 519 | { 520 | obj.SetValue(EnableProperty, value); 521 | } 522 | public static readonly DependencyProperty EnableProperty = DependencyProperty.RegisterAttached("Enable", typeof(bool), typeof(SelectAllFocusBehavior), new PropertyMetadata(false, OnEnableChanged)); 523 | private static void OnEnableChanged(object d, DependencyPropertyChangedEventArgs e) 524 | { 525 | var frameworkElement = d as FrameworkElement; 526 | if (frameworkElement == null) return; 527 | 528 | if (e.NewValue is bool == false) return; 529 | 530 | if ((bool)e.NewValue) 531 | { 532 | frameworkElement.GotFocus += SelectAll; 533 | frameworkElement.PreviewMouseDown += IgnoreMouseButton; 534 | } 535 | else 536 | { 537 | frameworkElement.GotFocus -= SelectAll; 538 | frameworkElement.PreviewMouseDown -= IgnoreMouseButton; 539 | } 540 | } 541 | 542 | private static void SelectAll(object sender, RoutedEventArgs e) 543 | { 544 | var frameworkElement = e.OriginalSource as FrameworkElement; 545 | if (frameworkElement is TextBox) 546 | ((TextBox)frameworkElement).SelectAll(); 547 | else if (frameworkElement is PasswordBox) 548 | ((PasswordBox)frameworkElement).SelectAll(); 549 | } 550 | 551 | private static void IgnoreMouseButton 552 | (object sender, System.Windows.Input.MouseButtonEventArgs e) 553 | { 554 | var frameworkElement = sender as FrameworkElement; 555 | if (frameworkElement == null || frameworkElement.IsKeyboardFocusWithin) return; 556 | e.Handled = true; 557 | frameworkElement.Focus(); 558 | } 559 | } 560 | public static class NumericByteValueOnlyBehavior 561 | { 562 | public static bool GetIsNumericByteValueOnly(DependencyObject obj) 563 | { 564 | return (bool)obj.GetValue(IsNumericByteValueOnlyProperty); 565 | } 566 | public static void SetIsNumericByteValueOnly(DependencyObject obj, bool value) 567 | { 568 | obj.SetValue(IsNumericByteValueOnlyProperty, value); 569 | } 570 | public static readonly DependencyProperty IsNumericByteValueOnlyProperty = DependencyProperty.RegisterAttached("IsNumericByteValueOnly", typeof(bool), typeof(NumericByteValueOnlyBehavior), new PropertyMetadata(false, OnIsNumericByteValueOnlyChanged)); 571 | private static void OnIsNumericByteValueOnlyChanged(object sender, DependencyPropertyChangedEventArgs e) 572 | { 573 | if (sender.GetType() == typeof(TextBox)) 574 | { 575 | TextBox txt = (TextBox)sender; 576 | txt.TextChanged += Txt_TextChanged; 577 | } 578 | } 579 | private static void Txt_TextChanged(object sender, TextChangedEventArgs e) 580 | { 581 | try 582 | { 583 | TextBox txt = (TextBox)sender; 584 | foreach (TextChange item in e.Changes) 585 | { 586 | if (item.AddedLength != 0) 587 | { 588 | foreach (char chr in txt.Text.Substring(item.Offset, item.AddedLength).ToCharArray()) 589 | { 590 | bool scrap = false; 591 | if (chr != '1' 592 | && chr != '2' 593 | && chr != '3' 594 | && chr != '4' 595 | && chr != '5' 596 | && chr != '6' 597 | && chr != '7' 598 | && chr != '8' 599 | && chr != '9' 600 | && chr != '0' 601 | && chr != '\n' 602 | && chr != '\r') 603 | { 604 | scrap = true; 605 | } 606 | if (scrap) 607 | { 608 | int temp = txt.CaretIndex; 609 | txt.Text = txt.Text.Remove(item.Offset, item.AddedLength); 610 | txt.CaretIndex = temp - 1; 611 | } 612 | } 613 | } 614 | } 615 | if (txt.Text.Length > 3) 616 | { 617 | int temp = txt.CaretIndex; 618 | txt.Text = txt.Text.Substring(0, 3); 619 | if (temp > 3) temp = 3; 620 | txt.CaretIndex = temp; 621 | } 622 | if (int.TryParse(txt.Text, out int i)) 623 | { 624 | if (i > 255) 625 | { 626 | int temp = txt.CaretIndex; 627 | txt.Text = 255.ToString(); 628 | if (temp > 3) temp = 3; 629 | txt.CaretIndex = temp; 630 | } 631 | if (i < 0) 632 | { 633 | int temp = txt.CaretIndex; 634 | txt.Text = 0.ToString(); 635 | if (temp > 3) temp = 3; 636 | txt.CaretIndex = temp; 637 | } 638 | } 639 | } 640 | catch { } 641 | } 642 | } 643 | } 644 | --------------------------------------------------------------------------------