From 57f3c9d3d7487054cb8e19c08de2d211ebd7ea98 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Mon, 5 Oct 2015 20:21:18 +0200 Subject: [PATCH] Remove WpfDesigner Dependecy to SD Widgets --- .editorconfig | 3 + samples/XamlDesigner/Toolbox.cs | 2 +- .../Project/Controls/CollapsiblePanel.cs | 198 +++++++++++ .../Project/Controls/ColorHelper.cs | 110 ++++++ .../Project/Controls/ColorPicker.xaml | 232 +++++++++++++ .../Project/Controls/ColorPicker.xaml.cs | 191 +++++++++++ .../Project/Controls/ControlStyles.xaml | 2 +- .../Project/Controls/NumericUpDown.cs | 323 ++++++++++++++++++ .../Project/Controls/NumericUpDown.xaml | 140 ++++++++ .../Project/Controls/PanelMoveAdorner.cs | 5 - .../Project/Controls/Picker.cs | 151 ++++++++ .../Project/Controls/RelayCommand.cs | 118 +++++++ .../Project/Controls/ZoomButtons.cs | 66 ++++ .../Project/Controls/ZoomControl.cs | 5 +- .../Project/Controls/ZoomScrollViewer.cs | 184 ++++++++++ .../Project/Controls/ZoomScrollViewer.xaml | 94 +++++ .../Project/DesignSurface.xaml | 3 +- .../Extensions/QuickOperationMenuExtension.cs | 3 +- .../Project/FocusNavigator.cs | 5 +- .../Editors/BrushEditor/BrushEditorView.xaml | 4 +- .../Editors/BrushEditor/SolidBrushEditor.xaml | 2 +- .../PropertyGrid/Editors/NumberEditor.xaml | 2 +- .../Project/WpfDesign.Designer.csproj | 29 +- .../Project/themes/generic.xaml | 4 +- 24 files changed, 1844 insertions(+), 32 deletions(-) create mode 100644 .editorconfig create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/CollapsiblePanel.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorHelper.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.xaml create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/Picker.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/RelayCommand.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomButtons.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.xaml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..a087785bcf --- /dev/null +++ b/.editorconfig @@ -0,0 +1,3 @@ +root = true +[*] +indent_style = tab \ No newline at end of file diff --git a/samples/XamlDesigner/Toolbox.cs b/samples/XamlDesigner/Toolbox.cs index b4b400e8c6..c25b4f3972 100644 --- a/samples/XamlDesigner/Toolbox.cs +++ b/samples/XamlDesigner/Toolbox.cs @@ -70,7 +70,7 @@ namespace ICSharpCode.XamlDesigner { AddAssembly(Environment.ExpandEnvironmentVariables(path), false); } - catch (Exception ex) + catch (Exception) { } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/CollapsiblePanel.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/CollapsiblePanel.cs new file mode 100644 index 0000000000..99c43d1ab9 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/CollapsiblePanel.cs @@ -0,0 +1,198 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media.Animation; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + /// + /// Allows animated collapsing of the content of this panel. + /// + public class CollapsiblePanel : ContentControl + { + static CollapsiblePanel() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(CollapsiblePanel), + new FrameworkPropertyMetadata(typeof(CollapsiblePanel))); + FocusableProperty.OverrideMetadata(typeof(CollapsiblePanel), + new FrameworkPropertyMetadata(false)); + } + + public static readonly DependencyProperty IsCollapsedProperty = DependencyProperty.Register( + "IsCollapsed", typeof(bool), typeof(CollapsiblePanel), + new UIPropertyMetadata(false, new PropertyChangedCallback(OnIsCollapsedChanged))); + + public bool IsCollapsed { + get { return (bool)GetValue(IsCollapsedProperty); } + set { SetValue(IsCollapsedProperty, value); } + } + + public static readonly DependencyProperty CollapseOrientationProperty = + DependencyProperty.Register("CollapseOrientation", typeof(Orientation), typeof(CollapsiblePanel), + new FrameworkPropertyMetadata(Orientation.Vertical)); + + public Orientation CollapseOrientation { + get { return (Orientation)GetValue(CollapseOrientationProperty); } + set { SetValue(CollapseOrientationProperty, value); } + } + + public static readonly DependencyProperty DurationProperty = DependencyProperty.Register( + "Duration", typeof(TimeSpan), typeof(CollapsiblePanel), + new UIPropertyMetadata(TimeSpan.FromMilliseconds(250))); + + /// + /// The duration in milliseconds of the animation. + /// + public TimeSpan Duration { + get { return (TimeSpan)GetValue(DurationProperty); } + set { SetValue(DurationProperty, value); } + } + + protected internal static readonly DependencyProperty AnimationProgressProperty = DependencyProperty.Register( + "AnimationProgress", typeof(double), typeof(CollapsiblePanel), + new FrameworkPropertyMetadata(1.0)); + + /// + /// Value between 0 and 1 specifying how far the animation currently is. + /// + protected internal double AnimationProgress { + get { return (double)GetValue(AnimationProgressProperty); } + set { SetValue(AnimationProgressProperty, value); } + } + + protected internal static readonly DependencyProperty AnimationProgressXProperty = DependencyProperty.Register( + "AnimationProgressX", typeof(double), typeof(CollapsiblePanel), + new FrameworkPropertyMetadata(1.0)); + + /// + /// Value between 0 and 1 specifying how far the animation currently is. + /// + protected internal double AnimationProgressX { + get { return (double)GetValue(AnimationProgressXProperty); } + set { SetValue(AnimationProgressXProperty, value); } + } + + protected internal static readonly DependencyProperty AnimationProgressYProperty = DependencyProperty.Register( + "AnimationProgressY", typeof(double), typeof(CollapsiblePanel), + new FrameworkPropertyMetadata(1.0)); + + /// + /// Value between 0 and 1 specifying how far the animation currently is. + /// + protected internal double AnimationProgressY { + get { return (double)GetValue(AnimationProgressYProperty); } + set { SetValue(AnimationProgressYProperty, value); } + } + + static void OnIsCollapsedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((CollapsiblePanel)d).SetupAnimation((bool)e.NewValue); + } + + void SetupAnimation(bool isCollapsed) + { + if (this.IsLoaded) { + // If the animation is already running, calculate remaining portion of the time + double currentProgress = AnimationProgress; + if (!isCollapsed) { + currentProgress = 1.0 - currentProgress; + } + + DoubleAnimation animation = new DoubleAnimation(); + animation.To = isCollapsed ? 0.0 : 1.0; + animation.Duration = TimeSpan.FromSeconds(Duration.TotalSeconds * currentProgress); + animation.FillBehavior = FillBehavior.HoldEnd; + + this.BeginAnimation(AnimationProgressProperty, animation); + if (CollapseOrientation == Orientation.Horizontal) { + this.BeginAnimation(AnimationProgressXProperty, animation); + this.AnimationProgressY = 1.0; + } else { + this.AnimationProgressX = 1.0; + this.BeginAnimation(AnimationProgressYProperty, animation); + } + } else { + this.AnimationProgress = isCollapsed ? 0.0 : 1.0; + this.AnimationProgressX = (CollapseOrientation == Orientation.Horizontal) ? this.AnimationProgress : 1.0; + this.AnimationProgressY = (CollapseOrientation == Orientation.Vertical) ? this.AnimationProgress : 1.0; + } + } + } + + sealed class CollapsiblePanelProgressToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is double) + return (double)value > 0 ? Visibility.Visible : Visibility.Collapsed; + else + return Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } + + public class SelfCollapsingPanel : CollapsiblePanel + { + public static readonly DependencyProperty CanCollapseProperty = + DependencyProperty.Register("CanCollapse", typeof(bool), typeof(SelfCollapsingPanel), + new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnCanCollapseChanged))); + + public bool CanCollapse { + get { return (bool)GetValue(CanCollapseProperty); } + set { SetValue(CanCollapseProperty, value); } + } + + static void OnCanCollapseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + SelfCollapsingPanel panel = (SelfCollapsingPanel)d; + if ((bool)e.NewValue) { + if (!panel.HeldOpenByMouse) + panel.IsCollapsed = true; + } else { + panel.IsCollapsed = false; + } + } + + bool HeldOpenByMouse { + get { return IsMouseOver || IsMouseCaptureWithin; } + } + + protected override void OnMouseLeave(MouseEventArgs e) + { + base.OnMouseLeave(e); + if (CanCollapse && !HeldOpenByMouse) + IsCollapsed = true; + } + + protected override void OnLostMouseCapture(MouseEventArgs e) + { + base.OnLostMouseCapture(e); + if (CanCollapse && !HeldOpenByMouse) + IsCollapsed = true; + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorHelper.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorHelper.cs new file mode 100644 index 0000000000..bbda652b78 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorHelper.cs @@ -0,0 +1,110 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Windows.Media; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + public static class ColorHelper + { + public static Color ColorFromString(string s) + { + if (string.IsNullOrEmpty(s)) { + return Colors.White; + } + if (s[0] != '#') s = "#" + s; + try { + return (Color)ColorConverter.ConvertFromString(s); + } + catch { + return Colors.White; + } + } + + public static string StringFromColor(Color c) + { + return c.ToString().Substring(1); + } + + public static Color ColorFromHsv(double h, double s, double v) + { + double r, g, b; + RgbFromHsv(h, s, v, out r, out g, out b); + return Color.FromRgb((byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); + + } + + public static void HsvFromColor(Color c, out double h, out double s, out double v) + { + HsvFromRgb(c.R / 255.0, c.G / 255.0, c.B / 255.0, out h, out s, out v); + } + + // http://en.wikipedia.org/wiki/HSV_color_space + public static void HsvFromRgb(double r, double g, double b, out double h, out double s, out double v) + { + var max = Math.Max(r, Math.Max(g, b)); + var min = Math.Min(r, Math.Min(g, b)); + + if (max == min) { + h = 0; + } + else if (max == r) { + h = (60 * (g - b) / (max - min)) % 360; + } + else if (max == g) { + h = 60 * (b - r) / (max - min) + 120; + } + else { + h = 60 * (r - g) / (max - min) + 240; + } + + if (h < 0) h += 360; // C# '%' can return negative values, use real modulus instead + + if (max == 0) { + s = 0; + } + else { + s = 1 - min / max; + } + + v = max; + } + + // http://en.wikipedia.org/wiki/HSV_color_space + public static void RgbFromHsv(double h, double s, double v, out double r, out double g, out double b) + { + h = h % 360; + if (h < 0) h += 360; // C# '%' can return negative values, use real modulus instead + int hi = (int)(h / 60) % 6; + var f = h / 60 - (int)(h / 60); + var p = v * (1 - s); + var q = v * (1 - f * s); + var t = v * (1 - (1 - f) * s); + + switch (hi) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + default: r = v; g = p; b = q; break; + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml new file mode 100644 index 0000000000..fdc76ccdb3 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml.cs new file mode 100644 index 0000000000..667e3a3c33 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ColorPicker.xaml.cs @@ -0,0 +1,191 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + public partial class ColorPicker + { + public ColorPicker() + { + InitializeComponent(); + } + + public static readonly DependencyProperty ColorProperty = + DependencyProperty.Register("Color", typeof(Color), typeof(ColorPicker), + new FrameworkPropertyMetadata(new Color(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + public Color Color { + get { return (Color)GetValue(ColorProperty); } + set { SetValue(ColorProperty, value); } + } + + public static readonly DependencyProperty HProperty = + DependencyProperty.Register("H", typeof(int), typeof(ColorPicker)); + + public int H { + get { return (int)GetValue(HProperty); } + set { SetValue(HProperty, value); } + } + + public static readonly DependencyProperty SProperty = + DependencyProperty.Register("S", typeof(int), typeof(ColorPicker)); + + public int S { + get { return (int)GetValue(SProperty); } + set { SetValue(SProperty, value); } + } + + public static readonly DependencyProperty VProperty = + DependencyProperty.Register("V", typeof(int), typeof(ColorPicker)); + + public int V { + get { return (int)GetValue(VProperty); } + set { SetValue(VProperty, value); } + } + + public static readonly DependencyProperty RProperty = + DependencyProperty.Register("R", typeof(byte), typeof(ColorPicker)); + + public byte R { + get { return (byte)GetValue(RProperty); } + set { SetValue(RProperty, value); } + } + + public static readonly DependencyProperty GProperty = + DependencyProperty.Register("G", typeof(byte), typeof(ColorPicker)); + + public byte G { + get { return (byte)GetValue(GProperty); } + set { SetValue(GProperty, value); } + } + + public static readonly DependencyProperty BProperty = + DependencyProperty.Register("B", typeof(byte), typeof(ColorPicker)); + + public byte B { + get { return (byte)GetValue(BProperty); } + set { SetValue(BProperty, value); } + } + + public static readonly DependencyProperty AProperty = + DependencyProperty.Register("A", typeof(byte), typeof(ColorPicker)); + + public byte A { + get { return (byte)GetValue(AProperty); } + set { SetValue(AProperty, value); } + } + + public static readonly DependencyProperty HexProperty = + DependencyProperty.Register("Hex", typeof(string), typeof(ColorPicker)); + + public string Hex { + get { return (string)GetValue(HexProperty); } + set { SetValue(HexProperty, value); } + } + + public static readonly DependencyProperty HueColorProperty = + DependencyProperty.Register("HueColor", typeof(Color), typeof(ColorPicker)); + + public Color HueColor { + get { return (Color)GetValue(HueColorProperty); } + set { SetValue(HueColorProperty, value); } + } + + bool updating; + + protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + if (updating) return; + updating = true; + try { + if (e.Property == ColorProperty) { + UpdateSource(ColorSource.Hsv); + UpdateRest(ColorSource.Hsv); + } else if (e.Property == HProperty || e.Property == SProperty || e.Property == VProperty) { + var c = ColorHelper.ColorFromHsv(H, S / 100.0, V / 100.0); + c.A = A; + Color = c; + UpdateRest(ColorSource.Hsv); + } else if (e.Property == RProperty || e.Property == GProperty || e.Property == BProperty || e.Property == AProperty) { + Color = Color.FromArgb(A, R, G, B); + UpdateRest(ColorSource.Rgba); + } else if (e.Property == HexProperty) { + Color = ColorHelper.ColorFromString(Hex); + UpdateRest(ColorSource.Hex); + } + } finally { + updating = false; + } + } + + void UpdateRest(ColorSource source) + { + HueColor = ColorHelper.ColorFromHsv(H, 1, 1); + UpdateSource((ColorSource)(((int)source + 1) % 3)); + UpdateSource((ColorSource)(((int)source + 2) % 3)); + } + + void UpdateSource(ColorSource source) + { + if (source == ColorSource.Hsv) { + double h, s, v; + ColorHelper.HsvFromColor(Color, out h, out s, out v); + + H = (int)h; + S = (int)(s * 100); + V = (int)(v * 100); + } + else if (source == ColorSource.Rgba) { + R = Color.R; + G = Color.G; + B = Color.B; + A = Color.A; + } + else { + Hex = ColorHelper.StringFromColor(Color); + } + } + + enum ColorSource + { + Hsv, Rgba, Hex + } + } + + class HexTextBox : TextBox + { + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Enter) { + var b = BindingOperations.GetBindingExpressionBase(this, TextProperty); + if (b != null) { + b.UpdateTarget(); + } + SelectAll(); + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ControlStyles.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ControlStyles.xaml index 237a66ce9f..bb5d4d52b1 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ControlStyles.xaml +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ControlStyles.xaml @@ -3,7 +3,7 @@ xmlns:Controls="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls" xmlns:ControlConvertors="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls.Converters" xmlns:Converters="clr-namespace:ICSharpCode.WpfDesign.Designer.Converters" - xmlns:Widgets="http://icsharpcode.net/sharpdevelop/widgets"> + xmlns:Widgets="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls"> diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.cs new file mode 100644 index 0000000000..7d6072f321 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.cs @@ -0,0 +1,323 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Controls.Primitives; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + public class NumericUpDown : Control + { + static NumericUpDown() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), + new FrameworkPropertyMetadata(typeof(NumericUpDown))); + } + + TextBox textBox; + DragRepeatButton upButton; + DragRepeatButton downButton; + + public static readonly DependencyProperty DecimalPlacesProperty = + DependencyProperty.Register("DecimalPlaces", typeof(int), typeof(NumericUpDown)); + + public int DecimalPlaces { + get { return (int)GetValue(DecimalPlacesProperty); } + set { SetValue(DecimalPlacesProperty, value); } + } + + public static readonly DependencyProperty MinimumProperty = + DependencyProperty.Register("Minimum", typeof(double), typeof(NumericUpDown)); + + public double Minimum { + get { return (double)GetValue(MinimumProperty); } + set { SetValue(MinimumProperty, value); } + } + + public static readonly DependencyProperty MaximumProperty = + DependencyProperty.Register("Maximum", typeof(double), typeof(NumericUpDown), + new FrameworkPropertyMetadata(100.0)); + + public double Maximum { + get { return (double)GetValue(MaximumProperty); } + set { SetValue(MaximumProperty, value); } + } + + public static readonly DependencyProperty ValueProperty = + DependencyProperty.Register("Value", typeof(double), typeof(NumericUpDown), + new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + public double Value { + get { return (double)GetValue(ValueProperty); } + set { SetValue(ValueProperty, value); } + } + + public static readonly DependencyProperty SmallChangeProperty = + DependencyProperty.Register("SmallChange", typeof(double), typeof(NumericUpDown), + new FrameworkPropertyMetadata(1.0)); + + public double SmallChange { + get { return (double)GetValue(SmallChangeProperty); } + set { SetValue(SmallChangeProperty, value); } + } + + public static readonly DependencyProperty LargeChangeProperty = + DependencyProperty.Register("LargeChange", typeof(double), typeof(NumericUpDown), + new FrameworkPropertyMetadata(10.0)); + + public double LargeChange { + get { return (double)GetValue(LargeChangeProperty); } + set { SetValue(LargeChangeProperty, value); } + } + + bool IsDragging { + get { + return upButton.IsDragging; + } + set { + upButton.IsDragging = value; downButton.IsDragging = value; + } + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + upButton = (DragRepeatButton)Template.FindName("PART_UpButton", this); + downButton = (DragRepeatButton)Template.FindName("PART_DownButton", this); + textBox = (TextBox)Template.FindName("PART_TextBox", this); + + upButton.Click += upButton_Click; + downButton.Click += downButton_Click; + + textBox.LostFocus += (sender, e) => OnLostFocus(e); + + var upDrag = new DragListener(upButton); + var downDrag = new DragListener(downButton); + + upDrag.Started += drag_Started; + upDrag.Changed += drag_Changed; + upDrag.Completed += drag_Completed; + + downDrag.Started += drag_Started; + downDrag.Changed += drag_Changed; + downDrag.Completed += drag_Completed; + + Print(); + } + + void drag_Started(DragListener drag) + { + OnDragStarted(); + } + + void drag_Changed(DragListener drag) + { + IsDragging = true; + MoveValue(-drag.DeltaDelta.Y * SmallChange); + } + + void drag_Completed(DragListener drag) + { + IsDragging = false; + OnDragCompleted(); + } + + void downButton_Click(object sender, RoutedEventArgs e) + { + if (!IsDragging) SmallDown(); + } + + void upButton_Click(object sender, RoutedEventArgs e) + { + if (!IsDragging) SmallUp(); + } + + protected virtual void OnDragStarted() + { + } + + protected virtual void OnDragCompleted() + { + } + + public void SmallUp() + { + MoveValue(SmallChange); + } + + public void SmallDown() + { + MoveValue(-SmallChange); + } + + public void LargeUp() + { + MoveValue(LargeChange); + } + + public void LargeDown() + { + MoveValue(-LargeChange); + } + + void MoveValue(double delta) + { + double result; + if (double.IsNaN(Value) || double.IsInfinity(Value)) { + SetValue(delta); + } + else if (double.TryParse(textBox.Text, out result)) { + SetValue(result + delta); + } + else { + SetValue(Value + delta); + } + } + + void Print() + { + if (textBox != null) { + textBox.Text = Value.ToString("F" + DecimalPlaces); + textBox.CaretIndex = int.MaxValue; + } + } + + //wpf bug?: Value = -1 updates bindings without coercing, workaround + //update: not derived from RangeBase - no problem + void SetValue(double newValue) + { + newValue = CoerceValue(newValue); + if (Value != newValue) { + Value = newValue; + } + } + + double CoerceValue(double newValue) + { + return Math.Max(Minimum, Math.Min(newValue, Maximum)); + } + + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + base.OnPreviewKeyDown(e); + switch (e.Key) { + case Key.Enter: + SetInputValue(); + textBox.SelectAll(); + e.Handled = true; + break; + case Key.Up: + SmallUp(); + e.Handled = true; + break; + case Key.Down: + SmallDown(); + e.Handled = true; + break; + case Key.PageUp: + LargeUp(); + e.Handled = true; + break; + case Key.PageDown: + LargeDown(); + e.Handled = true; + break; +// case Key.Home: +// Maximize(); +// e.Handled = true; +// break; +// case Key.End: +// Minimize(); +// e.Handled = true; +// break; + } + } + + void SetInputValue() + { + double result; + if (double.TryParse(textBox.Text, out result)) { + SetValue(result); + } else { + Print(); + } + } + + protected override void OnLostFocus(RoutedEventArgs e) + { + base.OnLostFocus(e); + SetInputValue(); + } + + //protected override void OnMouseWheel(MouseWheelEventArgs e) + //{ + // if (e.Delta > 0) + // { + // if (Keyboard.IsKeyDown(Key.LeftShift)) + // { + // LargeUp(); + // } + // else + // { + // SmallUp(); + // } + // } + // else + // { + // if (Keyboard.IsKeyDown(Key.LeftShift)) + // { + // LargeDown(); + // } + // else + // { + // SmallDown(); + // } + // } + // e.Handled = true; + //} + + protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + if (e.Property == ValueProperty) { + Value = CoerceValue((double)e.NewValue); + Print(); + } + else if (e.Property == SmallChangeProperty && + ReadLocalValue(LargeChangeProperty) == DependencyProperty.UnsetValue) { + LargeChange = SmallChange * 10; + } + } + } + + public class DragRepeatButton : RepeatButton + { + public static readonly DependencyProperty IsDraggingProperty = + DependencyProperty.Register("IsDragging", typeof(bool), typeof(DragRepeatButton)); + + public bool IsDragging { + get { return (bool)GetValue(IsDraggingProperty); } + set { SetValue(IsDraggingProperty, value); } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.xaml new file mode 100644 index 0000000000..a622073f1c --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/NumericUpDown.xaml @@ -0,0 +1,140 @@ + + + #DADFEA + #E6EBEF + #B6BDD3 + #7F9DB9 + Black + #B6BDD3 + + + + + + + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PanelMoveAdorner.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PanelMoveAdorner.cs index 37a186e5fa..215a736290 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PanelMoveAdorner.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PanelMoveAdorner.cs @@ -16,17 +16,12 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Windows.Controls; using System.Windows; using System.Windows.Input; using ICSharpCode.WpfDesign.Designer.Services; using System.Windows.Media; using ICSharpCode.WpfDesign.Designer.Converters; -using System.Globalization; using System.Windows.Data; using ICSharpCode.WpfDesign.UIExtensions; diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/Picker.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/Picker.cs new file mode 100644 index 0000000000..347149fef9 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/Picker.cs @@ -0,0 +1,151 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Windows; +using System.Windows.Input; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Data; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + public class Picker : Grid + { + public Picker() + { + SizeChanged += delegate { UpdateValueOffset(); }; + } + + public static readonly DependencyProperty MarkerProperty = + DependencyProperty.Register("Marker", typeof(UIElement), typeof(Picker)); + + public UIElement Marker { + get { return (UIElement)GetValue(MarkerProperty); } + set { SetValue(MarkerProperty, value); } + } + + public static readonly DependencyProperty ValueProperty = + DependencyProperty.Register("Value", typeof(double), typeof(Picker), + new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + public double Value { + get { return (double)GetValue(ValueProperty); } + set { SetValue(ValueProperty, value); } + } + + public static readonly DependencyProperty ValueOffsetProperty = + DependencyProperty.Register("ValueOffset", typeof(double), typeof(Picker)); + + public double ValueOffset { + get { return (double)GetValue(ValueOffsetProperty); } + set { SetValue(ValueOffsetProperty, value); } + } + + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Picker)); + + public Orientation Orientation { + get { return (Orientation)GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } + } + + public static readonly DependencyProperty MinimumProperty = + DependencyProperty.Register("Minimum", typeof(double), typeof(Picker)); + + public double Minimum { + get { return (double)GetValue(MinimumProperty); } + set { SetValue(MinimumProperty, value); } + } + + public static readonly DependencyProperty MaximumProperty = + DependencyProperty.Register("Maximum", typeof(double), typeof(Picker), + new FrameworkPropertyMetadata(100.0)); + + public double Maximum { + get { return (double)GetValue(MaximumProperty); } + set { SetValue(MaximumProperty, value); } + } + + protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + if (e.Property == MarkerProperty) { + TranslateTransform t = Marker.RenderTransform as TranslateTransform; + if (t == null) { + t = new TranslateTransform(); + Marker.RenderTransform = t; + } + var property = Orientation == Orientation.Horizontal ? TranslateTransform.XProperty : TranslateTransform.YProperty; + BindingOperations.SetBinding(t, property, new Binding("ValueOffset") { + Source = this + }); + } + else if (e.Property == ValueProperty) { + UpdateValueOffset(); + } + } + + bool isMouseDown; + + protected override void OnPreviewMouseDown(MouseButtonEventArgs e) + { + isMouseDown = true; + CaptureMouse(); + UpdateValue(); + } + + protected override void OnPreviewMouseMove(MouseEventArgs e) + { + if (isMouseDown) { + UpdateValue(); + } + } + + protected override void OnPreviewMouseUp(MouseButtonEventArgs e) + { + isMouseDown = false; + ReleaseMouseCapture(); + } + + void UpdateValue() + { + Point p = Mouse.GetPosition(this); + double length = 0, pos = 0; + + if (Orientation == Orientation.Horizontal) { + length = ActualWidth; + pos = p.X; + } + else { + length = ActualHeight; + pos = p.Y; + } + + pos = Math.Max(0, Math.Min(length, pos)); + Value = Minimum + (Maximum - Minimum) * pos / length; + } + + void UpdateValueOffset() + { + var length = Orientation == Orientation.Horizontal ? ActualWidth : ActualHeight; + ValueOffset = length * (Value - Minimum) / (Maximum - Minimum); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/RelayCommand.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/RelayCommand.cs new file mode 100644 index 0000000000..a9878552e5 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/RelayCommand.cs @@ -0,0 +1,118 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Windows.Input; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + /// + /// A command that invokes a delegate. + /// The command parameter must be of type T. + /// + public class RelayCommand : ICommand + { + readonly Predicate canExecute; + readonly Action execute; + + public RelayCommand(Action execute) + { + if (execute == null) + throw new ArgumentNullException("execute"); + this.execute = execute; + } + + public RelayCommand(Action execute, Predicate canExecute) + { + if (execute == null) + throw new ArgumentNullException("execute"); + this.execute = execute; + this.canExecute = canExecute; + } + + public event EventHandler CanExecuteChanged { + add { + if (canExecute != null) + CommandManager.RequerySuggested += value; + } + remove { + if (canExecute != null) + CommandManager.RequerySuggested -= value; + } + } + + public bool CanExecute(object parameter) + { + if (parameter != null && !(parameter is T)) + return false; + return canExecute == null ? true : canExecute((T)parameter); + } + + public void Execute(object parameter) + { + execute((T)parameter); + } + } + + /// + /// A command that invokes a delegate. + /// This class does not provide the command parameter to the delegate - + /// if you need that, use the generic version of this class instead. + /// + public class RelayCommand : ICommand + { + readonly Func canExecute; + readonly Action execute; + + public RelayCommand(Action execute) + { + if (execute == null) + throw new ArgumentNullException("execute"); + this.execute = execute; + } + + public RelayCommand(Action execute, Func canExecute) + { + if (execute == null) + throw new ArgumentNullException("execute"); + this.execute = execute; + this.canExecute = canExecute; + } + + public event EventHandler CanExecuteChanged { + add { + if (canExecute != null) + CommandManager.RequerySuggested += value; + } + remove { + if (canExecute != null) + CommandManager.RequerySuggested -= value; + } + } + + public bool CanExecute(object parameter) + { + return canExecute == null ? true : canExecute(); + } + + public void Execute(object parameter) + { + execute(); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomButtons.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomButtons.cs new file mode 100644 index 0000000000..753d369ef5 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomButtons.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Windows; +using System.Windows.Controls.Primitives; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + public class ZoomButtons : RangeBase + { + static ZoomButtons() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomButtons), + new FrameworkPropertyMetadata(typeof(ZoomButtons))); + } + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + var uxPlus = (ButtonBase)Template.FindName("uxPlus", this); + var uxMinus = (ButtonBase)Template.FindName("uxMinus", this); + var uxReset = (ButtonBase)Template.FindName("uxReset", this); + + if (uxPlus != null) + uxPlus.Click += OnZoomInClick; + if (uxMinus != null) + uxMinus.Click += OnZoomOutClick; + if (uxReset != null) + uxReset.Click += OnResetClick; + } + + const double ZoomFactor = 1.1; + + void OnZoomInClick(object sender, EventArgs e) + { + SetCurrentValue(ValueProperty, ZoomScrollViewer.RoundToOneIfClose(this.Value * ZoomFactor)); + } + + void OnZoomOutClick(object sender, EventArgs e) + { + SetCurrentValue(ValueProperty, ZoomScrollViewer.RoundToOneIfClose(this.Value / ZoomFactor)); + } + + void OnResetClick(object sender, EventArgs e) + { + SetCurrentValue(ValueProperty, 1.0); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomControl.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomControl.cs index ff7e4d4ce9..6e16859422 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomControl.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomControl.cs @@ -15,16 +15,13 @@ // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. - -using System; + using System.IO; using System.Reflection; using System.Resources; using System.Windows; using System.Windows.Input; -using ICSharpCode.SharpDevelop.Widgets; - namespace ICSharpCode.WpfDesign.Designer.Controls { public class ZoomControl : ZoomScrollViewer diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.cs new file mode 100644 index 0000000000..5db1c27b1b --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.cs @@ -0,0 +1,184 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + public class ZoomScrollViewer : ScrollViewer + { + static ZoomScrollViewer() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomScrollViewer), + new FrameworkPropertyMetadata(typeof(ZoomScrollViewer))); + } + + public static readonly DependencyProperty CurrentZoomProperty = + DependencyProperty.Register("CurrentZoom", typeof(double), typeof(ZoomScrollViewer), + new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CalculateZoomButtonCollapsed, CoerceZoom)); + + public double CurrentZoom { + get { return (double)GetValue(CurrentZoomProperty); } + set { SetValue(CurrentZoomProperty, value); } + } + + static object CoerceZoom(DependencyObject d, object baseValue) + { + var zoom = (double)baseValue; + ZoomScrollViewer sv = (ZoomScrollViewer)d; + return Math.Max(sv.MinimumZoom, Math.Min(sv.MaximumZoom, zoom)); + } + + public static readonly DependencyProperty MinimumZoomProperty = + DependencyProperty.Register("MinimumZoom", typeof(double), typeof(ZoomScrollViewer), + new FrameworkPropertyMetadata(0.2)); + + public double MinimumZoom { + get { return (double)GetValue(MinimumZoomProperty); } + set { SetValue(MinimumZoomProperty, value); } + } + + public static readonly DependencyProperty MaximumZoomProperty = + DependencyProperty.Register("MaximumZoom", typeof(double), typeof(ZoomScrollViewer), + new FrameworkPropertyMetadata(5.0)); + + public double MaximumZoom { + get { return (double)GetValue(MaximumZoomProperty); } + set { SetValue(MaximumZoomProperty, value); } + } + + public static readonly DependencyProperty MouseWheelZoomProperty = + DependencyProperty.Register("MouseWheelZoom", typeof(bool), typeof(ZoomScrollViewer), + new FrameworkPropertyMetadata(true)); + + public bool MouseWheelZoom { + get { return (bool)GetValue(MouseWheelZoomProperty); } + set { SetValue(MouseWheelZoomProperty, value); } + } + + public static readonly DependencyProperty AlwaysShowZoomButtonsProperty = + DependencyProperty.Register("AlwaysShowZoomButtons", typeof(bool), typeof(ZoomScrollViewer), + new FrameworkPropertyMetadata(false, CalculateZoomButtonCollapsed)); + + public bool AlwaysShowZoomButtons { + get { return (bool)GetValue(AlwaysShowZoomButtonsProperty); } + set { SetValue(AlwaysShowZoomButtonsProperty, value); } + } + + static readonly DependencyPropertyKey ComputedZoomButtonCollapsedPropertyKey = + DependencyProperty.RegisterReadOnly("ComputedZoomButtonCollapsed", typeof(bool), typeof(ZoomScrollViewer), + new FrameworkPropertyMetadata(true)); + + public static readonly DependencyProperty ComputedZoomButtonCollapsedProperty = ComputedZoomButtonCollapsedPropertyKey.DependencyProperty; + + public bool ComputedZoomButtonCollapsed { + get { return (bool)GetValue(ComputedZoomButtonCollapsedProperty); } + private set { SetValue(ComputedZoomButtonCollapsedPropertyKey, value); } + } + + static void CalculateZoomButtonCollapsed(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ZoomScrollViewer z = d as ZoomScrollViewer; + if (z != null) + z.ComputedZoomButtonCollapsed = (z.AlwaysShowZoomButtons == false) && (z.CurrentZoom == 1.0); + } + + protected override void OnMouseWheel(MouseWheelEventArgs e) + { + if (!e.Handled && Keyboard.Modifiers == ModifierKeys.Control && MouseWheelZoom) { + double oldZoom = CurrentZoom; + double newZoom = RoundToOneIfClose(CurrentZoom * Math.Pow(1.001, e.Delta)); + newZoom = Math.Max(this.MinimumZoom, Math.Min(this.MaximumZoom, newZoom)); + + // adjust scroll position so that mouse stays over the same virtual coordinate + ContentPresenter presenter = Template.FindName("PART_Presenter", this) as ContentPresenter; + Vector relMousePos; + if (presenter != null) { + Point mousePos = e.GetPosition(presenter); + relMousePos = new Vector(mousePos.X / presenter.ActualWidth, mousePos.Y / presenter.ActualHeight); + } else { + relMousePos = new Vector(0.5, 0.5); + } + + Point scrollOffset = new Point(this.HorizontalOffset, this.VerticalOffset); + Vector oldHalfViewport = new Vector(this.ViewportWidth / 2, this.ViewportHeight / 2); + Vector newHalfViewport = oldHalfViewport / newZoom * oldZoom; + Point oldCenter = scrollOffset + oldHalfViewport; + Point virtualMousePos = scrollOffset + new Vector(relMousePos.X * this.ViewportWidth, relMousePos.Y * this.ViewportHeight); + + // As newCenter, we want to choose a point between oldCenter and virtualMousePos. The more we zoom in, the closer + // to virtualMousePos. We'll create the line x = oldCenter + lambda * (virtualMousePos-oldCenter). + // On this line, we need to choose lambda between -1 and 1: + // -1 = zoomed out completely + // 0 = zoom unchanged + // +1 = zoomed in completely + // But the zoom factor (newZoom/oldZoom) we have is in the range [0,+Infinity]. + + // Basically, I just played around until I found a function that maps this to [-1,1] and works well. + // "f" is squared because otherwise the mouse simply stays over virtualMousePos, but I wanted virtualMousePos + // to move towards the middle -> squaring f causes lambda to be closer to 1, giving virtualMousePos more weight + // then oldCenter. + + double f = Math.Min(newZoom, oldZoom) / Math.Max(newZoom, oldZoom); + double lambda = 1 - f*f; + if (oldZoom > newZoom) + lambda = -lambda; + + Point newCenter = oldCenter + lambda * (virtualMousePos - oldCenter); + scrollOffset = newCenter - newHalfViewport; + + SetCurrentValue(CurrentZoomProperty, newZoom); + + this.ScrollToHorizontalOffset(scrollOffset.X); + this.ScrollToVerticalOffset(scrollOffset.Y); + + e.Handled = true; + } + base.OnMouseWheel(e); + } + + internal static double RoundToOneIfClose(double val) + { + if (Math.Abs(val - 1.0) < 0.001) + return 1.0; + else + return val; + } + } + + sealed class IsNormalZoomConverter : IValueConverter + { + public static readonly IsNormalZoomConverter Instance = new IsNormalZoomConverter(); + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (parameter is bool && (bool)parameter) + return true; + return ((double)value) == 1.0; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.xaml new file mode 100644 index 0000000000..53cff7a136 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/ZoomScrollViewer.xaml @@ -0,0 +1,94 @@ + + + + + + + + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml index b7e1dee70c..79defd0aef 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/DesignSurface.xaml @@ -1,10 +1,9 @@  + xmlns:Widgets="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls">