├── 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 |
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 |
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 |
--------------------------------------------------------------------------------