using iNKORE.UI.WPF.Modern.Controls; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using ColorPicker; using iNKORE.UI.WPF.Helpers; using static Ink_Canvas.Popups.ColorPalette; using System.Drawing; using Ink_Canvas.Helpers; using Color = System.Windows.Media.Color; using Point = System.Windows.Point; using Image = System.Windows.Controls.Image; using System.Reflection; namespace Ink_Canvas.Popups { public partial class ColorPalette : UserControl { public enum ColorPaletteColor { ColorBlack, ColorWhite, ColorRed, ColorOrange, ColorYellow, ColorLime, ColorGreen, ColorTeal, ColorCyan, ColorBlue, ColorIndigo, ColorPurple, ColorFuchsia, ColorPink, ColorRose, ColorCustom }; private Color[] _darkColors = new Color[] { Color.FromRgb(9, 9, 11), Color.FromRgb(250, 250, 250), Color.FromRgb(220, 38, 38), Color.FromRgb(234, 88, 12), Color.FromRgb(250, 204, 21), Color.FromRgb(101, 163, 13), Color.FromRgb(22, 163, 74), Color.FromRgb(13, 148, 136), Color.FromRgb(8, 145, 178), Color.FromRgb(37, 99, 235), Color.FromRgb(79, 70, 229), Color.FromRgb(124, 58, 237), Color.FromRgb(192, 38, 211), Color.FromRgb(219, 39, 119), Color.FromRgb(225, 29, 72), }; private Color[] _lightColors = new Color[] { Color.FromRgb(9, 9, 11), Color.FromRgb(250, 250, 250), Color.FromRgb(239, 68, 68), Color.FromRgb(249, 115, 22), Color.FromRgb(253, 224, 71), Color.FromRgb(163, 230, 53), Color.FromRgb(74, 222, 128), Color.FromRgb(94, 234, 212), Color.FromRgb(34, 211, 238), Color.FromRgb(59, 130, 246), Color.FromRgb(129, 140, 248), Color.FromRgb(168, 85, 247), Color.FromRgb(217, 70, 239), Color.FromRgb(236, 72, 153), Color.FromRgb(244, 63, 94), }; public string[] ColorPaletteColorStrings = new[] { "black", "white", "red", "orange", "yellow", "lime", "green", "teal", "cyan", "blue", "indigo", "purple", "fuchsia", "pink", "rose" }; public Border[] ColorPaletteColorButtonBorders; public Border[] PenModeTabButtonBorders; public SimpleStackPanel[] PenModeTabButtonIndicators; public GeometryDrawing[] PenModeTabButtonIcons; public TextBlock[] PenModeTabButtonTexts; #region 暗色亮色成員 /// /// 內部變量,代表是否使用暗色配色方案 /// private bool _usingDarkColors = true; /// /// 公共成員,讀取或修改是否使用暗色配色方案 /// public bool UsingDarkColors { get => _usingDarkColors; set { var pre = _usingDarkColors; _usingDarkColors = value; UpdateColorPaletteColorsAndColorModeChangeButton(); ColorModeChanged?.Invoke(this, new ColorModeChangedEventArgs() { IsPreviousUsedDarkColor = pre, IsNowUsingDarkColor = value, TriggerMode = TriggerMode.TriggeredByCode, }); } } public class ColorModeChangedEventArgs : EventArgs { public bool IsPreviousUsedDarkColor { get; set; } public bool IsNowUsingDarkColor { get; set; } public TriggerMode TriggerMode { get; set; } } #endregion #region 選中顏色成員 /// /// 內部變量,代表當前選中的顏色 /// private ColorPaletteColor _colorSelected = ColorPaletteColor.ColorRed; /// /// 公共成員,讀取或修改當前選中的顏色,實時生效 /// public ColorPaletteColor SelectedColor { get => _colorSelected; set { var pre = _colorSelected; _colorSelected = value; UpdateColorButtonsCheckedDisplayStatus(); UpdateCustomColorButtonCheckedDisplayStatus(); ColorSelectionChanged?.Invoke(this, new ColorSelectionChangedEventArgs { PreviousColor = pre, NowColor = value, TriggerMode = TriggerMode.TriggeredByCode, CustomColor = value == ColorPaletteColor.ColorCustom ? (Color)_customColor : new Color(), }); } } public class ColorSelectionChangedEventArgs : EventArgs { public ColorPaletteColor PreviousColor { get; set; } public ColorPaletteColor NowColor { get; set; } public TriggerMode TriggerMode { get; set; } public Color CustomColor { get; set; } } #endregion #region 自定義顏色成員 private Color? _customColor = null; public Color? CustomColor { get => _customColor; set { var pre = _customColor; _customColor = value; CustomColorChanged?.Invoke(this, new CustomColorChangedEventArgs { PreviousColor = pre, NowColor = value, TriggerMode = TriggerMode.TriggeredByCode, }); } } public class CustomColorChangedEventArgs : EventArgs { public Color? PreviousColor { get; set; } public Color? NowColor { get; set; } public TriggerMode TriggerMode { get; set; } } #endregion #region 墨跡顏色相關邏輯 /// /// 按下的顏色按鈕,用於顏色按鈕鼠標事件 /// private Border lastColorBtnDown; #region 顏色按鈕和自定義顏色按鈕 選中狀態管理 /// /// 顏色按鈕的選中狀態更新函數,根據選中的顏色判斷對勾的前景色 /// public void UpdateColorButtonsCheckedDisplayStatus() { foreach (var bd in ColorPaletteColorButtonBorders) { bd.Child = null; } UpdateCustomColorButtonCheckedDisplayStatus(); if (_colorSelected == ColorPaletteColor.ColorCustom) return; var index = (int)_colorSelected; var bdSel = ColorPaletteColorButtonBorders[index]; Image checkedImage = new Image(); checkedImage.Width = 24; checkedImage.Height = 24; var checkLight = this.FindResource("CheckedLightIcon"); var checkDark = this.FindResource("CheckedDarkIcon"); if (ColorUtilities.GetReverseForegroundColor(ColorUtilities.GetGrayLevel((_usingDarkColors?_darkColors:_lightColors)[(int)_colorSelected])) == Colors.Black) checkedImage.Source = checkDark as DrawingImage; else checkedImage.Source = checkLight as DrawingImage; bdSel.Child = checkedImage; } /// /// 更新自定義顏色按鈕的選中狀態 /// private void UpdateCustomColorButtonCheckedDisplayStatus() { if (_customColor == null) { CustomColorButtonColorBorder.Visibility = Visibility.Collapsed; CustomColorButtonIcon.Visibility = Visibility.Visible; } else { CustomColorButtonColorBorder.Visibility = Visibility.Visible; CustomColorButtonColorBorder.Background = new SolidColorBrush((Color)_customColor); CustomColorButtonIcon.Visibility = Visibility.Collapsed; if (_colorSelected == ColorPaletteColor.ColorCustom) CustomColorButtonColorBorderCheckIcon.Visibility = Visibility.Visible; else CustomColorButtonColorBorderCheckIcon.Visibility = Visibility.Collapsed; CustomColorButtonColorBorderCheckIcon.Source = this.FindResource(ColorUtilities.GetReverseForegroundColor(ColorUtilities.GetGrayLevel((Color)_customColor)) == Colors.White ? "CheckedLightIcon" : "CheckedDarkIcon") as DrawingImage; } } #endregion #region 顏色按鈕的鼠標事件 private void ColorButton_MouseDown(object sender, MouseButtonEventArgs e) { lastColorBtnDown = sender as Border; ColorBtnStoryboardScaleSmaller(sender); } private void ColorButton_MouseUp(object sender, MouseButtonEventArgs e) { if (lastColorBtnDown != sender) return; lastColorBtnDown = null; var border = (Border)sender; var index = Array.IndexOf(ColorPaletteColorStrings, border.Name.ToLower()); var pre = _colorSelected; _colorSelected = (ColorPaletteColor)index; UpdateColorButtonsCheckedDisplayStatus(); ColorBtnStoryboardScaleLarger(sender); ColorSelectionChanged?.Invoke(this, new ColorSelectionChangedEventArgs { PreviousColor = pre, NowColor = (ColorPaletteColor)index, TriggerMode = TriggerMode.TriggeredByUser, }); } private void ColorButton_MouseLeave(object sender, MouseEventArgs e) { if (lastColorBtnDown != null) { var la = lastColorBtnDown as Border; var st = la.RenderTransform as ScaleTransform; if (st.ScaleX != 1 && st.ScaleY != 1) ColorBtnStoryboardScaleLarger(lastColorBtnDown); } lastColorBtnDown = null; } #endregion #region 自定義顏色 Picker 相關邏輯 private void UpdateCustomColorPickerDisplayStatus() { if (_customColor == null) { CustomColorHexTextBox.Text = "请在上方选择一个颜色"; CustomColorHexBorder.Background = new SolidColorBrush(Colors.Transparent); } else { CustomColorHexTextBox.Text = "#" + CustomColorPicker.SelectedColor.R.ToString("X2") + CustomColorPicker.SelectedColor.G.ToString("X2") + CustomColorPicker.SelectedColor.B.ToString("X2"); CustomColorHexBorder.Background = new SolidColorBrush(CustomColorPicker.SelectedColor); } } private void CustomColorPicker_ColorChanged(object sender, RoutedEventArgs e) { var cp = sender as SquarePicker; var pre = _customColor; _customColor = cp.SelectedColor; Trace.WriteLine(_customColor); if (_colorSelected != ColorPaletteColor.ColorCustom) { var mode_pre = _colorSelected; _colorSelected = ColorPaletteColor.ColorCustom; ColorSelectionChanged?.Invoke(this, new ColorSelectionChangedEventArgs { PreviousColor = mode_pre, NowColor = _colorSelected, TriggerMode = TriggerMode.TriggeredByUser, CustomColor = cp.SelectedColor, }); } CustomColorChanged?.Invoke(this, new CustomColorChangedEventArgs { PreviousColor = pre, NowColor = _customColor, TriggerMode = TriggerMode.TriggeredByUser, }); UpdateCustomColorPickerDisplayStatus(); UpdateCustomColorButtonCheckedDisplayStatus(); UpdateColorButtonsCheckedDisplayStatus(); } #endregion #region 顏色按鈕Storyboard動畫邏輯 private void ColorBtnStoryBoardScaleAnimation(object sender, double from, double to) { var border = sender as Border; var sb = new Storyboard(); var scaleAnimationX = new DoubleAnimation() { From = from, To = to, Duration = new Duration(TimeSpan.FromMilliseconds(100)) }; var scaleAnimationY = new DoubleAnimation() { From = from, To = to, Duration = new Duration(TimeSpan.FromMilliseconds(100)) }; scaleAnimationY.EasingFunction = new CubicEase(); scaleAnimationX.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(scaleAnimationX, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)")); Storyboard.SetTargetProperty(scaleAnimationY, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)")); sb.Children.Add(scaleAnimationX); sb.Children.Add(scaleAnimationY); sb.Begin(border); } private void ColorBtnStoryboardScaleSmaller(object sender) { ColorBtnStoryBoardScaleAnimation(sender, 1D, 0.9); } private void ColorBtnStoryboardScaleLarger(object sender) { ColorBtnStoryBoardScaleAnimation(sender, 0.9, 1D); } #endregion #region 顏色按鈕的亮色暗色和切換按鈕 相關邏輯 /// /// 根據是否使用暗色配色方案來更新顏色按鈕的背景顏色,並更新切換按鈕的UI文字 /// private void UpdateColorPaletteColorsAndColorModeChangeButton() { foreach (var bd in ColorPaletteColorButtonBorders) { bd.Background = new SolidColorBrush((_usingDarkColors ? _darkColors : _lightColors)[ Array.IndexOf(ColorPaletteColorStrings, bd.Name.ToLower())]); } ChangedColorButtonsTransparentVisibility(_penModeSelected == PenMode.HighlighterMode); var tb = ((SimpleStackPanel)ColorModeChangeButton.Content).Children.OfType().Single(); tb.Text = _usingDarkColors ? "亮色" : "暗色"; } private void ColorModeChangeButton_Clicked(object sender, RoutedEventArgs e) { var pre = _usingDarkColors; _usingDarkColors = !_usingDarkColors; UpdateColorPaletteColorsAndColorModeChangeButton(); UpdateColorButtonsCheckedDisplayStatus(); ColorModeChanged?.Invoke(this, new ColorModeChangedEventArgs() { IsPreviousUsedDarkColor = pre, IsNowUsingDarkColor = _usingDarkColors, TriggerMode = TriggerMode.TriggeredByUser, }); } #endregion #region GetRawColor /// /// 根據傳入的 獲取對應的顏色 /// /// 傳入 ColorPaletteColor /// 如果傳入true,則不會遵循調色板的亮色暗色配色方案, 則不能為null。 /// 指定是否使用暗色配色方案,如果為 false,使用亮色,如果在 true 的情況下傳入 null 會報錯。 /// 傳入的 ColorPaletteColor 對應的顏色 public Color GetColor(ColorPaletteColor color, bool doNotFollowPaletteColor, bool? isDarkPalette) { if (doNotFollowPaletteColor && isDarkPalette == null) throw new ArgumentNullException(nameof(isDarkPalette),"指定了自訂的配色方案卻沒有傳入正確的 isDarkPalette。"); if (color == ColorPaletteColor.ColorCustom) return _customColor??new Color(); return (doNotFollowPaletteColor ? ((bool)isDarkPalette ? _darkColors : _lightColors) : (_usingDarkColors ? _darkColors : _lightColors))[(int)color]; } #endregion #region 隨機切換顏色按鈕 邏輯 public static int StrictNext(int maxValue = int.MaxValue) { return new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)).Next(maxValue); } private void RandomColorButton_Clicked(object sender, RoutedEventArgs e) { var pre = _colorSelected; _colorSelected = (ColorPaletteColor)StrictNext(14); UpdateColorButtonsCheckedDisplayStatus(); UpdateCustomColorButtonCheckedDisplayStatus(); ColorSelectionChanged?.Invoke(this, new ColorSelectionChangedEventArgs { PreviousColor = pre, NowColor = _colorSelected, TriggerMode = TriggerMode.TriggeredByUser, }); } #endregion #region 顏色按鈕透明度 邏輯 private void ChangedColorButtonsTransparentVisibility(bool isTransparent) { foreach (var bd in ColorPaletteColorButtonBorders) { var ori_color = ((SolidColorBrush)bd.Background).Color; if (isTransparent) ori_color.A = (byte)Math.Round(byte.MaxValue * 0.6,0); else ori_color.A = byte.MaxValue; bd.Background = new SolidColorBrush(ori_color); } } #endregion #endregion #region 自定義顏色 相關邏輯 private void CustomColorButton_Clicked(object sender, RoutedEventArgs e) { if (_customColor == null) { CustomColorPanel.Visibility = Visibility.Visible; } else { if (_colorSelected == ColorPaletteColor.ColorCustom) CustomColorPanel.Visibility = Visibility.Visible; else { var pre = _colorSelected; _colorSelected = ColorPaletteColor.ColorCustom; ColorSelectionChanged?.Invoke(this, new ColorSelectionChangedEventArgs() { PreviousColor = pre, NowColor = _colorSelected, TriggerMode = TriggerMode.TriggeredByUser, CustomColor = (Color)_customColor, }); UpdateColorButtonsCheckedDisplayStatus(); UpdateCustomColorButtonCheckedDisplayStatus(); UpdateCustomColorPickerDisplayStatus(); } } } #endregion #region 墨跡糾正成員 private bool _inkRecognition = true; public bool InkRecognition { get => _inkRecognition; set { var pre = _inkRecognition; _inkRecognition = value; InkRecognitionChanged?.Invoke(this, new InkRecognitionChangedEventArgs { PreviousStatus = pre, NowStatus = value, TriggerMode = TriggerMode.TriggeredByCode, }); } } public class InkRecognitionChangedEventArgs : EventArgs { public bool PreviousStatus { get; set; } public bool NowStatus { get; set; } public TriggerMode TriggerMode { get; set; } public InkRecognitionOptions Options { get; set; } } #endregion #region 墨跡糾正相關邏輯 private void InkRecognitionMoreButton_MouseDown(object sender, MouseButtonEventArgs e) { var ircm = this.FindResource("InkRecognitionContextMenu") as ContextMenu; var transform = InkRecognitionMoreButton.TransformToVisual(this); var pt = transform.Transform(new Point(0, 0)); ircm.VerticalOffset = pt.Y-4; ircm.HorizontalOffset = pt.X-4; ircm.IsOpen = true; ircm.StaysOpen = true; } #region 墨跡糾正更多菜單 相關邏輯 /// /// 更新墨跡糾正菜單裡面菜單項的選中狀態,根據_inkRecognition來做更新 /// private void UpdateInkRecognitionContextMenuDisplayStatus() { var ircm = (ContextMenu)this.FindResource("InkRecognitionContextMenu"); var enableRecog = ircm.Items[0] as MenuItem; enableRecog.IsChecked = _inkRecognition; var recogTri = ircm.Items[2] as MenuItem; var recogQua = ircm.Items[3] as MenuItem; var recogEll = ircm.Items[4] as MenuItem; var recogPlg = ircm.Items[5] as MenuItem; var recogSub = new MenuItem[] { recogTri, recogQua, recogEll, recogPlg, }; foreach (var mi in recogSub) mi.IsEnabled = _inkRecognition; } private void InkRecognitionContextMenuItem_Checked(object sender, RoutedEventArgs e) { var mi = (MenuItem)sender; if (mi.Name == "EnableRecog") { var pre = _inkRecognition; Trace.WriteLine(mi.IsChecked); _inkRecognition = mi.IsChecked; UpdateInkRecognitionContextMenuDisplayStatus(); InkRecognitionToggleSwitchImage.Source = this.FindResource(_inkRecognition ? "SwitchOnImage" : "SwitchOffImage") as DrawingImage; InkRecognitionChanged?.Invoke(this, new InkRecognitionChangedEventArgs { PreviousStatus = pre, NowStatus = _inkRecognition, TriggerMode = TriggerMode.TriggeredByUser, }); } else { UpdateInkRecognitionContextMenuDisplayStatus(); InkRecognitionChanged?.Invoke(this, new InkRecognitionChangedEventArgs { PreviousStatus = _inkRecognition, NowStatus = _inkRecognition, TriggerMode = TriggerMode.TriggeredByUser, }); } } private void InkRecognitionContextMenu_Closed(object sender, RoutedEventArgs e) { InkRecognitionMoreButton.Background = new SolidColorBrush(Colors.Transparent); UpdateInkRecognitionContextMenuDisplayStatus(); } private void InkRecognitionContextMenu_Opened(object sender, RoutedEventArgs e) { InkRecognitionMoreButton.Background = new SolidColorBrush(Color.FromArgb(34,39, 39, 42)); UpdateInkRecognitionContextMenuDisplayStatus(); } #endregion #region 墨跡糾正開關 相關邏輯 private void InkRecognitionToggleSwitchButton_Clicked(object sender, RoutedEventArgs e) { var pre = _inkRecognition; _inkRecognition = !_inkRecognition; InkRecognitionToggleSwitchImage.Source = this.FindResource(_inkRecognition ? "SwitchOnImage" : "SwitchOffImage") as DrawingImage; UpdateInkRecognitionContextMenuDisplayStatus(); InkRecognitionChanged?.Invoke(this, new InkRecognitionChangedEventArgs { PreviousStatus = pre, NowStatus = _inkRecognition, TriggerMode = TriggerMode.TriggeredByUser, }); } #endregion #endregion #region 關閉彈窗 邏輯 private bool isCloseButtonDown = false; private void CloseButtonBorder_MouseDown(object sender, MouseButtonEventArgs e) { isCloseButtonDown = true; CloseButtonBorder.Background = new SolidColorBrush(Color.FromArgb(34, 220, 38, 38)); } private void CloseButtonBorder_MouseLeave(object sender, MouseEventArgs e) { isCloseButtonDown = false; CloseButtonBorder.Background = new SolidColorBrush(Colors.Transparent); } private void CloseButtonBorder_MouseUp(object sender, MouseButtonEventArgs e) { if (!isCloseButtonDown) return; CloseButtonBorder_MouseLeave(null, null); PaletteShouldCloseEvent?.Invoke(this,new RoutedEventArgs()); } #endregion #region 筆觸模式 成員 public enum PenMode { PenMode, HighlighterMode, LaserPenMode } private PenMode _penModeSelected = PenMode.PenMode; public PenMode PenModeSelected { get => _penModeSelected; set { var pre = _penModeSelected; _penModeSelected = value; UpdatePenModeButtonsCheckedDisplayStatus(); ChangedColorButtonsTransparentVisibility(_penModeSelected == PenMode.HighlighterMode); PenModeChanged?.Invoke(this, new PenModeChangedEventArgs() { PreviousMode = pre, NowMode = value, TriggerMode = TriggerMode.TriggeredByCode, }); } } public class PenModeChangedEventArgs : EventArgs { public PenMode PreviousMode { get; set; } public PenMode NowMode { get; set; } public TriggerMode TriggerMode { get; set; } } #endregion #region 筆觸模式 相關邏輯 private void UpdatePenModeButtonsCheckedDisplayStatus() { foreach (var bd in PenModeTabButtonBorders) { bd.Background = new SolidColorBrush(Colors.Transparent); } foreach (var indicator in PenModeTabButtonIndicators) { indicator.Visibility = Visibility.Hidden; } foreach (var gd in PenModeTabButtonIcons) { gd.Brush = new SolidColorBrush(Color.FromRgb(63, 63, 70)); } foreach (var text in PenModeTabButtonTexts) { text.Foreground = new SolidColorBrush(Color.FromRgb(63, 63, 70)); text.FontWeight = FontWeights.Normal; } PenModeTabButtonBorders[(int)_penModeSelected].Background = new SolidColorBrush(Color.FromArgb(34, 59, 130, 246)); PenModeTabButtonIndicators[(int)_penModeSelected].Visibility = Visibility.Visible; PenModeTabButtonIcons[(int)_penModeSelected].Brush = new SolidColorBrush(Color.FromRgb(37, 99, 235)); PenModeTabButtonTexts[(int)_penModeSelected].Foreground = new SolidColorBrush(Color.FromRgb(37, 99, 235)); PenModeTabButtonTexts[(int)_penModeSelected].FontWeight = FontWeights.Bold; } /// /// 根據傳入的觸筆模式修改Quick Action 子項目的可見性。 /// /// private void UpdateQuickActionItemsVisibilityByPenMode(PenMode penMode) { _isDisplayQuickActions = true; if (penMode == PenMode.PenMode) { _isDisplayInkRecognitionQuickAction = true; _isDisplayInkPressureQuickAction = true; _isDisplayCircleTipShapeQuickAction = false; _isDisplayFingerModeQuickAction = true; } else if (penMode == PenMode.HighlighterMode) { _isDisplayInkRecognitionQuickAction = true; _isDisplayInkPressureQuickAction = false; _isDisplayCircleTipShapeQuickAction = true; _isDisplayFingerModeQuickAction = true; } else if (penMode == PenMode.LaserPenMode) { _isDisplayQuickActions = false; } UpdateQuickActionVisibility(_isDisplayQuickActions); UpdateQuickActionItemsVisibility(new QuickActionItemsVisibility() { InkRecognition = _isDisplayInkRecognitionQuickAction, InkPressure = _isDisplayInkPressureQuickAction, CircleTipShape = _isDisplayCircleTipShapeQuickAction, FingerMode = _isDisplayFingerModeQuickAction }); } private void PenTabButton_MouseDown(object sender, MouseButtonEventArgs e) { var pre = _penModeSelected; _penModeSelected = (PenMode)Array.IndexOf(PenModeTabButtonBorders, (Border)sender); QuickActionItemsScrollToLeft(); UpdatePenModeButtonsCheckedDisplayStatus(); ChangedColorButtonsTransparentVisibility(_penModeSelected == PenMode.HighlighterMode); UpdateQuickActionItemsVisibilityByPenMode(_penModeSelected); PenModeChanged?.Invoke(this, new PenModeChangedEventArgs() { PreviousMode = pre, NowMode = _penModeSelected, TriggerMode = TriggerMode.TriggeredByUser, }); } #endregion #region Quick Action 滾動邏輯 private void SCManipulationBoundaryFeedback(object sender, ManipulationBoundaryFeedbackEventArgs e) { e.Handled = true; } public static class ScrollViewerBehavior { public static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.RegisterAttached("HorizontalOffset", typeof(double), typeof(ScrollViewerBehavior), new UIPropertyMetadata(0.0, OnHorizontalOffsetChanged)); public static void SetHorizontalOffset(FrameworkElement target, double value) => target.SetValue(HorizontalOffsetProperty, value); public static double GetHorizontalOffset(FrameworkElement target) => (double)target.GetValue(HorizontalOffsetProperty); private static void OnHorizontalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) => (target as ScrollViewer)?.ScrollToHorizontalOffset((double)e.NewValue); public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), typeof(ScrollViewerBehavior), new UIPropertyMetadata(0.0, OnVerticalOffsetChanged)); public static void SetVerticalOffset(FrameworkElement target, double value) => target.SetValue(VerticalOffsetProperty, value); public static double GetVerticalOffset(FrameworkElement target) => (double)target.GetValue(VerticalOffsetProperty); private static void OnVerticalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) => (target as ScrollViewer)?.ScrollToVerticalOffset((double)e.NewValue); } private void UpdateQuickActionsDotsIndicatorDisplayStatus(int highlightedDotIndex) { if (highlightedDotIndex == 1) { QuickActionDot1.Background = new SolidColorBrush(Color.FromRgb(39, 39, 42)); QuickActionDot2.Background = new SolidColorBrush(Color.FromRgb(212, 212, 216)); } else { QuickActionDot2.Background = new SolidColorBrush(Color.FromRgb(39, 39, 42)); QuickActionDot1.Background = new SolidColorBrush(Color.FromRgb(212, 212, 216)); } } private void QuickActionsScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) { UpdateQuickActionsDotsIndicatorDisplayStatus(((ScrollViewer)sender).HorizontalOffset >= 110 ? 2 : 1); } private void QuickActionsScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) { ScrollViewer scrollViewer = (ScrollViewer)sender; if (e.Delta < 0) { //scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + 270); var sb = new Storyboard(); var ofs = scrollViewer.HorizontalOffset; var animation = new DoubleAnimation { From = ofs, To = 120, Duration = TimeSpan.FromMilliseconds(200) }; animation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut, }; Storyboard.SetTargetProperty(animation, new PropertyPath(ScrollViewerBehavior.HorizontalOffsetProperty)); Storyboard.SetTargetName(animation,"QuickActionScrollViewer"); sb.Children.Add(animation); scrollViewer.ScrollToHorizontalOffset(ofs); sb.Begin(scrollViewer); } else { //scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - 270); var sb = new Storyboard(); var ofs = scrollViewer.HorizontalOffset; var animation = new DoubleAnimation { From = ofs, To = 0, Duration = TimeSpan.FromMilliseconds(200) }; animation.EasingFunction = new CubicEase(){ EasingMode = EasingMode.EaseOut, }; Storyboard.SetTargetProperty(animation, new PropertyPath(ScrollViewerBehavior.HorizontalOffsetProperty)); Storyboard.SetTargetName(animation,"QuickActionScrollViewer"); sb.Children.Add(animation); scrollViewer.ScrollToHorizontalOffset(ofs); sb.Begin(scrollViewer); } e.Handled = true; } private void QuickActionItemsScrollToLeft() { var sb = new Storyboard(); var ofs = QuickActionScrollViewer.HorizontalOffset; var animation = new DoubleAnimation { From = ofs, To = 0, Duration = TimeSpan.FromMilliseconds(200) }; animation.EasingFunction = new CubicEase(){ EasingMode = EasingMode.EaseOut, }; Storyboard.SetTargetProperty(animation, new PropertyPath(ScrollViewerBehavior.HorizontalOffsetProperty)); Storyboard.SetTargetName(animation,"QuickActionScrollViewer"); sb.Children.Add(animation); QuickActionScrollViewer.ScrollToHorizontalOffset(ofs); sb.Begin(QuickActionScrollViewer); } #endregion #region Quick Action 可見性管理 成員 private bool _isDisplayQuickActions = true; public bool IsDisplayQuickActions { get => _isDisplayQuickActions; set { var pre = _isDisplayQuickActions; _isDisplayQuickActions = value; UpdateQuickActionVisibility(value); UpdateQuickActionItemsVisibility(new QuickActionItemsVisibility() { InkRecognition = _isDisplayInkRecognitionQuickAction, InkPressure = _isDisplayInkPressureQuickAction, CircleTipShape = _isDisplayCircleTipShapeQuickAction, FingerMode = _isDisplayFingerModeQuickAction }); QuickActionsVisibilityChanged?.Invoke(this, new QuickActionsVisibilityChangedEventsArgs() { PreviousStatus = pre, NowStatus = value, TriggerMode = TriggerMode.TriggeredByCode, }); } } public struct QuickActionItemsVisibility { public bool InkRecognition; public bool InkPressure; public bool CircleTipShape; public bool FingerMode; } public class QuickActionsVisibilityChangedEventsArgs : EventArgs { public bool PreviousStatus { get; set; } public bool NowStatus { get; set; } public TriggerMode TriggerMode { get; set; } public bool IsItemsVisibilityChanged { get; set; } = false; public QuickActionItemsVisibility ItemsVisibility { get; set; } } private void InvokeQuickActionItemsVisibilityEvent() { UpdateQuickActionItemsVisibility(new QuickActionItemsVisibility() { InkRecognition = _isDisplayInkRecognitionQuickAction, InkPressure = _isDisplayInkPressureQuickAction, CircleTipShape = _isDisplayCircleTipShapeQuickAction, FingerMode = _isDisplayFingerModeQuickAction }); QuickActionsVisibilityChanged?.Invoke(this, new QuickActionsVisibilityChangedEventsArgs() { PreviousStatus = _isDisplayQuickActions, NowStatus = _isDisplayQuickActions, TriggerMode = TriggerMode.TriggeredByCode, IsItemsVisibilityChanged = true, ItemsVisibility = new QuickActionItemsVisibility() { InkRecognition = _isDisplayInkRecognitionQuickAction, InkPressure = _isDisplayInkPressureQuickAction, CircleTipShape = _isDisplayCircleTipShapeQuickAction, FingerMode = _isDisplayFingerModeQuickAction } }); } private bool _isDisplayInkRecognitionQuickAction = true; public bool IsDisplayInkRecognitionQuickAction { get => _isDisplayInkRecognitionQuickAction; set { _isDisplayInkRecognitionQuickAction = value; InvokeQuickActionItemsVisibilityEvent(); } } private bool _isDisplayInkPressureQuickAction = true; public bool IsDisplayInkPressureQuickAction { get => _isDisplayInkPressureQuickAction; set { _isDisplayInkPressureQuickAction = value; InvokeQuickActionItemsVisibilityEvent(); } } private bool _isDisplayCircleTipShapeQuickAction = true; public bool IsDisplayCircleTipShapeQuickAction { get => _isDisplayCircleTipShapeQuickAction; set { _isDisplayCircleTipShapeQuickAction = value; InvokeQuickActionItemsVisibilityEvent(); } } private bool _isDisplayFingerModeQuickAction = true; public bool IsDisplayFingerModeQuickAction { get => _isDisplayFingerModeQuickAction; set { _isDisplayFingerModeQuickAction = value; InvokeQuickActionItemsVisibilityEvent(); } } #endregion #region Quick Action 可見性管理 相關邏輯 private void UpdateQuickActionItemsVisibility(QuickActionItemsVisibility visibility) { QuickActionItems.Children[0].Visibility = visibility.InkRecognition ? Visibility.Visible : Visibility.Collapsed; QuickActionItems.Children[1].Visibility = visibility.InkPressure ? Visibility.Visible : Visibility.Collapsed; QuickActionItems.Children[2].Visibility = visibility.CircleTipShape ? Visibility.Visible : Visibility.Collapsed; QuickActionItems.Children[3].Visibility = visibility.FingerMode ? Visibility.Visible : Visibility.Collapsed; } private void UpdateQuickActionVisibility(bool isVisible) { foreach (var fe in new FrameworkElement[] { _QuickAction_Grid, _QuickAction_Dots, _QuickAction_Line1, _QuickAction_Line2 }) { fe.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed; } } #endregion public enum TriggerMode { TriggeredByUser, TriggeredByCode } public enum PressureSimulation { PointSimulate, VelocitySimulate, None } private PressureSimulation _simulatePressure = PressureSimulation.PointSimulate; public PressureSimulation SimulatePressure { get => _simulatePressure; set { var pre = _simulatePressure; _simulatePressure = value; PressureSimulationChanged?.Invoke(this, new PressureSimulationChangedEventArgs() { PreviousMode = pre, NowMode = value, TriggerMode = TriggerMode.TriggeredByCode, }); } } private void UpdateSimulatePressureContextMenuDisplayStatus() { var spcm = (ContextMenu)this.FindResource("SimulatePressureContextMenu"); var pointSP = spcm.Items[0] as MenuItem; var velocitySP = spcm.Items[1] as MenuItem; var noneSP = spcm.Items[2] as MenuItem; var pressSub = new MenuItem[] { pointSP, velocitySP, noneSP }; isSimulatePressureCheckedByUser = false; foreach (var mi in pressSub) { if (mi.Name=="PointSP") mi.IsChecked = _simulatePressure == PressureSimulation.PointSimulate; else if (mi.Name == "VelocitySP") mi.IsChecked = _simulatePressure == PressureSimulation.VelocitySimulate; else if (mi.Name == "NoneSP") mi.IsChecked = _simulatePressure == PressureSimulation.None; } isSimulatePressureCheckedByUser = true; } private void SimulatePressureContextMenu_Closed(object sender, RoutedEventArgs e) { SimulatePressureMoreButton.Background = new SolidColorBrush(Colors.Transparent); UpdateSimulatePressureContextMenuDisplayStatus(); } private void SimulatePressureContextMenu_Opened(object sender, RoutedEventArgs e) { SimulatePressureMoreButton.Background = new SolidColorBrush(Color.FromArgb(34, 39, 39, 42)); UpdateSimulatePressureContextMenuDisplayStatus(); } private bool isSimulatePressureCheckedByUser = true; private void SimulatePressureContextMenuItem_Checked(object sender, RoutedEventArgs e) { if (!isSimulatePressureCheckedByUser) return; var mi = (MenuItem)sender; var pre = _simulatePressure; Trace.WriteLine(mi.Name); _simulatePressure = mi.Name == "PointSP" ? PressureSimulation.PointSimulate : mi.Name == "VelocitySP" ? PressureSimulation.VelocitySimulate : PressureSimulation.None; SimulatePressureToggleSwitchImage.Source = this.FindResource(_simulatePressure != PressureSimulation.None ? "SwitchOnImage" : "SwitchOffImage") as DrawingImage; UpdateSimulatePressureContextMenuDisplayStatus(); PressureSimulationChanged?.Invoke(this, new PressureSimulationChangedEventArgs() { PreviousMode = pre, NowMode = _simulatePressure, TriggerMode = TriggerMode.TriggeredByUser, }); } private void SimulatePressureToggleSwitchButton_Clicked(object sender, RoutedEventArgs e) { var pre = _simulatePressure; _simulatePressure = _simulatePressure == PressureSimulation.None ? PressureSimulation.PointSimulate : PressureSimulation.None; SimulatePressureToggleSwitchImage.Source = this.FindResource(_simulatePressure != PressureSimulation.None ? "SwitchOnImage" : "SwitchOffImage") as DrawingImage; UpdateSimulatePressureContextMenuDisplayStatus(); PressureSimulationChanged?.Invoke(this, new PressureSimulationChangedEventArgs() { PreviousMode = pre, NowMode = _simulatePressure, TriggerMode = TriggerMode.TriggeredByUser, }); } private void SimulatePressureMoreButton_MouseDown(object sender, MouseButtonEventArgs e) { var spcm = this.FindResource("SimulatePressureContextMenu") as ContextMenu; var transform = SimulatePressureMoreButton.TransformToVisual(this); var pt = transform.Transform(new Point(0, 0)); spcm.VerticalOffset = pt.Y - 4; spcm.HorizontalOffset = pt.X - 4; spcm.IsOpen = !spcm.IsOpen; } public class PressureSimulationChangedEventArgs : EventArgs { public PressureSimulation PreviousMode { get; set; } public PressureSimulation NowMode { get; set; } public TriggerMode TriggerMode { get; set; } } public class InkRecognitionOptions { public bool isEnableInkRecognition; public bool isRecognizeEllipse; public bool isRecognizeTriangle; public bool isRecognizeQuadrilateral; public bool isRecognizePolygon; } private void BackToPaletteButton_Clicked(object sender, RoutedEventArgs e) { CustomColorPanel.Visibility = Visibility.Collapsed; } public event EventHandler ColorSelectionChanged; public event EventHandler CustomColorChanged; public event EventHandler PenModeChanged; public event EventHandler InkRecognitionChanged; public event EventHandler PressureSimulationChanged; public event EventHandler ColorModeChanged; public event EventHandler QuickActionsVisibilityChanged; public event EventHandler PaletteShouldCloseEvent; public ColorPalette() { InitializeComponent(); ColorPaletteColorButtonBorders = new Border[] { Black, White, Red, Orange, Yellow, Lime, Green, Teal, Cyan, Blue, Indigo, Purple, Fuchsia, Pink, Rose }; PenModeTabButtonBorders = new Border[] { PenTabButton, HighlighterTabButton, LaserPenTabButton }; PenModeTabButtonIndicators = new SimpleStackPanel[] { PenTabButtonIndicator, HighlighterTabButtonIndicator, LaserPenTabButtonIndicator }; PenModeTabButtonIcons = new GeometryDrawing[] { PenTabButtonIcon, HighlighterTabButtonIcon, LaserPenTabButtonIcon }; PenModeTabButtonTexts = new TextBlock[] { PenTabButtonText, HighlighterTabButtonText, LaserPenTabButtonText }; foreach (var bd in ColorPaletteColorButtonBorders) { bd.RenderTransformOrigin = new Point(0.5, 0.5); bd.RenderTransform = new ScaleTransform(1, 1); } UpdatePenModeButtonsCheckedDisplayStatus(); UpdateColorButtonsCheckedDisplayStatus(); UpdateColorPaletteColorsAndColorModeChangeButton(); ChangedColorButtonsTransparentVisibility(false); UpdateQuickActionsDotsIndicatorDisplayStatus(1); UpdateQuickActionItemsVisibilityByPenMode(PenMode.PenMode); } } }