mirror of
https://github.com/DarthAffe/RGBSyncPlus
synced 2026-11-11 13:06:28 +00:00
Initial implementation
This commit is contained in:
1 parent
278e22020e
commit
fd662dcf34
63 files changed
+4881
No files matched your search
@@ -0,0 +1,87 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RGBSyncPlus.Controls
|
||||
{
|
||||
[TemplatePart(Name = "PART_Decoration", Type = typeof(FrameworkElement))]
|
||||
[TemplatePart(Name = "PART_Content", Type = typeof(FrameworkElement))]
|
||||
[TemplatePart(Name = "PART_CloseButton", Type = typeof(Button))]
|
||||
[TemplatePart(Name = "PART_MinimizeButton", Type = typeof(Button))]
|
||||
[TemplatePart(Name = "PART_IconButton", Type = typeof(Button))]
|
||||
public class BlurredDecorationWindow : Window
|
||||
{
|
||||
#region DependencyProperties
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
public static readonly DependencyProperty BackgroundImageProperty = DependencyProperty.Register(
|
||||
"BackgroundImage", typeof(ImageSource), typeof(BlurredDecorationWindow), new PropertyMetadata(default(ImageSource)));
|
||||
|
||||
public ImageSource BackgroundImage
|
||||
{
|
||||
get => (ImageSource)GetValue(BackgroundImageProperty);
|
||||
set => SetValue(BackgroundImageProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DecorationHeightProperty = DependencyProperty.Register(
|
||||
"DecorationHeight", typeof(double), typeof(BlurredDecorationWindow), new PropertyMetadata(20.0));
|
||||
|
||||
public double DecorationHeight
|
||||
{
|
||||
get => (double)GetValue(DecorationHeightProperty);
|
||||
set => SetValue(DecorationHeightProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IconToolTipProperty = DependencyProperty.Register(
|
||||
"IconToolTip", typeof(string), typeof(BlurredDecorationWindow), new PropertyMetadata(default(string)));
|
||||
|
||||
public string IconToolTip
|
||||
{
|
||||
get => (string)GetValue(IconToolTipProperty);
|
||||
set => SetValue(IconToolTipProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IconCommandProperty = DependencyProperty.Register(
|
||||
"IconCommand", typeof(ICommand), typeof(BlurredDecorationWindow), new PropertyMetadata(default(ICommand)));
|
||||
|
||||
public ICommand IconCommand
|
||||
{
|
||||
get => (ICommand)GetValue(IconCommandProperty);
|
||||
set => SetValue(IconCommandProperty, value);
|
||||
}
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
static BlurredDecorationWindow()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(BlurredDecorationWindow), new FrameworkPropertyMetadata(typeof(BlurredDecorationWindow)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
if (GetTemplateChild("PART_Decoration") is FrameworkElement decoration)
|
||||
decoration.MouseLeftButtonDown += (sender, args) => DragMove();
|
||||
|
||||
if (GetTemplateChild("PART_CloseButton") is Button closeButton)
|
||||
closeButton.Click += (sender, args) => ApplicationManager.Instance.ExitCommand.Execute(null);
|
||||
|
||||
if (GetTemplateChild("PART_MinimizeButton") is Button minimizeButton)
|
||||
minimizeButton.Click += (sender, args) => Hide();
|
||||
|
||||
if (GetTemplateChild("PART_IconButton") is Button iconButton)
|
||||
iconButton.Click += (sender, args) => IconCommand?.Execute(null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -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 RGBSyncPlus.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<double> 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<double> routedPropertyChangedEventArgs)
|
||||
{
|
||||
if (_ignorePropertyChanged) return;
|
||||
|
||||
_r = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);
|
||||
RGBChanged();
|
||||
}
|
||||
|
||||
private void GChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
|
||||
{
|
||||
if (_ignorePropertyChanged) return;
|
||||
|
||||
_g = (byte)routedPropertyChangedEventArgs.NewValue.Clamp(0, byte.MaxValue);
|
||||
RGBChanged();
|
||||
}
|
||||
|
||||
private void BChanged(object sender, RoutedPropertyChangedEventArgs<double> 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<double> routedPropertyChangedEventArgs)
|
||||
{
|
||||
if (_ignorePropertyChanged) return;
|
||||
|
||||
_hue = routedPropertyChangedEventArgs.NewValue.Clamp(0, 360);
|
||||
HSVChanged();
|
||||
}
|
||||
|
||||
private void SaturationChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
|
||||
{
|
||||
if (_ignorePropertyChanged) return;
|
||||
|
||||
_saturation = routedPropertyChangedEventArgs.NewValue.Clamp(0, 1);
|
||||
HSVChanged();
|
||||
}
|
||||
|
||||
private void ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace RGBSyncPlus.Controls
|
||||
{
|
||||
public class Form : Panel
|
||||
{
|
||||
#region DependencyProperties
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
public static readonly DependencyProperty RowHeightProperty = DependencyProperty.Register("RowHeight", typeof(double), typeof(Form),
|
||||
new FrameworkPropertyMetadata(24.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
|
||||
|
||||
public double RowHeight
|
||||
{
|
||||
get => (double)GetValue(RowHeightProperty);
|
||||
set
|
||||
{
|
||||
if (value < 0) throw new ArgumentOutOfRangeException(nameof(RowHeight), "Row height can't be negative");
|
||||
SetValue(RowHeightProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.Register("LabelWidth", typeof(double), typeof(Form),
|
||||
new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
|
||||
|
||||
public double LabelWidth
|
||||
{
|
||||
get => (double)GetValue(LabelWidthProperty);
|
||||
set
|
||||
{
|
||||
if (value < 0) throw new ArgumentOutOfRangeException(nameof(RowHeight), "Label width can't be negative");
|
||||
SetValue(LabelWidthProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ElementSpacingProperty = DependencyProperty.Register("ElementSpacing", typeof(double), typeof(Form),
|
||||
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
|
||||
|
||||
public double ElementSpacing
|
||||
{
|
||||
get => (double)GetValue(ElementSpacingProperty);
|
||||
set => SetValue(ElementSpacingProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty RowSpacingProperty = DependencyProperty.Register("RowSpacing", typeof(double), typeof(Form),
|
||||
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
|
||||
|
||||
public double RowSpacing
|
||||
{
|
||||
get => (double)GetValue(RowSpacingProperty);
|
||||
set => SetValue(RowSpacingProperty, value);
|
||||
}
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
#endregion
|
||||
|
||||
#region AttachedProperties
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
public static readonly DependencyProperty IsLabelProperty = DependencyProperty.RegisterAttached("IsLabel", typeof(bool), typeof(Form),
|
||||
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
|
||||
|
||||
public static void SetIsLabel(UIElement element, bool value) => element.SetValue(IsLabelProperty, value);
|
||||
public static bool GetIsLabel(UIElement element) => (bool)element.GetValue(IsLabelProperty);
|
||||
|
||||
public static readonly DependencyProperty LineBreaksProperty = DependencyProperty.RegisterAttached("LineBreaks", typeof(int), typeof(Form),
|
||||
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
|
||||
|
||||
public static void SetLineBreaks(UIElement element, int value) => element.SetValue(LineBreaksProperty, value);
|
||||
public static int GetLineBreaks(UIElement element) => (int)element.GetValue(LineBreaksProperty);
|
||||
|
||||
public static readonly DependencyProperty RowSpanProperty = DependencyProperty.RegisterAttached("RowSpan", typeof(int), typeof(Form),
|
||||
new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
|
||||
|
||||
public static void SetRowSpan(DependencyObject element, int value) => element.SetValue(RowSpanProperty, value);
|
||||
public static int GetRowSpan(DependencyObject element) => (int)element.GetValue(RowSpanProperty);
|
||||
|
||||
public static readonly DependencyProperty FillProperty = DependencyProperty.RegisterAttached("Fill", typeof(bool), typeof(Form),
|
||||
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
|
||||
|
||||
public static void SetFill(DependencyObject element, bool value) => element.SetValue(FillProperty, value);
|
||||
public static bool GetFill(DependencyObject element) => (bool)element.GetValue(FillProperty);
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (InternalChildren.Count == 0) return new Size(0, 0);
|
||||
|
||||
FormLayout layout = new FormLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing);
|
||||
|
||||
foreach (UIElement child in InternalChildren)
|
||||
{
|
||||
child.Measure(availableSize);
|
||||
layout.AddElement(child, 0);
|
||||
}
|
||||
|
||||
return new Size(layout.Width, layout.Height);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (InternalChildren.Count == 0) return new Size(0, 0);
|
||||
|
||||
FormLayout layout = new FormLayout(RowHeight, LabelWidth, ElementSpacing, RowSpacing);
|
||||
|
||||
foreach (UIElement child in InternalChildren)
|
||||
child.Arrange(layout.AddElement(child, finalSize.Width));
|
||||
|
||||
return new Size(finalSize.Width, layout.Height);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
|
||||
private class FormLayout
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly double _rowHeight;
|
||||
private readonly double _labelWidth;
|
||||
private readonly double _elementSpacing;
|
||||
private readonly double _rowSpacing;
|
||||
|
||||
private double _currentRowWidth;
|
||||
|
||||
private int _newRows = 0;
|
||||
private int _rows = -1;
|
||||
private double _currentMaxWidth;
|
||||
public double Width => Math.Max((Math.Max(_currentMaxWidth, _currentRowWidth) - _elementSpacing), 0);
|
||||
public double Height => ((_rows + 1) * _rowHeight) + (_rows * _rowSpacing);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public FormLayout(double rowHeight, double labelWidth, double elementSpacing, double rowSpacing)
|
||||
{
|
||||
this._rowHeight = rowHeight;
|
||||
this._labelWidth = labelWidth;
|
||||
this._elementSpacing = elementSpacing;
|
||||
this._rowSpacing = rowSpacing;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
public Rect AddElement(UIElement element, double targetWidth)
|
||||
{
|
||||
bool isLabel = GetIsLabel(element);
|
||||
int lineBreaks = GetLineBreaks(element);
|
||||
int rowSpan = GetRowSpan(element);
|
||||
|
||||
double elementWidth = isLabel ? _labelWidth : element.DesiredSize.Width;
|
||||
double height = _rowHeight;
|
||||
|
||||
if (_newRows > 0)
|
||||
{
|
||||
AddLineBreaks(_newRows);
|
||||
_newRows = 0;
|
||||
}
|
||||
|
||||
if (lineBreaks > 0) AddLineBreaks(lineBreaks);
|
||||
else if (isLabel) AddLineBreaks(1);
|
||||
else if (_rows < 0) _rows = 0;
|
||||
|
||||
if (!isLabel && (_currentRowWidth < _labelWidth))
|
||||
_currentRowWidth = _labelWidth + _elementSpacing;
|
||||
|
||||
if (rowSpan > 1)
|
||||
{
|
||||
height = (rowSpan * _rowHeight) + ((rowSpan - 1) * _rowSpacing);
|
||||
_newRows = Math.Max(_newRows, rowSpan - 1);
|
||||
}
|
||||
|
||||
if (element is FrameworkElement fe)
|
||||
fe.MaxHeight = height;
|
||||
|
||||
double width = elementWidth;
|
||||
if ((targetWidth >= 1) && GetFill(element))
|
||||
width = targetWidth - _currentRowWidth;
|
||||
|
||||
Rect rect = new Rect(new Point(_currentRowWidth, (_rows * _rowHeight) + (_rows * _rowSpacing)), new Size(width, height));
|
||||
|
||||
_currentRowWidth += width + _elementSpacing;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
private void AddLineBreaks(int count)
|
||||
{
|
||||
if (count <= 0) return;
|
||||
|
||||
_currentMaxWidth = Math.Max(_currentMaxWidth, _currentRowWidth);
|
||||
|
||||
_currentRowWidth = 0;
|
||||
_rows += count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
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 RGBSyncPlus.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<Rectangle> _previewRectangles = new List<Rectangle>();
|
||||
private readonly Dictionary<GradientStop, ContentControl> _stops = new Dictionary<GradientStop, ContentControl>();
|
||||
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<GradientStop> 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));
|
||||
|
||||
//DarthAffe 09.02.2018: Forced rounding to prevent render issues on resize
|
||||
Canvas.SetLeft(rect, Math.Floor(referenceWidth * from.Clamp(0, 1)));
|
||||
rect.Width = Math.Ceiling(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<GradientStop> 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<GradientStop> 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<GradientStop> stopsToRemove = new List<GradientStop>();
|
||||
foreach (KeyValuePair<GradientStop, ContentControl> 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RGBSyncPlus.Controls
|
||||
{
|
||||
public class ImageButton : Button
|
||||
{
|
||||
#region Properties & Fields
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(
|
||||
"Image", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource)));
|
||||
|
||||
public ImageSource Image
|
||||
{
|
||||
get => (ImageSource)GetValue(ImageProperty);
|
||||
set => SetValue(ImageProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty HoverImageProperty = DependencyProperty.Register(
|
||||
"HoverImage", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource)));
|
||||
|
||||
public ImageSource HoverImage
|
||||
{
|
||||
get => (ImageSource)GetValue(HoverImageProperty);
|
||||
set => SetValue(HoverImageProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PressedImageProperty = DependencyProperty.Register(
|
||||
"PressedImage", typeof(ImageSource), typeof(ImageButton), new PropertyMetadata(default(ImageSource)));
|
||||
|
||||
public ImageSource PressedImage
|
||||
{
|
||||
get => (ImageSource)GetValue(PressedImageProperty);
|
||||
set => SetValue(PressedImageProperty, value);
|
||||
}
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
static ImageButton()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user