24 changed files with 1844 additions and 32 deletions
@ -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 |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Allows animated collapsing of the content of this panel.
|
||||||
|
/// </summary>
|
||||||
|
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))); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The duration in milliseconds of the animation.
|
||||||
|
/// </summary>
|
||||||
|
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)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value between 0 and 1 specifying how far the animation currently is.
|
||||||
|
/// </summary>
|
||||||
|
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)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value between 0 and 1 specifying how far the animation currently is.
|
||||||
|
/// </summary>
|
||||||
|
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)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value between 0 and 1 specifying how far the animation currently is.
|
||||||
|
/// </summary>
|
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,232 @@ |
|||||||
|
<UserControl x:Class="ICSharpCode.WpfDesign.Designer.Controls.ColorPicker" |
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:widgets="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls" |
||||||
|
x:Name="this" |
||||||
|
Padding="5" |
||||||
|
Width="373"> |
||||||
|
|
||||||
|
<UserControl.Resources> |
||||||
|
|
||||||
|
<DrawingBrush x:Key="ChessBrush" |
||||||
|
TileMode="Tile" |
||||||
|
ViewportUnits="Absolute" |
||||||
|
Viewport="0 0 9 9"> |
||||||
|
<DrawingBrush.Drawing> |
||||||
|
<DrawingGroup> |
||||||
|
<GeometryDrawing Brush="White"> |
||||||
|
<GeometryDrawing.Geometry> |
||||||
|
<RectangleGeometry Rect="0 0 2 2" /> |
||||||
|
</GeometryDrawing.Geometry> |
||||||
|
</GeometryDrawing> |
||||||
|
<GeometryDrawing Brush="Gray"> |
||||||
|
<GeometryDrawing.Geometry> |
||||||
|
<GeometryGroup> |
||||||
|
<RectangleGeometry Rect="0 0 1 1" /> |
||||||
|
<RectangleGeometry Rect="1 1 1 1" /> |
||||||
|
</GeometryGroup> |
||||||
|
</GeometryDrawing.Geometry> |
||||||
|
</GeometryDrawing> |
||||||
|
</DrawingGroup> |
||||||
|
</DrawingBrush.Drawing> |
||||||
|
</DrawingBrush> |
||||||
|
|
||||||
|
</UserControl.Resources> |
||||||
|
|
||||||
|
<DockPanel> |
||||||
|
|
||||||
|
<StackPanel VerticalAlignment="Top" |
||||||
|
DockPanel.Dock="Right" |
||||||
|
Margin="10 0 0 0"> |
||||||
|
|
||||||
|
<Border Background="{StaticResource ChessBrush}" |
||||||
|
HorizontalAlignment="Right" |
||||||
|
BorderBrush="Black" |
||||||
|
BorderThickness="1" |
||||||
|
Height="50" |
||||||
|
Width="70"> |
||||||
|
<Rectangle> |
||||||
|
<Rectangle.Fill> |
||||||
|
<SolidColorBrush Color="{Binding Color, ElementName=this}" /> |
||||||
|
</Rectangle.Fill> |
||||||
|
</Rectangle> |
||||||
|
</Border> |
||||||
|
|
||||||
|
<DockPanel Margin="0 3 0 0"> |
||||||
|
<widgets:HexTextBox Text="{Binding Hex, ElementName=this, UpdateSourceTrigger=PropertyChanged}" |
||||||
|
Width="70" |
||||||
|
Margin="5 0 0 0" |
||||||
|
DockPanel.Dock="Right" /> |
||||||
|
<TextBlock Text="#" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Right" /> |
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
<DockPanel Margin="0 3 0 0"> |
||||||
|
<widgets:NumericUpDown Value="{Binding H, ElementName=this}" |
||||||
|
Maximum="360" |
||||||
|
Width="70" |
||||||
|
Margin="5 0 0 0" |
||||||
|
DockPanel.Dock="Right" /> |
||||||
|
<TextBlock Text="H" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Right" /> |
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
<DockPanel Margin="0 3 0 0"> |
||||||
|
<widgets:NumericUpDown Value="{Binding S, ElementName=this}" |
||||||
|
Width="70" |
||||||
|
Margin="5 0 0 0" |
||||||
|
DockPanel.Dock="Right" /> |
||||||
|
<TextBlock Text="S" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Right" /> |
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
<DockPanel Margin="0 3 0 0"> |
||||||
|
<widgets:NumericUpDown Value="{Binding V, ElementName=this}" |
||||||
|
Width="70" |
||||||
|
Margin="5 0 0 0" |
||||||
|
DockPanel.Dock="Right" /> |
||||||
|
<TextBlock Text="V" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Right" /> |
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
<DockPanel Margin="0 3 0 0"> |
||||||
|
<widgets:NumericUpDown Value="{Binding R, ElementName=this}" |
||||||
|
Maximum="255" |
||||||
|
Width="70" |
||||||
|
Margin="5 0 0 0" |
||||||
|
DockPanel.Dock="Right" /> |
||||||
|
<TextBlock Text="R" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Right" /> |
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
<DockPanel Margin="0 3 0 0"> |
||||||
|
<widgets:NumericUpDown Value="{Binding G, ElementName=this}" |
||||||
|
Maximum="255" |
||||||
|
Width="70" |
||||||
|
Margin="5 0 0 0" |
||||||
|
DockPanel.Dock="Right" /> |
||||||
|
<TextBlock Text="G" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Right" /> |
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
<DockPanel Margin="0 3 0 0"> |
||||||
|
<widgets:NumericUpDown Value="{Binding B, ElementName=this}" |
||||||
|
Maximum="255" |
||||||
|
Width="70" |
||||||
|
Margin="5 0 0 0" |
||||||
|
DockPanel.Dock="Right" /> |
||||||
|
<TextBlock Text="B" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Right" /> |
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
<DockPanel Margin="0 3 0 0"> |
||||||
|
<widgets:NumericUpDown Value="{Binding A, ElementName=this}" |
||||||
|
Maximum="255" |
||||||
|
Width="70" |
||||||
|
Margin="5 0 0 0" |
||||||
|
DockPanel.Dock="Right" /> |
||||||
|
<TextBlock Text="A" |
||||||
|
VerticalAlignment="Center" |
||||||
|
HorizontalAlignment="Right" /> |
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
</StackPanel> |
||||||
|
|
||||||
|
<Border Margin="10 0 0 0" |
||||||
|
DockPanel.Dock="Right"> |
||||||
|
<widgets:Picker Orientation="Vertical" |
||||||
|
Value="{Binding H, ElementName=this}" |
||||||
|
Minimum="360" |
||||||
|
Maximum="0" |
||||||
|
Marker="{Binding ElementName=arrows}" |
||||||
|
Width="20"> |
||||||
|
<Border Margin="0 -1"> |
||||||
|
<Border.Background> |
||||||
|
<LinearGradientBrush EndPoint="0 1"> |
||||||
|
<GradientStop Offset="0" |
||||||
|
Color="#F00" /> |
||||||
|
<GradientStop Offset="0.16" |
||||||
|
Color="#F0F" /> |
||||||
|
<GradientStop Offset="0.33" |
||||||
|
Color="#00F" /> |
||||||
|
<GradientStop Offset="0.5" |
||||||
|
Color="#0FF" /> |
||||||
|
<GradientStop Offset="0.76" |
||||||
|
Color="#0F0" /> |
||||||
|
<GradientStop Offset="0.85" |
||||||
|
Color="#FF0" /> |
||||||
|
<GradientStop Offset="1" |
||||||
|
Color="#F00" /> |
||||||
|
</LinearGradientBrush> |
||||||
|
</Border.Background> |
||||||
|
</Border> |
||||||
|
<Grid x:Name="arrows" |
||||||
|
IsHitTestVisible="False" |
||||||
|
VerticalAlignment="Top" |
||||||
|
Margin="-5"> |
||||||
|
<Path HorizontalAlignment="Left" |
||||||
|
Data="M 0 0 L 5 5 L 0 10 Z" |
||||||
|
Fill="Black" /> |
||||||
|
<Path HorizontalAlignment="Right" |
||||||
|
Data="M 0 0 L -5 5 L 0 10 Z" |
||||||
|
Fill="Black" /> |
||||||
|
</Grid> |
||||||
|
</widgets:Picker> |
||||||
|
</Border> |
||||||
|
|
||||||
|
<Border BorderBrush="Black" |
||||||
|
BorderThickness="1"> |
||||||
|
<widgets:Picker Value="{Binding S, ElementName=this}" |
||||||
|
Marker="{Binding ElementName=point}" |
||||||
|
ClipToBounds="True"> |
||||||
|
<widgets:Picker Orientation="Vertical" |
||||||
|
Value="{Binding V, ElementName=this}" |
||||||
|
Minimum="100" |
||||||
|
Maximum="0" |
||||||
|
Marker="{Binding ElementName=point}"> |
||||||
|
<Rectangle> |
||||||
|
<Rectangle.Fill> |
||||||
|
<LinearGradientBrush EndPoint="1 0"> |
||||||
|
<GradientStop Offset="0" |
||||||
|
Color="White" /> |
||||||
|
<GradientStop Offset="1" |
||||||
|
Color="{Binding HueColor, ElementName=this}" /> |
||||||
|
</LinearGradientBrush> |
||||||
|
</Rectangle.Fill> |
||||||
|
</Rectangle> |
||||||
|
<Rectangle> |
||||||
|
<Rectangle.Fill> |
||||||
|
<LinearGradientBrush EndPoint="0 1"> |
||||||
|
<GradientStop Offset="0" |
||||||
|
Color="#0000" /> |
||||||
|
<GradientStop Offset="1" |
||||||
|
Color="#F000" /> |
||||||
|
</LinearGradientBrush> |
||||||
|
</Rectangle.Fill> |
||||||
|
</Rectangle> |
||||||
|
<Grid x:Name="point" |
||||||
|
VerticalAlignment="Top" |
||||||
|
HorizontalAlignment="Left" |
||||||
|
Width="12" |
||||||
|
Height="12" |
||||||
|
Margin="-6 -6 0 0"> |
||||||
|
<Ellipse Stroke="Black" |
||||||
|
IsHitTestVisible="False" /> |
||||||
|
<Ellipse Stroke="White" |
||||||
|
Margin="1" |
||||||
|
IsHitTestVisible="False" /> |
||||||
|
</Grid> |
||||||
|
</widgets:Picker> |
||||||
|
</widgets:Picker> |
||||||
|
</Border> |
||||||
|
|
||||||
|
</DockPanel> |
||||||
|
|
||||||
|
</UserControl> |
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:Controls="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls"> |
||||||
|
|
||||||
|
<Brush x:Key="ButtonNormal">#DADFEA</Brush> |
||||||
|
<Brush x:Key="ButtonHover">#E6EBEF</Brush> |
||||||
|
<Brush x:Key="ButtonPressed">#B6BDD3</Brush> |
||||||
|
<Brush x:Key="BorderBrush">#7F9DB9</Brush> |
||||||
|
<Brush x:Key="ArrowBrush">Black</Brush> |
||||||
|
<Brush x:Key="ArrowsBorderBrush">#B6BDD3</Brush> |
||||||
|
|
||||||
|
<Style x:Key="UpButton" |
||||||
|
TargetType="RepeatButton"> |
||||||
|
<Setter Property="Focusable" |
||||||
|
Value="False" /> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="{x:Type Controls:DragRepeatButton}"> |
||||||
|
<Border x:Name="bg" |
||||||
|
Background="{StaticResource ButtonNormal}" |
||||||
|
CornerRadius="2 2 0 0"> |
||||||
|
<Path Fill="{StaticResource ArrowBrush}" |
||||||
|
Data="M 0 3 L 3.5 0 L 7 3" |
||||||
|
HorizontalAlignment="Center" |
||||||
|
VerticalAlignment="Center" /> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="IsMouseOver" |
||||||
|
Value="True"> |
||||||
|
<Setter TargetName="bg" |
||||||
|
Property="Background" |
||||||
|
Value="{StaticResource ButtonHover}" /> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsMouseCaptured" |
||||||
|
Value="True"> |
||||||
|
<Setter TargetName="bg" |
||||||
|
Property="Background" |
||||||
|
Value="{StaticResource ButtonPressed}" /> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsDragging" |
||||||
|
Value="True"> |
||||||
|
<Setter TargetName="bg" |
||||||
|
Property="Background" |
||||||
|
Value="{StaticResource ButtonPressed}" /> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
|
||||||
|
<Style x:Key="DownButton" |
||||||
|
TargetType="RepeatButton"> |
||||||
|
<Setter Property="Focusable" |
||||||
|
Value="False" /> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="{x:Type Controls:DragRepeatButton}"> |
||||||
|
<Border x:Name="bg" |
||||||
|
Background="{StaticResource ButtonNormal}" |
||||||
|
CornerRadius="0 0 2 2"> |
||||||
|
<Path Fill="{StaticResource ArrowBrush}" |
||||||
|
Data="M 0 0 L 3.5 3 L 7 0" |
||||||
|
HorizontalAlignment="Center" |
||||||
|
VerticalAlignment="Center" /> |
||||||
|
</Border> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="IsMouseOver" |
||||||
|
Value="True"> |
||||||
|
<Setter TargetName="bg" |
||||||
|
Property="Background" |
||||||
|
Value="{StaticResource ButtonHover}" /> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsMouseCaptured" |
||||||
|
Value="True"> |
||||||
|
<Setter TargetName="bg" |
||||||
|
Property="Background" |
||||||
|
Value="{StaticResource ButtonPressed}" /> |
||||||
|
</Trigger> |
||||||
|
<Trigger Property="IsDragging" |
||||||
|
Value="True"> |
||||||
|
<Setter TargetName="bg" |
||||||
|
Property="Background" |
||||||
|
Value="{StaticResource ButtonPressed}" /> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
|
||||||
|
<Style TargetType="{x:Type Controls:NumericUpDown}"> |
||||||
|
<Setter Property="Background" |
||||||
|
Value="White" /> |
||||||
|
<Setter Property="BorderThickness" |
||||||
|
Value="1" /> |
||||||
|
<Setter Property="BorderBrush" |
||||||
|
Value="{StaticResource BorderBrush}" /> |
||||||
|
<Setter Property="Focusable" |
||||||
|
Value="False" /> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="{x:Type Controls:NumericUpDown}"> |
||||||
|
<Border Background="{TemplateBinding Background}" |
||||||
|
BorderBrush="{TemplateBinding BorderBrush}" |
||||||
|
BorderThickness="{TemplateBinding BorderThickness}" |
||||||
|
Padding="1"> |
||||||
|
<Grid> |
||||||
|
<Grid.RowDefinitions> |
||||||
|
<RowDefinition /> |
||||||
|
<RowDefinition /> |
||||||
|
</Grid.RowDefinitions> |
||||||
|
<Grid.ColumnDefinitions> |
||||||
|
<ColumnDefinition /> |
||||||
|
<ColumnDefinition Width="15" /> |
||||||
|
</Grid.ColumnDefinitions> |
||||||
|
<TextBox x:Name="PART_TextBox" |
||||||
|
BorderThickness="0" |
||||||
|
Background="{x:Null}" |
||||||
|
Foreground="{TemplateBinding Foreground}" |
||||||
|
Grid.RowSpan="2" /> |
||||||
|
<Controls:DragRepeatButton x:Name="PART_UpButton" |
||||||
|
Style="{StaticResource UpButton}" |
||||||
|
Grid.Column="1" /> |
||||||
|
<Controls:DragRepeatButton x:Name="PART_DownButton" |
||||||
|
Style="{StaticResource DownButton}" |
||||||
|
Grid.Column="1" |
||||||
|
Grid.Row="1" /> |
||||||
|
<Border Grid.Column="1" |
||||||
|
Grid.RowSpan="2" |
||||||
|
BorderBrush="{StaticResource ArrowsBorderBrush}" |
||||||
|
BorderThickness="1" |
||||||
|
CornerRadius="2" /> |
||||||
|
</Grid> |
||||||
|
</Border> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
</ResourceDictionary> |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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 |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A command that invokes a delegate.
|
||||||
|
/// The command parameter must be of type T.
|
||||||
|
/// </summary>
|
||||||
|
public class RelayCommand<T> : ICommand |
||||||
|
{ |
||||||
|
readonly Predicate<T> canExecute; |
||||||
|
readonly Action<T> execute; |
||||||
|
|
||||||
|
public RelayCommand(Action<T> execute) |
||||||
|
{ |
||||||
|
if (execute == null) |
||||||
|
throw new ArgumentNullException("execute"); |
||||||
|
this.execute = execute; |
||||||
|
} |
||||||
|
|
||||||
|
public RelayCommand(Action<T> execute, Predicate<T> 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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public class RelayCommand : ICommand |
||||||
|
{ |
||||||
|
readonly Func<bool> canExecute; |
||||||
|
readonly Action execute; |
||||||
|
|
||||||
|
public RelayCommand(Action execute) |
||||||
|
{ |
||||||
|
if (execute == null) |
||||||
|
throw new ArgumentNullException("execute"); |
||||||
|
this.execute = execute; |
||||||
|
} |
||||||
|
|
||||||
|
public RelayCommand(Action execute, Func<bool> 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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:Controls="clr-namespace:ICSharpCode.WpfDesign.Designer.Controls"> |
||||||
|
|
||||||
|
<Style TargetType="{x:Type Controls:ZoomScrollViewer}"> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="{x:Type Controls:ZoomScrollViewer}"> |
||||||
|
<Grid Background="{TemplateBinding Panel.Background}"> |
||||||
|
<Grid.ColumnDefinitions> |
||||||
|
<ColumnDefinition Width="Auto" /> |
||||||
|
<ColumnDefinition Width="*" /> |
||||||
|
<ColumnDefinition Width="Auto" /> |
||||||
|
</Grid.ColumnDefinitions> |
||||||
|
<Grid.RowDefinitions> |
||||||
|
<RowDefinition Height="*" /> |
||||||
|
<RowDefinition Height="Auto" /> |
||||||
|
</Grid.RowDefinitions> |
||||||
|
<Controls:SelfCollapsingPanel Grid.Column="0" Grid.Row="1" CollapseOrientation="Horizontal" CanCollapse="{Binding Path=ComputedZoomButtonCollapsed, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"> |
||||||
|
<Controls:ZoomButtons x:Name="zoomButtons" Value="{Binding Path=CurrentZoom, RelativeSource={RelativeSource Mode=TemplatedParent}}" Minimum="{TemplateBinding MinimumZoom}" Maximum="{TemplateBinding MaximumZoom}" /> |
||||||
|
</Controls:SelfCollapsingPanel> |
||||||
|
<Rectangle Grid.Column="2" Grid.Row="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" /> |
||||||
|
<ScrollContentPresenter Name="PART_Presenter" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Margin="{TemplateBinding Control.Padding}" Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"> |
||||||
|
<ScrollContentPresenter.LayoutTransform> |
||||||
|
<ScaleTransform ScaleX="{Binding Path=CurrentZoom, RelativeSource={RelativeSource Mode=TemplatedParent}}" ScaleY="{Binding Path=CurrentZoom, RelativeSource={RelativeSource Mode=TemplatedParent}}"/> |
||||||
|
</ScrollContentPresenter.LayoutTransform> |
||||||
|
</ScrollContentPresenter> |
||||||
|
<ScrollBar Name="PART_VerticalScrollBar" Grid.Column="2" Grid.Row="0" Minimum="0" Maximum="{TemplateBinding ScrollableHeight}" ViewportSize="{TemplateBinding ViewportHeight}" Value="{TemplateBinding VerticalOffset}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" /> |
||||||
|
<ScrollBar Name="PART_HorizontalScrollBar" Orientation="Horizontal" Grid.Column="1" Grid.Row="1" Minimum="0" Maximum="{TemplateBinding ScrollableWidth}" ViewportSize="{TemplateBinding ViewportWidth}" Value="{TemplateBinding HorizontalOffset}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" /> |
||||||
|
</Grid> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
|
||||||
|
<Style x:Key="ZoomButtonStyle" |
||||||
|
TargetType="RepeatButton"> |
||||||
|
<Setter Property="Delay" Value="0" /> |
||||||
|
<Setter Property="Focusable" Value="False" /> |
||||||
|
<Setter Property="Opacity" Value="0.5" /> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="RepeatButton"> |
||||||
|
<ContentPresenter /> |
||||||
|
<ControlTemplate.Triggers> |
||||||
|
<Trigger Property="IsMouseOver" Value="True"> |
||||||
|
<Setter Property="Opacity" Value="1" /> |
||||||
|
</Trigger> |
||||||
|
</ControlTemplate.Triggers> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
|
||||||
|
<Style TargetType="{x:Type Controls:ZoomButtons}"> |
||||||
|
<Setter Property="Minimum" Value="0.2"/> |
||||||
|
<Setter Property="Maximum" Value="10"/> |
||||||
|
<Setter Property="Value" Value="1"/> |
||||||
|
<Setter Property="Template"> |
||||||
|
<Setter.Value> |
||||||
|
<ControlTemplate TargetType="{x:Type Controls:ZoomButtons}"> |
||||||
|
<Border Background="{TemplateBinding Background}" |
||||||
|
BorderBrush="{TemplateBinding BorderBrush}" |
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"> |
||||||
|
<StackPanel Orientation="Horizontal" |
||||||
|
Background="#3000"> |
||||||
|
<RepeatButton x:Name="uxPlus" |
||||||
|
Width="16" Height="16" Padding="0" BorderThickness="0" |
||||||
|
Style="{StaticResource ZoomButtonStyle}"> |
||||||
|
<Image Source="Images/ZoomIn.png" |
||||||
|
Stretch="Uniform"/> |
||||||
|
</RepeatButton> |
||||||
|
<RepeatButton x:Name="uxMinus" |
||||||
|
Width="16" Height="16" Padding="0" BorderThickness="0" |
||||||
|
Style="{StaticResource ZoomButtonStyle}"> |
||||||
|
<Image Source="Images/ZoomOut.png" |
||||||
|
Stretch="Uniform" /> |
||||||
|
</RepeatButton> |
||||||
|
<RepeatButton x:Name="uxReset" |
||||||
|
Width="16" Height="16" Padding="0" BorderThickness="0" |
||||||
|
Style="{StaticResource ZoomButtonStyle}"> |
||||||
|
<Border Background="#5000"> |
||||||
|
<TextBlock Foreground="White" |
||||||
|
HorizontalAlignment="Center" |
||||||
|
VerticalAlignment="Center">1</TextBlock> |
||||||
|
</Border> |
||||||
|
</RepeatButton> |
||||||
|
</StackPanel> |
||||||
|
</Border> |
||||||
|
</ControlTemplate> |
||||||
|
</Setter.Value> |
||||||
|
</Setter> |
||||||
|
</Style> |
||||||
|
</ResourceDictionary> |
Loading…
Reference in new issue