From cf959fd8eb5226461101b3fe94ef958bf0439ecb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 15 Dec 2014 02:14:07 +0100 Subject: [PATCH] Added RadioButton control. --- .../Media/StreamGeometryContextImpl.cs | 6 ++ Perspex.Controls/Image.cs | 4 +- Perspex.Controls/Perspex.Controls.csproj | 2 + Perspex.Controls/Primitives/ToggleButton.cs | 7 +- Perspex.Controls/RadioButton.cs | 46 +++++++++ Perspex.Controls/Shapes/Ellipse.cs | 38 +++++++ Perspex.SceneGraph/Media/EllipseGeometry.cs | 59 +++++++++++ .../Media/StreamGeometryContext.cs | 5 + Perspex.SceneGraph/Media/SweepDirection.cs | 14 +++ Perspex.SceneGraph/Perspex.SceneGraph.csproj | 2 + .../Platform/IStreamGeometryContextImpl.cs | 3 + Perspex.SceneGraph/Rect.cs | 10 +- Perspex.Themes.Default/DefaultTheme.cs | 1 + .../Perspex.Themes.Default.csproj | 1 + Perspex.Themes.Default/RadioButtonStyle.cs | 97 ++++++++++++++++++ TestApplication/Program.cs | 9 ++ .../Ellipse/Circle_1px_Stroke.expected.png | Bin 0 -> 3323 bytes .../Perspex.Direct2D1.RenderTests.csproj | 1 + .../Shapes/EllipseTests.cs | 41 ++++++++ .../Media/StreamGeometryContextImpl.cs | 17 +++ .../Perspex.Direct2D1/PrimitiveExtensions.cs | 5 + 21 files changed, 364 insertions(+), 4 deletions(-) create mode 100644 Perspex.Controls/RadioButton.cs create mode 100644 Perspex.Controls/Shapes/Ellipse.cs create mode 100644 Perspex.SceneGraph/Media/EllipseGeometry.cs create mode 100644 Perspex.SceneGraph/Media/SweepDirection.cs create mode 100644 Perspex.Themes.Default/RadioButtonStyle.cs create mode 100644 TestFiles/Direct2D1/Shapes/Ellipse/Circle_1px_Stroke.expected.png create mode 100644 Windows/Perspex.Direct2D1.RenderTests/Shapes/EllipseTests.cs diff --git a/Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs b/Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs index 12e15ad503..e49c7ab450 100644 --- a/Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs +++ b/Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs @@ -6,6 +6,7 @@ namespace Perspex.Cairo.Media { + using Perspex.Media; using Perspex.Platform; public class StreamGeometryContextImpl : IStreamGeometryContextImpl @@ -15,6 +16,11 @@ namespace Perspex.Cairo.Media // TODO: Implement } + public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection) + { + // TODO: Implement + } + public void BeginFigure(Point startPoint, bool isFilled) { // TODO: Implement diff --git a/Perspex.Controls/Image.cs b/Perspex.Controls/Image.cs index 051e47cf1f..d63a0958d8 100644 --- a/Perspex.Controls/Image.cs +++ b/Perspex.Controls/Image.cs @@ -47,10 +47,10 @@ namespace Perspex.Controls Vector scale = CalculateScaling(this.ActualSize, sourceSize, this.Stretch); Size scaledSize = sourceSize * scale; Rect destRect = viewPort - .Center(new Rect(scaledSize)) + .CenterIn(new Rect(scaledSize)) .Intersect(viewPort); Rect sourceRect = new Rect(sourceSize) - .Center(new Rect(destRect.Size / scale)); + .CenterIn(new Rect(destRect.Size / scale)); drawingContext.DrawImage(source, 1, sourceRect, destRect); } diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj index 8c3511e128..d4aa3f3254 100644 --- a/Perspex.Controls/Perspex.Controls.csproj +++ b/Perspex.Controls/Perspex.Controls.csproj @@ -36,6 +36,7 @@ + @@ -75,6 +76,7 @@ + diff --git a/Perspex.Controls/Primitives/ToggleButton.cs b/Perspex.Controls/Primitives/ToggleButton.cs index 101127be71..adaa5f0bcb 100644 --- a/Perspex.Controls/Primitives/ToggleButton.cs +++ b/Perspex.Controls/Primitives/ToggleButton.cs @@ -20,7 +20,7 @@ namespace Perspex.Controls.Primitives public ToggleButton() { - this.Click += (s, e) => this.IsChecked = !this.IsChecked; + this.Click += (s, e) => this.Toggle(); } public bool IsChecked @@ -28,5 +28,10 @@ namespace Perspex.Controls.Primitives get { return this.GetValue(IsCheckedProperty); } set { this.SetValue(IsCheckedProperty, value); } } + + protected virtual void Toggle() + { + this.IsChecked = !this.IsChecked; + } } } diff --git a/Perspex.Controls/RadioButton.cs b/Perspex.Controls/RadioButton.cs new file mode 100644 index 0000000000..33c9bfa298 --- /dev/null +++ b/Perspex.Controls/RadioButton.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls +{ + using System; + using System.Linq; + using Perspex.Controls.Primitives; + + public class RadioButton : ToggleButton + { + public RadioButton() + { + this.GetObservable(IsCheckedProperty).Subscribe(this.IsCheckedChanged); + } + + protected override void Toggle() + { + if (!this.IsChecked) + { + this.IsChecked = true; + } + } + + private void IsCheckedChanged(bool value) + { + var parent = this.GetVisualParent(); + + if (value && parent != null) + { + var siblings = parent + .GetVisualChildren() + .OfType() + .Where(x => x != this); + + foreach (var sibling in siblings) + { + sibling.IsChecked = false; + } + } + } + } +} diff --git a/Perspex.Controls/Shapes/Ellipse.cs b/Perspex.Controls/Shapes/Ellipse.cs new file mode 100644 index 0000000000..00098f3991 --- /dev/null +++ b/Perspex.Controls/Shapes/Ellipse.cs @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls.Shapes +{ + using Perspex.Media; + + public class Ellipse : Shape + { + private Geometry geometry; + + private Size geometrySize; + + public override Geometry DefiningGeometry + { + get + { + if (this.geometry == null || this.geometrySize != this.ActualSize) + { + var rect = new Rect(0, 0, this.ActualSize.Width, this.ActualSize.Height); + rect = rect.Deflate(this.StrokeThickness / 2); + this.geometry = new EllipseGeometry(rect); + this.geometrySize = this.ActualSize; + } + + return this.geometry; + } + } + + protected override Size MeasureOverride(Size availableSize) + { + return new Size(this.StrokeThickness, this.StrokeThickness); + } + } +} diff --git a/Perspex.SceneGraph/Media/EllipseGeometry.cs b/Perspex.SceneGraph/Media/EllipseGeometry.cs new file mode 100644 index 0000000000..90610ce574 --- /dev/null +++ b/Perspex.SceneGraph/Media/EllipseGeometry.cs @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Media +{ + using System; + using Perspex.Platform; + using Splat; + + public class EllipseGeometry : Geometry + { + public EllipseGeometry(Rect rect) + { + IPlatformRenderInterface factory = Locator.Current.GetService(); + IStreamGeometryImpl impl = factory.CreateStreamGeometry(); + + using (IStreamGeometryContextImpl ctx = impl.Open()) + { + double ControlPointRatio = (Math.Sqrt(2) - 1) * 4 / 3; + var center = rect.Center; + var radius = new Vector(rect.Width / 2, rect.Height / 2); + + var x0 = center.X - radius.X; + var x1 = center.X - radius.X * ControlPointRatio; + var x2 = center.X; + var x3 = center.X + radius.X * ControlPointRatio; + var x4 = center.X + radius.X; + + var y0 = center.Y - radius.Y; + var y1 = center.Y - radius.Y * ControlPointRatio; + var y2 = center.Y; + var y3 = center.Y + radius.Y * ControlPointRatio; + var y4 = center.Y + radius.Y; + + ctx.BeginFigure(new Point(x2, y0), true); + ctx.BezierTo(new Point(x3, y0), new Point(x4, y1), new Point(x4, y2)); + ctx.BezierTo(new Point(x4, y3), new Point(x3, y4), new Point(x2, y4)); + ctx.BezierTo(new Point(x1, y4), new Point(x0, y3), new Point(x0, y2)); + ctx.BezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0)); + ctx.EndFigure(true); + } + + this.PlatformImpl = impl; + } + + public override Rect Bounds + { + get { return this.PlatformImpl.Bounds; } + } + + public override Geometry Clone() + { + return new EllipseGeometry(this.Bounds); + } + } +} diff --git a/Perspex.SceneGraph/Media/StreamGeometryContext.cs b/Perspex.SceneGraph/Media/StreamGeometryContext.cs index 84677cb066..62d24b3d7e 100644 --- a/Perspex.SceneGraph/Media/StreamGeometryContext.cs +++ b/Perspex.SceneGraph/Media/StreamGeometryContext.cs @@ -18,6 +18,11 @@ namespace Perspex.Media this.impl = impl; } + public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection) + { + this.impl.ArcTo(point, size, rotationAngle, isLargeArc, sweepDirection); + } + public void BeginFigure(Point startPoint, bool isFilled) { this.impl.BeginFigure(startPoint, isFilled); diff --git a/Perspex.SceneGraph/Media/SweepDirection.cs b/Perspex.SceneGraph/Media/SweepDirection.cs new file mode 100644 index 0000000000..14cd7d2f3c --- /dev/null +++ b/Perspex.SceneGraph/Media/SweepDirection.cs @@ -0,0 +1,14 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Media +{ + public enum SweepDirection + { + CounterClockwise, + Clockwise, + } +} diff --git a/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/Perspex.SceneGraph/Perspex.SceneGraph.csproj index d82ece6791..40b9cd6492 100644 --- a/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -58,6 +58,7 @@ + @@ -65,6 +66,7 @@ + diff --git a/Perspex.SceneGraph/Platform/IStreamGeometryContextImpl.cs b/Perspex.SceneGraph/Platform/IStreamGeometryContextImpl.cs index dc783491e1..aacd8801a8 100644 --- a/Perspex.SceneGraph/Platform/IStreamGeometryContextImpl.cs +++ b/Perspex.SceneGraph/Platform/IStreamGeometryContextImpl.cs @@ -7,9 +7,12 @@ namespace Perspex.Platform { using System; + using Perspex.Media; public interface IStreamGeometryContextImpl : IDisposable { + void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection); + void BeginFigure(Point startPoint, bool isFilled); void BezierTo(Point point1, Point point2, Point point3); diff --git a/Perspex.SceneGraph/Rect.cs b/Perspex.SceneGraph/Rect.cs index 82df33050c..c9933353f1 100644 --- a/Perspex.SceneGraph/Rect.cs +++ b/Perspex.SceneGraph/Rect.cs @@ -183,6 +183,14 @@ namespace Perspex get { return new Point(this.Right, this.Bottom); } } + /// + /// Gets the center point of the rectangle. + /// + public Point Center + { + get { return new Point(this.x + (this.width / 2), this.y + (this.height / 2)); } + } + /// /// Gets a value that indicates whether the rectangle is empty. /// @@ -255,7 +263,7 @@ namespace Perspex p.Y >= this.y && p.Y < this.y + this.height; } - public Rect Center(Rect rect) + public Rect CenterIn(Rect rect) { return new Rect( this.x + ((this.width - rect.width) / 2), diff --git a/Perspex.Themes.Default/DefaultTheme.cs b/Perspex.Themes.Default/DefaultTheme.cs index 51d915a43d..5d7ea5b1a5 100644 --- a/Perspex.Themes.Default/DefaultTheme.cs +++ b/Perspex.Themes.Default/DefaultTheme.cs @@ -19,6 +19,7 @@ namespace Perspex.Themes.Default this.Add(new ItemsControlStyle()); this.Add(new ListBoxStyle()); this.Add(new ListBoxItemStyle()); + this.Add(new RadioButtonStyle()); this.Add(new ScrollBarStyle()); this.Add(new ScrollViewerStyle()); this.Add(new TabControlStyle()); diff --git a/Perspex.Themes.Default/Perspex.Themes.Default.csproj b/Perspex.Themes.Default/Perspex.Themes.Default.csproj index e9c253b038..807f66800d 100644 --- a/Perspex.Themes.Default/Perspex.Themes.Default.csproj +++ b/Perspex.Themes.Default/Perspex.Themes.Default.csproj @@ -66,6 +66,7 @@ + diff --git a/Perspex.Themes.Default/RadioButtonStyle.cs b/Perspex.Themes.Default/RadioButtonStyle.cs new file mode 100644 index 0000000000..1f374df041 --- /dev/null +++ b/Perspex.Themes.Default/RadioButtonStyle.cs @@ -0,0 +1,97 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Themes.Default +{ + using System; + using System.Linq; + using Perspex.Controls; + using Perspex.Layout; + using Perspex.Media; + using Perspex.Controls.Shapes; + using Perspex.Styling; + using Perspex.Controls.Presenters; + + public class RadioButtonStyle : Styles + { + public RadioButtonStyle() + { + this.AddRange(new[] + { + new Style(x => x.OfType()) + { + Setters = new[] + { + new Setter(Button.TemplateProperty, ControlTemplate.Create(this.Template)), + }, + }, + new Style(x => x.OfType().Template().Id("checkMark")) + { + Setters = new[] + { + new Setter(Shape.IsVisibleProperty, false), + }, + }, + new Style(x => x.OfType().Class(":checked").Template().Id("checkMark")) + { + Setters = new[] + { + new Setter(Shape.IsVisibleProperty, true), + }, + }, + }); + } + + private Control Template(RadioButton control) + { + Border result = new Border + { + [~Border.BackgroundProperty] = control[~RadioButton.BackgroundProperty], + Content = new Grid + { + ColumnDefinitions = new ColumnDefinitions + { + new ColumnDefinition(GridLength.Auto), + new ColumnDefinition(new GridLength(1, GridUnitType.Star)), + }, + Children = new Controls + { + new Ellipse + { + Id = "checkBorder", + Stroke = Brushes.Black, + StrokeThickness = 2, + Width = 18, + Height = 18, + VerticalAlignment = VerticalAlignment.Center, + [Grid.ColumnProperty] = 0, + }, + new Ellipse + { + Id = "checkMark", + Fill = Brushes.Black, + Width = 10, + Height = 10, + Stretch = Stretch.Uniform, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + [Grid.ColumnProperty] = 0, + }, + new ContentPresenter + { + Margin = new Thickness(4, 0, 0, 0), + VerticalAlignment = VerticalAlignment.Center, + [~ContentPresenter.ContentProperty] = control[~RadioButton.ContentProperty], + [Grid.ColumnProperty] = 1, + }, + }, + }, + }; + + return result; + } + } +} diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs index a3e520117d..a5f402aa37 100644 --- a/TestApplication/Program.cs +++ b/TestApplication/Program.cs @@ -198,6 +198,15 @@ namespace TestApplication { Content = "Checkbox", }, + new RadioButton + { + Content = "RadioButton 1", + IsChecked = true, + }, + new RadioButton + { + Content = "RadioButton 2", + }, } }, }; diff --git a/TestFiles/Direct2D1/Shapes/Ellipse/Circle_1px_Stroke.expected.png b/TestFiles/Direct2D1/Shapes/Ellipse/Circle_1px_Stroke.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..e63700654599316d8ba1c237658161c19ed2ef6c GIT binary patch literal 3323 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 FV1fjw84dsd literal 0 HcmV?d00001 diff --git a/Windows/Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj b/Windows/Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj index bace0f0688..d85dc70820 100644 --- a/Windows/Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj +++ b/Windows/Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj @@ -62,6 +62,7 @@ + diff --git a/Windows/Perspex.Direct2D1.RenderTests/Shapes/EllipseTests.cs b/Windows/Perspex.Direct2D1.RenderTests/Shapes/EllipseTests.cs new file mode 100644 index 0000000000..00874c20b7 --- /dev/null +++ b/Windows/Perspex.Direct2D1.RenderTests/Shapes/EllipseTests.cs @@ -0,0 +1,41 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Direct2D1.RenderTests.Shapes +{ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Perspex.Controls; + using Perspex.Media; + using Perspex.Controls.Shapes; + + [TestClass] + public class EllipseTests : TestBase + { + public EllipseTests() + : base(@"Shapes\Ellipse") + { + } + + [TestMethod] + public void Circle_1px_Stroke() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Content = new Ellipse + { + Stroke = Brushes.Black, + StrokeThickness = 1, + } + }; + + this.RenderToFile(target); + this.CompareImages(); + } + } +} diff --git a/Windows/Perspex.Direct2D1/Media/StreamGeometryContextImpl.cs b/Windows/Perspex.Direct2D1/Media/StreamGeometryContextImpl.cs index 53f98a24da..15fb88d7d0 100644 --- a/Windows/Perspex.Direct2D1/Media/StreamGeometryContextImpl.cs +++ b/Windows/Perspex.Direct2D1/Media/StreamGeometryContextImpl.cs @@ -18,6 +18,23 @@ namespace Perspex.Direct2D1.Media this.sink = sink; } + public void ArcTo( + Point point, + Size size, + double rotationAngle, + bool isLargeArc, + Perspex.Media.SweepDirection sweepDirection) + { + this.sink.AddArc(new ArcSegment + { + Point = point.ToSharpDX(), + Size = size.ToSharpDX(), + RotationAngle = (float)rotationAngle, + ArcSize = isLargeArc ? ArcSize.Large : ArcSize.Small, + SweepDirection = (SweepDirection)sweepDirection, + }); + } + public void BeginFigure(Point startPoint, bool isFilled) { this.sink.BeginFigure(startPoint.ToSharpDX(), isFilled ? FigureBegin.Filled : FigureBegin.Hollow); diff --git a/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs b/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs index d18f8cb1e7..c2512b7edb 100644 --- a/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs +++ b/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs @@ -26,6 +26,11 @@ namespace Perspex.Direct2D1 return new Vector2((float)p.X, (float)p.Y); } + public static Size2F ToSharpDX(this Perspex.Size p) + { + return new Size2F((float)p.Width, (float)p.Height); + } + /// /// Converts a brush to Direct2D. ///