From dd28cc345f1d1d6b3e2021c78c7c7329dd80748e Mon Sep 17 00:00:00 2001 From: Huy Hoang Nguyen Date: Tue, 27 Oct 2020 22:29:43 +0100 Subject: [PATCH 01/11] [feature] ColorPicker --- .../Renderers/GradientLayoutRenderer.cs | 93 ++++ .../TemplateUI.Gallery.Android.csproj | 10 +- .../Renderers/GradientLayoutRenderer.cs | 75 +++ .../OpacityGradientLayoutRenderer.cs | 100 ++++ .../TemplateUI.Gallery.iOS.csproj | 9 + .../ViewModels/MainViewModel.cs | 1 + .../Views/ColorPickerGallery.xaml | 37 ++ src/TemplateUI.Gallery/Views/MainView.xaml.cs | 5 + .../Controls/ColorPicker/ColorPicker.cs | 483 ++++++++++++++++++ .../ColorPicker/GradientColorStackMode.cs | 15 + .../Controls/ColorPicker/GradientLayout.cs | 28 + .../ColorPicker/OpacityGradientLayout.cs | 32 ++ src/TemplateUI/Converters/ColorConverter.cs | 45 ++ .../Converters/ColorNumberConverter.cs | 43 ++ src/TemplateUI/TemplateUI.csproj | 6 +- src/TemplateUI/Themes/Generic.xaml | 1 + src/TemplateUI/Themes/Styles/ColorPicker.xaml | 128 +++++ .../Themes/Styles/ColorPicker.xaml.cs | 12 + 18 files changed, 1121 insertions(+), 2 deletions(-) create mode 100644 src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs create mode 100644 src/TemplateUI.Gallery.iOS/Renderers/GradientLayoutRenderer.cs create mode 100644 src/TemplateUI.Gallery.iOS/Renderers/OpacityGradientLayoutRenderer.cs create mode 100644 src/TemplateUI.Gallery/Views/ColorPickerGallery.xaml create mode 100644 src/TemplateUI/Controls/ColorPicker/ColorPicker.cs create mode 100644 src/TemplateUI/Controls/ColorPicker/GradientColorStackMode.cs create mode 100644 src/TemplateUI/Controls/ColorPicker/GradientLayout.cs create mode 100644 src/TemplateUI/Controls/ColorPicker/OpacityGradientLayout.cs create mode 100644 src/TemplateUI/Converters/ColorConverter.cs create mode 100644 src/TemplateUI/Converters/ColorNumberConverter.cs create mode 100644 src/TemplateUI/Themes/Styles/ColorPicker.xaml create mode 100644 src/TemplateUI/Themes/Styles/ColorPicker.xaml.cs diff --git a/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs b/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs new file mode 100644 index 0000000..201cfa4 --- /dev/null +++ b/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs @@ -0,0 +1,93 @@ +using System; +using Android.Content; +using TemplateUI.Controls; +using TemplateUI.Gallery.Droid.Renderers; +using Xamarin.Forms; +using Xamarin.Forms.Platform.Android; + +[assembly: ExportRenderer(typeof(GradientLayout), typeof(GradientLayoutRenderer))] + +namespace TemplateUI.Gallery.Droid.Renderers +{ + public class GradientLayoutRenderer : VisualElementRenderer + { + private Color[] Colors { get; set; } + + private GradientColorStackMode Mode { get; set; } + + public GradientLayoutRenderer(Context ctx) : base(ctx) + { } + + protected override void DispatchDraw(global::Android.Graphics.Canvas canvas) + { + Android.Graphics.LinearGradient gradient; + + int[] colors = new int[Colors.Length]; + + for (int i = 0, l = Colors.Length; i < l; i++) + { + colors[i] = Colors[i].ToAndroid().ToArgb(); + } + + switch (Mode) + { + default: + case GradientColorStackMode.ToRight: + gradient = new Android.Graphics.LinearGradient(0, 0, Width, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + break; + case GradientColorStackMode.ToLeft: + gradient = new Android.Graphics.LinearGradient(Width, 0, 0, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + break; + case GradientColorStackMode.ToTop: + gradient = new Android.Graphics.LinearGradient(0, Height, 0, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + break; + case GradientColorStackMode.ToBottom: + gradient = new Android.Graphics.LinearGradient(0, 0, 0, Height, colors, null, Android.Graphics.Shader.TileMode.Mirror); + break; + case GradientColorStackMode.ToTopLeft: + gradient = new Android.Graphics.LinearGradient(Width, Height, 0, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + break; + case GradientColorStackMode.ToTopRight: + gradient = new Android.Graphics.LinearGradient(0, Height, Width, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + break; + case GradientColorStackMode.ToBottomLeft: + gradient = new Android.Graphics.LinearGradient(Width, 0, 0, Height, colors, null, Android.Graphics.Shader.TileMode.Mirror); + break; + case GradientColorStackMode.ToBottomRight: + gradient = new Android.Graphics.LinearGradient(0, 0, Width, Height, colors, null, Android.Graphics.Shader.TileMode.Mirror); + break; + } + + var paint = new Android.Graphics.Paint() + { + Dither = true, + }; + + paint.SetShader(gradient); + canvas.DrawPaint(paint); + + base.DispatchDraw(canvas); + } + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.OldElement != null || Element == null) + return; + + try + { + if (e.NewElement is GradientLayout layout) + { + Colors = layout.Colors; + Mode = layout.Mode; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(@"ERROR:", ex.Message); + } + } + } +} \ No newline at end of file diff --git a/src/TemplateUI.Gallery.Android/TemplateUI.Gallery.Android.csproj b/src/TemplateUI.Gallery.Android/TemplateUI.Gallery.Android.csproj index 889bc42..c0452fa 100644 --- a/src/TemplateUI.Gallery.Android/TemplateUI.Gallery.Android.csproj +++ b/src/TemplateUI.Gallery.Android/TemplateUI.Gallery.Android.csproj @@ -41,7 +41,6 @@ prompt 4 true - false @@ -51,6 +50,9 @@ + + ..\..\..\..\..\..\Library\Frameworks\Xamarin.iOS.framework\Versions\14.0.0.0\lib\mono\Xamarin.iOS\Xamarin.iOS.dll + @@ -60,6 +62,7 @@ + @@ -206,12 +209,17 @@ + {D9A11260-71D9-42DB-969F-733793E98E34} TemplateUI.Gallery + + {E3954AAF-D1E4-4BDD-A25B-C3A22D436501} + TemplateUI + diff --git a/src/TemplateUI.Gallery.iOS/Renderers/GradientLayoutRenderer.cs b/src/TemplateUI.Gallery.iOS/Renderers/GradientLayoutRenderer.cs new file mode 100644 index 0000000..4cd6636 --- /dev/null +++ b/src/TemplateUI.Gallery.iOS/Renderers/GradientLayoutRenderer.cs @@ -0,0 +1,75 @@ +using System; +using System.ComponentModel; +using CoreAnimation; +using CoreGraphics; +using TemplateUI.Controls; +using TemplateUI.Gallery.iOS.Renderers; +using Xamarin.Essentials; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(GradientLayout), typeof(GradientLayoutRenderer))] + +namespace TemplateUI.Gallery.iOS.Renderers +{ + public class GradientLayoutRenderer : VisualElementRenderer + { + public override void Draw(CGRect rect) + { + base.Draw(rect); + + GradientLayout layout = (GradientLayout)Element; + + CGColor[] colors = new CGColor[layout.Colors.Length]; + + for (int i = 0, l = colors.Length; i < l; i++) + { + colors[i] = layout.Colors[i].ToCGColor(); + } + + var gradientLayer = new CAGradientLayer(); + + switch (layout.Mode) + { + default: + case GradientColorStackMode.ToRight: + gradientLayer.StartPoint = new CGPoint(0, 0.5); + gradientLayer.EndPoint = new CGPoint(1, 0.5); + break; + case GradientColorStackMode.ToLeft: + gradientLayer.StartPoint = new CGPoint(1, 0.5); + gradientLayer.EndPoint = new CGPoint(0, 0.5); + break; + case GradientColorStackMode.ToTop: + gradientLayer.StartPoint = new CGPoint(0.5, 0); + gradientLayer.EndPoint = new CGPoint(0.5, 1); + break; + case GradientColorStackMode.ToBottom: + gradientLayer.StartPoint = new CGPoint(0.5, 1); + gradientLayer.EndPoint = new CGPoint(0.5, 0); + break; + case GradientColorStackMode.ToTopLeft: + gradientLayer.StartPoint = new CGPoint(1, 0); + gradientLayer.EndPoint = new CGPoint(0, 1); + break; + case GradientColorStackMode.ToTopRight: + gradientLayer.StartPoint = new CGPoint(0, 1); + gradientLayer.EndPoint = new CGPoint(1, 0); + break; + case GradientColorStackMode.ToBottomLeft: + gradientLayer.StartPoint = new CGPoint(1, 1); + gradientLayer.EndPoint = new CGPoint(0, 0); + break; + case GradientColorStackMode.ToBottomRight: + gradientLayer.StartPoint = new CGPoint(0, 0); + gradientLayer.EndPoint = new CGPoint(1, 1); + break; + } + + gradientLayer.Frame = rect; + gradientLayer.Colors = colors; + + NativeView.Layer.InsertSublayer(gradientLayer, 0); + } + } +} diff --git a/src/TemplateUI.Gallery.iOS/Renderers/OpacityGradientLayoutRenderer.cs b/src/TemplateUI.Gallery.iOS/Renderers/OpacityGradientLayoutRenderer.cs new file mode 100644 index 0000000..10a6a9a --- /dev/null +++ b/src/TemplateUI.Gallery.iOS/Renderers/OpacityGradientLayoutRenderer.cs @@ -0,0 +1,100 @@ +using System.ComponentModel; +using CoreAnimation; +using CoreGraphics; +using TemplateUI.Controls; +using TemplateUI.Gallery.iOS.Renderers; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(OpacityGradientLayout), typeof(OpacityGradientLayoutRenderer))] + +namespace TemplateUI.Gallery.iOS.Renderers +{ + public class OpacityGradientLayoutRenderer : VisualElementRenderer + { + private static int counter = 0; + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + SetNeedsDisplay(); + } + + public override void Draw(CGRect rect) + { + base.Draw(rect); + OpacityGradientLayout layout = (OpacityGradientLayout)Element; + + var gradientLayer = new CAGradientLayer(); + + switch (layout.Mode) + { + default: + case GradientColorStackMode.ToRight: + gradientLayer.StartPoint = new CGPoint(0, 0.5); + gradientLayer.EndPoint = new CGPoint(1, 0.5); + break; + case GradientColorStackMode.ToLeft: + gradientLayer.StartPoint = new CGPoint(1, 0.5); + gradientLayer.EndPoint = new CGPoint(0, 0.5); + break; + case GradientColorStackMode.ToTop: + gradientLayer.StartPoint = new CGPoint(0.5, 0); + gradientLayer.EndPoint = new CGPoint(0.5, 1); + break; + case GradientColorStackMode.ToBottom: + gradientLayer.StartPoint = new CGPoint(0.5, 1); + gradientLayer.EndPoint = new CGPoint(0.5, 0); + break; + case GradientColorStackMode.ToTopLeft: + gradientLayer.StartPoint = new CGPoint(1, 0); + gradientLayer.EndPoint = new CGPoint(0, 1); + break; + case GradientColorStackMode.ToTopRight: + gradientLayer.StartPoint = new CGPoint(0, 1); + gradientLayer.EndPoint = new CGPoint(1, 0); + break; + case GradientColorStackMode.ToBottomLeft: + gradientLayer.StartPoint = new CGPoint(1, 1); + gradientLayer.EndPoint = new CGPoint(0, 0); + break; + case GradientColorStackMode.ToBottomRight: + gradientLayer.StartPoint = new CGPoint(0, 0); + gradientLayer.EndPoint = new CGPoint(1, 1); + break; + } + + gradientLayer.Frame = rect; + gradientLayer.Colors = new CGColor[] { + Color.White.ToCGColor(), + layout.SelectedColor.ToCGColor() + }; + + var maskLayer = new CAGradientLayer(); + + + //maskLayer.AnchorPoint = new CGPoint(x: 0.75, y: 0.75); + //gradientLayer.AnchorPoint = new CGPoint(x: 0.75, y: 0.75); + + + maskLayer.StartPoint = new CGPoint(x: 0.0, y: 0.5); + maskLayer.EndPoint = new CGPoint(x: 1.0, y: 0.5); + + + var lightZero = Color.FromHsla(0d, 1d, 0, 0.0d).ToCGColor(); + var light100 = Color.FromHsla(0d, 1d, 0, 1.0d).ToCGColor(); + CGColor[] maskedColors = + { + lightZero, + light100 + }; + maskLayer.Colors = maskedColors; + maskLayer.StartPoint = new CGPoint(0.5, 0); + maskLayer.EndPoint = new CGPoint(0.5, 1); + maskLayer.Frame = rect; + + NativeView.Layer.InsertSublayer(gradientLayer, counter++); + NativeView.Layer.InsertSublayer(maskLayer, counter++); + } + } +} diff --git a/src/TemplateUI.Gallery.iOS/TemplateUI.Gallery.iOS.csproj b/src/TemplateUI.Gallery.iOS/TemplateUI.Gallery.iOS.csproj index ce3b59b..80ebf35 100644 --- a/src/TemplateUI.Gallery.iOS/TemplateUI.Gallery.iOS.csproj +++ b/src/TemplateUI.Gallery.iOS/TemplateUI.Gallery.iOS.csproj @@ -69,6 +69,8 @@ + + @@ -132,6 +134,10 @@ {D9A11260-71D9-42DB-969F-733793E98E34} TemplateUI.Gallery + + {E3954AAF-D1E4-4BDD-A25B-C3A22D436501} + TemplateUI + @@ -165,5 +171,8 @@ + + + \ No newline at end of file diff --git a/src/TemplateUI.Gallery/ViewModels/MainViewModel.cs b/src/TemplateUI.Gallery/ViewModels/MainViewModel.cs index 1242e17..80cd57d 100644 --- a/src/TemplateUI.Gallery/ViewModels/MainViewModel.cs +++ b/src/TemplateUI.Gallery/ViewModels/MainViewModel.cs @@ -61,6 +61,7 @@ void LoadData() new GalleryItem { Title = "ChatBubble", SubTitle = "Allow to show a speech bubble message.", Icon = "chatbubble.png", Color = Color.DarkSeaGreen }, new GalleryItem { Title = "CircularLayout", SubTitle = "Is a simple Layout derivative that lays out its children in a circular arrangement.", Icon = "circularlayout.png", Color = Color.BlueViolet }, //new GalleryItem { Title = "CircleProgressBar", SubTitle = "Shows a control that indicates the progress percentage of an on-going operation by circular shape.", Icon = "circleprogressbar.png", Color = Color.LightGray, Status = GalleryItemStatus.InProgress }, + new GalleryItem { Title = "ColorPicker", SubTitle = "Picker for choosing Color.", Icon = "circularlayout.png", Color = Color.Red}, new GalleryItem { Title = "ComparerView", SubTitle = "Provides an option for displaying a split-screen of two views, which can help you to make comparisons.", Icon = "comparerview.png", Color = Color.DarkViolet, Status = GalleryItemStatus.InProgress }, new GalleryItem { Title = "DataVisualization", SubTitle = "Several series graphs.", Icon = "chart.png", Color = Color.LightCoral, Status = GalleryItemStatus.Preview }, new GalleryItem { Title = "Divider", SubTitle = "Displays a separator between views.", Icon = "divider.png", Color = Color.Orchid }, diff --git a/src/TemplateUI.Gallery/Views/ColorPickerGallery.xaml b/src/TemplateUI.Gallery/Views/ColorPickerGallery.xaml new file mode 100644 index 0000000..dd2fc89 --- /dev/null +++ b/src/TemplateUI.Gallery/Views/ColorPickerGallery.xaml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TemplateUI.Gallery/Views/MainView.xaml.cs b/src/TemplateUI.Gallery/Views/MainView.xaml.cs index d64e4b3..6e99ab4 100644 --- a/src/TemplateUI.Gallery/Views/MainView.xaml.cs +++ b/src/TemplateUI.Gallery/Views/MainView.xaml.cs @@ -25,6 +25,11 @@ void OnCarouselViewClicked(object sender, EventArgs e) Navigation.PushAsync(new CarouselViewGallery()); } + void OnColorPickerClicked(object sender, EventArgs e) + { + Navigation.PushAsync(new ColorPickerGallery()); + } + void OnDataVisualizationClicked(object sender, EventArgs e) { Navigation.PushAsync(new DataVisualizationGallery()); diff --git a/src/TemplateUI/Controls/ColorPicker/ColorPicker.cs b/src/TemplateUI/Controls/ColorPicker/ColorPicker.cs new file mode 100644 index 0000000..b2b88e8 --- /dev/null +++ b/src/TemplateUI/Controls/ColorPicker/ColorPicker.cs @@ -0,0 +1,483 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using Xamarin.Forms; +using Xamarin.Forms.Shapes; + +namespace TemplateUI.Controls +{ + public class ColorPicker : TemplatedView, INotifyPropertyChanged + { + const string ElementThumb = "PART_Thumb"; + const string ElementSaturationAndLightPicker = "PART_SaturationAndLight"; + const string ElementHuePicker = "PART_HuePicker"; + const string ElementHueThumb = "PART_HueThumb"; + + const double ExtraLargeSize = 64; + const double LargeSize = 48; + const double MediumSize = 36; + const double SmallSize = 24; + const double ExtraSmallSize = 18; + + const double ExtraLargeFontSize = 24; + const double LargeFontSize = 18; + const double MediumFontSize = 12; + const double SmallFontSize = 10; + const double ExtraSmallFontSize = 8; + + Frame _thumb; + Frame _hueThumb; + OpacityGradientLayout _hslPicker; + GradientLayout _rgbPicker; + + double _previousPositionX; + double _previousPostionY; + double _hueThumbPreviousPostionY; + + /************************************************** + * Bindable Properties + **************************************************/ + public static readonly BindableProperty ValueXhslThumbProperty = + BindableProperty.Create(nameof(ValueXhslThumb), typeof(double), typeof(ColorPicker), 0.0d, + propertyChanged: OnHslValueChanged); + + public static readonly BindableProperty ValueYhslThumbProperty = + BindableProperty.Create(nameof(ValueYhslThumb), typeof(double), typeof(ColorPicker), 0.0d, + propertyChanged: OnHslValueChanged); + + static void OnHslValueChanged(BindableObject bindable, object oldValue, object newValue) + { + if (bindable is ColorPicker colorPicker) + { + colorPicker.ValueChanged?.Invoke(bindable, new ValueChangedEventArgs((double)newValue)); + colorPicker.UpdateHSLPicker(); + colorPicker.CalculatePickedColor(); + } + } + + public double ValueXhslThumb + { + get => (double)GetValue(ValueXhslThumbProperty); + set { SetValue(ValueXhslThumbProperty, value); } + } + + public double ValueYhslThumb + { + get => (double)GetValue(ValueYhslThumbProperty); + set { SetValue(ValueYhslThumbProperty, value); } + } + + public static readonly BindableProperty ValueXrgbThumbProperty = + BindableProperty.Create(nameof(ValueXrgbThumb), typeof(double), typeof(ColorPicker), 0.0d, + propertyChanged: OnRgbValueChanged); + + public double ValueXrgbThumb + { + get => (double)GetValue(ValueXrgbThumbProperty); + set { SetValue(ValueXrgbThumbProperty, value); } + } + + public static readonly BindableProperty ValueYrgbThumbProperty = + BindableProperty.Create(nameof(ValueYrgbThumb), typeof(double), typeof(ColorPicker), 0.0d, + propertyChanged: OnRgbValueChanged); + + public double ValueYrgbThumb + { + get => (double)GetValue(ValueYrgbThumbProperty); + set { SetValue(ValueYrgbThumbProperty, value); } + } + + static void OnRgbValueChanged(BindableObject bindable, object oldValue, object newValue) + { + if (bindable is ColorPicker colorPicker) + { + colorPicker.ValueChanged?.Invoke(bindable, new ValueChangedEventArgs((double)newValue)); + colorPicker.UpdateRGBPicker(); + colorPicker.CalculatePickedColor(); + } + } + + public static readonly BindableProperty MaximumXProperty = + BindableProperty.Create(nameof(MaximumX), typeof(double), typeof(ColorPicker), 10.0d); + + public double MaximumX + { + get => (double)GetValue(MaximumXProperty); + set { SetValue(MaximumXProperty, value); } + } + + public static readonly BindableProperty MaximumYProperty = + BindableProperty.Create(nameof(MaximumY), typeof(double), typeof(ColorPicker), 10.0d); + + public double MaximumY + { + get => (double)GetValue(MaximumYProperty); + set { SetValue(MaximumYProperty, value); } + } + + /************************************************** + * Properties + **************************************************/ + private double red = 0.0d; + public double Red + { + get + { + return this.red; + } + set + { + this.red = value; + OnPropertyChanged(); + ConvertToHsl(); + } + } + + private double blue = 0.0d; + public double Blue + { + get + { + return this.blue; + } + set + { + this.blue = value; + OnPropertyChanged(); + ConvertToHsl(); + } + } + + private double green = 0.0d; + public double Green + { + get + { + return this.green; + } + set + { + this.green = value; + OnPropertyChanged(); + ConvertToHsl(); + } + } + + private double hue = 0.0d; + public double Hue + { + get + { + return this.hue; + } + set + { + this.hue = value; + OnPropertyChanged(); + ConvertToRgb(); + } + } + + private double saturation = 1.0d; + public double Saturation + { + get + { + return this.saturation; + } + set + { + this.saturation = value; + OnPropertyChanged(); + ConvertToRgb(); + } + } + + private double lightness = 0.5d; + public double Lightness + { + get + { + return this.lightness; + } + set + { + this.lightness = value; + OnPropertyChanged(); + ConvertToRgb(); + } + } + + private double hexCode; + public double HexCode + { + get + { + return this.hexCode; + } + set + { + this.hexCode = value; + OnPropertyChanged(); + } + } + + private void ConvertToHsl() + { + //Color color = Color.FromRgb(this.Red, this.Blue, this.Green); + //this.Hue = color.Hue; + //this.Saturation = color.Saturation; + //this.Lightness = color.Luminosity; + } + + private void ConvertToRgb() + { + //Color color = Color.FromRgb(this.Hue, this.Saturation, this.Lightness); + //this.Red = color.R; + //this.Blue = color.B; + //this.Green = color.G; + } + + private Color pickedColor; + public Color PickedColor + { + get + { + return this.pickedColor; + } + set + { + this.pickedColor = value; + OnPropertyChanged(); + } + } + + private Color pickedColorWithoutSaturationAndLightness; + public Color PickedColorWithoutSaturationAndLightness + { + get + { + return this.pickedColorWithoutSaturationAndLightness; + } + set + { + this.pickedColorWithoutSaturationAndLightness = value; + OnPropertyChanged(); + } + } + + public double HslPicklerHeight + { + get + { + return this._hslPicker.Height; + } + } + + public double HslPickerWidth + { + get + { + return this._hslPicker.Width; + } + } + + public double RgbPickerHeight + { + get + { + return this._rgbPicker.Height; + } + } + + public double RgbPickerWidth + { + get + { + return this._rgbPicker.Width; + } + } + + /************************************************** + * Hooks + **************************************************/ + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _thumb = (Frame)GetTemplateChild(ElementThumb); + _hslPicker = (OpacityGradientLayout)GetTemplateChild(ElementSaturationAndLightPicker); + _hueThumb = (Frame)GetTemplateChild(ElementHueThumb); + _rgbPicker = (GradientLayout)GetTemplateChild(ElementHuePicker); + + UpdateIsEnabled(); + } + + protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + base.OnPropertyChanged(propertyName); + + if (propertyName == IsEnabledProperty.PropertyName) + UpdateIsEnabled(); + } + + protected override void OnSizeAllocated(double width, double height) + { + base.OnSizeAllocated(width, height); + + UpdateHSLPicker(); + UpdateRGBPicker(); + } + + /************************************************** + * Events + **************************************************/ + public event EventHandler ValueChanged; + + /************************************************** + * Private Methods + **************************************************/ + void UpdateIsEnabled() + { + if (IsEnabled) + { + var panGestureRecognizer = new PanGestureRecognizer(); + panGestureRecognizer.PanUpdated += PanGestureRecognizer_PanUpdated; + _thumb.GestureRecognizers.Add(panGestureRecognizer); + + var panGestureRecognizer2 = new PanGestureRecognizer(); + panGestureRecognizer2.PanUpdated += RgbPanGestureRecognizer_PanUpdated; + _hueThumb.GestureRecognizers.Add(panGestureRecognizer2); + } + else + { + _thumb.GestureRecognizers.Clear(); + _hueThumb.GestureRecognizers.Clear(); + } + } + + void PanGestureRecognizer_PanUpdated(System.Object sender, Xamarin.Forms.PanUpdatedEventArgs e) + { + switch (e.StatusType) + { + case GestureStatus.Started: + _previousPositionX = e.TotalX; + _previousPostionY = e.TotalY; + + if (Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.macOS) + { + _previousPositionX += _thumb.TranslationX; + _previousPostionY += _thumb.TranslationY; + } + break; + case GestureStatus.Running: + double totalX = _previousPositionX + e.TotalX; + double totalY = _previousPostionY + e.TotalY; + + if (Device.RuntimePlatform == Device.Android) + { + totalX += _thumb.TranslationX; + totalY += _thumb.TranslationY; + } + + var positionX = totalX < 0 ? 0 : totalX > _hslPicker.Width - _thumb.Width ? _hslPicker.Width - _thumb.Width : totalX; + ValueXhslThumb = positionX * MaximumX / _hslPicker.Width; + var positionY = totalY < 0 ? 0 : totalY > _hslPicker.Height - _thumb.Height ? _hslPicker.Height - _thumb.Height : totalY; + ValueYhslThumb = positionY * MaximumY / _hslPicker.Height; + break; + case GestureStatus.Completed: + case GestureStatus.Canceled: + break; + } + } + + void RgbPanGestureRecognizer_PanUpdated(System.Object sender, Xamarin.Forms.PanUpdatedEventArgs e) + { + switch (e.StatusType) + { + case GestureStatus.Started: + _hueThumbPreviousPostionY = e.TotalY; + + if (Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.macOS) + { + _hueThumbPreviousPostionY += _hueThumb.TranslationY; + } + break; + case GestureStatus.Running: + double totalY = _hueThumbPreviousPostionY + e.TotalY; + + if (Device.RuntimePlatform == Device.Android) + { + totalY += _hueThumb.TranslationY; + } + + var positionY = totalY < 0 ? 0 : totalY > _rgbPicker.Height - _hueThumb.Height ? _rgbPicker.Height - _hueThumb.Height : totalY; + ValueYrgbThumb = positionY * MaximumY / _rgbPicker.Height; + break; + case GestureStatus.Completed: + case GestureStatus.Canceled: + break; + } + } + + private void CalculatePickedColor() + { + // HUE + double positionY = ValueYrgbThumb / MaximumX * _rgbPicker.Height; + double hue = 360.0d * (positionY / _rgbPicker.Height); + //this.Hue = hue; + + // LIGHTNESS + double positionXhsl = ValueXhslThumb / MaximumX * _hslPicker.Width; + double positionXhslInv = _hslPicker.Width - positionXhsl; + double positionYhsl = ValueYhslThumb / MaximumY * _hslPicker.Height; + double positionYhslInv = _hslPicker.Height - positionYhsl; + double minimumLightness = 50.0d; + double maximumLightness = minimumLightness + (positionXhslInv / _hslPicker.Width * 50.0d); + double lightness = positionYhslInv / _hslPicker.Height * maximumLightness; + //this.Lightness = lightness; + + // SATURATION + double saturation = positionXhsl / _hslPicker.Width * 100; + //this.Saturation = saturation; + + // RED + //this.PickedColor = Color.FromHsla(1.0 / 360 * this.Hue, this.Saturation / 100, this.Lightness / 100); + this.PickedColor = Color.FromHsla(1.0 / 360 * hue, saturation / 100, lightness / 100); + this.PickedColorWithoutSaturationAndLightness = Color.FromHsla(1.0 / 360 * hue, 1.0d, 0.5d); + //this.Red = PickedColor.R; + //this.Blue = PickedColor.B; + //this.Green = PickedColor.G; + } + + void UpdateHSLPicker() + { + var positionX = ValueXhslThumb / MaximumX * _hslPicker.Width; + var positionY = ValueYhslThumb / MaximumX * _hslPicker.Height; + + if (positionX < 0) + positionX = 0; + if (positionY < 0) + positionY = 0; + + _thumb.TranslationX = positionX; + _thumb.TranslationY = positionY; + } + + void UpdateRGBPicker() + { + var positionX = ValueXrgbThumb / MaximumX * _rgbPicker.Width; + var positionY = ValueYrgbThumb / MaximumX * _rgbPicker.Height; + + if (positionX < 0) + positionX = 0; + if (positionY < 0) + positionY = 0; + + _hueThumb.TranslationX = positionX; + _hueThumb.TranslationY = positionY; + } + } +} \ No newline at end of file diff --git a/src/TemplateUI/Controls/ColorPicker/GradientColorStackMode.cs b/src/TemplateUI/Controls/ColorPicker/GradientColorStackMode.cs new file mode 100644 index 0000000..1d39e14 --- /dev/null +++ b/src/TemplateUI/Controls/ColorPicker/GradientColorStackMode.cs @@ -0,0 +1,15 @@ +using System; +namespace TemplateUI.Controls +{ + public enum GradientColorStackMode + { + ToRight, + ToLeft, + ToTop, + ToBottom, + ToTopLeft, + ToTopRight, + ToBottomLeft, + ToBottomRight + } +} diff --git a/src/TemplateUI/Controls/ColorPicker/GradientLayout.cs b/src/TemplateUI/Controls/ColorPicker/GradientLayout.cs new file mode 100644 index 0000000..59bfed6 --- /dev/null +++ b/src/TemplateUI/Controls/ColorPicker/GradientLayout.cs @@ -0,0 +1,28 @@ +using System; +using Xamarin.Forms; + +namespace TemplateUI.Controls +{ + public class GradientLayout : AbsoluteLayout + { + public string ColorsList { get; set; } + + public Color[] Colors + { + get + { + string[] hex = ColorsList.Split(','); + Color[] colors = new Color[hex.Length]; + + for (int i = 0; i < hex.Length; i++) + { + colors[i] = Color.FromHex(hex[i].Trim()); + } + + return colors; + } + } + + public GradientColorStackMode Mode { get; set; } + } +} diff --git a/src/TemplateUI/Controls/ColorPicker/OpacityGradientLayout.cs b/src/TemplateUI/Controls/ColorPicker/OpacityGradientLayout.cs new file mode 100644 index 0000000..e16fb6a --- /dev/null +++ b/src/TemplateUI/Controls/ColorPicker/OpacityGradientLayout.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.CompilerServices; +using Xamarin.Forms; + +namespace TemplateUI.Controls +{ + public class OpacityGradientLayout : AbsoluteLayout + { + public static BindableProperty SelectedColorProperty = + BindableProperty.Create(nameof(SelectedColor), typeof(Color), typeof(OpacityGradientLayout), Color.White, defaultBindingMode: BindingMode.TwoWay, propertyChanged: OnColorChanged); + + static void OnColorChanged(BindableObject bindable, object oldValue, object newValue) + { + if (bindable is OpacityGradientLayout opacityGradientLayout) + { + opacityGradientLayout.ValueChanged?.Invoke(bindable, null); + opacityGradientLayout.SelectedColor = (Color)newValue; + //opacityGradientLayout.OnPropertyChanged(); + } + } + + public event EventHandler ValueChanged; + + public Color SelectedColor + { + get => (Color)GetValue(SelectedColorProperty); + set => SetValue(SelectedColorProperty, value); + } + + public GradientColorStackMode Mode { get; set; } + } +} diff --git a/src/TemplateUI/Converters/ColorConverter.cs b/src/TemplateUI/Converters/ColorConverter.cs new file mode 100644 index 0000000..5a983a7 --- /dev/null +++ b/src/TemplateUI/Converters/ColorConverter.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Xamarin.Forms; + +namespace TemplateUI.Converters +{ + public class ColorConverter : IMultiValueConverter + { + public double PickerWidth { get; set; } + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values == null) + { + return 0; + } + + List valueList = new List(); + valueList.AddRange(values); + double hexCode = values.GetValue(0) != null ? (double)values.GetValue(0) : 0.0d; + double red = values.GetValue(1) != null ? (double)values.GetValue(1) : 0.0d; + double green = values.GetValue(2) != null ? (double)values.GetValue(2) : 0.0d; + double blue = values.GetValue(3) != null ? (double)values.GetValue(3) : 0.0d; + double hue = values.GetValue(4) != null ? (double)values.GetValue(4) : 0.0d; + double saturation = values.GetValue(5) != null ? (double)values.GetValue(5) : 0.0d; + double lightness = values.GetValue(6) != null ? (double)values.GetValue(6) : 0.0d; + //double hslHeight = values.GetValue(7) != null ? (double)values.GetValue(7) : 0.0d; + //double hslWidth = values.GetValue(8) != null ? (double)values.GetValue(8) : 0.0d; + + if (hue != 0) + { + double x = PickerWidth / 360 * hue; + return x; + } + return 20; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/TemplateUI/Converters/ColorNumberConverter.cs b/src/TemplateUI/Converters/ColorNumberConverter.cs new file mode 100644 index 0000000..542d7ef --- /dev/null +++ b/src/TemplateUI/Converters/ColorNumberConverter.cs @@ -0,0 +1,43 @@ +using System; +using System.Globalization; +using Xamarin.Forms; + +namespace TemplateUI.Converters +{ + public class ColorNumberConverter : IValueConverter + { + readonly int maxRGBNumber = 255; + readonly int maxHue = 359; + readonly int maxSaturation = 100; + readonly int maxLightness = 100; + + public object Convert(object value, Type targetType, object parameter, CultureInfo ci) + { + if (value != null && value is double) + { + double doubleValue = (double)value; + switch (parameter) + { + case "R": + case "G": + case "B": + return Math.Round(doubleValue * maxRGBNumber, 0); + case "H": + return Math.Round(doubleValue * maxHue, 0); + case "S": + return Math.Round(doubleValue * maxSaturation, 0); + case "L": + return Math.Round(doubleValue * maxLightness, 0); + default: + return 0.0; + } + } + return 0.0; + } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo ci) + { + Console.WriteLine(value); + return value; + } + } +} diff --git a/src/TemplateUI/TemplateUI.csproj b/src/TemplateUI/TemplateUI.csproj index 2b65112..48d2451 100644 --- a/src/TemplateUI/TemplateUI.csproj +++ b/src/TemplateUI/TemplateUI.csproj @@ -14,10 +14,14 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all + + + + diff --git a/src/TemplateUI/Themes/Generic.xaml b/src/TemplateUI/Themes/Generic.xaml index 95c556a..e7f2530 100644 --- a/src/TemplateUI/Themes/Generic.xaml +++ b/src/TemplateUI/Themes/Generic.xaml @@ -18,6 +18,7 @@ + diff --git a/src/TemplateUI/Themes/Styles/ColorPicker.xaml b/src/TemplateUI/Themes/Styles/ColorPicker.xaml new file mode 100644 index 0000000..afeeee8 --- /dev/null +++ b/src/TemplateUI/Themes/Styles/ColorPicker.xaml @@ -0,0 +1,128 @@ + + + + + + diff --git a/src/TemplateUI/Themes/Styles/ColorPicker.xaml.cs b/src/TemplateUI/Themes/Styles/ColorPicker.xaml.cs new file mode 100644 index 0000000..2cab147 --- /dev/null +++ b/src/TemplateUI/Themes/Styles/ColorPicker.xaml.cs @@ -0,0 +1,12 @@ +using Xamarin.Forms; + +namespace TemplateUI.Themes +{ + public partial class ColorPicker : ResourceDictionary + { + public ColorPicker() + { + InitializeComponent(); + } + } +} From a6f84e2520ceddb3a4954f7b07177a7ff71d6e9d Mon Sep 17 00:00:00 2001 From: Huy Hoang Nguyen Date: Tue, 27 Oct 2020 22:41:33 +0100 Subject: [PATCH 02/11] =?UTF-8?q?Autoren=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Renderers/GradientLayoutRenderer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs b/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs index 201cfa4..d489af8 100644 --- a/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs +++ b/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs @@ -7,6 +7,7 @@ [assembly: ExportRenderer(typeof(GradientLayout), typeof(GradientLayoutRenderer))] +// @author: https://stackoverflow.com/users/9654227/na2axl namespace TemplateUI.Gallery.Droid.Renderers { public class GradientLayoutRenderer : VisualElementRenderer From ce10c81cfccb3935982ec50cb8389e1f23058d27 Mon Sep 17 00:00:00 2001 From: Huy Hoang Nguyen Date: Tue, 27 Oct 2020 22:56:19 +0100 Subject: [PATCH 03/11] [bugfix] Android-Gradienten-Renderer angepasst --- .../Renderers/GradientLayoutRenderer.cs | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs b/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs index d489af8..7b69c7f 100644 --- a/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs +++ b/src/TemplateUI.Gallery.Android/Renderers/GradientLayoutRenderer.cs @@ -5,14 +5,14 @@ using Xamarin.Forms; using Xamarin.Forms.Platform.Android; -[assembly: ExportRenderer(typeof(GradientLayout), typeof(GradientLayoutRenderer))] +[assembly: ExportRenderer(typeof(OpacityGradientLayout), typeof(GradientLayoutRenderer))] // @author: https://stackoverflow.com/users/9654227/na2axl namespace TemplateUI.Gallery.Droid.Renderers { - public class GradientLayoutRenderer : VisualElementRenderer + public class GradientLayoutRenderer : VisualElementRenderer { - private Color[] Colors { get; set; } + private Color SelectedColor { get; set; } private GradientColorStackMode Mode { get; set; } @@ -23,39 +23,34 @@ protected override void DispatchDraw(global::Android.Graphics.Canvas canvas) { Android.Graphics.LinearGradient gradient; - int[] colors = new int[Colors.Length]; - - for (int i = 0, l = Colors.Length; i < l; i++) - { - colors[i] = Colors[i].ToAndroid().ToArgb(); - } + int[] androidColors = new int[] { Color.White.ToAndroid().ToArgb(), SelectedColor.ToAndroid().ToArgb() }; switch (Mode) { default: case GradientColorStackMode.ToRight: - gradient = new Android.Graphics.LinearGradient(0, 0, Width, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + gradient = new Android.Graphics.LinearGradient(0, 0, Width, 0, androidColors, null, Android.Graphics.Shader.TileMode.Mirror); break; case GradientColorStackMode.ToLeft: - gradient = new Android.Graphics.LinearGradient(Width, 0, 0, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + gradient = new Android.Graphics.LinearGradient(Width, 0, 0, 0, androidColors, null, Android.Graphics.Shader.TileMode.Mirror); break; case GradientColorStackMode.ToTop: - gradient = new Android.Graphics.LinearGradient(0, Height, 0, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + gradient = new Android.Graphics.LinearGradient(0, Height, 0, 0, androidColors, null, Android.Graphics.Shader.TileMode.Mirror); break; case GradientColorStackMode.ToBottom: - gradient = new Android.Graphics.LinearGradient(0, 0, 0, Height, colors, null, Android.Graphics.Shader.TileMode.Mirror); + gradient = new Android.Graphics.LinearGradient(0, 0, 0, Height, androidColors, null, Android.Graphics.Shader.TileMode.Mirror); break; case GradientColorStackMode.ToTopLeft: - gradient = new Android.Graphics.LinearGradient(Width, Height, 0, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + gradient = new Android.Graphics.LinearGradient(Width, Height, 0, 0, androidColors, null, Android.Graphics.Shader.TileMode.Mirror); break; case GradientColorStackMode.ToTopRight: - gradient = new Android.Graphics.LinearGradient(0, Height, Width, 0, colors, null, Android.Graphics.Shader.TileMode.Mirror); + gradient = new Android.Graphics.LinearGradient(0, Height, Width, 0, androidColors, null, Android.Graphics.Shader.TileMode.Mirror); break; case GradientColorStackMode.ToBottomLeft: - gradient = new Android.Graphics.LinearGradient(Width, 0, 0, Height, colors, null, Android.Graphics.Shader.TileMode.Mirror); + gradient = new Android.Graphics.LinearGradient(Width, 0, 0, Height, androidColors, null, Android.Graphics.Shader.TileMode.Mirror); break; case GradientColorStackMode.ToBottomRight: - gradient = new Android.Graphics.LinearGradient(0, 0, Width, Height, colors, null, Android.Graphics.Shader.TileMode.Mirror); + gradient = new Android.Graphics.LinearGradient(0, 0, Width, Height, androidColors, null, Android.Graphics.Shader.TileMode.Mirror); break; } @@ -70,7 +65,7 @@ protected override void DispatchDraw(global::Android.Graphics.Canvas canvas) base.DispatchDraw(canvas); } - protected override void OnElementChanged(ElementChangedEventArgs e) + protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); @@ -79,9 +74,9 @@ protected override void OnElementChanged(ElementChangedEventArgs e) try { - if (e.NewElement is GradientLayout layout) + if (e.NewElement is OpacityGradientLayout layout) { - Colors = layout.Colors; + SelectedColor = layout.SelectedColor; Mode = layout.Mode; } } From 7c18c56cb647b4cae9f44058a85e625899f82483 Mon Sep 17 00:00:00 2001 From: Huy Hoang Nguyen Date: Wed, 28 Oct 2020 23:43:38 +0100 Subject: [PATCH 04/11] * ColorNumberHelper.cs: * ColorNumberConverter.cs: * ColorPicker.cs: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ColorPicker.xaml: [feature] Ändern der Farben über Textfelder --- .../Controls/ColorPicker/ColorPicker.cs | 116 ++++++++++++++++++ .../Converters/ColorNumberConverter.cs | 16 +-- src/TemplateUI/Helpers/ColorNumberHelper.cs | 61 +++++++++ src/TemplateUI/Themes/Styles/ColorPicker.xaml | 29 +++-- 4 files changed, 201 insertions(+), 21 deletions(-) create mode 100644 src/TemplateUI/Helpers/ColorNumberHelper.cs diff --git a/src/TemplateUI/Controls/ColorPicker/ColorPicker.cs b/src/TemplateUI/Controls/ColorPicker/ColorPicker.cs index b2b88e8..708038b 100644 --- a/src/TemplateUI/Controls/ColorPicker/ColorPicker.cs +++ b/src/TemplateUI/Controls/ColorPicker/ColorPicker.cs @@ -3,6 +3,8 @@ using System.ComponentModel; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; +using System.Windows.Input; +using TemplateUI.Helpers; using Xamarin.Forms; using Xamarin.Forms.Shapes; @@ -14,6 +16,12 @@ public class ColorPicker : TemplatedView, INotifyPropertyChanged const string ElementSaturationAndLightPicker = "PART_SaturationAndLight"; const string ElementHuePicker = "PART_HuePicker"; const string ElementHueThumb = "PART_HueThumb"; + const string ElementEntryRed = "PART_ENTRY_red"; + const string ElementEntryGreen = "PART_ENTRY_green"; + const string ElementEntryBlue = "PART_ENTRY_blue"; + const string ElementEntryHue = "PART_ENTRY_hue"; + const string ElementEntrySaturation = "PART_ENTRY_saturation"; + const string ElementEntryLuminosity = "PART_ENTRY_luminosity"; const double ExtraLargeSize = 64; const double LargeSize = 48; @@ -31,6 +39,12 @@ public class ColorPicker : TemplatedView, INotifyPropertyChanged Frame _hueThumb; OpacityGradientLayout _hslPicker; GradientLayout _rgbPicker; + Entry _entryRed; + Entry _entryGreen; + Entry _entryBlue; + Entry _entryHue; + Entry _entrySaturation; + Entry _entryLuminosity; double _previousPositionX; double _previousPostionY; @@ -311,6 +325,12 @@ protected override void OnApplyTemplate() _hslPicker = (OpacityGradientLayout)GetTemplateChild(ElementSaturationAndLightPicker); _hueThumb = (Frame)GetTemplateChild(ElementHueThumb); _rgbPicker = (GradientLayout)GetTemplateChild(ElementHuePicker); + _entryRed = (Entry)GetTemplateChild(ElementEntryRed); + _entryGreen = (Entry)GetTemplateChild(ElementEntryGreen); + _entryBlue = (Entry)GetTemplateChild(ElementEntryBlue); + _entryHue = (Entry)GetTemplateChild(ElementEntryHue); + _entrySaturation = (Entry)GetTemplateChild(ElementEntrySaturation); + _entryLuminosity = (Entry)GetTemplateChild(ElementEntryLuminosity); UpdateIsEnabled(); } @@ -350,11 +370,102 @@ void UpdateIsEnabled() var panGestureRecognizer2 = new PanGestureRecognizer(); panGestureRecognizer2.PanUpdated += RgbPanGestureRecognizer_PanUpdated; _hueThumb.GestureRecognizers.Add(panGestureRecognizer2); + + _entryRed.Completed += _entryRed_Completed; + _entryGreen.Completed += _entryGreen_Completed; + _entryBlue.Completed += _entryBlue_Completed; + _entryHue.Completed += _entryHue_Completed; + _entrySaturation.Completed += _entrySaturation_Completed; + _entryLuminosity.Completed += _entryLuminosity_Completed; } else { _thumb.GestureRecognizers.Clear(); _hueThumb.GestureRecognizers.Clear(); + _entryRed.Completed -= _entryRed_Completed; + _entryGreen.Completed -= _entryGreen_Completed; + _entryBlue.Completed -= _entryBlue_Completed; + _entryHue.Completed -= _entryHue_Completed; + _entrySaturation.Completed -= _entrySaturation_Completed; + _entryLuminosity.Completed -= _entryLuminosity_Completed; + } + } + + private void _entryLuminosity_Completed(object sender, EventArgs e) + { + double newValue = 1.0d; + bool parseSuccess = double.TryParse(_entryLuminosity.Text, out newValue); + if (parseSuccess) + { + double convertedNewValue = ColorNumberHelper.FromTargetToSourceLuminosity(newValue); + Color newColor = Color.FromHsla(PickedColor.Hue, PickedColor.Saturation, convertedNewValue); + PickedColor = newColor; + PickedColorWithoutSaturationAndLightness = Color.FromHsla(PickedColor.Hue, 1.0d, 0.5d); + } + } + + private void _entrySaturation_Completed(object sender, EventArgs e) + { + double newValue = 1.0d; + bool parseSuccess = double.TryParse(_entrySaturation.Text, out newValue); + if (parseSuccess) + { + double convertedNewValue = ColorNumberHelper.FromTargetToSourceSaturation(newValue); + Color newColor = Color.FromHsla(PickedColor.Hue, convertedNewValue, PickedColor.Luminosity); + PickedColor = newColor; + PickedColorWithoutSaturationAndLightness = Color.FromHsla(PickedColor.Hue, 1.0d, 0.5d); + } + } + + private void _entryHue_Completed(object sender, EventArgs e) + { + double newValue = 1.0d; + bool parseSuccess = double.TryParse(_entryHue.Text, out newValue); + if (parseSuccess) + { + double convertedNewValue = ColorNumberHelper.FromTargetToSourceHue(newValue); + Color newColor = Color.FromHsla(convertedNewValue, PickedColor.Saturation, PickedColor.Luminosity); + PickedColor = newColor; + PickedColorWithoutSaturationAndLightness = Color.FromHsla(PickedColor.Hue, 1.0d, 0.5d); + } + } + + private void _entryBlue_Completed(object sender, EventArgs e) + { + double newValue = 1.0d; + bool parseSuccess = double.TryParse(_entryBlue.Text, out newValue); + if (parseSuccess) + { + double convertedNewValue = ColorNumberHelper.FromTargetToSourceRGB(newValue); + Color newColor = Color.FromRgb(PickedColor.R, PickedColor.G, convertedNewValue); + PickedColor = newColor; + PickedColorWithoutSaturationAndLightness = Color.FromHsla(PickedColor.Hue, 1.0d, 0.5d); + } + } + + private void _entryGreen_Completed(object sender, EventArgs e) + { + double newValue = 1.0d; + bool parseSuccess = double.TryParse(_entryGreen.Text, out newValue); + if (parseSuccess) + { + double convertedNewValue = ColorNumberHelper.FromTargetToSourceRGB(newValue); + Color newColor = Color.FromRgb(PickedColor.R, convertedNewValue, PickedColor.B); + PickedColor = newColor; + PickedColorWithoutSaturationAndLightness = Color.FromHsla(PickedColor.Hue, 1.0d, 0.5d); + } + } + + private void _entryRed_Completed(object sender, EventArgs e) + { + double newValue = 1.0d; + bool parseSuccess = double.TryParse(_entryRed.Text, out newValue); + if (parseSuccess) + { + double convertedNewValue = ColorNumberHelper.FromTargetToSourceRGB(newValue); + Color newColor = Color.FromRgb(convertedNewValue, PickedColor.G, PickedColor.B); + PickedColor = newColor; + PickedColorWithoutSaturationAndLightness = Color.FromHsla(PickedColor.Hue, 1.0d, 0.5d); } } @@ -422,6 +533,11 @@ void RgbPanGestureRecognizer_PanUpdated(System.Object sender, Xamarin.Forms.PanU } } + private void CalculatePickedColorFromEntries() + { + this.PickedColor = PickedColor; + } + private void CalculatePickedColor() { // HUE diff --git a/src/TemplateUI/Converters/ColorNumberConverter.cs b/src/TemplateUI/Converters/ColorNumberConverter.cs index 542d7ef..2dce6a6 100644 --- a/src/TemplateUI/Converters/ColorNumberConverter.cs +++ b/src/TemplateUI/Converters/ColorNumberConverter.cs @@ -1,16 +1,12 @@ using System; using System.Globalization; +using TemplateUI.Helpers; using Xamarin.Forms; namespace TemplateUI.Converters { public class ColorNumberConverter : IValueConverter { - readonly int maxRGBNumber = 255; - readonly int maxHue = 359; - readonly int maxSaturation = 100; - readonly int maxLightness = 100; - public object Convert(object value, Type targetType, object parameter, CultureInfo ci) { if (value != null && value is double) @@ -21,13 +17,13 @@ public object Convert(object value, Type targetType, object parameter, CultureIn case "R": case "G": case "B": - return Math.Round(doubleValue * maxRGBNumber, 0); + return Math.Round(ColorNumberHelper.FromSourceToTargetRGB(doubleValue), 0); case "H": - return Math.Round(doubleValue * maxHue, 0); + return Math.Round(ColorNumberHelper.FromSourceToTargetHue(doubleValue), 0); case "S": - return Math.Round(doubleValue * maxSaturation, 0); + return Math.Round(ColorNumberHelper.FromSourceToTargetSaturation(doubleValue), 0); case "L": - return Math.Round(doubleValue * maxLightness, 0); + return Math.Round(ColorNumberHelper.FromSourceToTargetLuminosity(doubleValue), 0); default: return 0.0; } @@ -36,7 +32,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo ci) { - Console.WriteLine(value); + Console.WriteLine($"Convert Back: {value}"); return value; } } diff --git a/src/TemplateUI/Helpers/ColorNumberHelper.cs b/src/TemplateUI/Helpers/ColorNumberHelper.cs new file mode 100644 index 0000000..a1b947e --- /dev/null +++ b/src/TemplateUI/Helpers/ColorNumberHelper.cs @@ -0,0 +1,61 @@ +using System; +namespace TemplateUI.Helpers +{ + public class ColorNumberHelper + { + readonly static int maxRGB = 255; + readonly static int maxHue = 359; + readonly static int maxSaturation = 100; + readonly static int maxLuminosity = 100; + readonly static double maxInterValue = 1.0d; + + private ColorNumberHelper() + { + // UTILITY + } + + public static double FromSourceToTargetRGB(double sourceValue) + { + return sourceValue * maxRGB; + } + + public static double FromSourceToTargetHue(double sourceValue) + { + return sourceValue * maxHue; + } + + public static double FromSourceToTargetSaturation(double sourceValue) + { + return sourceValue * maxSaturation; + } + + public static double FromSourceToTargetLuminosity(double sourceValue) + { + return sourceValue * maxLuminosity; + } + + public static double FromTargetToSourceRGB(double targetValue) + { + targetValue = targetValue > maxRGB ? maxRGB : targetValue; + return maxInterValue / maxRGB * targetValue; + } + + public static double FromTargetToSourceHue(double targetValue) + { + targetValue = targetValue > maxHue ? maxHue : targetValue; + return maxInterValue / maxHue * targetValue; + } + + public static double FromTargetToSourceSaturation(double targetValue) + { + targetValue = targetValue > maxSaturation ? maxSaturation : targetValue; + return maxInterValue / maxSaturation * targetValue; + } + + public static double FromTargetToSourceLuminosity(double targetValue) + { + targetValue = targetValue > maxLuminosity ? maxLuminosity : targetValue; + return maxInterValue / maxLuminosity * targetValue; + } + } +} diff --git a/src/TemplateUI/Themes/Styles/ColorPicker.xaml b/src/TemplateUI/Themes/Styles/ColorPicker.xaml index afeeee8..1d5192b 100644 --- a/src/TemplateUI/Themes/Styles/ColorPicker.xaml +++ b/src/TemplateUI/Themes/Styles/ColorPicker.xaml @@ -21,7 +21,7 @@ - + - + - +