Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/xamarin/Xamarin.PropertyEditing.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCartBlanche <savagesoftware@gmail.com>2019-09-16 21:19:56 +0300
committerCartBlanche <savagesoftware@gmail.com>2020-01-08 13:48:31 +0300
commit301b472e7483e05d4b5cbec71bc49629fc1bfe10 (patch)
tree83bbc7c9e78f6c34fd681ec07dae9075e1333789
parent78a861400708d6e64bc6b935ff0a8c1698005e9f (diff)
[Mac] Initial Autoresizing Control Implementationdominique-autoresizing
-rw-r--r--Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingMaskView.cs297
-rw-r--r--Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingPreviewView.cs263
-rw-r--r--Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingView.cs275
-rw-r--r--Xamarin.PropertyEditing.Mac/Controls/AutoResizingMaskEditorControl.cs78
-rw-r--r--Xamarin.PropertyEditing.Mac/PropertyEditingResource/Contents/Resources/pe-device-frame.pngbin0 -> 123189 bytes
-rw-r--r--Xamarin.PropertyEditing.Mac/PropertyEditingResource/Contents/Resources/pe-device-frame~dark.pngbin0 -> 123189 bytes
-rw-r--r--Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs2
-rw-r--r--Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj3
-rw-r--r--Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs3
-rw-r--r--Xamarin.PropertyEditing/Common/AutoResizingFlags.cs2
-rw-r--r--Xamarin.PropertyEditing/Properties/Resources.Designer.cs12
-rw-r--r--Xamarin.PropertyEditing/Properties/Resources.resx8
-rw-r--r--Xamarin.PropertyEditing/ViewModels/AutoResizingPropertyViewModel.cs2
-rw-r--r--Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs33
14 files changed, 954 insertions, 24 deletions
diff --git a/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingMaskView.cs b/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingMaskView.cs
new file mode 100644
index 0000000..4216209
--- /dev/null
+++ b/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingMaskView.cs
@@ -0,0 +1,297 @@
+using System;
+using AppKit;
+using CoreGraphics;
+using Xamarin.PropertyEditing.Common;
+
+namespace Xamarin.PropertyEditing.Mac
+{
+ internal class AutoResizingMaskView : NSView
+ {
+ public event EventHandler MaskChanged;
+
+ private readonly NSColor internalBorderColor = NSColor.FromDeviceRgba (.54f, .54f, .54f, 1);
+ internal static NSColor activeArrowFillColor = NSColor.FromDeviceRgba (.96f, .43f, .31f, 1);
+ private readonly NSColor inactiveArrowFillColor = NSColor.FromDeviceRgba (.96f, .67f, .62f, 1);
+ internal static NSColor disabledArrowFillColor = NSColor.FromDeviceRgba (.96f, .43f, .31f, 0.5f);
+ internal static NSColor backgroundFillColor = NSColor.WindowBackground;
+
+ private bool enabled;
+ private AutoResizingFlags mask;
+ private readonly AutoResizingFlags[] autoResizingFlagsList;
+
+ public AutoResizingFlags Mask
+ {
+ get { return this.mask; }
+
+ internal set
+ {
+ if (this.mask == value)
+ return;
+
+ this.mask = value;
+
+ // Our state has changed, repaint
+ MaskChanged?.Invoke (this, EventArgs.Empty);
+ NeedsDisplay = true;
+ }
+ }
+
+ #region Overrriden Methods and Properties
+
+ public override CGSize IntrinsicContentSize
+ {
+ get { return new CGSize (76, 76); }
+ }
+
+ public override bool IsFlipped
+ {
+ get { return true; }
+ }
+
+ public bool Enabled
+ {
+ get { return this.enabled; }
+
+ internal set
+ {
+ if (this.enabled == value)
+ return;
+
+ this.enabled = value;
+
+ // Our state has changed, repaint
+ NeedsDisplay = true;
+ }
+ }
+
+ public override bool CanBecomeKeyView
+ {
+ get { return this.enabled; }
+ }
+
+ public override CGRect FocusRingMaskBounds => Bounds;
+
+ public override bool AcceptsFirstResponder ()
+ {
+ return this.enabled;
+ }
+
+ public override void DrawRect (CGRect dirtyRect)
+ {
+ var rect = Bounds;
+ backgroundFillColor.Set ();
+ NSGraphics.RectFill (rect);
+ DrawingUtils.DrawLightShadedBezel (rect, flipped: IsFlipped);
+
+ // Draw inner rectangle
+ var fourthWidth = (int)rect.Width / 4;
+ var fourthHeight = (int)rect.Height / 4;
+ var innerRect = new CGRect (fourthWidth, fourthHeight, 2 * fourthWidth, 2 * fourthHeight);
+ backgroundFillColor.Set ();
+ NSGraphics.RectFill (innerRect);
+ this.internalBorderColor.Set ();
+ NSGraphics.FrameRectWithWidth (innerRect, 1);
+
+ const int ArrowOffset = 3;
+
+ // Draw position arrows
+ DrawPositionArrow (new CGPoint (ArrowOffset, 2 * fourthHeight),
+ new CGPoint (fourthWidth - ArrowOffset, 2 * fourthHeight),
+ !Mask.HasFlag (AutoResizingFlags.FlexibleLeftMargin) && !Mask.HasFlag (AutoResizingFlags.FlexibleMargins));
+ DrawPositionArrow (new CGPoint (3 * fourthWidth + ArrowOffset - 1, 2 * fourthHeight),
+ new CGPoint ((int)rect.Width - ArrowOffset - 1, 2 * fourthHeight),
+ !Mask.HasFlag (AutoResizingFlags.FlexibleRightMargin) && !Mask.HasFlag (AutoResizingFlags.FlexibleMargins));
+ DrawPositionArrow (new CGPoint (2 * fourthWidth, ArrowOffset),
+ new CGPoint (2 * fourthWidth, fourthHeight - ArrowOffset),
+ !Mask.HasFlag (AutoResizingFlags.FlexibleTopMargin) && !Mask.HasFlag (AutoResizingFlags.FlexibleMargins));
+ DrawPositionArrow (new CGPoint (2 * fourthWidth, 3 * fourthHeight + ArrowOffset - 1),
+ new CGPoint (2 * fourthWidth, (int)rect.Height - ArrowOffset - 1),
+ !Mask.HasFlag (AutoResizingFlags.FlexibleBottomMargin) && !Mask.HasFlag (AutoResizingFlags.FlexibleMargins));
+
+ // Draw size arrows
+ DrawSizeArrow (new CGPoint (fourthWidth + ArrowOffset, 2 * fourthHeight), new CGPoint (3 * fourthWidth - ArrowOffset, 2 * fourthHeight),
+ Mask.HasFlag (AutoResizingFlags.FlexibleWidth) || Mask.HasFlag (AutoResizingFlags.FlexibleDimensions));
+ DrawSizeArrow (new CGPoint (2 * fourthWidth, fourthHeight + ArrowOffset), new CGPoint (2 * fourthWidth, 3 * fourthHeight - ArrowOffset),
+ Mask.HasFlag (AutoResizingFlags.FlexibleHeight) || Mask.HasFlag (AutoResizingFlags.FlexibleDimensions));
+ }
+
+ public override void MouseDown (NSEvent theEvent)
+ {
+ if (!this.enabled)
+ return;
+
+ var mousePosition = ConvertPointFromView (Window.MouseLocationOutsideOfEventStream, null);
+ var x = mousePosition.X;
+ var y = mousePosition.Y;
+
+ const int Tolerance = 5;
+ var rect = Bounds;
+
+ var inHorizontal = y > rect.Height / 2 - Tolerance && y < rect.Height / 2 + Tolerance;
+ var inVertical = x > rect.Width / 2 - Tolerance && x < rect.Width / 2 + Tolerance;
+ if (!inHorizontal && !inVertical)
+ return;
+
+ var fourthWidth = rect.Width / 4;
+ var fourthHeight = rect.Height / 4;
+
+ if (inHorizontal) {
+ if (x < fourthWidth)
+ ToggleMaskFlag (AutoResizingFlags.FlexibleLeftMargin);
+ else if (x > 3 * fourthWidth)
+ ToggleMaskFlag (AutoResizingFlags.FlexibleRightMargin);
+ else
+ ToggleMaskFlag (AutoResizingFlags.FlexibleWidth);
+ } else {
+ if (y < fourthHeight)
+ ToggleMaskFlag (AutoResizingFlags.FlexibleTopMargin);
+ else if (y > 3 * fourthHeight)
+ ToggleMaskFlag (AutoResizingFlags.FlexibleBottomMargin);
+ else
+ ToggleMaskFlag (AutoResizingFlags.FlexibleHeight);
+ }
+ }
+
+ public override void KeyUp (NSEvent theEvent)
+ {
+ if (!this.enabled)
+ return;
+
+ var currentOriginIndex = this.autoResizingFlagsList.IndexOf (this.mask);
+
+ switch (theEvent.KeyCode) {
+ // Move to next left flag position
+ case 123:
+ currentOriginIndex--;
+ if (currentOriginIndex < 0)
+ currentOriginIndex = this.autoResizingFlagsList.Length - 1;
+
+ Mask = this.autoResizingFlagsList[currentOriginIndex];
+ break;
+
+ // Move to next right flag position
+ case 124:
+ currentOriginIndex++;
+ if (currentOriginIndex > this.autoResizingFlagsList.Length - 1)
+ currentOriginIndex = 0;
+
+ Mask = this.autoResizingFlagsList[currentOriginIndex];
+ break;
+
+ default:
+ base.KeyUp (theEvent);
+ break;
+ }
+ }
+
+ public override bool BecomeFirstResponder ()
+ {
+ var willBecomeFirstResponder = base.BecomeFirstResponder ();
+ if (willBecomeFirstResponder) {
+ ScrollRectToVisible (Bounds);
+ }
+
+ return willBecomeFirstResponder;
+ }
+
+ public override void DrawFocusRingMask ()
+ {
+ NSGraphics.RectFill (Bounds);
+ }
+
+ public sealed override void ViewDidChangeEffectiveAppearance ()
+ {
+ base.ViewDidChangeEffectiveAppearance ();
+
+ AppearanceChanged ();
+ }
+ #endregion
+
+ public AutoResizingMaskView ()
+ {
+ this.autoResizingFlagsList = (AutoResizingFlags[])Enum.GetValues (typeof (AutoResizingFlags));
+ AppearanceChanged ();
+ }
+
+ private void DrawPositionArrow (CGPoint from, CGPoint to, bool full)
+ {
+ if (full && !this.enabled)
+ disabledArrowFillColor.Set ();
+ else if (full)
+ activeArrowFillColor.Set ();
+ else
+ this.inactiveArrowFillColor.Set ();
+
+ DrawUtils.DrawStraightLine (from, to, full ? null : new int[] { 0, 2, 2, 1, 4, 1, 2 });
+
+ const int KnobLength = 5;
+
+ Func<CGPoint, int, CGPoint> pointUpdater;
+ if (from.Y == to.Y)
+ pointUpdater = (p, m) => new CGPoint (p.X, p.Y + m * KnobLength + (1 - m) / 2);
+ else
+ pointUpdater = (p, m) => new CGPoint (p.X + m * KnobLength + (1 - m) / 2, p.Y);
+
+ DrawUtils.DrawStraightLine (pointUpdater (from, -1), pointUpdater (from, 1), full ? null : new int[] { 0, 1 });
+ DrawUtils.DrawStraightLine (pointUpdater (to, -1), pointUpdater (to, 1), full ? null : new int[] { 0, 1 });
+ }
+
+ private void DrawSizeArrow (CGPoint from, CGPoint to, bool full)
+ {
+ const int HeadOffset = 4;
+
+ if (full && !this.enabled)
+ disabledArrowFillColor.Set ();
+ else if (full)
+ activeArrowFillColor.Set ();
+ else
+ this.inactiveArrowFillColor.Set ();
+
+ //var padding = from.Y == to.Y ? new Size (3, 0) : new Size (0, 3);
+ DrawUtils.DrawStraightLine (from, to, full ? null : new int[] { 3, 1 });
+
+ Action<CGPoint, CGPoint> drawHead;
+
+ // Draw head
+ if (from.Y == to.Y) {
+ // horizontal
+ drawHead = (f, t) => {
+ var path = new NSBezierPath ();
+ path.Append (new CGPoint[] {
+ new CGPoint ((f.X < t.X) ? t.X - HeadOffset : t.X + HeadOffset, t.Y + HeadOffset + .5f),
+ new CGPoint (t.X, t.Y + .5f),
+ new CGPoint ((f.X < t.X) ? t.X - HeadOffset : t.X + HeadOffset, t.Y - HeadOffset + .5f),
+ });
+ path.Stroke ();
+ };
+ } else {
+ // vertical
+ drawHead = (f, t) => {
+ var path = new NSBezierPath ();
+ path.Append (new CGPoint[] {
+ new CGPoint (t.X + HeadOffset + .5f, (f.Y < t.Y) ? t.Y - HeadOffset : t.Y + HeadOffset),
+ new CGPoint (t.X + .5f, t.Y),
+ new CGPoint (t.X - HeadOffset + .5f, (f.Y < t.Y) ? t.Y - HeadOffset : t.Y + HeadOffset)
+ });
+ path.Stroke ();
+ };
+ }
+
+ drawHead (from, to);
+ drawHead (to, from);
+ }
+
+ private void ToggleMaskFlag (AutoResizingFlags flag)
+ {
+ Mask ^= flag;
+ MaskChanged?.Invoke (this, EventArgs.Empty);
+ NeedsDisplay = true;
+ }
+
+ private void AppearanceChanged ()
+ {
+ // Placeholder to handle theme changes
+ DrawingUtils.UpdateBezelGreys (EffectiveAppearance.Name.Contains ("dark")); // TODO temporary hack
+ }
+ }
+}
diff --git a/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingPreviewView.cs b/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingPreviewView.cs
new file mode 100644
index 0000000..ab505ac
--- /dev/null
+++ b/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingPreviewView.cs
@@ -0,0 +1,263 @@
+using System;
+using System.Drawing;
+using AppKit;
+using CoreGraphics;
+using Foundation;
+using Xamarin.PropertyEditing.Common;
+
+namespace Xamarin.PropertyEditing.Mac
+{
+ internal class AutoResizingPreviewView : NSView
+ {
+ private const int Height = 70;
+ private NSImage originalBackgroundImage;
+ private NSImage bgImage;
+ private CGSize bgImageSize;
+
+ private readonly NSColor backgroundFillColor = AutoResizingMaskView.backgroundFillColor;
+ private readonly NSColor windowBorderColor = NSColor.FromDeviceRgba (.75f, .75f, .75f, 1);
+ private readonly NSColor windowFillColor = NSColor.White;
+ private readonly NSColor previewBorderColor = NSColor.FromDeviceRgba (.33f, .33f, .33f, 1);
+ private readonly NSColor enabledElementFillColor = AutoResizingMaskView.activeArrowFillColor;
+ private readonly NSColor disabledElementFillColor = AutoResizingMaskView.disabledArrowFillColor;
+
+ private AutoResizingFlags mask;
+ private IDisposable currentAnimation;
+ private int currentAnimationRawValue;
+ private int currentAnimationValue;
+
+ private CGRect lastBounds;
+ private NSTrackingArea trackArea;
+ private bool enabled;
+
+ public AutoResizingFlags Mask
+ {
+ get { return this.mask; }
+ set
+ {
+ this.mask = value;
+ NeedsDisplay = true;
+ }
+ }
+
+ public AutoResizingPreviewView (NSImage backgroundImage)
+ {
+ this.originalBackgroundImage = backgroundImage;
+ WantsLayer = true;
+ this.trackArea = new NSTrackingArea (Frame, NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.ActiveInKeyWindow, this, null);
+ AddTrackingArea (this.trackArea);
+
+ AppearanceChanged ();
+ }
+
+ private void AppearanceChanged ()
+ {
+ // Place holder so we can handle them changes
+ }
+
+ #region Overrriden Methods and Properties
+
+ public override CGSize IntrinsicContentSize
+ {
+ get { return new CGSize ((int)(Height * originalBackgroundImage.Size.Width / originalBackgroundImage.Size.Height), Height + 6); }
+ }
+
+ public override bool IsFlipped
+ {
+ get { return true; }
+ }
+
+ public override void UpdateTrackingAreas ()
+ {
+ base.UpdateTrackingAreas ();
+ RemoveTrackingArea (trackArea);
+ this.trackArea = new NSTrackingArea (new CGRect (CGPoint.Empty, Frame.Size), NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.ActiveInKeyWindow, this, null);
+ AddTrackingArea (this.trackArea);
+ }
+
+ public override void DrawRect (CGRect dirtyRect)
+ {
+ var rect = Bounds;
+ UpdateBackgroundSurface (rect);
+ DrawPreviewBackground (rect);
+
+ var x = (int)((rect.Width - bgImageSize.Width) / 2);
+ var y = (int)((rect.Height - bgImageSize.Height) / 2);
+
+ // Overlay interface
+ var size = GetWindowSize (bgImageSize);
+ CGRect windowRect = new CGRect (x + 10, y + 10, size.Width, size.Height);
+
+ var elementRect = GetElementRectForWindowAndMask (new RectangleF ((float)windowRect.X, (float)windowRect.Y, (float)windowRect.Width, (float)windowRect.Height), this.mask);
+
+ this.windowFillColor.Set ();
+ NSGraphics.RectFill (windowRect);
+ this.windowBorderColor.Set ();
+ NSGraphics.FrameRectWithWidth (windowRect, 1);
+
+ if (this.enabled)
+ this.enabledElementFillColor.Set ();
+ else
+ this.disabledElementFillColor.Set ();
+ NSGraphics.RectFill (new CGRect (elementRect.X, elementRect.Y, elementRect.Size.Width, elementRect.Size.Height));
+ }
+
+ public override void MouseEntered (NSEvent theEvent)
+ {
+ base.MouseEntered (theEvent);
+ if (this.enabled)
+ StartAnimation ();
+ }
+
+ public override void MouseExited (NSEvent theEvent)
+ {
+ base.MouseExited (theEvent);
+ if (this.enabled)
+ StopAnimation ();
+ NeedsDisplay = true;
+ }
+
+ public sealed override void ViewDidChangeEffectiveAppearance ()
+ {
+ base.ViewDidChangeEffectiveAppearance ();
+
+ AppearanceChanged ();
+ }
+ #endregion
+
+ public bool Enabled
+ {
+ get { return this.enabled; }
+
+ internal set
+ {
+ if (this.enabled == value)
+ return;
+
+ this.enabled = value;
+
+ // Our state has changed, repaint
+ NeedsDisplay = true;
+ }
+ }
+
+ private void UpdateBackgroundSurface (CGRect bounds)
+ {
+ if (bounds == this.lastBounds)
+ return;
+
+ var rect = bounds;
+
+ this.bgImage = originalBackgroundImage;
+ this.bgImage.Size = GetBoxSize (bgImage, rect.Width - 8, rect.Height - 8);
+ this.bgImageSize = this.bgImage.Size;
+ this.lastBounds = bounds;
+ }
+
+ private CGSize GetBoxSize (NSImage image, double maxWidth, double maxHeight)
+ {
+ var size = image.Size;
+ var ratio = (nfloat)Math.Min (maxWidth / size.Width, maxHeight / size.Height);
+ return new CGSize (size.Width * ratio, size.Height * ratio);
+ }
+
+ private void DrawPreviewBackground (CGRect bounds)
+ {
+ var rect = bounds;
+ this.backgroundFillColor.Set ();
+ NSGraphics.RectFill (rect);
+ DrawingUtils.DrawLightShadedBezel (rect, flipped: IsFlipped);
+
+ var x = (int)((rect.Width - bgImageSize.Width) / 2);
+ var y = (int)((rect.Height - bgImageSize.Height) / 2);
+
+
+ this.bgImage.Draw (new CGPoint (x, y), CGRect.Empty, NSCompositingOperation.SourceIn, 1);
+ this.previewBorderColor.Set ();
+ NSGraphics.FrameRectWithWidth (new CGRect (x, y, this.bgImage.Size.Width, this.bgImage.Size.Height), 1);
+ }
+
+ private void StartAnimation ()
+ {
+ if (this.currentAnimation != null)
+ return;
+ const int LowerBound = 90;
+ const int UpperBound = 110;
+ const int TimeStep = 50;
+
+ this.currentAnimationRawValue = (UpperBound + LowerBound) / 2 - 1 - LowerBound;
+
+ this.currentAnimation = NSTimer.CreateRepeatingScheduledTimer (TimeSpan.FromMilliseconds (TimeStep), _ => {
+ // A oscillating function based on abs(x) which values goes linearly from LowerBound to UpperBound
+ this.currentAnimationRawValue = ((this.currentAnimationRawValue + 1 + (UpperBound - LowerBound)) % (2 * UpperBound + 1 - 2 * LowerBound)) - (UpperBound - LowerBound);
+ this.currentAnimationValue = Math.Abs (this.currentAnimationRawValue) + LowerBound;
+ NeedsDisplay = true;
+ });
+ }
+
+ private void StopAnimation ()
+ {
+ if (this.currentAnimation == null)
+ return;
+ this.currentAnimation.Dispose ();
+ this.currentAnimation = null;
+ }
+
+ private Size GetWindowSize (CGSize canvasSize)
+ {
+ var baseWidth = (int)(canvasSize.Width / 2);
+ var baseHeight = (int)(canvasSize.Height / 2);
+
+ if (this.currentAnimation != null) {
+ var adder = this.currentAnimationValue - 100;
+ baseWidth += adder;
+ baseHeight += adder;
+ }
+
+ return new Size (baseWidth, baseHeight);
+ }
+
+ public static RectangleF GetElementRectForWindowAndMask (RectangleF window, AutoResizingFlags mask)
+ {
+ const int Offset = 5;
+ var baseHeight = 10.0;
+ if (mask.HasFlag (AutoResizingFlags.FlexibleDimensions) || mask.HasFlag (AutoResizingFlags.FlexibleHeight)) {
+ baseHeight = window.Size.Height / 2.0;
+ if (!mask.HasFlag (AutoResizingFlags.FlexibleTopMargin) && !mask.HasFlag (AutoResizingFlags.FlexibleMargins))
+ baseHeight += window.Size.Height / 4.0 - Offset;
+ if (!mask.HasFlag (AutoResizingFlags.FlexibleBottomMargin) && !mask.HasFlag (AutoResizingFlags.FlexibleMargins))
+ baseHeight += window.Size.Height / 4.0 - Offset;
+ }
+
+ var baseWidth = 10.0;
+ if (mask.HasFlag (AutoResizingFlags.FlexibleDimensions) || mask.HasFlag (AutoResizingFlags.FlexibleWidth)) {
+ baseWidth = window.Size.Width / 2.0;
+ if (!mask.HasFlag (AutoResizingFlags.FlexibleLeftMargin) && !mask.HasFlag (AutoResizingFlags.FlexibleMargins))
+ baseWidth += window.Size.Width / 4.0 - Offset;
+ if (!mask.HasFlag (AutoResizingFlags.FlexibleRightMargin) && !mask.HasFlag (AutoResizingFlags.FlexibleMargins))
+ baseWidth += window.Size.Width / 4.0 - Offset;
+ }
+
+ double left = Offset;
+ if (mask.HasFlag (AutoResizingFlags.FlexibleLeftMargin) || mask.HasFlag (AutoResizingFlags.FlexibleMargins)) {
+ if (mask.HasFlag (AutoResizingFlags.FlexibleRightMargin) || mask.HasFlag (AutoResizingFlags.FlexibleMargins))
+ left = (window.Size.Width - baseWidth) / 2.0;
+ else
+ left = window.Size.Width - Offset - baseWidth;
+ }
+
+ double top = Offset;
+ if (mask.HasFlag (AutoResizingFlags.FlexibleTopMargin) || mask.HasFlag (AutoResizingFlags.FlexibleMargins)) {
+ if (mask.HasFlag (AutoResizingFlags.FlexibleBottomMargin) || mask.HasFlag (AutoResizingFlags.FlexibleMargins))
+ top = (window.Size.Height - baseHeight) / 2.0;
+ else
+ top = window.Size.Height - Offset - baseHeight;
+ }
+
+ return new RectangleF ((float)(window.X + left),
+ (float)(window.Y + top),
+ (float)(baseWidth),
+ (float)(baseHeight));
+ }
+ }
+}
diff --git a/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingView.cs b/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingView.cs
new file mode 100644
index 0000000..4b3f843
--- /dev/null
+++ b/Xamarin.PropertyEditing.Mac/Controls/AutoResizing/AutoResizingView.cs
@@ -0,0 +1,275 @@
+using System;
+using System.Collections.Generic;
+using AppKit;
+using CoreGraphics;
+
+namespace Xamarin.PropertyEditing.Mac
+{
+ internal class AutoResizingView : NSView
+ {
+ private static NSImage defaultBackground;
+ private readonly UnfocusableTextField maskLabel;
+ private readonly UnfocusableTextField previewLabel;
+ private bool enabled;
+ private IHostResourceProvider hostResources;
+
+ internal AutoResizingMaskView MaskView {
+ get;
+ private set;
+ }
+
+ internal AutoResizingPreviewView PreviewView {
+ get;
+ private set;
+ }
+
+ public bool Enabled {
+ get { return this.enabled; }
+
+ internal set {
+ if (this.enabled == value)
+ return;
+
+ this.enabled = value;
+
+ MaskView.Enabled = this.enabled;
+
+ PreviewView.Enabled = this.enabled;
+ }
+ }
+
+ private const string DeviceFrameName = "pe-device-frame";
+
+ public AutoResizingView (IHostResourceProvider hostResources)
+ {
+ if (hostResources == null)
+ throw new ArgumentNullException (nameof (hostResources));
+
+ this.hostResources = hostResources;
+
+ MaskView = new AutoResizingMaskView {
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ };
+
+ AddSubview (MaskView);
+
+ PreviewView = new AutoResizingPreviewView (GetBackgroundImage ()) {
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ };
+
+ AddSubview (PreviewView);
+
+ this.maskLabel = new UnfocusableTextField {
+ Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultDescriptionLabelFontSize),
+ StringValue = Properties.Resources.Autosizing.ToUpper (),
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ };
+
+ AddSubview (this.maskLabel);
+
+ MaskView.MaskChanged += MaskChanged;
+
+ this.previewLabel = new UnfocusableTextField {
+ Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultDescriptionLabelFontSize),
+ StringValue = Properties.Resources.Example.ToUpper (),
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ AddSubview (this.previewLabel);
+
+ AddConstraints (new NSLayoutConstraint[] {
+ NSLayoutConstraint.Create (MaskView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1, 0),
+ NSLayoutConstraint.Create (MaskView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1, 0),
+ NSLayoutConstraint.Create (MaskView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1, 0),
+
+ NSLayoutConstraint.Create (this.maskLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, MaskView, NSLayoutAttribute.Bottom, 1, 0),
+ NSLayoutConstraint.Create (this.maskLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 18),
+ NSLayoutConstraint.Create (this.maskLabel, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, MaskView, NSLayoutAttribute.CenterX, 1, 0),
+
+ NSLayoutConstraint.Create (PreviewView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, MaskView, NSLayoutAttribute.Left, 1, 0),
+ NSLayoutConstraint.Create (PreviewView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.maskLabel, NSLayoutAttribute.Bottom, 1, 5),
+ NSLayoutConstraint.Create (PreviewView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, MaskView, NSLayoutAttribute.Width, 1, 0),
+
+ NSLayoutConstraint.Create (this.previewLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, PreviewView, NSLayoutAttribute.Bottom, 1, 0),
+ NSLayoutConstraint.Create (this.previewLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 18),
+ NSLayoutConstraint.Create (this.previewLabel, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, PreviewView, NSLayoutAttribute.CenterX, 1, 0),
+ });
+
+ AppearanceChanged ();
+ }
+
+ #region Overrriden Methods and Properties
+
+
+ public override bool IsFlipped {
+ get { return true; }
+ }
+
+ public override CGSize IntrinsicContentSize {
+ get { return new CGSize (-1, 90); }
+ }
+
+ public sealed override void ViewDidChangeEffectiveAppearance ()
+ {
+ base.ViewDidChangeEffectiveAppearance ();
+
+ AppearanceChanged ();
+ }
+
+ #endregion
+
+ private void MaskChanged (object sender, EventArgs e)
+ {
+ PreviewView.Mask = MaskView.Mask;
+ }
+
+ internal void UpdateAccessibilityValues ()
+ {
+ if (MaskView != null) {
+ MaskView.AccessibilityEnabled = this.enabled;
+ MaskView.AccessibilityTitle = Properties.Resources.AccessibilityMaskView;
+ }
+
+ if (PreviewView != null) {
+ PreviewView.AccessibilityEnabled = this.enabled;
+ PreviewView.AccessibilityTitle = Properties.Resources.AccessibilityPreviewMaskView;
+ }
+ }
+
+ private void AppearanceChanged ()
+ {
+ NSColor labelColor = this.hostResources.GetNamedColor (NamedResources.DescriptionLabelColor);
+ this.maskLabel.TextColor = labelColor;
+ this.previewLabel.TextColor = labelColor;
+ }
+
+ public NSImage GetBackgroundImage ()
+ {
+ if (defaultBackground == null) {
+ defaultBackground = this.hostResources.GetNamedImage (DeviceFrameName);
+ // The workaround method isn't available in MonoMac
+#pragma warning disable 0618
+ defaultBackground.Flipped = true;
+#pragma warning restore 0618
+ }
+ return defaultBackground;
+ }
+ }
+
+ internal struct Line
+ {
+ public CGPoint P1;
+ public CGPoint P2;
+ }
+
+ internal static class DrawUtils
+ {
+ public static void DrawStraightLine (CGPoint p1, CGPoint p2, int[] dashes = null)
+ {
+ foreach (var segment in GetSegments (p1, p2, dashes)) {
+ var line = RectForLine (segment.P1, segment.P2);
+ NSGraphics.RectFill (line);
+ }
+ }
+
+ public static IEnumerable<Line> GetSegments (CGPoint p1, CGPoint p2, int[] dashes)
+ {
+ if (dashes == null) {
+ yield return new Line { P1 = p1, P2 = p2 };
+ yield break;
+ }
+
+ int axisMultiplier = GetAxisAdder (p1, p2);
+ int initialOrder = GlobalOrder (p1, p2);
+ var currentStart = p1;
+ var currentEnd = p1;
+ int dashIndex = 0;
+
+ while (GlobalOrder (currentEnd, p2) == initialOrder) {
+ var dash = dashes[dashIndex];
+ currentEnd = new CGPoint (currentEnd.X + (dash * (short)(axisMultiplier >> 16)),
+ currentEnd.Y + (dash * (short)(axisMultiplier & 0xFFFF)));
+
+ // if index is odd, we are skipping drawing and repositioning ourselves
+ if ((dashIndex % 2) == 1) {
+ currentStart = currentEnd;
+ dashIndex = (dashIndex + 1) % dashes.Length;
+ continue;
+ }
+
+ if (GlobalOrder (currentEnd, p2) == initialOrder)
+ yield return new Line { P1 = currentStart, P2 = currentEnd };
+ dashIndex = (dashIndex + 1) % dashes.Length;
+ }
+
+ yield return new Line { P1 = currentStart, P2 = p2 };
+ }
+
+ private static int GlobalOrder (CGPoint p1, CGPoint p2)
+ {
+ return (p1.X < p2.X ? 1 : 0) << 1 | (p1.Y < p2.Y ? 1 : 0);
+ }
+
+ private static int GetAxisAdder (CGPoint p1, CGPoint p2)
+ {
+ if (p1.Y == p2.Y)
+ return p1.X < p2.X ? ((short)1) << 16 : ((short)-1) << 16;
+ else
+ return p1.Y < p2.Y ? ((short)1) : ((short)-1);
+ }
+
+ private static CGRect RectForLine (CGPoint p1, CGPoint p2)
+ {
+ return new CGRect (p1.X < p2.X ? p1.X : p2.X,
+ p1.Y < p2.Y ? p1.Y : p2.Y,
+ Math.Max (1, Math.Abs (p2.X - p1.X)),
+ Math.Max (1, Math.Abs (p2.Y - p1.Y)));
+ }
+ }
+
+ internal static class DrawingUtils
+ {
+ private static nfloat[] lightBezelGreys, fullBezelGreys, fullBezelGreysPixelFix;
+
+ public static void UpdateBezelGreys (bool isLightTheme)
+ {
+ if (isLightTheme) {
+ lightBezelGreys = new nfloat[] { .59f, .71f, .71f, .71f };
+ fullBezelGreys = new nfloat[] { .71f, .96f, .71f, .96f, .61f, .89f, .96f };
+ fullBezelGreysPixelFix = new nfloat[] { .61f, .73f };
+ } else {
+ lightBezelGreys = new nfloat[] { .41f, .29f, .29f, .29f };
+ fullBezelGreys = new nfloat[] { .29f, .04f, .29f, .04f, .39f, .11f, .04f };
+ fullBezelGreysPixelFix = new nfloat[] { .39f, .27f };
+ }
+ }
+
+ /// <summary>
+ /// Draws a shaded, 1 pixel-wide, bezel around a rect
+ /// </summary>
+ public static CGRect DrawLightShadedBezel (CGRect rect, bool flipped = false)
+ {
+ return NSGraphics.DrawTiledRects (rect, rect,
+ new NSRectEdge[] { !flipped ? NSRectEdge.MaxYEdge : NSRectEdge.MinYEdge, NSRectEdge.MinXEdge, NSRectEdge.MaxXEdge, !flipped ? NSRectEdge.MinYEdge : NSRectEdge.MaxYEdge },
+ lightBezelGreys);
+ }
+
+ /// <summary>
+ /// Draws a shaded bezel of more than one pixel around a rect. Returned rectangle is the inner usable area.
+ /// </summary>
+ public static CGRect DrawFullShadedBezel (CGRect rect, bool flipped)
+ {
+ var top = !flipped ? NSRectEdge.MaxYEdge : NSRectEdge.MinYEdge;
+ var bottom = !flipped ? NSRectEdge.MinYEdge : NSRectEdge.MaxYEdge;
+
+ var inner = NSGraphics.DrawTiledRects (rect, rect,
+ new NSRectEdge[] { NSRectEdge.MinXEdge, NSRectEdge.MinXEdge, NSRectEdge.MaxXEdge, NSRectEdge.MaxXEdge, top, top, top },
+ fullBezelGreys);
+ // Redo the top and bottom to clear bad pixels
+ inner.Intersect (NSGraphics.DrawTiledRects (rect, rect,
+ new NSRectEdge[] { top, bottom },
+ fullBezelGreysPixelFix));
+ return inner;
+ }
+ }
+}
diff --git a/Xamarin.PropertyEditing.Mac/Controls/AutoResizingMaskEditorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/AutoResizingMaskEditorControl.cs
new file mode 100644
index 0000000..b48a267
--- /dev/null
+++ b/Xamarin.PropertyEditing.Mac/Controls/AutoResizingMaskEditorControl.cs
@@ -0,0 +1,78 @@
+using System;
+using AppKit;
+using Xamarin.PropertyEditing.ViewModels;
+
+namespace Xamarin.PropertyEditing.Mac
+{
+ internal class AutoResizingMaskEditorControl
+ : PropertyEditorControl<AutoResizingPropertyViewModel>
+ {
+ private readonly AutoResizingView sizeInspectorView;
+
+ public AutoResizingMaskEditorControl (IHostResourceProvider hostResources)
+ : base (hostResources)
+ {
+
+ this.sizeInspectorView = new AutoResizingView (hostResources) {
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ };
+
+ this.sizeInspectorView.MaskView.MaskChanged += (o, e) => {
+ ViewModel.Value = this.sizeInspectorView.MaskView.Mask;
+ };
+
+ AddSubview (this.sizeInspectorView);
+
+ AddConstraints (new[] {
+ NSLayoutConstraint.Create (this.sizeInspectorView, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1, 0),
+ NSLayoutConstraint.Create (this.sizeInspectorView, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1, 70),
+ NSLayoutConstraint.Create (this.sizeInspectorView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1, -6)
+ });
+
+ AppearanceChanged ();
+ }
+
+ #region Overridden Methods and Properties
+
+ private NSView firstKeyView;
+ public override NSView FirstKeyView => this.firstKeyView;
+ private NSView lastKeyView;
+ public override NSView LastKeyView => this.lastKeyView;
+
+ public override nint GetHeight (EditorViewModel vm)
+ {
+ return 200;
+ }
+
+ protected override void OnViewModelChanged (PropertyViewModel oldModel)
+ {
+ base.OnViewModelChanged (oldModel);
+
+ if (ViewModel == null)
+ return;
+
+ if (this.firstKeyView == null) {
+ this.firstKeyView = this.sizeInspectorView.MaskView;
+ this.lastKeyView = this.sizeInspectorView.MaskView;
+ }
+ }
+
+ protected override void SetEnabled ()
+ {
+ this.sizeInspectorView.Enabled = ViewModel.Property.CanWrite;
+ }
+
+ protected override void UpdateAccessibilityValues ()
+ {
+ this.sizeInspectorView.AccessibilityEnabled = this.sizeInspectorView.Enabled;
+ this.sizeInspectorView.AccessibilityTitle = string.Format (Properties.Resources.AccessibilityBoolean, ViewModel.Property.Name);
+ this.sizeInspectorView.UpdateAccessibilityValues ();
+ }
+
+ protected override void UpdateValue ()
+ {
+ this.sizeInspectorView.MaskView.Mask = ViewModel.Value;
+ }
+ #endregion
+ }
+}
diff --git a/Xamarin.PropertyEditing.Mac/PropertyEditingResource/Contents/Resources/pe-device-frame.png b/Xamarin.PropertyEditing.Mac/PropertyEditingResource/Contents/Resources/pe-device-frame.png
new file mode 100644
index 0000000..faa4db9
--- /dev/null
+++ b/Xamarin.PropertyEditing.Mac/PropertyEditingResource/Contents/Resources/pe-device-frame.png
Binary files differ
diff --git a/Xamarin.PropertyEditing.Mac/PropertyEditingResource/Contents/Resources/pe-device-frame~dark.png b/Xamarin.PropertyEditing.Mac/PropertyEditingResource/Contents/Resources/pe-device-frame~dark.png
new file mode 100644
index 0000000..faa4db9
--- /dev/null
+++ b/Xamarin.PropertyEditing.Mac/PropertyEditingResource/Contents/Resources/pe-device-frame~dark.png
Binary files differ
diff --git a/Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs b/Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs
index bff6d97..2d89d3d 100644
--- a/Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs
+++ b/Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs
@@ -61,7 +61,7 @@ namespace Xamarin.PropertyEditing.Mac
{typeof (ObjectPropertyViewModel), typeof (ObjectEditorControl)},
{typeof (TypePropertyViewModel), typeof (TypeEditorControl)},
{typeof (CollectionPropertyViewModel), typeof (CollectionInlineEditorControl)},
-
+ {typeof (AutoResizingPropertyViewModel), typeof (AutoResizingMaskEditorControl)},
};
}
}
diff --git a/Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj b/Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj
index eb49853..dedcb08 100644
--- a/Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj
+++ b/Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj
@@ -18,6 +18,9 @@
<ProjectReference Include="..\Xamarin.PropertyEditing\Xamarin.PropertyEditing.csproj" />
</ItemGroup>
+ <ItemGroup>
+ <Folder Include="Controls\AutoResizing\" />
+ </ItemGroup>
<Target Name="IncludeIconsInBundle" BeforeTargets="AssignTargetPaths">
<ItemGroup>
<PropertyEditingResourceBundlePath Include="PropertyEditingResource\**\*" />
diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
index 991ef5b..dadfe4c 100644
--- a/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
+++ b/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
@@ -12,6 +12,7 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
public MockSampleControl ()
{
AddProperty<AutoResizingFlags> ("Autoresizing", ReadWrite, valueSources: ValueSources.Local, ignoreEnum: true);
+ AddProperty<AutoResizingFlags> ("ReadOnlyAutoresizing", ReadOnly, false, valueSources: ValueSources.Local, ignoreEnum: true);
AddProperty<TimeSpan> ("TimeSpan", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding);
AddProperty<TimeSpan> ("TimeSpanReadOnly", ReadOnly, canWrite: false, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding);
AddProperty<bool> ("Boolean", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding);
@@ -102,7 +103,7 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
AddProperty<FilePath> ("FilePath", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding);
AddReadOnlyProperty<FilePath> ("ReadOnlyFilePath", ReadOnly);
AddProperty<DateTime> ("DateTime", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding);
- AddReadOnlyProperty<DateTime> ("ReadDateTime", ReadOnly);
+ AddReadOnlyProperty<DateTime> ("ReadOnlyDateTime", ReadOnly);
AddEvents ("Click", "Hover", "Focus");
diff --git a/Xamarin.PropertyEditing/Common/AutoResizingFlags.cs b/Xamarin.PropertyEditing/Common/AutoResizingFlags.cs
index 8c21b55..338c034 100644
--- a/Xamarin.PropertyEditing/Common/AutoResizingFlags.cs
+++ b/Xamarin.PropertyEditing/Common/AutoResizingFlags.cs
@@ -16,4 +16,4 @@ namespace Xamarin.PropertyEditing.Common
FlexibleDimensions = FlexibleHeight | FlexibleWidth,
All = FlexibleMargins | FlexibleDimensions
}
-}
+} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing/Properties/Resources.Designer.cs b/Xamarin.PropertyEditing/Properties/Resources.Designer.cs
index 2218d26..188dfed 100644
--- a/Xamarin.PropertyEditing/Properties/Resources.Designer.cs
+++ b/Xamarin.PropertyEditing/Properties/Resources.Designer.cs
@@ -1475,6 +1475,18 @@ namespace Xamarin.PropertyEditing.Properties {
}
}
+ public static string AccessibilityMaskView {
+ get {
+ return ResourceManager.GetString("AccessibilityMaskView", resourceCulture);
+ }
+ }
+
+ public static string AccessibilityPreviewMaskView {
+ get {
+ return ResourceManager.GetString("AccessibilityPreviewMaskView", resourceCulture);
+ }
+ }
+
public static string FilterTypePlaceholder {
get {
return ResourceManager.GetString("FilterTypePlaceholder", resourceCulture);
diff --git a/Xamarin.PropertyEditing/Properties/Resources.resx b/Xamarin.PropertyEditing/Properties/Resources.resx
index 87e5631..2aebabc 100644
--- a/Xamarin.PropertyEditing/Properties/Resources.resx
+++ b/Xamarin.PropertyEditing/Properties/Resources.resx
@@ -1039,6 +1039,14 @@
</data>
<data name="ObjectTypeLabelNone" xml:space="preserve">
<value>None</value>
+ </data>
+ <data name="AccessibilityMaskView" xml:space="preserve">
+ <value>{0} Autoresizing Mask Editor</value>
+ <comment>Editor that allows you to change auto resizing flags</comment>
+ </data>
+ <data name="AccessibilityPreviewMaskView" xml:space="preserve">
+ <value>{0} Autoresizing Preview View</value>
+ <comment>Previewer that allows you to view the changes you've made in the Autoresizing Mask Edito</comment>
</data>
<data name="FilterTypePlaceholder" xml:space="preserve">
<value>Filter Types</value>
diff --git a/Xamarin.PropertyEditing/ViewModels/AutoResizingPropertyViewModel.cs b/Xamarin.PropertyEditing/ViewModels/AutoResizingPropertyViewModel.cs
index 2f29af8..a0441a8 100644
--- a/Xamarin.PropertyEditing/ViewModels/AutoResizingPropertyViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/AutoResizingPropertyViewModel.cs
@@ -154,4 +154,4 @@ namespace Xamarin.PropertyEditing.ViewModels
}
}
}
-}
+} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs
index 380aea9..5862ad9 100644
--- a/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs
@@ -604,27 +604,20 @@ namespace Xamarin.PropertyEditing.ViewModels
private PropertyViewModel CreateViewModel (IPropertyInfo property, PropertyVariation variant = null)
{
PropertyViewModel vm;
- if (ViewModelMap.TryGetValue (property.Type, out var vmFactory))
+ Type[] interfaces = property.GetType ().GetInterfaces ();
+
+ Type hasPredefinedValues = interfaces.FirstOrDefault (t => t.IsGenericType && t.GetGenericTypeDefinition () == typeof(IHavePredefinedValues<>));
+ if (hasPredefinedValues != null) {
+ bool combinable = (bool) hasPredefinedValues.GetProperty (nameof(IHavePredefinedValues<bool>.IsValueCombinable)).GetValue (property);
+ Type type = combinable
+ ? typeof(CombinablePropertyViewModel<>).MakeGenericType (hasPredefinedValues.GenericTypeArguments[0])
+ : typeof(PredefinedValuesViewModel<>).MakeGenericType (hasPredefinedValues.GenericTypeArguments[0]);
+
+ vm = (PropertyViewModel) Activator.CreateInstance (type, TargetPlatform, property, this.objEditors, variant);
+ } else if (ViewModelMap.TryGetValue (property.Type, out var vmFactory))
vm = vmFactory (TargetPlatform, property, this.objEditors, variant);
- else {
- Type[] interfaces = property.GetType ().GetInterfaces ();
-
- Type hasPredefinedValues = interfaces.FirstOrDefault (t =>
- t.IsGenericType && t.GetGenericTypeDefinition () == typeof(IHavePredefinedValues<>));
- if (hasPredefinedValues != null) {
- bool combinable = (bool) hasPredefinedValues
- .GetProperty (nameof(IHavePredefinedValues<bool>.IsValueCombinable)).GetValue (property);
- Type type = combinable
- ? typeof(CombinablePropertyViewModel<>).MakeGenericType (hasPredefinedValues
- .GenericTypeArguments[0])
- : typeof(PredefinedValuesViewModel<>).MakeGenericType (
- hasPredefinedValues.GenericTypeArguments[0]);
-
- vm = (PropertyViewModel) Activator.CreateInstance (type, TargetPlatform, property, this.objEditors,
- variant);
- } else
- vm = new StringPropertyViewModel (TargetPlatform, property, this.objEditors, variant);
- }
+ else
+ vm = new StringPropertyViewModel (TargetPlatform, property, this.objEditors, variant);
vm.Parent = this;
vm.VariantsChanged += OnVariantsChanged;