From 2f9286e39da2cd3dcfe92e5a0c327b80b0625029 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sun, 4 Feb 2018 15:48:11 +0100 Subject: [PATCH] Added color-settings to resolve #2 --- KeyboardAudioVisualizer/App.xaml.cs | 8 +- KeyboardAudioVisualizer/ApplicationManager.cs | 28 +- .../Configuration/AbstractConfiguration.cs | 13 - .../Configuration/ColorSerializer.cs | 41 ++ .../Configuration/IConfiguration.cs | 5 +- .../Configuration/Settings.cs | 16 + .../Controls/ColorSelector.cs | 483 ++++++++++++++++++ .../Controls/GradientEditor.cs | 423 +++++++++++++++ .../Decorators/LevelBarDecorator.cs | 10 +- .../KeyboardAudioVisualizer.csproj | 62 ++- .../Legacy/ConfigurationUpdates.cs | 37 ++ .../Resources/KeyboardAudioVisualizer.xaml | 2 + .../Styles/ColorSelector.xaml | 305 +++++++++++ .../Styles/GradientEditor.xaml | 93 ++++ .../UI/Configuration/LevelConfiguration.xaml | 2 +- .../UI/ConfigurationWindow.xaml | 21 +- .../UI/Visualization/BeatVisualization.xaml | 4 +- .../UI/Visualization/BeatVisualizer.cs | 52 +- .../FrequencyBarsVisualization.xaml | 7 +- .../Visualization/FrequencyBarsVisualizer.cs | 41 +- .../UI/Visualization/LevelVisualization.xaml | 4 +- .../UI/Visualization/LevelVisualizer.cs | 55 +- KeyboardAudioVisualizer/packages.config | 30 +- 23 files changed, 1646 insertions(+), 96 deletions(-) create mode 100644 KeyboardAudioVisualizer/Configuration/ColorSerializer.cs create mode 100644 KeyboardAudioVisualizer/Controls/ColorSelector.cs create mode 100644 KeyboardAudioVisualizer/Controls/GradientEditor.cs create mode 100644 KeyboardAudioVisualizer/Legacy/ConfigurationUpdates.cs create mode 100644 KeyboardAudioVisualizer/Styles/ColorSelector.xaml create mode 100644 KeyboardAudioVisualizer/Styles/GradientEditor.xaml diff --git a/KeyboardAudioVisualizer/App.xaml.cs b/KeyboardAudioVisualizer/App.xaml.cs index 7ebe61d..a54e673 100644 --- a/KeyboardAudioVisualizer/App.xaml.cs +++ b/KeyboardAudioVisualizer/App.xaml.cs @@ -4,6 +4,7 @@ using System.Windows; using System.Windows.Controls; using Hardcodet.Wpf.TaskbarNotification; using KeyboardAudioVisualizer.AudioProcessing; +using KeyboardAudioVisualizer.Configuration; using KeyboardAudioVisualizer.Helper; using KeyboardAudioVisualizer.Legacy; using Newtonsoft.Json; @@ -40,7 +41,7 @@ namespace KeyboardAudioVisualizer //Settings settings = SerializationHelper.LoadObjectFromFile(PATH_SETTINGS); Settings settings = null; - try { settings = JsonConvert.DeserializeObject(File.ReadAllText(PATH_SETTINGS)); } + try { settings = JsonConvert.DeserializeObject(File.ReadAllText(PATH_SETTINGS), new ColorSerializer()); } catch (Exception ex) { Console.WriteLine(ex.Message); @@ -55,6 +56,9 @@ namespace KeyboardAudioVisualizer settings = new Settings(); _taskbarIcon.ShowBalloonTip("Keyboard Audio-Visualizer is starting in the tray!", "Click on the icon to open the configuration.", BalloonIcon.Info); } + + ConfigurationUpdates.PerformOn(settings); + ApplicationManager.Instance.Settings = settings; AudioVisualizationFactory.Initialize(); @@ -74,7 +78,7 @@ namespace KeyboardAudioVisualizer { base.OnExit(e); - File.WriteAllText(PATH_SETTINGS, JsonConvert.SerializeObject(ApplicationManager.Instance.Settings)); + File.WriteAllText(PATH_SETTINGS, JsonConvert.SerializeObject(ApplicationManager.Instance.Settings, new ColorSerializer())); ConfigurationMigrator.CleanupOldConfigs(); } diff --git a/KeyboardAudioVisualizer/ApplicationManager.cs b/KeyboardAudioVisualizer/ApplicationManager.cs index 97457b5..55b6589 100644 --- a/KeyboardAudioVisualizer/ApplicationManager.cs +++ b/KeyboardAudioVisualizer/ApplicationManager.cs @@ -75,6 +75,10 @@ namespace KeyboardAudioVisualizer ILedGroup background = new ListLedGroup(surface.Leds); background.Brush = new SolidColorBrush(new Color(64, 0, 0, 0)); //TODO DarthAffe 06.08.2017: A-Channel gives some kind of blur - settings! + LinearGradient primaryGradient = Settings[VisualizationIndex.Primary].Gradient; + LinearGradient secondaryGradient = Settings[VisualizationIndex.Primary].Gradient; + LinearGradient tertiaryGradient = Settings[VisualizationIndex.Primary].Gradient; + List<(ILedGroup, GetDecoratorFunc)> primaryGroups = new List<(ILedGroup, GetDecoratorFunc)>(); List<(ILedGroup, GetDecoratorFunc)> secondaryGroups = new List<(ILedGroup, GetDecoratorFunc)>(); List<(ILedGroup, GetDecoratorFunc)> tertiaryGroups = new List<(ILedGroup, GetDecoratorFunc)>(); @@ -90,42 +94,38 @@ namespace KeyboardAudioVisualizer { primary.RemoveLeds(lightbar.Leds); - IGradient keyboardLevelGradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)), new GradientStop(1, new Color(255, 0, 0))); - ILedGroup lightbarLeft = new ListLedGroup(lightbar.Left); - lightbarLeft.Brush = new LinearGradientBrush(new Point(1.0, 0.5), new Point(0.0, 0.5), keyboardLevelGradient); + lightbarLeft.Brush = new LinearGradientBrush(new Point(1.0, 0.5), new Point(0.0, 0.5), tertiaryGradient); tertiaryGroups.Add((lightbarLeft, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Left, 0))); ILedGroup lightbarRight = new ListLedGroup(lightbar.Right); - lightbarRight.Brush = new LinearGradientBrush(keyboardLevelGradient); + lightbarRight.Brush = new LinearGradientBrush(tertiaryGradient); tertiaryGroups.Add((lightbarRight, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Right, 1))); ILedGroup lightbarCenter = new ListLedGroup(lightbar.Center); - lightbarCenter.Brush = new SolidColorBrush(new Color(255, 255, 255)); + lightbarCenter.Brush = new LinearGradientBrush(secondaryGradient); secondaryGroups.Add((lightbarCenter, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer))); } - primary.Brush = new LinearGradientBrush(new RainbowGradient(300, -14)); - primaryGroups.Add((primary, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Horizontal))); + primary.Brush = new LinearGradientBrush(primaryGradient); + primaryGroups.Add((primary, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Horizontal, 0, primaryGradient))); break; case RGBDeviceType.Mousepad: case RGBDeviceType.LedStripe: - IGradient mousepadLevelGradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)), new GradientStop(1, new Color(255, 0, 0))); - ILedGroup left = new RectangleLedGroup(new Rectangle(device.Location.X, device.Location.Y, device.Size.Width / 2.0, device.Size.Height)); - left.Brush = new LinearGradientBrush(new Point(0.5, 1), new Point(0.5, 0), mousepadLevelGradient); + left.Brush = new LinearGradientBrush(new Point(0.5, 1), new Point(0.5, 0), tertiaryGradient); tertiaryGroups.Add((left, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Top, 0))); ILedGroup right = new RectangleLedGroup(new Rectangle(device.Location.X + (device.Size.Width / 2.0), device.Location.Y, device.Size.Width / 2.0, device.Size.Height)); - right.Brush = new LinearGradientBrush(new Point(0.5, 1), new Point(0.5, 0), mousepadLevelGradient); + right.Brush = new LinearGradientBrush(new Point(0.5, 1), new Point(0.5, 0), tertiaryGradient); tertiaryGroups.Add((right, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer, LevelBarDirection.Top, 1))); break; case RGBDeviceType.Mouse: case RGBDeviceType.Headset: ILedGroup deviceGroup = new ListLedGroup(device); - deviceGroup.Brush = new SolidColorBrush(new Color(255, 255, 255)); + deviceGroup.Brush = new LinearGradientBrush(secondaryGradient); secondaryGroups.Add((deviceGroup, (visualizationType, visualizer) => CreateDecorator(visualizationType, visualizer))); break; } @@ -167,13 +167,13 @@ namespace KeyboardAudioVisualizer } } - private IBrushDecorator CreateDecorator(VisualizationType visualizationType, IVisualizationProvider visualizationProvider, LevelBarDirection direction = LevelBarDirection.Top, int dataIndex = 0) + private IBrushDecorator CreateDecorator(VisualizationType visualizationType, IVisualizationProvider visualizationProvider, LevelBarDirection direction = LevelBarDirection.Top, int dataIndex = 0, LinearGradient gradient = null) { if (visualizationType == VisualizationType.FrequencyBars) return new FrequencyBarsDecorator(visualizationProvider); if (visualizationType == VisualizationType.Level) - return new LevelBarDecorator(visualizationProvider, direction, dataIndex); + return new LevelBarDecorator(visualizationProvider, direction, dataIndex, gradient); if (visualizationType == VisualizationType.Beat) return new BeatDecorator(visualizationProvider); diff --git a/KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs b/KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs index 6621774..e918473 100644 --- a/KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs +++ b/KeyboardAudioVisualizer/Configuration/AbstractConfiguration.cs @@ -1,25 +1,12 @@ using System; using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Xml.Serialization; using RGB.NET.Core; namespace KeyboardAudioVisualizer.Configuration { public class AbstractConfiguration : AbstractBindable, IConfiguration, INotifyPropertyChanged { - #region Properties & Fields - - private IBrush _brush; - [XmlIgnore] - public IBrush Brush - { - get => _brush; - set => SetProperty(ref _brush, value); - } - - #endregion - #region Methods protected override bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) diff --git a/KeyboardAudioVisualizer/Configuration/ColorSerializer.cs b/KeyboardAudioVisualizer/Configuration/ColorSerializer.cs new file mode 100644 index 0000000..8bff582 --- /dev/null +++ b/KeyboardAudioVisualizer/Configuration/ColorSerializer.cs @@ -0,0 +1,41 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using RGB.NET.Core; + +namespace KeyboardAudioVisualizer.Configuration +{ + public class ColorSerializer : JsonConverter + { + #region Methods + + public override bool CanConvert(Type objectType) => objectType == typeof(Color); + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (!(value is Color color)) return; + + writer.WriteStartObject(); + writer.WritePropertyName("A"); + writer.WriteValue(color.A); + writer.WritePropertyName("R"); + writer.WriteValue(color.R); + writer.WritePropertyName("G"); + writer.WriteValue(color.G); + writer.WritePropertyName("B"); + writer.WriteValue(color.B); + writer.WriteEndObject(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JObject jsonObject = JObject.Load(reader); + return new Color(jsonObject.Property("A").Value.ToObject(), + jsonObject.Property("R").Value.ToObject(), + jsonObject.Property("G").Value.ToObject(), + jsonObject.Property("B").Value.ToObject()); + } + + #endregion + } +} diff --git a/KeyboardAudioVisualizer/Configuration/IConfiguration.cs b/KeyboardAudioVisualizer/Configuration/IConfiguration.cs index 1d62075..d89f22e 100644 --- a/KeyboardAudioVisualizer/Configuration/IConfiguration.cs +++ b/KeyboardAudioVisualizer/Configuration/IConfiguration.cs @@ -1,10 +1,7 @@ using System.ComponentModel; -using RGB.NET.Core; namespace KeyboardAudioVisualizer.Configuration { public interface IConfiguration : INotifyPropertyChanged - { - IBrush Brush { get; set; } - } + { } } diff --git a/KeyboardAudioVisualizer/Configuration/Settings.cs b/KeyboardAudioVisualizer/Configuration/Settings.cs index a60e45a..59eb7d2 100644 --- a/KeyboardAudioVisualizer/Configuration/Settings.cs +++ b/KeyboardAudioVisualizer/Configuration/Settings.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider; using KeyboardAudioVisualizer.Helper; +using RGB.NET.Brushes.Gradients; +using RGB.NET.Core; namespace KeyboardAudioVisualizer.Configuration { @@ -9,6 +11,8 @@ namespace KeyboardAudioVisualizer.Configuration { #region Properties & Fields + public int Version { get; set; } = 0; + public double UpdateRate { get; set; } = 40.0; public Dictionary Visualizations { get; set; } = new Dictionary(); @@ -32,6 +36,8 @@ namespace KeyboardAudioVisualizer.Configuration public VisualizationType SelectedVisualization { get; set; } + public LinearGradient Gradient { get; set; } + public EqualizerConfiguration EqualizerConfiguration { get; set; } = new EqualizerConfiguration(); public FrequencyBarsVisualizationProviderConfiguration FrequencyBarsConfiguration { get; set; } = new FrequencyBarsVisualizationProviderConfiguration(); @@ -72,14 +78,24 @@ namespace KeyboardAudioVisualizer.Configuration { case VisualizationIndex.Primary: SelectedVisualization = VisualizationType.FrequencyBars; + Gradient = new LinearGradient(new GradientStop(0, Color.FromHSV(300, 1, 1)), + new GradientStop(0.20, Color.FromHSV(225, 1, 1)), + new GradientStop(0.35, Color.FromHSV(180, 1, 1)), + new GradientStop(0.50, Color.FromHSV(135, 1, 1)), + new GradientStop(0.65, Color.FromHSV(90, 1, 1)), + new GradientStop(0.80, Color.FromHSV(45, 1, 1)), + new GradientStop(0.95, Color.FromHSV(0, 1, 1))); break; case VisualizationIndex.Secondary: SelectedVisualization = VisualizationType.Beat; + Gradient = new LinearGradient(new GradientStop(0.5, new Color(255, 255, 255))); break; case VisualizationIndex.Tertiary: SelectedVisualization = VisualizationType.Level; + Gradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)), + new GradientStop(1, new Color(255, 0, 0))); break; } } diff --git a/KeyboardAudioVisualizer/Controls/ColorSelector.cs b/KeyboardAudioVisualizer/Controls/ColorSelector.cs new file mode 100644 index 0000000..8fdf0f3 --- /dev/null +++ b/KeyboardAudioVisualizer/Controls/ColorSelector.cs @@ -0,0 +1,483 @@ +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; +using RGB.NET.Core; +using Color = RGB.NET.Core.Color; +using Point = System.Windows.Point; +using Rectangle = System.Windows.Shapes.Rectangle; +using WpfColor = System.Windows.Media.Color; + +namespace KeyboardAudioVisualizer.Controls +{ + [TemplatePart(Name = "PART_Selector", Type = typeof(Panel))] + [TemplatePart(Name = "PART_SliderAlpha", Type = typeof(Slider))] + [TemplatePart(Name = "PART_SliderRed", Type = typeof(Slider))] + [TemplatePart(Name = "PART_SliderGreen", Type = typeof(Slider))] + [TemplatePart(Name = "PART_SliderBlue", Type = typeof(Slider))] + [TemplatePart(Name = "PART_SliderHue", Type = typeof(Slider))] + [TemplatePart(Name = "PART_SliderSaturation", Type = typeof(Slider))] + [TemplatePart(Name = "PART_SliderValue", Type = typeof(Slider))] + [TemplatePart(Name = "PART_Preview", Type = typeof(Rectangle))] + public class ColorSelector : Control + { + #region Properties & Fields + + private bool _ignorePropertyChanged; + private bool _dragSelector; + + private byte _a; + private byte _r; + private byte _g; + private byte _b; + private double _hue; + private double _saturation; + private double _value; + + private Panel _selector; + private Rectangle _selectorColor; + private Grid _selectorGrip; + private Slider _sliderAlpha; + private Slider _sliderRed; + private Slider _sliderGreen; + private Slider _sliderBlue; + private Slider _sliderHue; + private Slider _sliderSaturation; + private Slider _sliderValue; + private Rectangle _preview; + + private SolidColorBrush _previewBrush; + private SolidColorBrush _selectorBrush; + private LinearGradientBrush _alphaBrush; + private LinearGradientBrush _redBrush; + private LinearGradientBrush _greenBrush; + private LinearGradientBrush _blueBrush; + private LinearGradientBrush _hueBrush; + private LinearGradientBrush _saturationBrush; + private LinearGradientBrush _valueBrush; + + #endregion + + #region DependencyProperties + + public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register( + "SelectedColor", typeof(Color), typeof(ColorSelector), new FrameworkPropertyMetadata(new Color(255, 0, 0), + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + SelectedColorChanged)); + + public Color SelectedColor + { + get => (Color)GetValue(SelectedColorProperty); + set => SetValue(SelectedColorProperty, value); + } + + #endregion + + #region Methods + + public override void OnApplyTemplate() + { + if ((_selector = GetTemplateChild("PART_Selector") as Panel) != null) + { + _selectorBrush = new SolidColorBrush(); + _selectorColor = new Rectangle + { + VerticalAlignment = VerticalAlignment.Stretch, + HorizontalAlignment = HorizontalAlignment.Stretch, + SnapsToDevicePixels = true, + StrokeThickness = 0, + Fill = _selectorBrush + }; + _selector.Children.Add(_selectorColor); + + Rectangle selectorWhite = new Rectangle + { + VerticalAlignment = VerticalAlignment.Stretch, + HorizontalAlignment = HorizontalAlignment.Stretch, + SnapsToDevicePixels = true, + StrokeThickness = 0, + Fill = new LinearGradientBrush(WpfColor.FromRgb(255, 255, 255), WpfColor.FromArgb(0, 255, 255, 255), new Point(0, 0.5), new Point(1, 0.5)) + }; + _selector.Children.Add(selectorWhite); + + Rectangle selectorBlack = new Rectangle + { + VerticalAlignment = VerticalAlignment.Stretch, + HorizontalAlignment = HorizontalAlignment.Stretch, + SnapsToDevicePixels = true, + StrokeThickness = 0, + Fill = new LinearGradientBrush(WpfColor.FromRgb(0, 0, 0), WpfColor.FromArgb(0, 0, 0, 0), new Point(0.5, 1), new Point(0.5, 0)) + }; + _selector.Children.Add(selectorBlack); + + _selectorGrip = new Grid + { + VerticalAlignment = VerticalAlignment.Bottom, + HorizontalAlignment = HorizontalAlignment.Left, + SnapsToDevicePixels = true + }; + _selectorGrip.Children.Add(new Ellipse + { + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + SnapsToDevicePixels = true, + Stroke = new SolidColorBrush(WpfColor.FromRgb(0, 0, 0)), + StrokeThickness = 2, + Fill = null, + Width = 12, + Height = 12 + }); + _selectorGrip.Children.Add(new Ellipse + { + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + SnapsToDevicePixels = true, + Stroke = new SolidColorBrush(WpfColor.FromRgb(255, 255, 255)), + StrokeThickness = 1, + Fill = null, + Width = 10, + Height = 10 + }); + _selector.Children.Add(_selectorGrip); + + _selector.SizeChanged += (sender, args) => UpdateSelector(); + _selector.MouseLeftButtonDown += (sender, args) => + { + _dragSelector = true; + UpdateSelectorValue(args.GetPosition(_selector)); + }; + _selector.MouseEnter += (sender, args) => + { + if (args.LeftButton == MouseButtonState.Pressed) + { + _dragSelector = true; + UpdateSelectorValue(args.GetPosition(_selector)); + } + }; + _selector.MouseLeftButtonUp += (sender, args) => _dragSelector = false; + _selector.MouseLeave += (sender, args) => _dragSelector = false; + _selector.MouseMove += (sender, args) => UpdateSelectorValue(args.GetPosition(_selector)); + _selector.ClipToBounds = true; + } + + if ((_sliderAlpha = GetTemplateChild("PART_SliderAlpha") as Slider) != null) + { + _alphaBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0), + new GradientStop(new WpfColor(), 1) })); + _sliderAlpha.Background = _alphaBrush; + _sliderAlpha.ValueChanged += AChanged; + } + + if ((_sliderRed = GetTemplateChild("PART_SliderRed") as Slider) != null) + { + _redBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0), + new GradientStop(new WpfColor(), 1) })); + _sliderRed.Background = _redBrush; + _sliderRed.ValueChanged += RChanged; + } + + if ((_sliderGreen = GetTemplateChild("PART_SliderGreen") as Slider) != null) + { + _greenBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0), + new GradientStop(new WpfColor(), 1) })); + _sliderGreen.Background = _greenBrush; + _sliderGreen.ValueChanged += GChanged; + } + + if ((_sliderBlue = GetTemplateChild("PART_SliderBlue") as Slider) != null) + { + _blueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0), + new GradientStop(new WpfColor(), 1) })); + _sliderBlue.Background = _blueBrush; + _sliderBlue.ValueChanged += BChanged; + } + + if ((_sliderHue = GetTemplateChild("PART_SliderHue") as Slider) != null) + { + _hueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0), + new GradientStop(new WpfColor(), 1.0 / 6.0), + new GradientStop(new WpfColor(), 2.0 / 6.0), + new GradientStop(new WpfColor(), 3.0 / 6.0), + new GradientStop(new WpfColor(), 4.0 / 6.0), + new GradientStop(new WpfColor(), 5.0 / 6.0), + new GradientStop(new WpfColor(), 1) })); + _sliderHue.Background = _hueBrush; + _sliderHue.ValueChanged += HueChanged; + } + + if ((_sliderSaturation = GetTemplateChild("PART_SliderSaturation") as Slider) != null) + { + _saturationBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0), + new GradientStop(new WpfColor(), 1) })); + _sliderSaturation.Background = _saturationBrush; + _sliderSaturation.ValueChanged += SaturationChanged; + } + + if ((_sliderValue = GetTemplateChild("PART_SliderValue") as Slider) != null) + { + _valueBrush = new LinearGradientBrush(new GradientStopCollection(new[] { new GradientStop(new WpfColor(), 0), + new GradientStop(new WpfColor(), 1) })); + _sliderValue.Background = _valueBrush; + _sliderValue.ValueChanged += ValueChanged; + } + + if ((_preview = GetTemplateChild("PART_Preview") as Rectangle) != null) + { + _previewBrush = new SolidColorBrush(); + _preview.Fill = _previewBrush; + } + + SetColor(SelectedColor); + } + + private static void SelectedColorChanged(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (!(dependencyObject is ColorSelector cs) || !(dependencyPropertyChangedEventArgs.NewValue is Color color)) return; + cs.SetColor(color); + } + + private void SetColor(Color color) + { + if (_ignorePropertyChanged) return; + + SetA(color); + SetRGB(color); + SetHSV(color); + + UpdateSelector(); + UpdateUIColors(); + } + + private void AChanged(object sender, RoutedPropertyChangedEventArgs routedPropertyChangedEventArgs) + { + if (_ignorePropertyChanged) return; + + _a = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue); + Color color = new Color(_a, _r, _g, _b); + UpdateSelectedColor(color); + UpdateUIColors(); + UpdateSelector(); + } + + private void RChanged(object sender, RoutedPropertyChangedEventArgs routedPropertyChangedEventArgs) + { + if (_ignorePropertyChanged) return; + + _r = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue); + RGBChanged(); + } + + private void GChanged(object sender, RoutedPropertyChangedEventArgs routedPropertyChangedEventArgs) + { + if (_ignorePropertyChanged) return; + + _g = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue); + RGBChanged(); + } + + private void BChanged(object sender, RoutedPropertyChangedEventArgs routedPropertyChangedEventArgs) + { + if (_ignorePropertyChanged) return; + + _b = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue); + RGBChanged(); + } + + private void RGBChanged() + { + Color color = new Color(_a, _r, _g, _b); + UpdateSelectedColor(color); + SetHSV(color); + UpdateUIColors(); + UpdateSelector(); + } + + private void HueChanged(object sender, RoutedPropertyChangedEventArgs routedPropertyChangedEventArgs) + { + if (_ignorePropertyChanged) return; + + _hue = routedPropertyChangedEventArgs.NewValue.Clamp(0, 360); + HSVChanged(); + } + + private void SaturationChanged(object sender, RoutedPropertyChangedEventArgs routedPropertyChangedEventArgs) + { + if (_ignorePropertyChanged) return; + + _saturation = routedPropertyChangedEventArgs.NewValue.Clamp(0, 1); + HSVChanged(); + } + + private void ValueChanged(object sender, RoutedPropertyChangedEventArgs routedPropertyChangedEventArgs) + { + if (_ignorePropertyChanged) return; + + _value = routedPropertyChangedEventArgs.NewValue.Clamp(0, 1); + HSVChanged(); + } + + private void HSVChanged() + { + Color color = Color.FromHSV(_a, _hue, _saturation, _value); + UpdateSelectedColor(color); + SetRGB(color); + UpdateUIColors(); + UpdateSelector(); + } + + private void SetA(Color color) + { + _ignorePropertyChanged = true; + + _a = color.A; + if (_sliderAlpha != null) + _sliderAlpha.Value = _a; + + _ignorePropertyChanged = false; + } + + private void SetRGB(Color color) + { + _ignorePropertyChanged = true; + + _r = color.R; + if (_sliderRed != null) + _sliderRed.Value = _r; + + _g = color.G; + if (_sliderGreen != null) + _sliderGreen.Value = _g; + + _b = color.B; + if (_sliderBlue != null) + _sliderBlue.Value = _b; + + _ignorePropertyChanged = false; + } + + private void SetHSV(Color color) + { + _ignorePropertyChanged = true; + + _hue = color.Hue; + if (_sliderHue != null) + _sliderHue.Value = _hue; + + _saturation = color.Saturation; + if (_sliderSaturation != null) + _sliderSaturation.Value = _saturation; + + _value = color.Value; + if (_sliderValue != null) + _sliderValue.Value = _value; + + _ignorePropertyChanged = false; + } + + private void UpdateSelectedColor(Color color) + { + _ignorePropertyChanged = true; + + SelectedColor = color; + + _ignorePropertyChanged = false; + } + + private void UpdateSelector() + { + if (_selector == null) return; + + double selectorX = (_selector.ActualWidth * _saturation) - (_selectorGrip.ActualWidth / 2); + double selectorY = (_selector.ActualHeight * _value) - (_selectorGrip.ActualHeight / 2); + if (!double.IsNaN(selectorX) && !double.IsNaN(selectorY)) + _selectorGrip.Margin = new Thickness(selectorX, 0, 0, selectorY); + } + + private void UpdateSelectorValue(Point mouseLocation) + { + if (!_dragSelector) return; + + double saturation = mouseLocation.X / _selector.ActualWidth; + double value = 1 - (mouseLocation.Y / _selector.ActualHeight); + if (!double.IsNaN(saturation) && !double.IsNaN(value)) + { + _saturation = saturation; + _value = value; + HSVChanged(); + } + } + + private void UpdateUIColors() + { + Color hueColor = Color.FromHSV(_hue, 1, 1); + + if (_previewBrush != null) + _previewBrush.Color = WpfColor.FromArgb(_a, _r, _g, _b); + + if (_selectorBrush != null) + _selectorBrush.Color = WpfColor.FromRgb(hueColor.R, hueColor.G, hueColor.B); + + if (_alphaBrush != null) + { + _alphaBrush.GradientStops[0].Color = WpfColor.FromArgb(0, _r, _g, _b); + _alphaBrush.GradientStops[1].Color = WpfColor.FromArgb(255, _r, _g, _b); + } + + if (_redBrush != null) + { + _redBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 0, _g, _b); + _redBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, 255, _g, _b); + } + + if (_greenBrush != null) + { + _greenBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, _r, 0, _b); + _greenBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, _r, 255, _b); + } + + if (_blueBrush != null) + { + _blueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, _r, _g, 0); + _blueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, _r, _g, 255); + } + + if (_hueBrush != null) + { + Color referenceColor1 = Color.FromHSV(0, _saturation, _value); + Color referenceColor2 = Color.FromHSV(60, _saturation, _value); + Color referenceColor3 = Color.FromHSV(120, _saturation, _value); + Color referenceColor4 = Color.FromHSV(180, _saturation, _value); + Color referenceColor5 = Color.FromHSV(240, _saturation, _value); + Color referenceColor6 = Color.FromHSV(300, _saturation, _value); + + _hueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, referenceColor1.R, referenceColor1.G, referenceColor1.B); + _hueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor2.R, referenceColor2.G, referenceColor2.B); + _hueBrush.GradientStops[2].Color = WpfColor.FromArgb(_a, referenceColor3.R, referenceColor3.G, referenceColor3.B); + _hueBrush.GradientStops[3].Color = WpfColor.FromArgb(_a, referenceColor4.R, referenceColor4.G, referenceColor4.B); + _hueBrush.GradientStops[4].Color = WpfColor.FromArgb(_a, referenceColor5.R, referenceColor5.G, referenceColor5.B); + _hueBrush.GradientStops[5].Color = WpfColor.FromArgb(_a, referenceColor6.R, referenceColor6.G, referenceColor6.B); + _hueBrush.GradientStops[6].Color = WpfColor.FromArgb(_a, referenceColor1.R, referenceColor1.G, referenceColor1.B); + } + + if (_saturationBrush != null) + { + Color referenceColor = Color.FromHSV(_hue, 1, _value); + + _saturationBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 255, 255, 255); + _saturationBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor.R, referenceColor.G, referenceColor.B); + } + + if (_valueBrush != null) + { + Color referenceColor = Color.FromHSV(_hue, _saturation, 1); + + _valueBrush.GradientStops[0].Color = WpfColor.FromArgb(_a, 0, 0, 0); + _valueBrush.GradientStops[1].Color = WpfColor.FromArgb(_a, referenceColor.R, referenceColor.G, referenceColor.B); + } + } + + #endregion + } +} diff --git a/KeyboardAudioVisualizer/Controls/GradientEditor.cs b/KeyboardAudioVisualizer/Controls/GradientEditor.cs new file mode 100644 index 0000000..bf560e1 --- /dev/null +++ b/KeyboardAudioVisualizer/Controls/GradientEditor.cs @@ -0,0 +1,423 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using RGB.NET.Brushes.Gradients; +using RGB.NET.Core; +using Color = System.Windows.Media.Color; +using Point = System.Windows.Point; +using Size = System.Windows.Size; +using Rectangle = System.Windows.Shapes.Rectangle; +using GradientStop = RGB.NET.Brushes.Gradients.GradientStop; + +namespace KeyboardAudioVisualizer.Controls +{ + [TemplatePart(Name = "PART_Gradient", Type = typeof(Canvas))] + [TemplatePart(Name = "PART_Stops", Type = typeof(Canvas))] + public class GradientEditor : Control + { + #region Properties & Fields + + private Canvas _gradientContainer; + private Canvas _stopContainer; + private readonly List _previewRectangles = new List(); + private readonly Dictionary _stops = new Dictionary(); + private ContentControl _draggingStop; + private AdornerLayer _adornerLayer; + private ColorPickerAdorner _adorner; + private Window _window; + + #endregion + + #region DepdencyProperties + + public static readonly DependencyProperty GradientProperty = DependencyProperty.Register( + "Gradient", typeof(LinearGradient), typeof(GradientEditor), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + OnGradientChanged)); + + public LinearGradient Gradient + { + get => (LinearGradient)GetValue(GradientProperty); + set => SetValue(GradientProperty, value); + } + + public static readonly DependencyProperty GradientStopStyleProperty = DependencyProperty.Register( + "GradientStopStyle", typeof(Style), typeof(GradientEditor), new PropertyMetadata(default(Style))); + + public Style GradientStopStyle + { + get => (Style)GetValue(GradientStopStyleProperty); + set => SetValue(GradientStopStyleProperty, value); + } + + public static readonly DependencyProperty SelectedStopProperty = DependencyProperty.Register( + "SelectedStop", typeof(GradientStop), typeof(GradientEditor), new PropertyMetadata(default(GradientStop), SelectedStopChanged)); + + public GradientStop SelectedStop + { + get => (GradientStop)GetValue(SelectedStopProperty); + set => SetValue(SelectedStopProperty, value); + } + + public static readonly DependencyProperty ColorSelectorTemplateProperty = DependencyProperty.Register( + "ColorSelectorTemplate", typeof(DataTemplate), typeof(GradientEditor), new PropertyMetadata(default(DataTemplate))); + + public DataTemplate ColorSelectorTemplate + { + get => (DataTemplate)GetValue(ColorSelectorTemplateProperty); + set => SetValue(ColorSelectorTemplateProperty, value); + } + + public static readonly DependencyProperty CanAddOrDeleteStopsProperty = DependencyProperty.Register( + "CanAddOrDeleteStops", typeof(bool), typeof(GradientEditor), new PropertyMetadata(true)); + + public bool CanAddOrDeleteStops + { + get => (bool)GetValue(CanAddOrDeleteStopsProperty); + set => SetValue(CanAddOrDeleteStopsProperty, value); + } + + #endregion + + #region AttachedProperties + + public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.RegisterAttached( + "IsSelected", typeof(bool), typeof(GradientEditor), new PropertyMetadata(default(bool))); + + public static void SetIsSelected(DependencyObject element, bool value) => element.SetValue(IsSelectedProperty, value); + public static bool GetIsSelected(DependencyObject element) => (bool)element.GetValue(IsSelectedProperty); + + #endregion + + #region Constructors + + public GradientEditor() + { + if (Gradient == null) + Gradient = new LinearGradient(); + } + + #endregion + + #region Methods + + public override void OnApplyTemplate() + { + if ((_gradientContainer = GetTemplateChild("PART_Gradient") as Canvas) != null) + { + _gradientContainer.SizeChanged += (sender, args) => UpdateGradientPreview(); + _gradientContainer.MouseDown += GradientContainerOnMouseDown; + } + + if ((_stopContainer = GetTemplateChild("PART_Stops") as Canvas) != null) + _stopContainer.SizeChanged += (sender, args) => UpdateGradientStops(); + + _adornerLayer = AdornerLayer.GetAdornerLayer(this); + _window = Window.GetWindow(this); + if (_window != null) + { + _window.PreviewMouseDown += WindowMouseDown; + _window.PreviewKeyDown += (sender, args) => + { + if (args.Key == Key.Escape) + SelectedStop = null; + }; + } + + UpdateGradientPreview(); + UpdateGradientStops(); + } + + private void UpdateGradientPreview() + { + if ((_gradientContainer == null) || (Gradient == null)) return; + + List gradientStops = Gradient.GradientStops.OrderBy(x => x.Offset).ToList(); + if (gradientStops.Count == 0) + UpdatePreviewRectangleCount(gradientStops.Count); + else if (gradientStops.Count == 1) + { + UpdatePreviewRectangleCount(gradientStops.Count); + GradientStop firstStop = gradientStops[0]; + UpdatePreviewRectangle(_previewRectangles[0], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, 0, 1, firstStop.Color, firstStop.Color); + } + else + { + UpdatePreviewRectangleCount(gradientStops.Count + 1); + + GradientStop firstStop = gradientStops[0]; + UpdatePreviewRectangle(_previewRectangles[0], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, 0, firstStop.Offset, firstStop.Color, firstStop.Color); + for (int i = 0; i < (gradientStops.Count - 1); i++) + { + GradientStop stop = gradientStops[i]; + GradientStop nextStop = gradientStops[i + 1]; + Rectangle rect = _previewRectangles[i + 1]; + UpdatePreviewRectangle(rect, _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, stop.Offset, nextStop.Offset, stop.Color, nextStop.Color); + } + GradientStop lastStop = gradientStops[gradientStops.Count - 1]; + UpdatePreviewRectangle(_previewRectangles[_previewRectangles.Count - 1], _gradientContainer.ActualWidth, _gradientContainer.ActualHeight, lastStop.Offset, 1, lastStop.Color, lastStop.Color); + } + } + + private void UpdatePreviewRectangle(Rectangle rect, double referenceWidth, double referenceHeight, double from, double to, + RGB.NET.Core.Color startColor, RGB.NET.Core.Color endColor) + { + rect.Fill = new LinearGradientBrush(Color.FromArgb(startColor.A, startColor.R, startColor.G, startColor.B), + Color.FromArgb(endColor.A, endColor.R, endColor.G, endColor.B), + new Point(0, 0.5), new Point(1, 0.5)); + + Canvas.SetLeft(rect, referenceWidth * from.Clamp(0, 1)); + rect.Width = referenceWidth * (to.Clamp(0, 1) - from.Clamp(0, 1)); + + Canvas.SetTop(rect, 0); + rect.Height = referenceHeight; + } + + private void UpdatePreviewRectangleCount(int gradientCount) + { + int countDiff = gradientCount - _previewRectangles.Count; + if (countDiff > 0) + for (int i = 0; i < countDiff; i++) + { + Rectangle rect = new Rectangle { VerticalAlignment = VerticalAlignment.Stretch }; + _previewRectangles.Add(rect); + _gradientContainer.Children.Add(rect); + } + + if (countDiff < 0) + for (int i = 0; i < Math.Abs(countDiff); i++) + { + int index = _previewRectangles.Count - i - 1; + Rectangle rect = _previewRectangles[index]; + _previewRectangles.RemoveAt(index); + _gradientContainer.Children.Remove(rect); + } + } + + private void UpdateGradientStops() + { + if (Gradient == null) return; + + List gradientStops = Gradient.GradientStops.OrderBy(x => x.Offset).ToList(); + UpdateGradientStopsCount(gradientStops); + foreach (GradientStop stop in gradientStops) + UpdateGradientStop(_stops[stop], _stopContainer.ActualWidth, _stopContainer.ActualHeight, stop); + } + + private void UpdateGradientStop(ContentControl control, double referenceWidth, double referenceHeight, GradientStop stop) + { + control.Background = new SolidColorBrush(Color.FromArgb(stop.Color.A, stop.Color.R, stop.Color.G, stop.Color.B)); + + Canvas.SetLeft(control, (referenceWidth * stop.Offset.Clamp(0, 1)) - (control.Width / 2.0)); + + Canvas.SetTop(control, 0); + control.Height = referenceHeight; + } + + private void UpdateGradientStopsCount(List gradientStops) + { + foreach (GradientStop stop in gradientStops) + { + if (!_stops.ContainsKey(stop)) + { + ContentControl control = new ContentControl + { + VerticalAlignment = VerticalAlignment.Stretch, + Style = GradientStopStyle, + Content = stop + }; + control.MouseDown += GradientStopOnMouseDown; + _stops.Add(stop, control); + _stopContainer.Children.Add(control); + } + } + + List stopsToRemove = new List(); + foreach (KeyValuePair stopPair in _stops) + if (!gradientStops.Contains(stopPair.Key)) + { + ContentControl control = stopPair.Value; + control.MouseDown -= GradientStopOnMouseDown; + stopsToRemove.Add(stopPair.Key); + _stopContainer.Children.Remove(control); + } + + foreach (GradientStop stop in stopsToRemove) + _stops.Remove(stop); + } + + private void AttachGradient(AbstractGradient gradient) => gradient.GradientChanged += GradientChanged; + private void DetachGradient(AbstractGradient gradient) => gradient.GradientChanged -= GradientChanged; + + private void GradientChanged(object o, EventArgs eventArgs) + { + UpdateGradientPreview(); + UpdateGradientStops(); + } + + private static void OnGradientChanged(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (!(dependencyObject is GradientEditor ge)) return; + + if (dependencyPropertyChangedEventArgs.OldValue is AbstractGradient oldGradient) + ge.DetachGradient(oldGradient); + + if (dependencyPropertyChangedEventArgs.NewValue is AbstractGradient newGradient) + ge.AttachGradient(newGradient); + } + + private void GradientContainerOnMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs) + { + if ((mouseButtonEventArgs.ChangedButton != MouseButton.Left) || (Gradient == null) || !CanAddOrDeleteStops) return; + + double offset = mouseButtonEventArgs.GetPosition(_gradientContainer).X / _gradientContainer.ActualWidth; + RGB.NET.Core.Color color = Gradient.GetColor(offset); + GradientStop newStop = new GradientStop(offset, color); + Gradient.GradientStops.Add(newStop); + SelectedStop = newStop; + } + + private void GradientStopOnMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs) + { + if (!((o as ContentControl)?.Content is GradientStop stop) || (Gradient == null)) return; + + if (mouseButtonEventArgs.ChangedButton == MouseButton.Right) + { + if (CanAddOrDeleteStops) + Gradient.GradientStops.Remove(stop); + } + else if (mouseButtonEventArgs.ChangedButton == MouseButton.Left) + { + SelectedStop = stop; + _draggingStop = (ContentControl)o; + } + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + if (_draggingStop?.Content is GradientStop stop) + { + double location = e.GetPosition(_gradientContainer).X; + stop.Offset = (location / _gradientContainer.ActualWidth).Clamp(0, 1); + } + } + + protected override void OnMouseLeave(MouseEventArgs e) + { + base.OnMouseLeave(e); + + _draggingStop = null; + } + + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonUp(e); + + _draggingStop = null; + } + + private static void SelectedStopChanged(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (!(dependencyObject is GradientEditor gradientEditor)) return; + + if (gradientEditor._adorner != null) + gradientEditor._adornerLayer.Remove(gradientEditor._adorner); + + if (dependencyPropertyChangedEventArgs.OldValue is GradientStop oldStop) + { + if (gradientEditor._stops.TryGetValue(oldStop, out ContentControl oldcontrol)) + SetIsSelected(oldcontrol, false); + } + + if (dependencyPropertyChangedEventArgs.NewValue is GradientStop stop) + { + ContentControl stopContainer = gradientEditor._stops[stop]; + SetIsSelected(stopContainer, true); + + if (gradientEditor._adornerLayer != null) + { + ContentControl contentControl = new ContentControl + { + ContentTemplate = gradientEditor.ColorSelectorTemplate, + Content = stop + }; + + ColorPickerAdorner adorner = new ColorPickerAdorner(stopContainer, contentControl); + gradientEditor._adorner = adorner; + gradientEditor._adornerLayer.Add(adorner); + } + } + } + + private void WindowMouseDown(object o, MouseButtonEventArgs mouseButtonEventArgs) + { + if ((_adorner != null) && (VisualTreeHelper.HitTest(_adorner, mouseButtonEventArgs.GetPosition(_adorner)) == null)) + SelectedStop = null; + } + + #endregion + } + + public class ColorPickerAdorner : Adorner + { + #region Properties & Fields + + private readonly VisualCollection _visualChildren; + private readonly FrameworkElement _colorSelector; + protected override int VisualChildrenCount => 1; + protected override Visual GetVisualChild(int index) => _colorSelector; + + #endregion + + #region Constructors + + public ColorPickerAdorner(UIElement adornedElement, FrameworkElement colorSelector) + : base(adornedElement) + { + this._colorSelector = colorSelector; + + _visualChildren = new VisualCollection(this) { colorSelector }; + } + + #endregion + + #region Methods + + protected override Size ArrangeOverride(Size finalSize) + { + Window referenceWindow = Window.GetWindow(AdornedElement); + Point referenceLocation = AdornedElement.TranslatePoint(new Point(0, 0), referenceWindow); + + double referenceWidth = ((FrameworkElement)AdornedElement).ActualWidth / 2.0; + double referenceHeight = ((FrameworkElement)AdornedElement).Height; + double referenceX = referenceLocation.X + referenceWidth; + double halfWidth = finalSize.Width / 2.0; + double maxOffset = referenceWindow.Width - halfWidth; + + double offset = (referenceX < halfWidth ? referenceX + : (((referenceX + (referenceWidth * 2)) > maxOffset) + ? halfWidth - ((maxOffset - referenceX) - (referenceWidth * 2)) + : halfWidth)); + + _colorSelector.Arrange(new Rect(new Point(referenceWidth - offset, referenceHeight), finalSize)); + return _colorSelector.RenderSize; + } + + protected override Size MeasureOverride(Size constraint) + { + _colorSelector.Measure(constraint); + return _colorSelector.DesiredSize; + } + + #endregion + } +} diff --git a/KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs b/KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs index 4e0c58a..78ca2b5 100644 --- a/KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs +++ b/KeyboardAudioVisualizer/Decorators/LevelBarDecorator.cs @@ -1,4 +1,5 @@ using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider; +using RGB.NET.Brushes.Gradients; using RGB.NET.Core; namespace KeyboardAudioVisualizer.Decorators @@ -10,16 +11,18 @@ namespace KeyboardAudioVisualizer.Decorators private readonly IVisualizationProvider _visualizationProvider; public LevelBarDirection Direction { get; set; } public int DataIndex { get; set; } + public LinearGradient Gradient { get; set; } #endregion #region Constructors - public LevelBarDecorator(IVisualizationProvider visualizationProvider, LevelBarDirection direction, int dataIndex) + public LevelBarDecorator(IVisualizationProvider visualizationProvider, LevelBarDirection direction, int dataIndex, LinearGradient gradient) { this._visualizationProvider = visualizationProvider; this.Direction = direction; this.DataIndex = dataIndex; + this.Gradient = gradient; } #endregion @@ -34,17 +37,22 @@ namespace KeyboardAudioVisualizer.Decorators if (Direction == LevelBarDirection.Horizontal) { + if (offset < 0) { offset = (-offset * 2); if (offset >= _visualizationProvider.VisualizationData[0]) return color.SetA(0); + else + return Gradient.GetColor(offset); } else { offset *= 2; if (offset >= _visualizationProvider.VisualizationData[1]) return color.SetA(0); + else + return Gradient.GetColor(offset); } } else diff --git a/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj b/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj index 9f8150c..63d18ae 100644 --- a/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj +++ b/KeyboardAudioVisualizer/KeyboardAudioVisualizer.csproj @@ -57,40 +57,40 @@ ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - ..\packages\RGB.NET.Brushes.0.0.1.28\lib\net45\RGB.NET.Brushes.dll + ..\packages\RGB.NET.Brushes.0.0.1.31\lib\net45\RGB.NET.Brushes.dll - ..\packages\RGB.NET.Core.0.0.1.28\lib\net45\RGB.NET.Core.dll + ..\packages\RGB.NET.Core.0.0.1.31\lib\net45\RGB.NET.Core.dll - ..\packages\RGB.NET.Decorators.0.0.1.28\lib\net45\RGB.NET.Decorators.dll + ..\packages\RGB.NET.Decorators.0.0.1.31\lib\net45\RGB.NET.Decorators.dll - ..\packages\RGB.NET.Devices.Asus.0.0.1.28\lib\net45\RGB.NET.Devices.Asus.dll + ..\packages\RGB.NET.Devices.Asus.0.0.1.31\lib\net45\RGB.NET.Devices.Asus.dll - ..\packages\RGB.NET.Devices.CoolerMaster.0.0.1.28\lib\net45\RGB.NET.Devices.CoolerMaster.dll + ..\packages\RGB.NET.Devices.CoolerMaster.0.0.1.31\lib\net45\RGB.NET.Devices.CoolerMaster.dll - ..\packages\RGB.NET.Devices.Corsair.0.0.1.28\lib\net45\RGB.NET.Devices.Corsair.dll + ..\packages\RGB.NET.Devices.Corsair.0.0.1.31\lib\net45\RGB.NET.Devices.Corsair.dll - ..\packages\RGB.NET.Devices.Logitech.0.0.1.28\lib\net45\RGB.NET.Devices.Logitech.dll + ..\packages\RGB.NET.Devices.Logitech.0.0.1.31\lib\net45\RGB.NET.Devices.Logitech.dll - ..\packages\RGB.NET.Devices.Msi.0.0.1.28\lib\net45\RGB.NET.Devices.Msi.dll + ..\packages\RGB.NET.Devices.Msi.0.0.1.31\lib\net45\RGB.NET.Devices.Msi.dll - ..\packages\RGB.NET.Devices.Novation.0.0.1.28\lib\net45\RGB.NET.Devices.Novation.dll + ..\packages\RGB.NET.Devices.Novation.0.0.1.31\lib\net45\RGB.NET.Devices.Novation.dll - ..\packages\RGB.NET.Devices.Razer.0.0.1.28\lib\net45\RGB.NET.Devices.Razer.dll + ..\packages\RGB.NET.Devices.Razer.0.0.1.31\lib\net45\RGB.NET.Devices.Razer.dll - ..\packages\RGB.NET.Groups.0.0.1.28\lib\net45\RGB.NET.Groups.dll + ..\packages\RGB.NET.Groups.0.0.1.31\lib\net45\RGB.NET.Groups.dll - - ..\packages\Sanford.Multimedia.Midi.6.4.2\lib\net20\Sanford.Multimedia.Midi.dll + + ..\packages\Sanford.Multimedia.Midi.6.5.0\lib\net20\Sanford.Multimedia.Midi.dll @@ -152,6 +152,9 @@ + + + @@ -180,6 +183,7 @@ + @@ -236,6 +240,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -248,6 +256,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -316,21 +328,21 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - + + + + + + - - - - - + + + + + \ No newline at end of file diff --git a/KeyboardAudioVisualizer/Legacy/ConfigurationUpdates.cs b/KeyboardAudioVisualizer/Legacy/ConfigurationUpdates.cs new file mode 100644 index 0000000..83ab13d --- /dev/null +++ b/KeyboardAudioVisualizer/Legacy/ConfigurationUpdates.cs @@ -0,0 +1,37 @@ +using KeyboardAudioVisualizer.Helper; +using RGB.NET.Brushes.Gradients; +using RGB.NET.Core; + +namespace KeyboardAudioVisualizer.Legacy +{ + public static class ConfigurationUpdates + { + #region Methods + + public static void PerformOn(Configuration.Settings settings) + { + if (settings.Version < 1) + UpdateTo1(settings); + } + + private static void UpdateTo1(Configuration.Settings settings) + { + settings.Visualizations[VisualizationIndex.Primary].Gradient = new LinearGradient(new GradientStop(0, Color.FromHSV(300, 1, 1)), + new GradientStop(0.20, Color.FromHSV(225, 1, 1)), + new GradientStop(0.35, Color.FromHSV(180, 1, 1)), + new GradientStop(0.50, Color.FromHSV(135, 1, 1)), + new GradientStop(0.65, Color.FromHSV(90, 1, 1)), + new GradientStop(0.80, Color.FromHSV(45, 1, 1)), + new GradientStop(0.95, Color.FromHSV(0, 1, 1))); + + settings.Visualizations[VisualizationIndex.Secondary].Gradient = new LinearGradient(new GradientStop(0.5, new Color(255, 255, 255))); + + settings.Visualizations[VisualizationIndex.Tertiary].Gradient = new LinearGradient(new GradientStop(0, new Color(0, 0, 255)), + new GradientStop(1, new Color(255, 0, 0))); + + settings.Version = 1; + } + + #endregion + } +} diff --git a/KeyboardAudioVisualizer/Resources/KeyboardAudioVisualizer.xaml b/KeyboardAudioVisualizer/Resources/KeyboardAudioVisualizer.xaml index d1fdcb2..531c554 100644 --- a/KeyboardAudioVisualizer/Resources/KeyboardAudioVisualizer.xaml +++ b/KeyboardAudioVisualizer/Resources/KeyboardAudioVisualizer.xaml @@ -14,6 +14,8 @@ + + diff --git a/KeyboardAudioVisualizer/Styles/ColorSelector.xaml b/KeyboardAudioVisualizer/Styles/ColorSelector.xaml new file mode 100644 index 0000000..048591a --- /dev/null +++ b/KeyboardAudioVisualizer/Styles/ColorSelector.xaml @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/KeyboardAudioVisualizer/UI/Visualization/BeatVisualizer.cs b/KeyboardAudioVisualizer/UI/Visualization/BeatVisualizer.cs index c7d9ef7..8ade753 100644 --- a/KeyboardAudioVisualizer/UI/Visualization/BeatVisualizer.cs +++ b/KeyboardAudioVisualizer/UI/Visualization/BeatVisualizer.cs @@ -4,14 +4,20 @@ using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider; +using KeyboardAudioVisualizer.Helper; +using RGB.NET.Brushes.Gradients; using RGB.NET.Core; -using Color = System.Windows.Media.Color; -using Point = System.Windows.Point; namespace KeyboardAudioVisualizer.UI.Visualization { public class BeatVisualizer : Control { + #region Properties & Fields + + private LinearGradient _gradient; + + #endregion + #region DependencyProperties // ReSharper disable InconsistentNaming @@ -24,6 +30,15 @@ namespace KeyboardAudioVisualizer.UI.Visualization set => SetValue(VisualizationProviderProperty, value); } + public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register( + "VisualizationIndex", typeof(VisualizationIndex?), typeof(BeatVisualizer), new PropertyMetadata(null, VisualizationIndexChanged)); + + public VisualizationIndex? VisualizationIndex + { + get => (VisualizationIndex?)GetValue(VisualizationIndexProperty); + set => SetValue(VisualizationIndexProperty, value); + } + public static readonly DependencyProperty BrushProperty = DependencyProperty.Register( "Brush", typeof(Brush), typeof(BeatVisualizer), new PropertyMetadata(default(Brush))); @@ -52,7 +67,7 @@ namespace KeyboardAudioVisualizer.UI.Visualization RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal); //TODO DarthAffe 12.08.2017: Create brush from config - Brush = new SolidColorBrush(Color.FromRgb(255, 255, 255)); + } #endregion @@ -75,6 +90,37 @@ namespace KeyboardAudioVisualizer.UI.Visualization } } + private static void VisualizationIndexChanged(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (!(dependencyObject is BeatVisualizer visualizer)) return; + visualizer.UpdateGradient(); + } + + private void UpdateGradient() + { + void GradientChanged(object sender, EventArgs args) => UpdateColor(); + if (_gradient != null) + _gradient.GradientChanged -= GradientChanged; + + _gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null; + if (_gradient != null) + _gradient.GradientChanged += GradientChanged; + + UpdateColor(); + } + + private void UpdateColor() + { + if (_gradient == null) return; + + GradientStopCollection gradientStops = new GradientStopCollection(); + foreach (RGB.NET.Brushes.Gradients.GradientStop stop in _gradient.GradientStops) + gradientStops.Add(new System.Windows.Media.GradientStop(System.Windows.Media.Color.FromArgb(stop.Color.A, stop.Color.R, stop.Color.G, stop.Color.B), stop.Offset)); + + Brush = new LinearGradientBrush(gradientStops, new System.Windows.Point(0, 0.5), new System.Windows.Point(1, 0.5)); + } + #endregion } } diff --git a/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualization.xaml b/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualization.xaml index 9a6029f..611e792 100644 --- a/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualization.xaml +++ b/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualization.xaml @@ -31,8 +31,11 @@ - - + + diff --git a/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs b/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs index 2e1abf2..4b58646 100644 --- a/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs +++ b/KeyboardAudioVisualizer/UI/Visualization/FrequencyBarsVisualizer.cs @@ -5,6 +5,7 @@ using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider; +using KeyboardAudioVisualizer.Helper; using RGB.NET.Brushes.Gradients; using RGB.NET.Core; using Color = System.Windows.Media.Color; @@ -27,12 +28,22 @@ namespace KeyboardAudioVisualizer.UI.Visualization get => (IVisualizationProvider)GetValue(VisualizationProviderProperty); set => SetValue(VisualizationProviderProperty, value); } + + public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register( + "VisualizationIndex", typeof(VisualizationIndex?), typeof(FrequencyBarsVisualizer), new PropertyMetadata(null, VisualizationIndexChanged)); + + public VisualizationIndex? VisualizationIndex + { + get => (VisualizationIndex?)GetValue(VisualizationIndexProperty); + set => SetValue(VisualizationIndexProperty, value); + } + // ReSharper restore InconsistentNaming #endregion #region Properties & Fields - private IGradient _gradient; + private LinearGradient _gradient; private Panel _panel; private Rectangle[] _bars = new Rectangle[0]; @@ -44,9 +55,6 @@ namespace KeyboardAudioVisualizer.UI.Visualization { RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal); SizeChanged += (sender, args) => UpdateSizes(); - - //TODO DarthAffe 12.08.2017: Get gradient from config - _gradient = new RainbowGradient(300, -14); } #endregion @@ -65,8 +73,7 @@ namespace KeyboardAudioVisualizer.UI.Visualization private static void VisualizationProviderChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { - FrequencyBarsVisualizer visualizer = dependencyObject as FrequencyBarsVisualizer; - if (visualizer == null) return; + if (!(dependencyObject is FrequencyBarsVisualizer visualizer)) return; void ConfigurationOnPropertyChanged(object sender, PropertyChangedEventArgs args) => visualizer.ConfigurationChanged(args.PropertyName); @@ -77,6 +84,26 @@ namespace KeyboardAudioVisualizer.UI.Visualization newVisualizationProvider.Configuration.PropertyChanged += ConfigurationOnPropertyChanged; } + private static void VisualizationIndexChanged(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (!(dependencyObject is FrequencyBarsVisualizer visualizer)) return; + visualizer.UpdateGradient(); + } + + private void UpdateGradient() + { + void GradientChanged(object sender, EventArgs args) => UpdateColors(); + if (_gradient != null) + _gradient.GradientChanged -= GradientChanged; + + _gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null; + if (_gradient != null) + _gradient.GradientChanged += GradientChanged; + + UpdateColors(); + } + private void ConfigurationChanged(string changedPropertyName) { if ((changedPropertyName == null) || (changedPropertyName == nameof(FrequencyBarsVisualizationProviderConfiguration.Bars))) @@ -119,6 +146,8 @@ namespace KeyboardAudioVisualizer.UI.Visualization private void UpdateColors() { + if (_gradient == null) return; + for (int i = 0; i < _bars.Length; i++) { RGB.NET.Core.Color color = _gradient.GetColor((double)i / _bars.Length); diff --git a/KeyboardAudioVisualizer/UI/Visualization/LevelVisualization.xaml b/KeyboardAudioVisualizer/UI/Visualization/LevelVisualization.xaml index 56e1111..9bac595 100644 --- a/KeyboardAudioVisualizer/UI/Visualization/LevelVisualization.xaml +++ b/KeyboardAudioVisualizer/UI/Visualization/LevelVisualization.xaml @@ -63,7 +63,9 @@ - + diff --git a/KeyboardAudioVisualizer/UI/Visualization/LevelVisualizer.cs b/KeyboardAudioVisualizer/UI/Visualization/LevelVisualizer.cs index a746688..00ad810 100644 --- a/KeyboardAudioVisualizer/UI/Visualization/LevelVisualizer.cs +++ b/KeyboardAudioVisualizer/UI/Visualization/LevelVisualizer.cs @@ -1,17 +1,27 @@ using System; +using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using KeyboardAudioVisualizer.AudioProcessing.VisualizationProvider; +using KeyboardAudioVisualizer.Helper; +using RGB.NET.Brushes.Gradients; using RGB.NET.Core; using Color = System.Windows.Media.Color; +using GradientStop = RGB.NET.Brushes.Gradients.GradientStop; using Point = System.Windows.Point; namespace KeyboardAudioVisualizer.UI.Visualization { public class LevelVisualizer : Control { + #region Properties & Fields + + private LinearGradient _gradient; + + #endregion + #region DependencyProperties // ReSharper disable InconsistentNaming @@ -24,6 +34,15 @@ namespace KeyboardAudioVisualizer.UI.Visualization set => SetValue(VisualizationProviderProperty, value); } + public static readonly DependencyProperty VisualizationIndexProperty = DependencyProperty.Register( + "VisualizationIndex", typeof(VisualizationIndex?), typeof(LevelVisualizer), new PropertyMetadata(null, VisualizationIndexChanged)); + + public VisualizationIndex? VisualizationIndex + { + get => (VisualizationIndex?)GetValue(VisualizationIndexProperty); + set => SetValue(VisualizationIndexProperty, value); + } + public static readonly DependencyProperty BrushLeftProperty = DependencyProperty.Register( "BrushLeft", typeof(Brush), typeof(LevelVisualizer), new PropertyMetadata(default(Brush))); @@ -68,10 +87,6 @@ namespace KeyboardAudioVisualizer.UI.Visualization public LevelVisualizer() { RGBSurface.Instance.Updated += args => Dispatcher.BeginInvoke(new Action(Update), DispatcherPriority.Normal); - - //TODO DarthAffe 12.08.2017: Create brush from config - BrushLeft = new LinearGradientBrush(Color.FromRgb(255, 0, 0), Color.FromRgb(0, 0, 255), new Point(0, 0.5), new Point(1, 0.5)); - BrushRight = new LinearGradientBrush(Color.FromRgb(0, 0, 255), Color.FromRgb(255, 0, 0), new Point(0, 0.5), new Point(1, 0.5)); } #endregion @@ -92,6 +107,38 @@ namespace KeyboardAudioVisualizer.UI.Visualization SizeRight = horizontalSizeRight; } + private void SetBrushes() + { + if (_gradient == null) return; + + GradientStopCollection gradientStops = new GradientStopCollection(); + foreach (GradientStop stop in _gradient.GradientStops) + gradientStops.Add(new System.Windows.Media.GradientStop(Color.FromArgb(stop.Color.A, stop.Color.R, stop.Color.G, stop.Color.B), stop.Offset)); + + BrushLeft = new LinearGradientBrush(gradientStops, new Point(1, 0.5), new Point(0, 0.5)); + BrushRight = new LinearGradientBrush(gradientStops, new Point(0, 0.5), new Point(1, 0.5)); + } + + private static void VisualizationIndexChanged(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (!(dependencyObject is LevelVisualizer visualizer)) return; + visualizer.UpdateGradient(); + } + + private void UpdateGradient() + { + void GradientChanged(object sender, EventArgs args) => SetBrushes(); + if (_gradient != null) + _gradient.GradientChanged -= GradientChanged; + + _gradient = VisualizationIndex.HasValue ? ApplicationManager.Instance.Settings[VisualizationIndex.Value].Gradient : null; + if (_gradient != null) + _gradient.GradientChanged += GradientChanged; + + SetBrushes(); + } + #endregion } } diff --git a/KeyboardAudioVisualizer/packages.config b/KeyboardAudioVisualizer/packages.config index 9cef844..b23d924 100644 --- a/KeyboardAudioVisualizer/packages.config +++ b/KeyboardAudioVisualizer/packages.config @@ -5,20 +5,20 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file