459 lines
14 KiB
C#
459 lines
14 KiB
C#
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using WPFDark.Controls.Effects;
|
|
using WPFDark.Internals;
|
|
|
|
namespace WPFDark.Controls
|
|
{
|
|
internal class BiaHsvWheelBackground : Canvas
|
|
{
|
|
#region Hue
|
|
|
|
public double Hue
|
|
{
|
|
get => _Hue;
|
|
set
|
|
{
|
|
if (NumberHelper.AreClose(value, _Hue) == false)
|
|
SetValue(HueProperty, value);
|
|
}
|
|
}
|
|
|
|
private double _Hue;
|
|
|
|
public static readonly DependencyProperty HueProperty =
|
|
DependencyProperty.Register(nameof(Hue), typeof(double), typeof(BiaHsvWheelBackground),
|
|
new FrameworkPropertyMetadata(
|
|
0.0,
|
|
(s, e) =>
|
|
{
|
|
var self = (BiaHsvWheelBackground) s;
|
|
self._Hue = (double) e.NewValue;
|
|
}));
|
|
|
|
#endregion
|
|
|
|
#region Saturation
|
|
|
|
public double Saturation
|
|
{
|
|
get => _Saturation;
|
|
set
|
|
{
|
|
if (NumberHelper.AreClose(value, _Saturation) == false)
|
|
SetValue(SaturationProperty, value);
|
|
}
|
|
}
|
|
|
|
private double _Saturation;
|
|
|
|
public static readonly DependencyProperty SaturationProperty =
|
|
DependencyProperty.Register(nameof(Saturation), typeof(double), typeof(BiaHsvWheelBackground),
|
|
new FrameworkPropertyMetadata(
|
|
0.0,
|
|
(s, e) =>
|
|
{
|
|
var self = (BiaHsvWheelBackground) s;
|
|
self._Saturation = (double) e.NewValue;
|
|
}));
|
|
|
|
#endregion
|
|
|
|
#region Value
|
|
|
|
public double Value
|
|
{
|
|
get => _Value;
|
|
set
|
|
{
|
|
if (NumberHelper.AreClose(value, _Value) == false)
|
|
SetValue(ValueProperty, value);
|
|
}
|
|
}
|
|
|
|
private double _Value;
|
|
|
|
public static readonly DependencyProperty ValueProperty =
|
|
DependencyProperty.Register(nameof(Value), typeof(double), typeof(BiaHsvWheelBackground),
|
|
new FrameworkPropertyMetadata(
|
|
0.0,
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|
(s, e) =>
|
|
{
|
|
var self = (BiaHsvWheelBackground) s;
|
|
self._Value = (double) e.NewValue;
|
|
|
|
self._effect.Value = NumberHelper.Clamp01(self._Value);
|
|
}));
|
|
|
|
#endregion
|
|
|
|
#region IsReadOnly
|
|
|
|
public bool IsReadOnly
|
|
{
|
|
get => _IsReadOnly;
|
|
set
|
|
{
|
|
if (value != _IsReadOnly)
|
|
SetValue(IsReadOnlyProperty, value);
|
|
}
|
|
}
|
|
|
|
private bool _IsReadOnly;
|
|
|
|
public static readonly DependencyProperty IsReadOnlyProperty =
|
|
DependencyProperty.Register(nameof(IsReadOnly), typeof(bool), typeof(BiaHsvWheelBackground),
|
|
new FrameworkPropertyMetadata(
|
|
false,
|
|
FrameworkPropertyMetadataOptions.AffectsRender |
|
|
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
|
(s, e) =>
|
|
{
|
|
var self = (BiaHsvWheelBackground) s;
|
|
self._IsReadOnly = (bool) e.NewValue;
|
|
}));
|
|
|
|
#endregion
|
|
|
|
#region StartedContinuousEditingCommand
|
|
|
|
public ICommand? StartedContinuousEditingCommand
|
|
{
|
|
get => _StartedContinuousEditingCommand;
|
|
set
|
|
{
|
|
if (value != _StartedContinuousEditingCommand)
|
|
SetValue(StartedContinuousEditingCommandProperty, value);
|
|
}
|
|
}
|
|
|
|
private ICommand? _StartedContinuousEditingCommand;
|
|
|
|
public static readonly DependencyProperty StartedContinuousEditingCommandProperty =
|
|
DependencyProperty.Register(
|
|
nameof(StartedContinuousEditingCommand),
|
|
typeof(ICommand),
|
|
typeof(BiaHsvWheelBackground),
|
|
new PropertyMetadata(
|
|
default(ICommand),
|
|
(s, e) =>
|
|
{
|
|
var self = (BiaHsvWheelBackground) s;
|
|
self._StartedContinuousEditingCommand = (ICommand) e.NewValue;
|
|
}));
|
|
|
|
#endregion
|
|
|
|
#region EndContinuousEditingCommand
|
|
|
|
public ICommand? EndContinuousEditingCommand
|
|
{
|
|
get => _EndContinuousEditingCommand;
|
|
set
|
|
{
|
|
if (value != _EndContinuousEditingCommand)
|
|
SetValue(EndContinuousEditingCommandProperty, value);
|
|
}
|
|
}
|
|
|
|
private ICommand? _EndContinuousEditingCommand;
|
|
|
|
public static readonly DependencyProperty EndContinuousEditingCommandProperty =
|
|
DependencyProperty.Register(
|
|
nameof(EndContinuousEditingCommand),
|
|
typeof(ICommand),
|
|
typeof(BiaHsvWheelBackground),
|
|
new PropertyMetadata(
|
|
default(ICommand),
|
|
(s, e) =>
|
|
{
|
|
var self = (BiaHsvWheelBackground) s;
|
|
self._EndContinuousEditingCommand = (ICommand) e.NewValue;
|
|
}));
|
|
|
|
#endregion
|
|
|
|
#region StartedBatchEditingCommand
|
|
|
|
public ICommand? StartedBatchEditingCommand
|
|
{
|
|
get => _StartedBatchEditingCommand;
|
|
set
|
|
{
|
|
if (value != _StartedBatchEditingCommand)
|
|
SetValue(StartedBatchEditingCommandProperty, value);
|
|
}
|
|
}
|
|
|
|
private ICommand? _StartedBatchEditingCommand;
|
|
|
|
public static readonly DependencyProperty StartedBatchEditingCommandProperty =
|
|
DependencyProperty.Register(
|
|
nameof(StartedBatchEditingCommand),
|
|
typeof(ICommand),
|
|
typeof(BiaHsvWheelBackground),
|
|
new PropertyMetadata(
|
|
default(ICommand),
|
|
(s, e) =>
|
|
{
|
|
var self = (BiaHsvWheelBackground) s;
|
|
self._StartedBatchEditingCommand = (ICommand) e.NewValue;
|
|
}));
|
|
|
|
#endregion
|
|
|
|
#region EndBatchEditingCommand
|
|
|
|
public ICommand? EndBatchEditingCommand
|
|
{
|
|
get => _EndBatchEditingCommand;
|
|
set
|
|
{
|
|
if (value != _EndBatchEditingCommand)
|
|
SetValue(EndBatchEditingCommandProperty, value);
|
|
}
|
|
}
|
|
|
|
private ICommand? _EndBatchEditingCommand;
|
|
|
|
public static readonly DependencyProperty EndBatchEditingCommandProperty =
|
|
DependencyProperty.Register(
|
|
nameof(EndBatchEditingCommand),
|
|
typeof(ICommand),
|
|
typeof(BiaHsvWheelBackground),
|
|
new PropertyMetadata(
|
|
default(ICommand),
|
|
(s, e) =>
|
|
{
|
|
var self = (BiaHsvWheelBackground) s;
|
|
self._EndBatchEditingCommand = (ICommand) e.NewValue;
|
|
}));
|
|
|
|
#endregion
|
|
|
|
private readonly HsvWheelBackgroundEffect _effect = new HsvWheelBackgroundEffect();
|
|
|
|
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
|
private readonly PropertyChangeNotifier _isEnabledChangeNotifier;
|
|
|
|
static BiaHsvWheelBackground()
|
|
{
|
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(BiaHsvWheelBackground),
|
|
new FrameworkPropertyMetadata(typeof(BiaHsvWheelBackground)));
|
|
}
|
|
|
|
public BiaHsvWheelBackground()
|
|
{
|
|
Effect = _effect;
|
|
|
|
RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);
|
|
|
|
_effect.BorderColor = ((ByteColor) ThemeManager.Current.TryFindResource("BackgroundBackgroundColorKey")).ToPoint3D();
|
|
_effect.DisableColor = ((ByteColor) ThemeManager.Current.TryFindResource("InactiveColorPickerColorKey")).ToPoint3D();
|
|
|
|
SizeChanged += (_, __) =>
|
|
{
|
|
(_effect.AspectRatioCorrectionX, _effect.AspectRatioCorrectionY) =
|
|
BiaHsvWheelCursor.MakeAspectRatioCorrection(ActualWidth, ActualHeight);
|
|
};
|
|
|
|
_isEnabledChangeNotifier = new PropertyChangeNotifier(this, IsEnabledProperty);
|
|
_isEnabledChangeNotifier.ValueChanged += (_, __) =>
|
|
{
|
|
_effect.IsEnabled = IsEnabled ? 1.0f : 0.0f;
|
|
InvalidateVisual();
|
|
};
|
|
}
|
|
|
|
protected override void OnRender(DrawingContext dc)
|
|
{
|
|
base.OnRender(dc);
|
|
|
|
if (ActualWidth <= 1 ||
|
|
ActualHeight <= 1)
|
|
return;
|
|
|
|
var rounder = new LayoutRounder(this);
|
|
|
|
var rect = rounder.RoundRenderRectangle(true);
|
|
|
|
dc.DrawRectangle(Brushes.Transparent, null, rect);
|
|
}
|
|
|
|
/// <returns>マウスがホイール外を指しているか?</returns>
|
|
private bool UpdateParams(in LayoutRounder rounder, MouseEventArgs e)
|
|
{
|
|
var pos = e.GetPosition(this);
|
|
|
|
var bw = rounder.RoundLayoutValue(FrameworkElementExtensions.BorderWidth);
|
|
|
|
var width = ActualWidth - bw * 2;
|
|
var height = ActualHeight - bw * 2;
|
|
|
|
var x = (pos.X - bw) / width;
|
|
var y = (pos.Y - bw) / height;
|
|
|
|
var dx = x - 0.5;
|
|
var dy = y - 0.5;
|
|
|
|
var (cx, cy) = BiaHsvWheelCursor.MakeAspectRatioCorrection(ActualWidth, ActualHeight);
|
|
dx *= cx;
|
|
dy *= cy;
|
|
|
|
var h = (Math.Atan2(-dy, -dx) + Math.PI) / (2.0 * Math.PI);
|
|
var s = Math.Sqrt(dx * dx + dy * dy) * 2;
|
|
var ss = s;
|
|
|
|
h = NumberHelper.Clamp01(h);
|
|
s = NumberHelper.Clamp01(s);
|
|
|
|
Hue = h;
|
|
Saturation = s;
|
|
|
|
return ss > 1;
|
|
}
|
|
|
|
private bool _isMouseDown;
|
|
private bool _isContinuousEdited;
|
|
private (double, double) _ContinuousEditingStartValue;
|
|
|
|
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
|
|
{
|
|
base.OnMouseLeftButtonDown(e);
|
|
|
|
if (IsReadOnly)
|
|
return;
|
|
|
|
var rounder = new LayoutRounder(this);
|
|
|
|
var pos = e.GetPosition(this);
|
|
if (IsOutSide(rounder, Unsafe.As<Point, ImmutableVec2_double>(ref pos)))
|
|
return;
|
|
|
|
_isMouseDown = true;
|
|
GuiHelper.HideCursor();
|
|
|
|
_ContinuousEditingStartValue = (Hue, Saturation);
|
|
_isContinuousEdited = true;
|
|
|
|
StartedContinuousEditingCommand?.ExecuteIfCan(null);
|
|
|
|
UpdateParams(rounder, e);
|
|
|
|
CaptureMouse();
|
|
|
|
this.SetMouseClipping();
|
|
|
|
e.Handled = true;
|
|
}
|
|
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
{
|
|
base.OnMouseMove(e);
|
|
|
|
if (IsReadOnly)
|
|
return;
|
|
|
|
if (_isMouseDown == false)
|
|
return;
|
|
|
|
var rounder = new LayoutRounder(this);
|
|
|
|
var isOut = UpdateParams(rounder, e);
|
|
|
|
// マウス位置を補正する
|
|
if (isOut)
|
|
{
|
|
var pos = BiaHsvWheelCursor.MakeCursorRenderPos(rounder, ActualWidth, ActualHeight, Hue, Saturation);
|
|
|
|
var mousePos = PointToScreen(Unsafe.As<ImmutableVec2_double, Point>(ref pos));
|
|
Win32Helper.SetCursorPos((int) mousePos.X, (int) mousePos.Y);
|
|
}
|
|
|
|
e.Handled = true;
|
|
}
|
|
|
|
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
|
|
{
|
|
base.OnMouseLeftButtonUp(e);
|
|
|
|
if (IsReadOnly)
|
|
return;
|
|
|
|
if (_isMouseDown == false)
|
|
return;
|
|
|
|
_isMouseDown = false;
|
|
GuiHelper.ShowCursor();
|
|
this.ResetMouseClipping();
|
|
ReleaseMouseCapture();
|
|
|
|
if (_isContinuousEdited)
|
|
{
|
|
if (EndContinuousEditingCommand != null)
|
|
{
|
|
if (EndContinuousEditingCommand.CanExecute(null))
|
|
{
|
|
var changedValue = (Hue, Saturation);
|
|
(Hue, Saturation) = _ContinuousEditingStartValue;
|
|
|
|
EndContinuousEditingCommand.Execute(null);
|
|
|
|
StartedBatchEditingCommand?.ExecuteIfCan(null);
|
|
|
|
(Hue, Saturation) = changedValue;
|
|
|
|
EndBatchEditingCommand?.ExecuteIfCan(null);
|
|
}
|
|
}
|
|
|
|
_isContinuousEdited = false;
|
|
}
|
|
|
|
e.Handled = true;
|
|
}
|
|
|
|
protected override void OnMouseLeave(MouseEventArgs e)
|
|
{
|
|
base.OnMouseLeave(e);
|
|
|
|
if (_isMouseDown)
|
|
{
|
|
_isMouseDown = false;
|
|
ReleaseMouseCapture();
|
|
GuiHelper.ShowCursor();
|
|
this.ResetMouseClipping();
|
|
}
|
|
|
|
e.Handled = true;
|
|
}
|
|
|
|
private bool IsOutSide(in LayoutRounder rounder, in ImmutableVec2_double pos)
|
|
{
|
|
var bw = rounder.RoundLayoutValue(FrameworkElementExtensions.BorderWidth);
|
|
|
|
var width = ActualWidth - bw * 2;
|
|
var height = ActualHeight - bw * 2;
|
|
|
|
var x = (pos.X - bw) / width;
|
|
var y = (pos.Y - bw) / height;
|
|
|
|
var dx = x - 0.5;
|
|
var dy = y - 0.5;
|
|
|
|
var (cx, cy) = BiaHsvWheelCursor.MakeAspectRatioCorrection(ActualWidth, ActualHeight);
|
|
dx *= cx;
|
|
dy *= cy;
|
|
|
|
var s = Math.Sqrt(dx * dx + dy * dy) * 2;
|
|
|
|
return s > 1;
|
|
}
|
|
}
|
|
} |