From 820c53ae01fa95075fd3990f56a81a30e38e8312 Mon Sep 17 00:00:00 2001 From: Dominique Louis Date: Thu, 10 Jan 2019 10:08:49 +0000 Subject: [Mac] Initial Binding Editor Dialog. --- .../Controls/BindingEditor/BindingEditorWindow.cs | 273 +++++++++++++++++ .../BindingEditor/BindingObjectSelectorControl.cs | 211 +++++++++++++ .../BindingEditor/BindingPathSelectorControl.cs | 337 +++++++++++++++++++++ .../BindingResourceSelectorControl.cs | 247 +++++++++++++++ .../BindingEditor/BindingTypeSelectorControl.cs | 311 +++++++++++++++++++ .../BindingEditor/CreateValueConverterWindow.cs | 119 ++++++++ .../Controls/BindingEditor/HeaderView.cs | 42 +++ .../Controls/Custom/BasePanelWindow.cs | 301 ++++++++++++++++++ .../Controls/Custom/PropertyButton.cs | 23 +- .../Controls/PropertyEditorControl.cs | 36 +-- .../Controls/TypeSelectorControl.cs | 2 + .../PropertyTableDataSource.cs | 4 +- .../CreateBindingViewModelTests.cs | 8 +- .../Properties/Resources.Designer.cs | 85 ++++-- Xamarin.PropertyEditing/Properties/Resources.resx | 22 ++ .../ViewModels/CreateBindingViewModel.cs | 8 +- 16 files changed, 1978 insertions(+), 51 deletions(-) create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingEditorWindow.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingObjectSelectorControl.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingPathSelectorControl.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceSelectorControl.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingTypeSelectorControl.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/HeaderView.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/Custom/BasePanelWindow.cs diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingEditorWindow.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingEditorWindow.cs new file mode 100644 index 0000000..7e66a6b --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingEditorWindow.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AppKit; +using CoreGraphics; +using Foundation; +using Xamarin.PropertyEditing.Common; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class BindingEditorWindow : BasePanelWindow + { + private readonly CreateBindingViewModel viewModel; + private readonly PropertyEditorSelector editorSelector = new PropertyEditorSelector (); + + internal BindingEditorWindow (IHostResourceProvider hostResources, PropertyViewModel propertyViewModel) + { + this.viewModel = new CreateBindingViewModel (propertyViewModel.TargetPlatform, propertyViewModel.Editors.Single (), propertyViewModel.Property); + + Title = this.viewModel.PropertyDisplay; + this.ButtonDone.Title = Properties.Resources.CreateBindingTitle; + + foreach (BindingSource item in this.viewModel.BindingSources.Value) { + this.BindingTypePopup.Menu.AddItem (new NSMenuItem (item.Name) { + RepresentedObject = new NSObjectFacade (item) + }); + } + + this.BindingTypePopup.Activated += (o, e) => { + if (this.BindingTypePopup.Menu.HighlightedItem.RepresentedObject is NSObjectFacade facade) { + this.viewModel.SelectedBindingSource = (BindingSource)facade.Target; + } + }; + + this.ValueConverterPopup.Activated += (o, e) => { + if (this.ValueConverterPopup.Menu.HighlightedItem.RepresentedObject is NSObjectFacade facade) { + this.viewModel.SelectedValueConverter = (Resource)facade.Target; + } + }; + + RepopulateValueConverterPopup (); + + this.AddConverterButton.Activated += (sender, e) => { + this.viewModel.SelectedValueConverter = CreateBindingViewModel.AddValueConverter; + }; + + var typeHeader = new HeaderView { + Title = Properties.Resources.Type, + }; + + this.AncestorTypeBox.AddSubview (typeHeader); + + this.AncestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (typeHeader, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (typeHeader, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (typeHeader, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (typeHeader, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 40), + }); + + var typeSelectorControl = new TypeSelectorControl { + Hidden = true, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.AncestorTypeBox.AddSubview (typeSelectorControl); + + this.AncestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 36f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Height, 1f, -35f) + }); + + var resourceSelectorControl = new BindingResourceSelectorControl (this.viewModel) { + Hidden = true, + }; + + this.AncestorTypeBox.AddSubview (resourceSelectorControl); + + this.AncestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 5f), + NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 5f), + NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Height, 1f, -10f) + }); + + var objectSelectorControl = new BindingObjectSelectorControl (this.viewModel) { + Hidden = true, + }; + + this.AncestorTypeBox.AddSubview (objectSelectorControl); + + this.AncestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 5f), + NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 5f), + NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Height, 1f, -10f) + }); + + var longDescription = new UnfocusableTextField { + Alignment = NSTextAlignment.Left, + TranslatesAutoresizingMaskIntoConstraints = false, + StringValue = string.Empty, + }; + + this.AncestorTypeBox.AddSubview (longDescription); + + this.AncestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (longDescription, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 10f), + NSLayoutConstraint.Create (longDescription, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 10f), + NSLayoutConstraint.Create (longDescription, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (longDescription, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + }); + + var pathHeader = new HeaderView { + Title = Properties.Resources.Path, + }; + + this.PathBox.AddSubview (pathHeader); + + this.PathBox.AddConstraints (new[] { + NSLayoutConstraint.Create (pathHeader, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (pathHeader, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (pathHeader, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (pathHeader, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 40), + }); + + var pathSelectorControl = new BindingPathSelectorControl (this.viewModel); + + this.PathBox.AddSubview (pathSelectorControl); + + this.PathBox.AddConstraints (new[] { + NSLayoutConstraint.Create (pathSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Top, 1f, 5f), + NSLayoutConstraint.Create (pathSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Left, 1f, 5f), + NSLayoutConstraint.Create (pathSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (pathSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Height, 1f, -10f) + }); + + this.viewModel.PropertyChanged += (sender, e) => { + if (e.PropertyName == nameof (CreateBindingViewModel.ShowLongDescription)) { + longDescription.Hidden = !this.viewModel.ShowLongDescription; + longDescription.StringValue = this.viewModel.ShowLongDescription ? this.viewModel.SelectedBindingSource.Description : string.Empty; + typeHeader.Hidden = this.viewModel.ShowLongDescription; + } + + if (e.PropertyName == nameof (CreateBindingViewModel.ShowObjectSelector)) { + if (this.viewModel.ShowObjectSelector) { + typeHeader.Title = Properties.Resources.SelectObjectTitle; + } + } + + if (e.PropertyName == nameof (CreateBindingViewModel.ShowTypeSelector)) { + typeSelectorControl.Hidden = !this.viewModel.ShowTypeSelector; + + if (this.viewModel.ShowTypeSelector) { + typeHeader.Title = Properties.Resources.SelectTypeTitle; + + if (this.viewModel.ShowTypeSelector && this.viewModel.TypeSelector != null) { + typeSelectorControl.ViewModel = this.viewModel.TypeSelector; + } + } + } + + if (e.PropertyName == nameof (CreateBindingViewModel.ShowResourceSelector)) { + if (this.viewModel.ShowResourceSelector) { + typeHeader.Title = Properties.Resources.SelectResourceTitle; + } + } + + if (e.PropertyName == nameof (CreateBindingViewModel.SelectedValueConverter)) { + RepopulateValueConverterPopup (); + } + }; + + this.viewModel.CreateValueConverterRequested += OnCreateValueConverterRequested; + + this.ButtonDone.Activated += (sender, e) => { + if (pathSelectorControl.CustomPath.Enabled && !string.IsNullOrEmpty (pathSelectorControl.CustomPath.Cell.Title)) { + this.viewModel.Path = pathSelectorControl.CustomPath.Cell.Title; + } + + Close (); + }; + + // More Settings + var controlTop = 6; + var identifier = "BindingProperties"; + + foreach (PropertyViewModel vm in this.viewModel.BindingProperties) { + IEditorView editor = this.editorSelector.GetEditor (hostResources, vm); + + NSView nSView = new EditorContainer (hostResources, editor) { + Identifier = identifier, + Label = vm.Property.Name, + TranslatesAutoresizingMaskIntoConstraints = false, + ViewModel = vm, + }; + + this.BindingPropertiesView.AddSubview (nSView); + + this.BindingPropertiesView.AddConstraints (new[] { + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Top, 1f, controlTop), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Left, 1f, 16f), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Width, 1f, -20f), + }); + + controlTop += PropertyEditorControl.DefaultControlHeight; + } + + var boundsHeight = controlTop; + + controlTop = 9; + identifier = "FlagsProperties"; + foreach (PropertyViewModel vm in this.viewModel.FlagsProperties) { + + IEditorView editor = this.editorSelector.GetEditor (hostResources, vm); + + NSView nSView = new EditorContainer (hostResources, editor) { + Identifier = identifier, + Label = vm.Property.Name, + TranslatesAutoresizingMaskIntoConstraints = false, + ViewModel = vm, + }; + + this.FlagsPropertiesView.AddSubview (nSView); + + this.FlagsPropertiesView.AddConstraints (new[] { + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.FlagsPropertiesView, NSLayoutAttribute.Top, 1f, controlTop), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.FlagsPropertiesView, NSLayoutAttribute.Left, 1f, 16f), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.FlagsPropertiesView, NSLayoutAttribute.Width, 1f, -20f), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 18f), + }); + + controlTop += PropertyEditorControl.DefaultControlHeight; + } + + if (boundsHeight < controlTop) + boundsHeight = controlTop; + + this.MoreSettingsViewHeight = boundsHeight + 8; + } + + private void RepopulateValueConverterPopup () + { + this.ValueConverterPopup.RemoveAllItems (); + foreach (Resource item in this.viewModel.ValueConverters.Value) { + this.ValueConverterPopup.Menu.AddItem (new NSMenuItem (item.Name) { + RepresentedObject = new NSObjectFacade (item) + }); + } + } + + private void OnCreateValueConverterRequested (object sender, CreateValueConverterEventArgs e) + { + ITypeInfo valueConverter = this.viewModel.TargetPlatform.EditorProvider.KnownTypes[typeof (CommonValueConverter)]; + + var typesTask = this.viewModel.TargetPlatform.EditorProvider.GetAssignableTypesAsync (valueConverter, childTypes: false) + .ContinueWith (t => t.Result.GetTypeTree (), TaskScheduler.Default); + + var createValueConverterWindow = new CreateValueConverterWindow (this.viewModel, new AsyncValue>> (typesTask)); + var result = (NSModalResponse)(int)NSApplication.SharedApplication.RunModalForWindow (createValueConverterWindow); + if (result == NSModalResponse.OK) { + if (createValueConverterWindow.ViewModel.SelectedType != null) { + e.Name = createValueConverterWindow.ValueConverterName; + e.ConverterType = createValueConverterWindow.ViewModel.SelectedType; + e.Source = createValueConverterWindow.ViewModel.Source; + } + } + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingObjectSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingObjectSelectorControl.cs new file mode 100644 index 0000000..9022c37 --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingObjectSelectorControl.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using AppKit; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class BindingObjectSelectorControl : NSView + { + internal class ObjectOutlineView : NSOutlineView + { + private IReadOnlyList viewModel; + public IReadOnlyList ViewModel { + get => this.viewModel; + set { + if (this.viewModel != value) { + this.viewModel = value; + var dataSource = new ObjectOutlineViewDataSource (this.viewModel); + Delegate = new ObjectOutlineViewDelegate (dataSource); + DataSource = dataSource; + } + + if (this.viewModel != null) { + ReloadData (); + + ExpandItem (null, true); + } + } + } + + public ObjectOutlineView () + { + Initialize (); + } + + // Called when created from unmanaged code + public ObjectOutlineView (IntPtr handle) : base (handle) + { + Initialize (); + } + + // Called when created directly from a XIB file + [Export ("initWithCoder:")] + public ObjectOutlineView (NSCoder coder) : base (coder) + { + Initialize (); + } + + [Export ("validateProposedFirstResponder:forEvent:")] + public bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) + { + return true; + } + + public void Initialize () + { + AutoresizingMask = NSViewResizingMask.WidthSizable; + HeaderView = null; + TranslatesAutoresizingMaskIntoConstraints = false; + } + } + + internal class ObjectOutlineViewDelegate : NSOutlineViewDelegate + { + private ObjectOutlineViewDataSource dataSource; + + public ObjectOutlineViewDelegate (ObjectOutlineViewDataSource dataSource) + { + this.dataSource = dataSource; + } + + public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) + { + return PropertyEditorControl.DefaultControlHeight; + } + + public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) + { + var labelContainer = (UnfocusableTextField)outlineView.MakeView ("type", this); + if (labelContainer == null) { + labelContainer = new UnfocusableTextField { + Identifier = "type", + }; + } + var target = (item as NSObjectFacade).Target; + + switch (target) { + case KeyValuePair kvp: + labelContainer.StringValue = kvp.Key; + break; + case TypeInfo info: + labelContainer.StringValue = info.Name; + break; + default: + labelContainer.StringValue = "Type Not Supported"; + break; + } + + return labelContainer; + } + + public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + return false; + case TypeInfo info: + return true; + + default: + return false; + } + } + } + + internal class ObjectOutlineViewDataSource : NSOutlineViewDataSource + { + public IReadOnlyList ViewModel { get; } + + internal ObjectOutlineViewDataSource (IReadOnlyList viewModel) + { + if (viewModel == null) + throw new ArgumentNullException (nameof (viewModel)); + + ViewModel = viewModel; + } + + public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) + { + var childCount = 0; + if (item == null) { + childCount = this.ViewModel != null ? this.ViewModel.Count () : 0; + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + childCount = kvp.Value.Count; + break; + case TypeInfo info: + childCount = 0; + break; + default: + childCount = 0; + break; + } + } + + return childCount; + } + + public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) + { + object element; + + if (item == null) { + element = this.ViewModel.ElementAt ((int)childIndex); + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + element = kvp.Value[(int)childIndex]; + break; + case TypeInfo info: + element = info; + break; + default: + return null; + } + } + + return new NSObjectFacade (element); + } + + public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + return kvp.Value.Count > 0; + case TypeInfo info: + return false; + default: + return false; + } + } + } + + internal ObjectOutlineView objectOutlineView; + + internal BindingObjectSelectorControl (CreateBindingViewModel viewModel) + { + if (viewModel == null) + throw new ArgumentNullException (nameof (viewModel)); + + this.objectOutlineView = new ObjectOutlineView (); + TranslatesAutoresizingMaskIntoConstraints = false; + + viewModel.PropertyChanged += (sender, e) => { + if (e.PropertyName == nameof (CreateBindingViewModel.ShowObjectSelector)) { + Hidden = !viewModel.ShowObjectSelector; + + if (viewModel.ShowObjectSelector && viewModel.ObjectElementRoots != null) { + this.objectOutlineView.ViewModel = viewModel.ObjectElementRoots.Value; + }; + } + }; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingPathSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingPathSelectorControl.cs new file mode 100644 index 0000000..0123d7b --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingPathSelectorControl.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AppKit; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class PathOutlineView : NSOutlineView + { + private IReadOnlyCollection viewModel; + private PropertyTreeRoot propertyTreeRoot; + + private IReadOnlyCollection ViewModel + { + get => this.viewModel; + set { + if (this.viewModel != value) { + this.viewModel = value; + + var dataSource = new PathOutlineViewDataSource (this.viewModel, this.targetName); + Delegate = new PathOutlineViewDelegate (dataSource); + DataSource = dataSource; + + ReloadData (); + + ExpandItem (ItemAtRow (0)); + } + } + } + + private string targetName { get; set; } + public PropertyTreeRoot PropertyTreeRoot + { + get { return this.propertyTreeRoot; } + internal set { + if (this.propertyTreeRoot != value) { + this.propertyTreeRoot = value; + if (this.propertyTreeRoot != null) { + targetName = this.propertyTreeRoot.TargetType.Name; + ViewModel = this.propertyTreeRoot.Children; + } else { + targetName = string.Empty; + ViewModel = null; + } + } + } + } + + public PathOutlineView () + { + Initialize (); + } + + // Called when created from unmanaged code + public PathOutlineView (IntPtr handle) : base (handle) + { + Initialize (); + } + + // Called when created directly from a XIB file + [Export ("initWithCoder:")] + public PathOutlineView (NSCoder coder) : base (coder) + { + Initialize (); + } + + [Export ("validateProposedFirstResponder:forEvent:")] + public bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) + { + return true; + } + + public void Initialize () + { + AutoresizingMask = NSViewResizingMask.WidthSizable; + HeaderView = null; + TranslatesAutoresizingMaskIntoConstraints = false; + } + } + + internal class PathOutlineViewDelegate : NSOutlineViewDelegate + { + private readonly PathOutlineViewDataSource dataSource; + + public PathOutlineViewDelegate (PathOutlineViewDataSource dataSource) + { + this.dataSource = dataSource; + } + + public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) + { + return PropertyEditorControl.DefaultControlHeight; + } + + public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) + { + var labelContainer = (UnfocusableTextField)outlineView.MakeView ("path", this); + if (labelContainer == null) { + labelContainer = new UnfocusableTextField { + Identifier = "path", + }; + } + var target = (item as NSObjectFacade).Target; + + switch (target) { + case PropertyTreeElement propertyTreeElement: + labelContainer.StringValue = string.Format("{0}: ({1})", propertyTreeElement.Property.Name, propertyTreeElement.Property.RealType.Name); + break; + + case string targetName: + labelContainer.StringValue = targetName; + break; + + default: + labelContainer.StringValue = "Type Not Supported"; + break; + } + + return labelContainer; + } + + public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) + { + if (item is NSObjectFacade facade) { + switch (facade.Target) { + case PropertyTreeElement propertyTreeElement: + var propertyTreeResult = propertyTreeElement.Children.Task.Result; + return propertyTreeResult.Count == 0; + + default: + return false; + } + } else { + return false; + } + } + } + + internal class PathOutlineViewDataSource : NSOutlineViewDataSource + { + private readonly IReadOnlyCollection viewModel; + private string targetName; + + internal PathOutlineViewDataSource (IReadOnlyCollection viewModel, string targetName) + { + this.viewModel = viewModel; + this.targetName = targetName; + } + + public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) + { + nint childCount; + if (item == null) { + childCount = this.viewModel != null ? this.viewModel.Count () + 1 : 0; + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case PropertyTreeElement propertyTreeElement: + IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; + childCount = propertyTrees.Count; + break; + + case string targetName: + childCount = this.viewModel.Count (); + break; + + default: + childCount = 0; + break; + } + } + + return childCount; + } + + public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) + { + if (childIndex == 0 && item == null) { + return new NSObjectFacade (targetName); + } + + if (item is NSObjectFacade objectFacade) { + var target = objectFacade.Target; + switch (target) { + case PropertyTreeElement propertyTreeElement: + IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; + return new NSObjectFacade (propertyTrees.ElementAt ((int)childIndex)); + + case string targetName: + return new NSObjectFacade (this.viewModel.ElementAt ((int)childIndex)); + + default: + return null; + } + + } + return null; + } + + public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) + { + if (item is NSObjectFacade objectFacade) { + var target = objectFacade.Target; + switch (target) { + case PropertyTreeElement propertyTreeElement: + IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; + return propertyTrees.Count > 0; + + case string targetName: + return true; + + default: + return false; + } + } + + return false; + } + } + + internal class BindingPathSelectorControl : NSView + { + private PathOutlineView pathOutlineView; + internal const string PathSelectorColumnColId = "PathSelectorColumn"; + private const float spacing = 10f; + private NSTextField customPath; + public NSTextField CustomPath { get; private set; } + + public BindingPathSelectorControl (CreateBindingViewModel viewModel) + { + TranslatesAutoresizingMaskIntoConstraints = false; + + var customCheckBox = new NSButton { + ControlSize = NSControlSize.Small, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), + Title = Properties.Resources.Custom, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + customCheckBox.SetButtonType (NSButtonType.Switch); + + AddSubview (customCheckBox); + AddConstraints (new[] { + NSLayoutConstraint.Create (customCheckBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 8f), + NSLayoutConstraint.Create (customCheckBox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Right, 1f, -90f), + }); + + this.customPath = new NSTextField { + ControlSize = NSControlSize.Mini, + Enabled = false, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + var customPathHeightConstraint = NSLayoutConstraint.Create (this.customPath, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 0); + + AddSubview (this.customPath); + AddConstraints (new[] { + NSLayoutConstraint.Create (this.customPath, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 45f), + NSLayoutConstraint.Create (this.customPath, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (this.customPath, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0), + customPathHeightConstraint, + }); + + // create a table view and a scroll view + var outlineViewContainer = new NSScrollView { + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + var outlineViewContainerTopConstraint = NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.customPath, NSLayoutAttribute.Bottom, 1f, 0f); + customCheckBox.Activated += (sender, e) => { + this.customPath.Enabled = customCheckBox.State == NSCellStateValue.On; + customPathHeightConstraint.Constant = this.customPath.Enabled ? PropertyEditorControl.DefaultControlHeight : 0; + outlineViewContainerTopConstraint.Constant = this.customPath.Enabled ? 10 : 0; + SetCustomPath (viewModel); + }; + + this.customPath.Changed += (sender, e) => { + viewModel.Path = this.customPath.StringValue; + }; + + this.pathOutlineView = new PathOutlineView { + + }; + + this.pathOutlineView.Activated += (sender, e) => { + if (sender is PathOutlineView pov) { + if (pov.SelectedRow != -1) { + if (pov.ItemAtRow (pov.SelectedRow) is NSObjectFacade facade) { + switch (facade.Target) { + case PropertyTreeElement propertyTreeElement: + viewModel.SelectedPropertyElement = propertyTreeElement; + break; + + default: + break; + } + SetCustomPath (viewModel); + } + } + } + }; + + var pathColumn = new NSTableColumn (PathSelectorColumnColId); + this.pathOutlineView.AddColumn (pathColumn); + + // Set OutlineTableColumn or the arrows showing children/expansion will not be drawn + this.pathOutlineView.OutlineTableColumn = pathColumn; + + // add the panel to the window + outlineViewContainer.DocumentView = this.pathOutlineView; + AddSubview (outlineViewContainer); + + AddConstraints (new[] { + outlineViewContainerTopConstraint, + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal,this, NSLayoutAttribute.Bottom, 1f, -5f), + }); + + viewModel.PropertyChanged += async (sender, e) => { + if (viewModel.PropertyRoot != null) { + this.pathOutlineView.PropertyTreeRoot = await viewModel.PropertyRoot.Task; + } else { + this.pathOutlineView.PropertyTreeRoot = null; + } + }; + } + + private void SetCustomPath (CreateBindingViewModel viewModel) + { + this.customPath.StringValue = this.customPath.Enabled ? viewModel.Path ?? string.Empty : string.Empty; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceSelectorControl.cs new file mode 100644 index 0000000..afcf746 --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceSelectorControl.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AppKit; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class BindingResourceSelectorControl : NSView + { + internal class ResourceOutlineView : NSOutlineView + { + private ILookup viewModel; + public ILookup ViewModel { + get => this.viewModel; + set { + if (this.viewModel != value) { + this.viewModel = value; + var dataSource = new ResourceOutlineViewDataSource (this.viewModel); + Delegate = new ResourceOutlineViewDelegate (dataSource); + DataSource = dataSource; + } + + if (this.viewModel != null) { + ReloadData (); + + ExpandItem (null, true); + } + } + } + + public ResourceOutlineView () + { + Initialize (); + } + + // Called when created from unmanaged code + public ResourceOutlineView (IntPtr handle) : base (handle) + { + Initialize (); + } + + // Called when created directly from a XIB file + [Export ("initWithCoder:")] + public ResourceOutlineView (NSCoder coder) : base (coder) + { + Initialize (); + } + + [Export ("validateProposedFirstResponder:forEvent:")] + public bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) + { + return true; + } + + public void Initialize () + { + AutoresizingMask = NSViewResizingMask.WidthSizable; + HeaderView = null; + TranslatesAutoresizingMaskIntoConstraints = false; + } + } + + internal class ResourceOutlineViewDelegate : NSOutlineViewDelegate + { + private readonly ResourceOutlineViewDataSource dataSource; + + public ResourceOutlineViewDelegate (ResourceOutlineViewDataSource datasource) + { + this.dataSource = datasource; + } + + public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) + { + return PropertyEditorControl.DefaultControlHeight; + } + + public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) + { + var labelContainer = (UnfocusableTextField)outlineView.MakeView ("resource", this); + if (labelContainer == null) { + labelContainer = new UnfocusableTextField { + Identifier = "resource", + }; + } + var target = (item as NSObjectFacade).Target; + + switch (target) { + case IGrouping kvp: + labelContainer.StringValue = kvp.Key.Name; + break; + case Resource resource: + labelContainer.StringValue = resource.Name; + break; + default: + labelContainer.StringValue = "Resource Not Supported"; + break; + } + + return labelContainer; + } + + public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case IGrouping kvp: + return false; + case Resource resource: + return true; + + default: + return false; + } + } + } + + internal class ResourceOutlineViewDataSource : NSOutlineViewDataSource + { + public ILookup ViewModel { get; } + + internal ResourceOutlineViewDataSource (ILookup viewModel) + { + if (viewModel == null) + throw new ArgumentNullException (nameof (viewModel)); + + ViewModel = viewModel; + } + + public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) + { + var childCount = 0; + if (item == null) { + childCount = this.ViewModel != null ? this.ViewModel.Count () : 0; + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case IGrouping < ResourceSource, Resource> kvp: + childCount = kvp.Count (); + break; + case Resource resource: + childCount = 0; + break; + default: + childCount = 0; + break; + } + } + + return childCount; + } + + public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) + { + object element; + + if (item == null) { + element = this.ViewModel.ElementAt ((int)childIndex); + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case IGrouping kvp: + element = kvp.ElementAt ((int)childIndex); + break; + case Resource resource: + element = resource; + break; + default: + return null; + } + } + + return new NSObjectFacade (element); + } + + public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case IGrouping kvp: + return kvp.Count() > 0; + case Resource resource: + return false; + default: + return false; + } + } + } + + internal const string ResourceSelectorColId = "ResourceSelectorColumn"; + + public ResourceOutlineView resourceOutlineView; + + internal BindingResourceSelectorControl (CreateBindingViewModel viewModel) + { + TranslatesAutoresizingMaskIntoConstraints = false; + + this.resourceOutlineView = new ResourceOutlineView { + + }; + this.resourceOutlineView.Activated += (sender, e) => { + if (sender is ResourceOutlineView rov) { + if (rov.SelectedRow != -1) { + if (rov.ItemAtRow (rov.SelectedRow) is NSObjectFacade item) { + if (item.Target is Resource resource) { + viewModel.SelectedResource = resource; + } + } + } + } + }; + + var resourceColumn = new NSTableColumn (ResourceSelectorColId); + this.resourceOutlineView.AddColumn (resourceColumn); + + // Set OutlineTableColumn or the arrows showing children/expansion will not be drawn + this.resourceOutlineView.OutlineTableColumn = resourceColumn; + + // create a table view and a scroll view + var outlineViewContainer = new NSScrollView { + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + // add the panel to the window + outlineViewContainer.DocumentView = this.resourceOutlineView; + AddSubview (outlineViewContainer); + + AddConstraints (new[] { + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 45f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 5f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Height, NSLayoutRelation.Equal,this, NSLayoutAttribute.Height, 1f, -50f), + }); + + viewModel.PropertyChanged += (sender, e) => { + if (e.PropertyName == nameof (CreateBindingViewModel.ShowResourceSelector)) { + Hidden = !viewModel.ShowResourceSelector; + + if (viewModel.ShowResourceSelector && viewModel.SourceResources != null) { + this.resourceOutlineView.ViewModel = viewModel.SourceResources.Value; + }; + } + }; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingTypeSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingTypeSelectorControl.cs new file mode 100644 index 0000000..bbfebc9 --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingTypeSelectorControl.cs @@ -0,0 +1,311 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using AppKit; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class BindingTypeSelectorControl : NSView + { + internal class TypeOutlineView : NSOutlineView + { + private TypeSelectorViewModel viewModel; + public TypeSelectorViewModel ViewModel { + get => this.viewModel; + set { + if (this.viewModel != null) { + this.viewModel.PropertyChanged -= OnPropertyChanged; + } + + if (this.viewModel != value) { + this.viewModel = value; + var dataSource = new TypeOutlineViewDataSource (this.viewModel); + Delegate = new TypeOutlineViewDelegate (dataSource); + DataSource = dataSource; + } + + OnPropertyChanged (this.viewModel, new PropertyChangedEventArgs (null)); + if (this.viewModel != null) { + this.viewModel.PropertyChanged += OnPropertyChanged; + } + } + } + + private void OnPropertyChanged (object sender, PropertyChangedEventArgs e) + { + ReloadData (); + + ExpandItem (null, true); + } + + public TypeOutlineView () + { + Initialize (); + } + + // Called when created from unmanaged code + public TypeOutlineView (IntPtr handle) : base (handle) + { + Initialize (); + } + + // Called when created directly from a XIB file + [Export ("initWithCoder:")] + public TypeOutlineView (NSCoder coder) : base (coder) + { + Initialize (); + } + + [Export ("validateProposedFirstResponder:forEvent:")] + public bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) + { + return true; + } + + public void Initialize () + { + AutoresizingMask = NSViewResizingMask.WidthSizable; + HeaderView = null; + TranslatesAutoresizingMaskIntoConstraints = false; + } + } + + internal class TypeOutlineViewDelegate : NSOutlineViewDelegate + { + private TypeOutlineViewDataSource dataSource; + + public TypeOutlineViewDelegate (TypeOutlineViewDataSource dataSource) + { + this.dataSource = dataSource; + } + + public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) + { + return PropertyEditorControl.DefaultControlHeight; + } + + public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) + { + var labelContainer = (UnfocusableTextField)outlineView.MakeView ("type", this); + if (labelContainer == null) { + labelContainer = new UnfocusableTextField { + Identifier = "type", + }; + } + var target = (item as NSObjectFacade).Target; + + switch (target) { + case KeyValuePair kvp: + labelContainer.StringValue = kvp.Key; + break; + case TypeInfo info: + labelContainer.StringValue = info.Name; + break; + default: + labelContainer.StringValue = "Type Not Supported"; + break; + } + + return labelContainer; + } + + public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + return false; + case TypeInfo info: + return true; + + default: + return false; + } + } + } + + internal class TypeOutlineViewDataSource : NSOutlineViewDataSource + { + public TypeSelectorViewModel ViewModel { get; } + + internal TypeOutlineViewDataSource (TypeSelectorViewModel viewModel) + { + if (viewModel == null) + throw new ArgumentNullException (nameof (viewModel)); + + ViewModel = viewModel; + } + + public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) + { + var childCount = 0; + if (item == null) { + childCount = this.ViewModel.Types != null ? this.ViewModel.Types.Count () : 0; + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + childCount = kvp.Value.Count; + break; + case TypeInfo info: + childCount = 0; + break; + default: + childCount = 0; + break; + } + } + + return childCount; + } + + public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) + { + object element; + + if (item == null) { + element = this.ViewModel.Types.ElementAt ((int)childIndex); + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + element = kvp.Value[(int)childIndex]; + break; + case TypeInfo info: + element = info; + break; + default: + return null; + } + } + + return new NSObjectFacade (element); + } + + public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + return kvp.Value.Count > 0; + case TypeInfo info: + return false; + default: + return false; + } + } + } + internal const string TypeSelectorColId = "TypeSelectorColumn"; + + internal TypeOutlineView typeOutlineView; + private NSButton showAllAssembliesCheckBox; + + public CreateBindingViewModel ViewModel { get; set; } + + public BindingTypeSelectorControl (CreateBindingViewModel viewModel) + { + ViewModel = viewModel; + TranslatesAutoresizingMaskIntoConstraints = false; + + this.typeOutlineView = new TypeOutlineView { + + }; + + this.typeOutlineView.Activated += (sender, e) => { + if (sender is TypeOutlineView tov) { + if (tov.SelectedRow != -1) { + if (tov.ItemAtRow (tov.SelectedRow) is NSObjectFacade item) { + if (item.Target is ITypeInfo typeInfo) { + viewModel.TypeSelector.SelectedType = typeInfo; + } + } + } + } + }; + + var typeColumn = new NSTableColumn (TypeSelectorColId); + this.typeOutlineView.AddColumn (typeColumn); + + // Set OutlineTableColumn or the arrows showing children/expansion will not be drawn + this.typeOutlineView.OutlineTableColumn = typeColumn; + + // create a table view and a scroll view + var tableContainer = new NSScrollView { + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + // add the panel to the window + tableContainer.DocumentView = this.typeOutlineView; + AddSubview (tableContainer); + + var filterObjects = new NSSearchField { + ControlSize = NSControlSize.Mini, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), + PlaceholderString = Properties.Resources.SearchObjectsTitle, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + filterObjects.Changed += (sender, e) => { + viewModel.TypeSelector.FilterText = filterObjects.Cell.Title; + this.typeOutlineView.ReloadData (); + this.typeOutlineView.ExpandItem (null, true); + }; + + AddSubview (filterObjects); + AddConstraints (new[] { + NSLayoutConstraint.Create (filterObjects, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 45f), + NSLayoutConstraint.Create (filterObjects, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 5f), + NSLayoutConstraint.Create (filterObjects, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (filterObjects, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + }); + + // Position based on filterObjects bottom and All Assemblies Top. + AddConstraints (new[] { + NSLayoutConstraint.Create (tableContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, filterObjects, NSLayoutAttribute.Bottom, 1f, 5f), + NSLayoutConstraint.Create (tableContainer, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 5f), + NSLayoutConstraint.Create (tableContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (tableContainer, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1f, -105f), + }); + + this.showAllAssembliesCheckBox = new NSButton { + ControlSize = NSControlSize.Small, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), + Title = Properties.Resources.ShowAllAssemblies, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.showAllAssembliesCheckBox.SetButtonType (NSButtonType.Switch); + this.showAllAssembliesCheckBox.Activated += SelectionChanged; + + AddSubview (this.showAllAssembliesCheckBox); + AddConstraints (new[] { + NSLayoutConstraint.Create (this.showAllAssembliesCheckBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Bottom, 1f, -30f), + NSLayoutConstraint.Create (this.showAllAssembliesCheckBox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 10f), + NSLayoutConstraint.Create (this.showAllAssembliesCheckBox, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (this.showAllAssembliesCheckBox, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + }); + + viewModel.PropertyChanged += (sender, e) => { + if (e.PropertyName == nameof (CreateBindingViewModel.ShowTypeSelector)) { + Hidden = !viewModel.ShowTypeSelector; + + if (viewModel.ShowTypeSelector && viewModel.TypeSelector != null) { + this.typeOutlineView.ViewModel = viewModel.TypeSelector; + + this.showAllAssembliesCheckBox.State = viewModel.TypeSelector.ShowAllAssemblies ? NSCellStateValue.On : NSCellStateValue.Off; + } + } + }; + } + + private void SelectionChanged (object sender, EventArgs e) + { + if (sender is NSButton button) { + this.typeOutlineView.ViewModel.ShowAllAssemblies = (button.State == NSCellStateValue.On) ? true : false; + } + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs new file mode 100644 index 0000000..6415ae6 --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AppKit; +using CoreGraphics; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class CreateValueConverterWindow : NSWindow + { + public AddValueConverterViewModel ViewModel { get; } + + private NSTextField valueConverterName; + public string ValueConverterName { + get { return this.valueConverterName.Cell.Title; } + } + + public CreateValueConverterWindow (CreateBindingViewModel viewModel, AsyncValue>> typetasks) + { + if (viewModel == null) + throw new ArgumentNullException (nameof (viewModel)); + + ViewModel = new AddValueConverterViewModel (viewModel.TargetPlatform, viewModel.Target, typetasks); + + StyleMask |= NSWindowStyle.Resizable; + + Title = Properties.Resources.AddValueConverterTitle; + + MaxSize = new CGSize (500, 560); // TODO discuss what the Max/Min Size should be and if we should have one. + MinSize = new CGSize (200, 320); + + var container = new NSView (new CGRect (CGPoint.Empty, new CGSize (400, 400))) { + TranslatesAutoresizingMaskIntoConstraints = false + }; + + var valueConverterLabel = new UnfocusableTextField { + StringValue = Properties.Resources.ValueConverterName, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + container.AddSubview (valueConverterLabel); + + container.AddConstraints (new[] { + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, container, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, container, NSLayoutAttribute.Left, 1f, 5f), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + }); + + this.valueConverterName = new NSTextField { + ControlSize = NSControlSize.Small, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + container.AddSubview (this.valueConverterName); + + container.AddConstraints (new[] { + NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Top, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Bottom, 1f, 1f), + NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Left, NSLayoutRelation.Equal, container, NSLayoutAttribute.Left, 1f, 5f), + NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Width, NSLayoutRelation.Equal, container, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + }); + + var typeSelectorControl = new TypeSelectorControl { + ViewModel = ViewModel, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + container.AddSubview (typeSelectorControl); + + container.AddConstraints (new[] { + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, container, NSLayoutAttribute.Top, 1f, 45f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, container, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, container, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, container, NSLayoutAttribute.Height, 1f, -50f) + }); + + var buttonDone = new NSButton { + BezelStyle = NSBezelStyle.Rounded, + Highlighted = true, + KeyEquivalent = "\r", // Fire when enter pressed + Title = Properties.Resources.DoneTitle, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + buttonDone.Activated += (sender, e) => { + NSApplication.SharedApplication.StopModalWithCode ((int)NSModalResponse.OK); + Close (); + }; + + container.AddSubview (buttonDone); + + container.AddConstraints (new[] { + NSLayoutConstraint.Create (buttonDone, NSLayoutAttribute.Top, NSLayoutRelation.Equal, container, NSLayoutAttribute.Bottom, 1f, -32f), + NSLayoutConstraint.Create (buttonDone, NSLayoutAttribute.Right, NSLayoutRelation.Equal, container, NSLayoutAttribute.Right, 1f, -16f), + NSLayoutConstraint.Create (buttonDone, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + }); + + ContentViewController = new NSViewController (null, null) { + View = container, + }; + + ViewModel.PropertyChanged += (sender, e) => { + if (e.PropertyName == nameof (ViewModel.SelectedType)) { + this.valueConverterName.StringValue = ViewModel.SelectedType.Name; + } + }; + } + + public override void KeyUp (NSEvent theEvent) + { + if (theEvent.KeyCode == 53) { + NSApplication.SharedApplication.StopModalWithCode ((int)NSModalResponse.Cancel); + Close (); + } else { + base.KeyUp (theEvent); + } + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/HeaderView.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/HeaderView.cs new file mode 100644 index 0000000..5fb377b --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/HeaderView.cs @@ -0,0 +1,42 @@ +using System; +using AppKit; +using CoreAnimation; +using CoreGraphics; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class HeaderView : NSView + { + public string Title + { + get { return this.headerText.StringValue; } + set { this.headerText.StringValue = value; } + } + + private UnfocusableTextField headerText = new UnfocusableTextField (); + + internal HeaderView () + { + TranslatesAutoresizingMaskIntoConstraints = false; + WantsLayer = true; + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = new CALayer { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, .5f), + BorderWidth = 1, + }; + + this.headerText.Alignment = NSTextAlignment.Center; + this.headerText.TranslatesAutoresizingMaskIntoConstraints = false; + + AddSubview (this.headerText); + AddConstraints (new[] { + NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1f, 0f), + }); + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/BasePanelWindow.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/BasePanelWindow.cs new file mode 100644 index 0000000..d22ea4b --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/BasePanelWindow.cs @@ -0,0 +1,301 @@ +using System; +using System.Linq; +using AppKit; +using CoreGraphics; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class BasePanelWindow : NSPanel + { + protected NSButton ButtonDone; + private NSButton buttonCancel; + private NSButton buttonHelp; + + protected NSPopUpButton BindingTypePopup; + + protected NSPopUpButton ValueConverterPopup; + + protected NSButton AddConverterButton; + + protected NSView AncestorTypeBox; + protected NSView PathBox; + + protected NSView MainContainer; + protected NSView BindingPropertiesView; + protected NSView FlagsPropertiesView; + protected NSButton ButtonMoreSettings; + + protected nfloat MoreSettingsViewHeight { get; set; } + + internal BasePanelWindow () + : base (new CGRect (0, 0, 728, 445), NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Resizable, NSBackingStore.Buffered, true) + { + FloatingPanel = true; + + MaxSize = new CGSize (960, 720); // TODO discuss what the Max/Min Size should be and if we should have one. + MinSize = new CGSize (320, 240); + + this.MainContainer = new NSView { + TranslatesAutoresizingMaskIntoConstraints = false + }; + + this.ButtonDone = new NSButton { + BezelStyle = NSBezelStyle.Rounded, + KeyEquivalent = "\r", // Fire when enter pressed + Highlighted = true, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.MainContainer.AddSubview (this.ButtonDone); + + this.buttonCancel = new NSButton { + BezelStyle = NSBezelStyle.Rounded, + KeyEquivalent = "0x1b", // TODO Need to double check this is right + Title = Properties.Resources.Cancel, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.buttonCancel.Activated += (sender, e) => { + Close (); + }; + + this.MainContainer.AddSubview (this.buttonCancel); + + this.buttonHelp = new NSButton { + BezelStyle = NSBezelStyle.HelpButton, + Title = string.Empty, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.MainContainer.AddSubview (this.buttonHelp); + + var bindingTypeLabel = new UnfocusableTextField { + TranslatesAutoresizingMaskIntoConstraints = false, + Alignment = NSTextAlignment.Right, + }; + + bindingTypeLabel.StringValue = Properties.Resources.BindingType; + this.MainContainer.AddSubview (bindingTypeLabel); + + this.BindingTypePopup = new FocusablePopUpButton { + TranslatesAutoresizingMaskIntoConstraints = false, + StringValue = String.Empty, + ControlSize = NSControlSize.Small, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), + }; + + var bindingTypeMenuList = new NSMenu (); + this.BindingTypePopup.Menu = bindingTypeMenuList; + this.MainContainer.AddSubview (this.BindingTypePopup); + + var valueConverterLabel = new UnfocusableTextField { + TranslatesAutoresizingMaskIntoConstraints = false, + Alignment = NSTextAlignment.Right, + }; + + this.AncestorTypeBox = new NSView { + TranslatesAutoresizingMaskIntoConstraints = false, + WantsLayer = true, + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, .5f), + BorderWidth = 1, + }, + }; + + this.MainContainer.AddSubview (this.AncestorTypeBox); + + this.PathBox = new NSView { + TranslatesAutoresizingMaskIntoConstraints = false, + WantsLayer = true, + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, .5f), + BorderWidth = 1, + }, + }; + + this.MainContainer.AddSubview (this.PathBox); + + valueConverterLabel.StringValue = Properties.Resources.Converter; + this.MainContainer.AddSubview (valueConverterLabel); + + this.ValueConverterPopup = new FocusablePopUpButton { + TranslatesAutoresizingMaskIntoConstraints = false, + StringValue = String.Empty, + ControlSize = NSControlSize.Small, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), + }; + + var valueConverterMenuList = new NSMenu (); + this.ValueConverterPopup.Menu = valueConverterMenuList; + this.MainContainer.AddSubview (this.ValueConverterPopup); + + this.AddConverterButton = new NSButton { + BezelStyle = NSBezelStyle.Rounded, + Image = NSImage.ImageNamed (NSImageName.AddTemplate), + Title = string.Empty, + ToolTip = Properties.Resources.AddValueConverterEllipsis, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.MainContainer.AddSubview (this.AddConverterButton); + + this.ButtonMoreSettings = new NSButton { + BezelStyle = NSBezelStyle.Disclosure, + Title = string.Empty, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + this.ButtonMoreSettings.SetButtonType (NSButtonType.PushOnPushOff); + + this.MainContainer.AddSubview (this.ButtonMoreSettings); + + var labelOtherSettings = new UnfocusableTextField { + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.MainContainer.AddSubview (labelOtherSettings); + + this.BindingPropertiesView = new NSView { + Hidden = true, + TranslatesAutoresizingMaskIntoConstraints = false, + WantsLayer = true, + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, .5f), + BorderWidth = 1, + }, + }; + + this.MainContainer.AddSubview (this.BindingPropertiesView); + + this.FlagsPropertiesView = new NSView { + Hidden = true, + TranslatesAutoresizingMaskIntoConstraints = false, + WantsLayer = true, + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, .5f), + BorderWidth = 1, + }, + }; + + this.MainContainer.AddSubview (this.FlagsPropertiesView); + + //Work out the titlebar height + var titleBarHeight = Frame.Size.Height - ContentRectFor (Frame).Size.Height; + + var ancestorTypeBoxHeightConstraint = NSLayoutConstraint.Create (this.AncestorTypeBox, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Bottom, 1f, -100f); + var heightConstant = ancestorTypeBoxHeightConstraint.Constant; + + var bindingPropertiesViewHeightConstraint = NSLayoutConstraint.Create (this.BindingPropertiesView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 124); + + this.ButtonMoreSettings.Activated += (sender, e) => { + if (sender is NSButton moreButton) { + ToggleSettingsLabel (moreButton.State == NSCellStateValue.Off, labelOtherSettings); + + this.BindingPropertiesView.Hidden = moreButton.State == NSCellStateValue.Off; + this.FlagsPropertiesView.Hidden = this.BindingPropertiesView.Hidden; + + bindingPropertiesViewHeightConstraint.Constant = this.MoreSettingsViewHeight; + ancestorTypeBoxHeightConstraint.Constant = this.BindingPropertiesView.Hidden ? heightConstant : heightConstant - (MoreSettingsViewHeight + 20); + this.MainContainer.SetFrameSize (new CGSize (this.MainContainer.Frame.Width, this.BindingPropertiesView.Hidden ? this.MainContainer.Frame.Height - MoreSettingsViewHeight : this.MainContainer.Frame.Height + MoreSettingsViewHeight)); + SetFrame (new CGRect (new CGPoint (Frame.X, Frame.Y), new CGSize (Frame.Width, this.MainContainer.Frame.Height + titleBarHeight)), false, true); + } + }; + + ToggleSettingsLabel (this.ButtonMoreSettings.State == NSCellStateValue.Off, labelOtherSettings); + + this.MainContainer.AddConstraints (new[] { + NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Top, 1f, 10f), + NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20f), + NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), + + NSLayoutConstraint.Create (this.BindingTypePopup, NSLayoutAttribute.Top, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.BindingTypePopup, NSLayoutAttribute.Left, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Right, 1f, 5f), + NSLayoutConstraint.Create (this.BindingTypePopup, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), + + NSLayoutConstraint.Create (this.AncestorTypeBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.BindingTypePopup, NSLayoutAttribute.Bottom, 1f, 10f), + NSLayoutConstraint.Create (this.AncestorTypeBox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20f), + NSLayoutConstraint.Create (this.AncestorTypeBox, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Left, 1f, -10f), + ancestorTypeBoxHeightConstraint, + + NSLayoutConstraint.Create (this.PathBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.PathBox, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Right, 1f, -20f), + NSLayoutConstraint.Create (this.PathBox, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (this.PathBox, NSLayoutAttribute.Height, NSLayoutRelation.Equal,this.AncestorTypeBox, NSLayoutAttribute.Height, 1f, 0f), + + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Bottom, 1f, 10f), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20f), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), + + NSLayoutConstraint.Create (this.ValueConverterPopup, NSLayoutAttribute.Top, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.ValueConverterPopup, NSLayoutAttribute.Left, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Right, 1f, 5f), + NSLayoutConstraint.Create (this.ValueConverterPopup, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), + + NSLayoutConstraint.Create (this.AddConverterButton, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ValueConverterPopup, NSLayoutAttribute.Top, 1f, 2f), + NSLayoutConstraint.Create (this.AddConverterButton, NSLayoutAttribute.Left, NSLayoutRelation.Equal,this.ValueConverterPopup, NSLayoutAttribute.Right, 1f, 5f), + NSLayoutConstraint.Create (this.AddConverterButton, NSLayoutAttribute.Width, NSLayoutRelation.Equal, 1f, 20), + NSLayoutConstraint.Create (this.AddConverterButton, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 20), + + NSLayoutConstraint.Create (this.ButtonMoreSettings, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ValueConverterPopup, NSLayoutAttribute.Bottom, 1f, 2f), + NSLayoutConstraint.Create (this.ButtonMoreSettings, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 16f), + NSLayoutConstraint.Create (this.ButtonMoreSettings, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + NSLayoutConstraint.Create (this.ButtonMoreSettings, NSLayoutAttribute.Width, NSLayoutRelation.Equal, 1f, 24), + + NSLayoutConstraint.Create (labelOtherSettings, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ButtonMoreSettings, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (labelOtherSettings, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.ButtonMoreSettings, NSLayoutAttribute.Right, 1f, -5f), + NSLayoutConstraint.Create (labelOtherSettings, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), + + NSLayoutConstraint.Create (this.BindingPropertiesView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ButtonMoreSettings, NSLayoutAttribute.Bottom, 1f, 10f), + NSLayoutConstraint.Create (this.BindingPropertiesView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20f), + NSLayoutConstraint.Create (this.BindingPropertiesView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.FlagsPropertiesView, NSLayoutAttribute.Left, 1f, -10f), + bindingPropertiesViewHeightConstraint, + + NSLayoutConstraint.Create (this.FlagsPropertiesView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.FlagsPropertiesView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Right, 1f, -20f), + NSLayoutConstraint.Create (this.FlagsPropertiesView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (this.FlagsPropertiesView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Height, 1f, 0f), + + NSLayoutConstraint.Create (this.buttonHelp, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Bottom, 1f, -40f), + NSLayoutConstraint.Create (this.buttonHelp, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20), + NSLayoutConstraint.Create (this.buttonHelp, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), + NSLayoutConstraint.Create (this.buttonHelp, NSLayoutAttribute.Width, NSLayoutRelation.Equal, 1f, 24), + + NSLayoutConstraint.Create (this.ButtonDone, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Bottom, 1f, -40f), + NSLayoutConstraint.Create (this.ButtonDone, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Right, 1f, -20f), + NSLayoutConstraint.Create (this.ButtonDone, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), + + NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ButtonDone, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.ButtonDone, NSLayoutAttribute.Left, 1f, -10f), + NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), + }); + + // put the MainContainer inside this panel's ContentView + ContentView.AddSubview (this.MainContainer); + + ContentView.AddConstraints (new[] { + NSLayoutConstraint.Create (this.MainContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.MainContainer, NSLayoutAttribute.Left, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.MainContainer, NSLayoutAttribute.Height, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Height, 1f, 0f), + NSLayoutConstraint.Create (this.MainContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Width, 1f, 0f), + + }); + } + + private static void ToggleSettingsLabel (bool show, UnfocusableTextField labelOtherSettings) + { + labelOtherSettings.StringValue = show ? Properties.Resources.ShowSettings : Properties.Resources.HideSettings; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs index 3e3347a..2533d97 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs @@ -107,10 +107,25 @@ namespace Xamarin.PropertyEditing.Mac this.popUpContextMenu.AddItem (mi2); } + if (this.viewModel.SupportsBindings) { + this.popUpContextMenu.AddItem (NSMenuItem.SeparatorItem); + + var mi3 = new NSMenuItem (Properties.Resources.CreateDataBindingMenuItem) { + AttributedTitle = new Foundation.NSAttributedString ( + Properties.Resources.CreateDataBindingMenuItem, + new CoreText.CTStringAttributes { + Font = new CoreText.CTFont (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize + 1), + }) + }; + + mi3.Activated += OnBindingRequested; + this.popUpContextMenu.AddItem (mi3); + } + this.popUpContextMenu.AddItem (NSMenuItem.SeparatorItem); // TODO If we add more menu items consider making the Label/Command a dictionary that we can iterate over to populate everything. - this.popUpContextMenu.AddItem (new CommandMenuItem (Properties.Resources.Reset, viewModel.ClearValueCommand) { + this.popUpContextMenu.AddItem (new CommandMenuItem (Properties.Resources.Reset, this.viewModel.ClearValueCommand) { AttributedTitle = new Foundation.NSAttributedString ( Properties.Resources.Reset, new CoreText.CTStringAttributes { @@ -236,5 +251,11 @@ namespace Xamarin.PropertyEditing.Mac resourceSelectorPopOver.Show (requestResourceView.Frame, (NSView)this, NSRectEdge.MinYEdge); } + + private void OnBindingRequested (object sender, EventArgs e) + { + var bindingEditorWindow = new BindingEditorWindow (this.hostResources, this.viewModel); + bindingEditorWindow.MakeKeyAndOrderFront (this); + } } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/PropertyEditorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/PropertyEditorControl.cs index 4b1e9b4..aff0ae5 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/PropertyEditorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/PropertyEditorControl.cs @@ -75,23 +75,25 @@ namespace Xamarin.PropertyEditing.Mac public void UpdateKeyViews () { - nint row = TableView.RowForView (this); - if (row <= 0) - return; - - NSView view; - PropertyEditorControl ctrl = null; - do { - row--; - view = TableView.GetView (0, row, makeIfNecessary: false); - ctrl = (view as EditorContainer)?.EditorView?.NativeView as PropertyEditorControl; - } while (row > 0 && ctrl == null); - - if (ctrl != null) { - ctrl.LastKeyView.NextKeyView = FirstKeyView; - ctrl.UpdateKeyViews (); - } else if (row == 0 && view is PanelHeaderEditorControl header) { - header.SetNextKeyView (FirstKeyView); + if (TableView != null) { + nint row = TableView.RowForView (this); + if (row <= 0) + return; + + NSView view; + PropertyEditorControl ctrl = null; + do { + row--; + view = TableView.GetView (0, row, makeIfNecessary: false); + ctrl = (view as EditorContainer)?.EditorView?.NativeView as PropertyEditorControl; + } while (row > 0 && ctrl == null); + + if (ctrl != null) { + ctrl.LastKeyView.NextKeyView = FirstKeyView; + ctrl.UpdateKeyViews (); + } else if (row == 0 && view is PanelHeaderEditorControl header) { + header.SetNextKeyView (FirstKeyView); + } } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs index 747ca64..e58ee32 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs @@ -109,6 +109,8 @@ namespace Xamarin.PropertyEditing.Mac for (int i = 0; i < this.outlineView.RowCount; i++) { this.outlineView.ExpandItem (this.outlineView.ItemAtRow (i)); } + } else { + this.outlineView.ExpandItem (null, true); } } diff --git a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs index 39e4f63..c5bada9 100644 --- a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs +++ b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs @@ -37,10 +37,10 @@ namespace Xamarin.PropertyEditing.Mac var facade = (NSObjectFacade)item; if (facade?.Target is ObjectPropertyViewModel ovm) return ovm.ValueModel.Properties.Count; - + int headerCount = (ShowHeader && !Filtering) ? 1 : 0; - int childCount; + nint childCount; if (this.vm.ArrangeMode == PropertyArrangeMode.Name) childCount = this.vm.ArrangedEditors[0].Editors.Count + headerCount; else { diff --git a/Xamarin.PropertyEditing.Tests/CreateBindingViewModelTests.cs b/Xamarin.PropertyEditing.Tests/CreateBindingViewModelTests.cs index 0c3b16b..60f6bbc 100644 --- a/Xamarin.PropertyEditing.Tests/CreateBindingViewModelTests.cs +++ b/Xamarin.PropertyEditing.Tests/CreateBindingViewModelTests.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Moq; using NUnit.Framework; +using NUnit.Framework.Internal; using Xamarin.PropertyEditing.Drawing; using Xamarin.PropertyEditing.Reflection; using Xamarin.PropertyEditing.Tests.MockControls; @@ -192,7 +193,12 @@ namespace Xamarin.PropertyEditing.Tests await vm.ValueConverters.Task; Assert.That (vm.ValueConverters.Value, Contains.Item (visi)); - Assert.That (vm.ValueConverters.Value.Count, Is.EqualTo (3)); // visi, No Converter, Request Converter + + if (OSPlatform.CurrentPlatform.IsWindows) { + Assert.That (vm.ValueConverters.Value.Count, Is.EqualTo (3)); // visi, No Converter, Request Converter + } else if (OSPlatform.CurrentPlatform.IsMacOSX) { + Assert.That (vm.ValueConverters.Value.Count, Is.EqualTo (2)); // visi, No Converter + } } [Test] diff --git a/Xamarin.PropertyEditing/Properties/Resources.Designer.cs b/Xamarin.PropertyEditing/Properties/Resources.Designer.cs index 9168be7..f617aab 100644 --- a/Xamarin.PropertyEditing/Properties/Resources.Designer.cs +++ b/Xamarin.PropertyEditing/Properties/Resources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -60,21 +60,12 @@ namespace Xamarin.PropertyEditing.Properties { } public static string Black { - get { - return ResourceManager.GetString ("Black", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Add Variant. - /// - public static string AddVariant { get { - return ResourceManager.GetString("AddVariant", resourceCulture); + return ResourceManager.GetString("Black", resourceCulture); } } - - public static string BlackInitial { + + public static string BlackInitial { get { return ResourceManager.GetString("BlackInitial", resourceCulture); } @@ -973,24 +964,14 @@ namespace Xamarin.PropertyEditing.Properties { return ResourceManager.GetString("AccessibilityXEditor", resourceCulture); } } - - public static string AccessibilityYEditor - { - get { - return ResourceManager.GetString("AccessibilityYEditor", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Remove Variant. - /// - public static string RemoveVariant { + + public static string AccessibilityYEditor { get { - return ResourceManager.GetString("RemoveVariant", resourceCulture); + return ResourceManager.GetString("AccessibilityYEditor", resourceCulture); } } - - public static string AccessibilityWidthEditor { + + public static string AccessibilityWidthEditor { get { return ResourceManager.GetString("AccessibilityWidthEditor", resourceCulture); } @@ -1169,5 +1150,53 @@ namespace Xamarin.PropertyEditing.Properties { return ResourceManager.GetString("Directory", resourceCulture); } } + + public static string AddVariant { + get { + return ResourceManager.GetString("AddVariant", resourceCulture); + } + } + + public static string RemoveVariant { + get { + return ResourceManager.GetString("RemoveVariant", resourceCulture); + } + } + + public static string CreateBindingTitle { + get { + return ResourceManager.GetString("CreateBindingTitle", resourceCulture); + } + } + + public static string ShowSettings { + get { + return ResourceManager.GetString("ShowSettings", resourceCulture); + } + } + + public static string HideSettings { + get { + return ResourceManager.GetString("HideSettings", resourceCulture); + } + } + + public static string Converter { + get { + return ResourceManager.GetString("Converter", resourceCulture); + } + } + + public static string SelectTypeTitle { + get { + return ResourceManager.GetString("SelectTypeTitle", resourceCulture); + } + } + + public static string DoneTitle { + get { + return ResourceManager.GetString("DoneTitle", resourceCulture); + } + } } } diff --git a/Xamarin.PropertyEditing/Properties/Resources.resx b/Xamarin.PropertyEditing/Properties/Resources.resx index fb081bb..0e4966b 100644 --- a/Xamarin.PropertyEditing/Properties/Resources.resx +++ b/Xamarin.PropertyEditing/Properties/Resources.resx @@ -751,5 +751,27 @@ Remove Variant + + + Create Binding + Create Binding for Object.Property + + + Show Settings + Show Settings for Binding to Object.Property + + + Hide Settings + Hide Settings for Binding to Object.Property + + + Converter + Converter for Binding to Object.Property + + + Select Type + + + Done diff --git a/Xamarin.PropertyEditing/ViewModels/CreateBindingViewModel.cs b/Xamarin.PropertyEditing/ViewModels/CreateBindingViewModel.cs index 4ae1fc5..6c1dff4 100644 --- a/Xamarin.PropertyEditing/ViewModels/CreateBindingViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/CreateBindingViewModel.cs @@ -452,7 +452,7 @@ namespace Xamarin.PropertyEditing.ViewModels } private static readonly Resource NoValueConverter = new Resource (Resources.NoValueConverter); - private static readonly Resource AddValueConverter = new Resource ("<" + Resources.AddValueConverterEllipsis + ">"); + internal static readonly Resource AddValueConverter = new Resource ("<" + Resources.AddValueConverterEllipsis + ">"); private readonly PropertyVariation variations; private readonly IObjectEditor targetEditor; @@ -497,7 +497,11 @@ namespace Xamarin.PropertyEditing.ViewModels var converters = await TargetPlatform.BindingProvider.GetValueConverterResourcesAsync (this.targetEditor.Target); this.valueConverters.AddRange (converters); - this.valueConverters.Add (AddValueConverter); + + // Don't add the AddValueConverter resource if we are on Mac + if (Environment.OSVersion.Platform != PlatformID.Unix) { + this.valueConverters.Add (AddValueConverter); + } return this.valueConverters; } -- cgit v1.2.3 From 6be4f60fa2d4adf31893c75d8a8ef8ce879b6bd3 Mon Sep 17 00:00:00 2001 From: Alan McGovern Date: Thu, 21 Mar 2019 12:37:17 +0000 Subject: [Mac] Handle all scenarios where the window is closed If we click the ok button then set the response to OK, otherwise every other scenario should be treated as cancelling. This includes presssing Esc,clicking the red traffic light button and also command-W. Make this a Panel again, so 'Esc' works --- .../BindingEditor/CreateValueConverterWindow.cs | 24 ++++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs index 6415ae6..e97a9b4 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs @@ -8,8 +8,13 @@ using Xamarin.PropertyEditing.ViewModels; namespace Xamarin.PropertyEditing.Mac { - internal class CreateValueConverterWindow : NSWindow + internal class CreateValueConverterWindow : NSPanel { + new ModalWindowCloseDelegate Delegate { + get => (ModalWindowCloseDelegate)base.Delegate; + set => base.Delegate = value; + } + public AddValueConverterViewModel ViewModel { get; } private NSTextField valueConverterName; @@ -22,6 +27,7 @@ namespace Xamarin.PropertyEditing.Mac if (viewModel == null) throw new ArgumentNullException (nameof (viewModel)); + Delegate = new ModalWindowCloseDelegate (); ViewModel = new AddValueConverterViewModel (viewModel.TargetPlatform, viewModel.Target, typetasks); StyleMask |= NSWindowStyle.Resizable; @@ -83,7 +89,7 @@ namespace Xamarin.PropertyEditing.Mac }; buttonDone.Activated += (sender, e) => { - NSApplication.SharedApplication.StopModalWithCode ((int)NSModalResponse.OK); + Delegate.Response = NSModalResponse.OK; Close (); }; @@ -105,15 +111,15 @@ namespace Xamarin.PropertyEditing.Mac } }; } + } - public override void KeyUp (NSEvent theEvent) + public class ModalWindowCloseDelegate : NSWindowDelegate + { + public NSModalResponse Response { get; set; } = NSModalResponse.Cancel; + + public override void WillClose (NSNotification notification) { - if (theEvent.KeyCode == 53) { - NSApplication.SharedApplication.StopModalWithCode ((int)NSModalResponse.Cancel); - Close (); - } else { - base.KeyUp (theEvent); - } + NSApplication.SharedApplication.StopModalWithCode ((int)Response); } } } -- cgit v1.2.3 From bbe46e57cb1627d26ccc0e68dc9b2ed877b3d170 Mon Sep 17 00:00:00 2001 From: Dominique Louis Date: Thu, 21 Mar 2019 13:54:10 +0000 Subject: [Mac] Make Binding Dialog Modal [Mac] Ensure property button view is cleared --- .../Main.storyboard | 8 +- .../BindingEditor/BaseSelectorOutlineView.cs | 38 ++ .../Controls/BindingEditor/BindingEditorWindow.cs | 572 ++++++++++++++++----- .../BindingEditor/BindingObjectSelectorControl.cs | 223 ++------ .../BindingEditor/BindingPathSelectorControl.cs | 357 ++++--------- .../BindingEditor/BindingResourceOutlineView.cs | 139 +++++ .../BindingResourceSelectorControl.cs | 243 ++------- .../BindingEditor/BindingTypeSelectorControl.cs | 311 ----------- .../BindingEditor/CreateValueConverterWindow.cs | 78 +-- .../Controls/BindingEditor/HeaderView.cs | 23 +- .../Controls/BindingEditor/ObjectOutlineView.cs | 140 +++++ .../Controls/BindingEditor/PathOutlineView.cs | 168 ++++++ .../Controls/Custom/BasePanelWindow.cs | 301 ----------- .../Controls/Custom/CommandButton.cs | 5 +- .../Controls/Custom/PropertyButton.cs | 25 +- .../Controls/EditorContainer.cs | 4 +- .../Controls/TypeSelectorControl.cs | 37 +- .../PropertyTableDataSource.cs | 9 +- .../CreateBindingViewModelTests.cs | 49 +- .../Properties/Resources.Designer.cs | 22 +- Xamarin.PropertyEditing/Properties/Resources.resx | 28 +- .../ViewModels/CreateBindingViewModel.cs | 30 +- 22 files changed, 1317 insertions(+), 1493 deletions(-) create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BaseSelectorOutlineView.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceOutlineView.cs delete mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingTypeSelectorControl.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/ObjectOutlineView.cs create mode 100644 Xamarin.PropertyEditing.Mac/Controls/BindingEditor/PathOutlineView.cs delete mode 100644 Xamarin.PropertyEditing.Mac/Controls/Custom/BasePanelWindow.cs diff --git a/Xamarin.PropertyEditing.Mac.Standalone/Main.storyboard b/Xamarin.PropertyEditing.Mac.Standalone/Main.storyboard index 4a91052..20a12de 100644 --- a/Xamarin.PropertyEditing.Mac.Standalone/Main.storyboard +++ b/Xamarin.PropertyEditing.Mac.Standalone/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -687,8 +687,8 @@ - - + + diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BaseSelectorOutlineView.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BaseSelectorOutlineView.cs new file mode 100644 index 0000000..e18c197 --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BaseSelectorOutlineView.cs @@ -0,0 +1,38 @@ +using System; +using AppKit; +using Foundation; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class BaseSelectorOutlineView : NSOutlineView + { + public BaseSelectorOutlineView () + { + Initialize (); + } + + // Called when created from unmanaged code + public BaseSelectorOutlineView (IntPtr handle) : base (handle) + { + Initialize (); + } + + // Called when created directly from a XIB file + [Export ("initWithCoder:")] + public BaseSelectorOutlineView (NSCoder coder) : base (coder) + { + Initialize (); + } + + public override bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) + { + return true; + } + + private void Initialize () + { + HeaderView = null; + TranslatesAutoresizingMaskIntoConstraints = false; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingEditorWindow.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingEditorWindow.cs index 7e66a6b..f41673f 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingEditorWindow.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingEditorWindow.cs @@ -10,243 +10,536 @@ using Xamarin.PropertyEditing.ViewModels; namespace Xamarin.PropertyEditing.Mac { - internal class BindingEditorWindow : BasePanelWindow + internal class BindingEditorWindow : NSPanel { - private readonly CreateBindingViewModel viewModel; + private NSButton buttonDone; + private NSButton buttonCancel; + + private NSPopUpButton bindingTypePopup; + + private NSPopUpButton valueConverterPopup; + + private NSButton addConverterButton; + + private NSView ancestorTypeBox; + + private NSView mainContainer; + private NSView bindingPropertiesView; + private NSView flagsPropertiesView; + private NSButton buttonMoreSettings; + + private nfloat MoreSettingsViewHeight { get; set; } + + private const float AddConverterButtonSize = 20; + + private new ModalWindowCloseDelegate Delegate + { + get => (ModalWindowCloseDelegate)base.Delegate; + set => base.Delegate = value; + } + private readonly PropertyEditorSelector editorSelector = new PropertyEditorSelector (); - internal BindingEditorWindow (IHostResourceProvider hostResources, PropertyViewModel propertyViewModel) + private const string BindingPropertiesIdentifier = "BindingProperties"; + private const string FlagPropertiesIdentifier = "FlagProperties"; + internal const float HeaderHeight = 28f; + + internal CreateBindingViewModel ViewModel { get; } + + private HeaderView typeHeader; + + private UnfocusableTextField longDescription; + private UnfocusableTextField labelOtherSettings; + private TypeSelectorControl typeSelectorControl; + + private nfloat heightConstant, titleBarHeight; + + private NSLayoutConstraint ancestorTypeBoxHeightConstraint; + private NSLayoutConstraint bindingPropertiesViewHeightConstraint; + + private BindingPathSelectorControl pathSelectorControl; + + internal BindingEditorWindow (IHostResourceProvider hostResources, PropertyViewModel propertyViewModel) + : base (new CGRect (0, 0, 728, 445), NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Resizable, NSBackingStore.Buffered, true) { - this.viewModel = new CreateBindingViewModel (propertyViewModel.TargetPlatform, propertyViewModel.Editors.Single (), propertyViewModel.Property); + if (hostResources == null) + throw new ArgumentNullException (nameof (hostResources)); + if (propertyViewModel == null) + throw new ArgumentNullException (nameof (propertyViewModel)); - Title = this.viewModel.PropertyDisplay; - this.ButtonDone.Title = Properties.Resources.CreateBindingTitle; + ViewModel = new CreateBindingViewModel (propertyViewModel.TargetPlatform, propertyViewModel.Editors.Single (), propertyViewModel.Property, includeAddValueConverter: false); - foreach (BindingSource item in this.viewModel.BindingSources.Value) { - this.BindingTypePopup.Menu.AddItem (new NSMenuItem (item.Name) { - RepresentedObject = new NSObjectFacade (item) - }); - } + Delegate = new ModalWindowCloseDelegate (); + + FloatingPanel = true; + + MaxSize = new CGSize (960, 720); // TODO discuss what the Max/Min Size should be and if we should have one. + MinSize = new CGSize (320, 240); + + this.mainContainer = new NSView { + TranslatesAutoresizingMaskIntoConstraints = false + }; + + this.buttonDone = new NSButton { + BezelStyle = NSBezelStyle.Rounded, + ControlSize = NSControlSize.Regular, + Highlighted = true, + KeyEquivalent = "\r", // Fire when enter pressed + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.mainContainer.AddSubview (this.buttonDone); + + this.buttonCancel = new NSButton { + BezelStyle = NSBezelStyle.Rounded, + ControlSize = NSControlSize.Regular, + Title = Properties.Resources.Cancel, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.buttonCancel.Activated += (sender, e) => { + Delegate.Response = NSModalResponse.Cancel; + Close (); + }; - this.BindingTypePopup.Activated += (o, e) => { - if (this.BindingTypePopup.Menu.HighlightedItem.RepresentedObject is NSObjectFacade facade) { - this.viewModel.SelectedBindingSource = (BindingSource)facade.Target; + this.mainContainer.AddSubview (this.buttonCancel); + + var bindingTypeLabel = new UnfocusableTextField { + Alignment = NSTextAlignment.Right, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, 13), + StringValue = Properties.Resources.BindingType + ":", + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.mainContainer.AddSubview (bindingTypeLabel); + + this.bindingTypePopup = new FocusablePopUpButton { + ControlSize = NSControlSize.Regular, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, 13), + StringValue = String.Empty, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + var bindingTypeMenuList = new NSMenu (); + this.bindingTypePopup.Menu = bindingTypeMenuList; + this.mainContainer.AddSubview (this.bindingTypePopup); + + var valueConverterLabel = new UnfocusableTextField { + Alignment = NSTextAlignment.Right, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, 13), + StringValue = Properties.Resources.Converter + ":", + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.ancestorTypeBox = new NSView { + TranslatesAutoresizingMaskIntoConstraints = false, + WantsLayer = true, + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, 1.0f), + BorderWidth = 1, + }, + }; + + this.mainContainer.AddSubview (this.ancestorTypeBox); + + this.mainContainer.AddSubview (valueConverterLabel); + + this.valueConverterPopup = new FocusablePopUpButton { + TranslatesAutoresizingMaskIntoConstraints = false, + StringValue = String.Empty, + ControlSize = NSControlSize.Regular, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, 13), + }; + + var valueConverterMenuList = new NSMenu (); + this.valueConverterPopup.Menu = valueConverterMenuList; + this.mainContainer.AddSubview (this.valueConverterPopup); + + this.addConverterButton = new CommandButton { + BezelStyle = NSBezelStyle.Rounded, + Command = ViewModel.RequestAddValueConverterCommand, + Image = NSImage.ImageNamed (NSImageName.AddTemplate), + Title = string.Empty, + ToolTip = Properties.Resources.AddValueConverterEllipsis, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.mainContainer.AddSubview (this.addConverterButton); + + this.buttonMoreSettings = new NSButton { + BezelStyle = NSBezelStyle.Disclosure, + Title = string.Empty, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + this.buttonMoreSettings.SetButtonType (NSButtonType.PushOnPushOff); + + this.mainContainer.AddSubview (this.buttonMoreSettings); + + this.labelOtherSettings = new UnfocusableTextField { + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, 13), + StringValue = Properties.Resources.OtherSettings, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + this.mainContainer.AddSubview (this.labelOtherSettings); + + this.bindingPropertiesView = new NSView { + Hidden = true, + TranslatesAutoresizingMaskIntoConstraints = false, + WantsLayer = true, + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, .5f), + BorderWidth = 1, + }, + }; + + this.mainContainer.AddSubview (this.bindingPropertiesView); + + this.flagsPropertiesView = new NSView { + Hidden = true, + TranslatesAutoresizingMaskIntoConstraints = false, + WantsLayer = true, + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, .5f), + BorderWidth = 1, + }, + }; + + this.mainContainer.AddSubview (this.flagsPropertiesView); + + //Work out the titlebar height + this.titleBarHeight = Frame.Size.Height - ContentRectFor (Frame).Size.Height; + + this.ancestorTypeBoxHeightConstraint = NSLayoutConstraint.Create (this.ancestorTypeBox, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this.mainContainer, NSLayoutAttribute.Bottom, 1f, -145f); + this.heightConstant = this.ancestorTypeBoxHeightConstraint.Constant; + + this.bindingPropertiesViewHeightConstraint = NSLayoutConstraint.Create (this.bindingPropertiesView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 124); + + this.buttonMoreSettings.Activated += OnButtonMoreSettingsToggled; + + this.pathSelectorControl = new BindingPathSelectorControl (ViewModel); + this.mainContainer.AddSubview (this.pathSelectorControl); + + this.mainContainer.AddConstraints (new[] { + NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.bindingTypePopup, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.mainContainer, NSLayoutAttribute.Left, 1f, 21f), + NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 20), + + NSLayoutConstraint.Create (this.bindingTypePopup, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.mainContainer, NSLayoutAttribute.Top, 1f, 18f), + NSLayoutConstraint.Create (this.bindingTypePopup, NSLayoutAttribute.Left, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Right, 1f, 10f), + + NSLayoutConstraint.Create (this.ancestorTypeBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.bindingTypePopup, NSLayoutAttribute.Bottom, 1f, 18f), + NSLayoutConstraint.Create (this.ancestorTypeBox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.ancestorTypeBox, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.pathSelectorControl, NSLayoutAttribute.Left, 1f, -8f), + this.ancestorTypeBoxHeightConstraint, + + NSLayoutConstraint.Create (this.pathSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.pathSelectorControl, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.mainContainer, NSLayoutAttribute.Right, 1f, -21f), + NSLayoutConstraint.Create (this.pathSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Width, 1f, -30f), + NSLayoutConstraint.Create (this.pathSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal,this.ancestorTypeBox, NSLayoutAttribute.Height, 1f, 0f), + + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Bottom, 1f, 22f), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 20), + + NSLayoutConstraint.Create (this.valueConverterPopup, NSLayoutAttribute.Top, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.valueConverterPopup, NSLayoutAttribute.Left, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Right, 1f, 10f), + NSLayoutConstraint.Create (this.valueConverterPopup, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Width, 1f, -AddConverterButtonSize - 83), // TODO Need a better calculation here + + NSLayoutConstraint.Create (this.addConverterButton, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.valueConverterPopup, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.addConverterButton, NSLayoutAttribute.Right, NSLayoutRelation.Equal,this.ancestorTypeBox, NSLayoutAttribute.Right, 1f, 0f), + NSLayoutConstraint.Create (this.addConverterButton, NSLayoutAttribute.Width, NSLayoutRelation.Equal, 1f, AddConverterButtonSize), + NSLayoutConstraint.Create (this.addConverterButton, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, AddConverterButtonSize), + + NSLayoutConstraint.Create (this.buttonMoreSettings, NSLayoutAttribute.Top, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Bottom, 1f, 18f), + NSLayoutConstraint.Create (this.buttonMoreSettings, NSLayoutAttribute.Left, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Left, 1f, -5f), + NSLayoutConstraint.Create (this.buttonMoreSettings, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 20), + NSLayoutConstraint.Create (this.buttonMoreSettings, NSLayoutAttribute.Width, NSLayoutRelation.Equal, 1f, 20), + + NSLayoutConstraint.Create (this.labelOtherSettings, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.buttonMoreSettings, NSLayoutAttribute.Top, 1f, -1f), + NSLayoutConstraint.Create (this.labelOtherSettings, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.buttonMoreSettings, NSLayoutAttribute.Right, 1f, 0f), + NSLayoutConstraint.Create (this.labelOtherSettings, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 20), + + NSLayoutConstraint.Create (this.bindingPropertiesView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.buttonMoreSettings, NSLayoutAttribute.Bottom, 1f, 10f), + NSLayoutConstraint.Create (this.bindingPropertiesView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.bindingPropertiesView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.flagsPropertiesView, NSLayoutAttribute.Left, 1f, -8f), + bindingPropertiesViewHeightConstraint, + NSLayoutConstraint.Create (this.bindingPropertiesView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), + + NSLayoutConstraint.Create (this.flagsPropertiesView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.bindingPropertiesView, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.flagsPropertiesView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.pathSelectorControl, NSLayoutAttribute.Right, 1f, 0f), + NSLayoutConstraint.Create (this.flagsPropertiesView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.pathSelectorControl, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (this.flagsPropertiesView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.bindingPropertiesView, NSLayoutAttribute.Height, 1f, 0f), + + NSLayoutConstraint.Create (this.buttonDone, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.mainContainer, NSLayoutAttribute.Bottom, 1f, -44f), + NSLayoutConstraint.Create (this.buttonDone, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.pathSelectorControl, NSLayoutAttribute.Right, 1f, 0f), + NSLayoutConstraint.Create (this.buttonDone, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1f, 80f), + + NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.buttonDone, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.buttonDone, NSLayoutAttribute.Left, 1f, -10f), + NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1f, 80f), + }); + + // put the MainContainer inside this panel's ContentView + ContentView.AddSubview (this.mainContainer); + + ContentView.AddConstraints (new[] { + NSLayoutConstraint.Create (this.mainContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.mainContainer, NSLayoutAttribute.Left, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.mainContainer, NSLayoutAttribute.Height, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Height, 1f, 0f), + NSLayoutConstraint.Create (this.mainContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Width, 1f, 0f), + + }); + + Title = ViewModel.PropertyDisplay; + this.buttonDone.Title = Properties.Resources.CreateBindingTitle; + this.buttonDone.Enabled = ViewModel.CanCreateBinding; + + ViewModel.BindingSources.Task.ContinueWith (t => { + foreach (BindingSource item in ViewModel.BindingSources.Value) { + this.bindingTypePopup.Menu.AddItem (new NSMenuItem (item.Name) { + RepresentedObject = new NSObjectFacade (item) + }); + } + }, TaskScheduler.FromCurrentSynchronizationContext ()); + + this.bindingTypePopup.Activated += (o, e) => { + if (this.bindingTypePopup.Menu.HighlightedItem.RepresentedObject is NSObjectFacade facade) { + ViewModel.SelectedBindingSource = (BindingSource)facade.Target; } }; - this.ValueConverterPopup.Activated += (o, e) => { - if (this.ValueConverterPopup.Menu.HighlightedItem.RepresentedObject is NSObjectFacade facade) { - this.viewModel.SelectedValueConverter = (Resource)facade.Target; + this.valueConverterPopup.Activated += (o, e) => { + if (this.valueConverterPopup.Menu.HighlightedItem.RepresentedObject is NSObjectFacade facade) { + ViewModel.SelectedValueConverter = (Resource)facade.Target; } }; RepopulateValueConverterPopup (); - this.AddConverterButton.Activated += (sender, e) => { - this.viewModel.SelectedValueConverter = CreateBindingViewModel.AddValueConverter; - }; - - var typeHeader = new HeaderView { + this.typeHeader = new HeaderView { Title = Properties.Resources.Type, }; - this.AncestorTypeBox.AddSubview (typeHeader); + this.ancestorTypeBox.AddSubview (this.typeHeader); - this.AncestorTypeBox.AddConstraints (new[] { - NSLayoutConstraint.Create (typeHeader, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (typeHeader, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 0f), - NSLayoutConstraint.Create (typeHeader, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), - NSLayoutConstraint.Create (typeHeader, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 40), + this.ancestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (this.typeHeader, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.typeHeader, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.typeHeader, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (this.typeHeader, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, HeaderHeight), }); - var typeSelectorControl = new TypeSelectorControl { + this.typeSelectorControl = new TypeSelectorControl { + Flush = true, Hidden = true, TranslatesAutoresizingMaskIntoConstraints = false, }; - this.AncestorTypeBox.AddSubview (typeSelectorControl); + this.ancestorTypeBox.AddSubview (this.typeSelectorControl); - this.AncestorTypeBox.AddConstraints (new[] { - NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 36f), - NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 0f), - NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), - NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Height, 1f, -35f) + this.ancestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (this.typeSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.typeHeader, NSLayoutAttribute.Bottom, 1f, 0f), + NSLayoutConstraint.Create (this.typeSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.typeSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (this.typeSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Height, 1f, -10f) }); - var resourceSelectorControl = new BindingResourceSelectorControl (this.viewModel) { + var resourceSelectorControl = new BindingResourceSelectorControl (ViewModel) { Hidden = true, }; - this.AncestorTypeBox.AddSubview (resourceSelectorControl); + this.ancestorTypeBox.AddSubview (resourceSelectorControl); - this.AncestorTypeBox.AddConstraints (new[] { - NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 5f), - NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 5f), - NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Height, 1f, -10f) + this.ancestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (resourceSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Height, 1f, 0f) }); - var objectSelectorControl = new BindingObjectSelectorControl (this.viewModel) { + var objectSelectorControl = new BindingObjectSelectorControl (ViewModel) { Hidden = true, }; - this.AncestorTypeBox.AddSubview (objectSelectorControl); + this.ancestorTypeBox.AddSubview (objectSelectorControl); - this.AncestorTypeBox.AddConstraints (new[] { - NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 5f), - NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 5f), - NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Height, 1f, -10f) + this.ancestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (objectSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Height, 1f, 0f) }); - var longDescription = new UnfocusableTextField { + this.longDescription = new UnfocusableTextField { Alignment = NSTextAlignment.Left, TranslatesAutoresizingMaskIntoConstraints = false, StringValue = string.Empty, }; - this.AncestorTypeBox.AddSubview (longDescription); + this.ancestorTypeBox.AddSubview (this.longDescription); - this.AncestorTypeBox.AddConstraints (new[] { - NSLayoutConstraint.Create (longDescription, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 10f), - NSLayoutConstraint.Create (longDescription, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Left, 1f, 10f), - NSLayoutConstraint.Create (longDescription, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (longDescription, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + this.ancestorTypeBox.AddConstraints (new[] { + NSLayoutConstraint.Create (this.longDescription, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Top, 1f, 10f), + NSLayoutConstraint.Create (this.longDescription, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Left, 1f, 10f), + NSLayoutConstraint.Create (this.longDescription, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.ancestorTypeBox, NSLayoutAttribute.Width, 1f, -10f), + NSLayoutConstraint.Create (this.longDescription, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), }); - var pathHeader = new HeaderView { - Title = Properties.Resources.Path, - }; + ViewModel.PropertyChanged += OnPropertyChanged; - this.PathBox.AddSubview (pathHeader); + ViewModel.CreateValueConverterRequested += OnCreateValueConverterRequested; - this.PathBox.AddConstraints (new[] { - NSLayoutConstraint.Create (pathHeader, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (pathHeader, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Left, 1f, 0f), - NSLayoutConstraint.Create (pathHeader, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Width, 1f, 0f), - NSLayoutConstraint.Create (pathHeader, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 40), - }); + this.buttonDone.Activated += OnButtonDoneActivated; - var pathSelectorControl = new BindingPathSelectorControl (this.viewModel); + CreateMorePropertiesEditors (hostResources); + } - this.PathBox.AddSubview (pathSelectorControl); + private void OnButtonDoneActivated (object sender, EventArgs e) + { + if (!string.IsNullOrEmpty (this.pathSelectorControl.CustomPath)) { + ViewModel.Path = this.pathSelectorControl.CustomPath; + } + Delegate.Response = NSModalResponse.OK; + Close (); + } - this.PathBox.AddConstraints (new[] { - NSLayoutConstraint.Create (pathSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Top, 1f, 5f), - NSLayoutConstraint.Create (pathSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Left, 1f, 5f), - NSLayoutConstraint.Create (pathSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (pathSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Height, 1f, -10f) - }); - this.viewModel.PropertyChanged += (sender, e) => { - if (e.PropertyName == nameof (CreateBindingViewModel.ShowLongDescription)) { - longDescription.Hidden = !this.viewModel.ShowLongDescription; - longDescription.StringValue = this.viewModel.ShowLongDescription ? this.viewModel.SelectedBindingSource.Description : string.Empty; - typeHeader.Hidden = this.viewModel.ShowLongDescription; - } + private void OnButtonMoreSettingsToggled (object sender, EventArgs e) + { + if (sender is NSButton moreButton) { + this.bindingPropertiesView.Hidden = moreButton.State == NSCellStateValue.Off; + this.flagsPropertiesView.Hidden = this.bindingPropertiesView.Hidden; + + this.bindingPropertiesViewHeightConstraint.Constant = this.MoreSettingsViewHeight; + this.ancestorTypeBoxHeightConstraint.Constant = this.bindingPropertiesView.Hidden ? this.heightConstant : this.heightConstant - (MoreSettingsViewHeight + 20); + this.mainContainer.SetFrameSize (new CGSize (this.mainContainer.Frame.Width, this.bindingPropertiesView.Hidden ? this.mainContainer.Frame.Height - MoreSettingsViewHeight : this.mainContainer.Frame.Height + MoreSettingsViewHeight)); + SetFrame (new CGRect (new CGPoint (Frame.X, Frame.Y), new CGSize (Frame.Width, this.mainContainer.Frame.Height + this.titleBarHeight)), false, true); + } + } - if (e.PropertyName == nameof (CreateBindingViewModel.ShowObjectSelector)) { - if (this.viewModel.ShowObjectSelector) { - typeHeader.Title = Properties.Resources.SelectObjectTitle; - } + + private void OnPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof (CreateBindingViewModel.ShowLongDescription)) { + this.longDescription.Hidden = !ViewModel.ShowLongDescription; + this.longDescription.StringValue = ViewModel.ShowLongDescription ? ViewModel.SelectedBindingSource.Description : string.Empty; + this.typeHeader.Hidden = ViewModel.ShowLongDescription; + } + + if (e.PropertyName == nameof (CreateBindingViewModel.ShowObjectSelector)) { + if (ViewModel.ShowObjectSelector) { + this.typeHeader.Title = Properties.Resources.SelectObjectTitle; } + } - if (e.PropertyName == nameof (CreateBindingViewModel.ShowTypeSelector)) { - typeSelectorControl.Hidden = !this.viewModel.ShowTypeSelector; + if (e.PropertyName == nameof (CreateBindingViewModel.ShowTypeSelector)) { + this.typeSelectorControl.Hidden = !ViewModel.ShowTypeSelector; - if (this.viewModel.ShowTypeSelector) { - typeHeader.Title = Properties.Resources.SelectTypeTitle; + if (ViewModel.ShowTypeSelector) { + this.typeHeader.Title = Properties.Resources.SelectTypeTitle; - if (this.viewModel.ShowTypeSelector && this.viewModel.TypeSelector != null) { - typeSelectorControl.ViewModel = this.viewModel.TypeSelector; - } + if (ViewModel.ShowTypeSelector && ViewModel.TypeSelector != null) { + this.typeSelectorControl.ViewModel = ViewModel.TypeSelector; } } + } - if (e.PropertyName == nameof (CreateBindingViewModel.ShowResourceSelector)) { - if (this.viewModel.ShowResourceSelector) { - typeHeader.Title = Properties.Resources.SelectResourceTitle; - } + if (e.PropertyName == nameof (CreateBindingViewModel.ShowResourceSelector)) { + if (ViewModel.ShowResourceSelector) { + this.typeHeader.Title = Properties.Resources.SelectResourceTitle; } + } - if (e.PropertyName == nameof (CreateBindingViewModel.SelectedValueConverter)) { - RepopulateValueConverterPopup (); - } - }; + if (e.PropertyName == nameof (CreateBindingViewModel.SelectedValueConverter)) { + RepopulateValueConverterPopup (); + } - this.viewModel.CreateValueConverterRequested += OnCreateValueConverterRequested; + if (e.PropertyName == nameof (CreateBindingViewModel.PropertyDisplay)) { + Title = ViewModel.PropertyDisplay; + } - this.ButtonDone.Activated += (sender, e) => { - if (pathSelectorControl.CustomPath.Enabled && !string.IsNullOrEmpty (pathSelectorControl.CustomPath.Cell.Title)) { - this.viewModel.Path = pathSelectorControl.CustomPath.Cell.Title; - } + if (e.PropertyName == nameof (CreateBindingViewModel.CanCreateBinding)) { + this.buttonDone.Enabled = ViewModel.CanCreateBinding; + } + } - Close (); - }; - // More Settings - var controlTop = 6; - var identifier = "BindingProperties"; + private void CreateMorePropertiesEditors (IHostResourceProvider hostResources) + { + var controlTop = 8; + int editorHeight; - foreach (PropertyViewModel vm in this.viewModel.BindingProperties) { + foreach (PropertyViewModel vm in ViewModel.BindingProperties) { IEditorView editor = this.editorSelector.GetEditor (hostResources, vm); - NSView nSView = new EditorContainer (hostResources, editor) { - Identifier = identifier, + NSView nSView = new EditorContainer (hostResources, editor, false) { + Identifier = BindingPropertiesIdentifier, Label = vm.Property.Name, TranslatesAutoresizingMaskIntoConstraints = false, ViewModel = vm, }; - this.BindingPropertiesView.AddSubview (nSView); + this.bindingPropertiesView.AddSubview (nSView); - this.BindingPropertiesView.AddConstraints (new[] { - NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Top, 1f, controlTop), - NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Left, 1f, 16f), - NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Width, 1f, -20f), + editorHeight = (int)editor.GetHeight (vm); + this.bindingPropertiesView.AddConstraints (new[] { + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.bindingPropertiesView, NSLayoutAttribute.Top, 1f, controlTop), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.bindingPropertiesView, NSLayoutAttribute.Right, 1f, -9f), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.bindingPropertiesView, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, editorHeight), }); - controlTop += PropertyEditorControl.DefaultControlHeight; + controlTop += editorHeight; } var boundsHeight = controlTop; - controlTop = 9; - identifier = "FlagsProperties"; - foreach (PropertyViewModel vm in this.viewModel.FlagsProperties) { + controlTop = 8; + foreach (PropertyViewModel vm in ViewModel.FlagsProperties) { - IEditorView editor = this.editorSelector.GetEditor (hostResources, vm); + IEditorView editor = this.editorSelector.GetEditor (hostResources, vm); - NSView nSView = new EditorContainer (hostResources, editor) { - Identifier = identifier, + NSView nSView = new EditorContainer (hostResources, editor, false) { + Identifier = FlagPropertiesIdentifier, Label = vm.Property.Name, TranslatesAutoresizingMaskIntoConstraints = false, ViewModel = vm, }; - this.FlagsPropertiesView.AddSubview (nSView); + this.flagsPropertiesView.AddSubview (nSView); - this.FlagsPropertiesView.AddConstraints (new[] { - NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.FlagsPropertiesView, NSLayoutAttribute.Top, 1f, controlTop), - NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.FlagsPropertiesView, NSLayoutAttribute.Left, 1f, 16f), - NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.FlagsPropertiesView, NSLayoutAttribute.Width, 1f, -20f), - NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 18f), + editorHeight = (int)editor.GetHeight (vm); + this.flagsPropertiesView.AddConstraints (new[] { + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.flagsPropertiesView, NSLayoutAttribute.Top, 1f, controlTop), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.flagsPropertiesView, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.flagsPropertiesView, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (nSView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, editorHeight) }); - controlTop += PropertyEditorControl.DefaultControlHeight; + controlTop += editorHeight; } if (boundsHeight < controlTop) boundsHeight = controlTop; - this.MoreSettingsViewHeight = boundsHeight + 8; + MoreSettingsViewHeight = boundsHeight + 8; } private void RepopulateValueConverterPopup () { - this.ValueConverterPopup.RemoveAllItems (); - foreach (Resource item in this.viewModel.ValueConverters.Value) { - this.ValueConverterPopup.Menu.AddItem (new NSMenuItem (item.Name) { + this.valueConverterPopup.RemoveAllItems (); + foreach (Resource item in ViewModel.ValueConverters.Value) { + this.valueConverterPopup.Menu.AddItem (new NSMenuItem (item.Name) { RepresentedObject = new NSObjectFacade (item) }); } @@ -254,12 +547,15 @@ namespace Xamarin.PropertyEditing.Mac private void OnCreateValueConverterRequested (object sender, CreateValueConverterEventArgs e) { - ITypeInfo valueConverter = this.viewModel.TargetPlatform.EditorProvider.KnownTypes[typeof (CommonValueConverter)]; + ITypeInfo valueConverter = ViewModel.TargetPlatform.EditorProvider.KnownTypes[typeof (CommonValueConverter)]; - var typesTask = this.viewModel.TargetPlatform.EditorProvider.GetAssignableTypesAsync (valueConverter, childTypes: false) + var typesTask = ViewModel.TargetPlatform.EditorProvider.GetAssignableTypesAsync (valueConverter, childTypes: false) .ContinueWith (t => t.Result.GetTypeTree (), TaskScheduler.Default); - var createValueConverterWindow = new CreateValueConverterWindow (this.viewModel, new AsyncValue>> (typesTask)); + var createValueConverterWindow = new CreateValueConverterWindow (ViewModel, new AsyncValue>> (typesTask)) { + Appearance = EffectiveAppearance, + }; + var result = (NSModalResponse)(int)NSApplication.SharedApplication.RunModalForWindow (createValueConverterWindow); if (result == NSModalResponse.OK) { if (createValueConverterWindow.ViewModel.SelectedType != null) { diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingObjectSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingObjectSelectorControl.cs index 9022c37..8e82477 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingObjectSelectorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingObjectSelectorControl.cs @@ -6,206 +6,73 @@ using Xamarin.PropertyEditing.ViewModels; namespace Xamarin.PropertyEditing.Mac { - internal class BindingObjectSelectorControl : NSView + internal class BindingObjectSelectorControl + : NotifyingView { - internal class ObjectOutlineView : NSOutlineView - { - private IReadOnlyList viewModel; - public IReadOnlyList ViewModel { - get => this.viewModel; - set { - if (this.viewModel != value) { - this.viewModel = value; - var dataSource = new ObjectOutlineViewDataSource (this.viewModel); - Delegate = new ObjectOutlineViewDelegate (dataSource); - DataSource = dataSource; - } - - if (this.viewModel != null) { - ReloadData (); - - ExpandItem (null, true); - } - } - } + private ObjectOutlineView objectOutlineView; - public ObjectOutlineView () - { - Initialize (); - } + private const string ObjectSelectorColId = "ObjectSelectorColumn"; - // Called when created from unmanaged code - public ObjectOutlineView (IntPtr handle) : base (handle) - { - Initialize (); - } + internal BindingObjectSelectorControl (CreateBindingViewModel viewModel) + { + if (viewModel == null) + throw new ArgumentNullException (nameof (viewModel)); - // Called when created directly from a XIB file - [Export ("initWithCoder:")] - public ObjectOutlineView (NSCoder coder) : base (coder) - { - Initialize (); - } + ViewModel = viewModel; - [Export ("validateProposedFirstResponder:forEvent:")] - public bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) - { - return true; - } + this.objectOutlineView = new ObjectOutlineView (); + TranslatesAutoresizingMaskIntoConstraints = false; - public void Initialize () - { - AutoresizingMask = NSViewResizingMask.WidthSizable; - HeaderView = null; - TranslatesAutoresizingMaskIntoConstraints = false; - } - } + this.objectOutlineView.Activated += OnObjectOutlineViewSelected; - internal class ObjectOutlineViewDelegate : NSOutlineViewDelegate - { - private ObjectOutlineViewDataSource dataSource; + var resourceColumn = new NSTableColumn (ObjectSelectorColId); + this.objectOutlineView.AddColumn (resourceColumn); - public ObjectOutlineViewDelegate (ObjectOutlineViewDataSource dataSource) - { - this.dataSource = dataSource; - } + // Set OutlineTableColumn or the arrows showing children/expansion will not be drawn + this.objectOutlineView.OutlineTableColumn = resourceColumn; - public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) - { - return PropertyEditorControl.DefaultControlHeight; - } + // create a table view and a scroll view + var outlineViewContainer = new NSScrollView { + TranslatesAutoresizingMaskIntoConstraints = false, + }; - public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) - { - var labelContainer = (UnfocusableTextField)outlineView.MakeView ("type", this); - if (labelContainer == null) { - labelContainer = new UnfocusableTextField { - Identifier = "type", - }; - } - var target = (item as NSObjectFacade).Target; - - switch (target) { - case KeyValuePair kvp: - labelContainer.StringValue = kvp.Key; - break; - case TypeInfo info: - labelContainer.StringValue = info.Name; - break; - default: - labelContainer.StringValue = "Type Not Supported"; - break; - } + // add the panel to the window + outlineViewContainer.DocumentView = this.objectOutlineView; + AddSubview (outlineViewContainer); - return labelContainer; - } + AddConstraints (new[] { + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 28f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal,this, NSLayoutAttribute.Bottom, 1f, 0f), + }); - public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) - { - var target = (item as NSObjectFacade).Target; - switch (target) { - case KeyValuePair kvp: - return false; - case TypeInfo info: - return true; - - default: - return false; - } - } + viewModel.PropertyChanged += OnPropertyChanged; } - internal class ObjectOutlineViewDataSource : NSOutlineViewDataSource + public override void OnPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e) { - public IReadOnlyList ViewModel { get; } - - internal ObjectOutlineViewDataSource (IReadOnlyList viewModel) - { - if (viewModel == null) - throw new ArgumentNullException (nameof (viewModel)); - - ViewModel = viewModel; - } - - public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) - { - var childCount = 0; - if (item == null) { - childCount = this.ViewModel != null ? this.ViewModel.Count () : 0; - } else { - var target = (item as NSObjectFacade).Target; - switch (target) { - case KeyValuePair kvp: - childCount = kvp.Value.Count; - break; - case TypeInfo info: - childCount = 0; - break; - default: - childCount = 0; - break; - } - } + if (e.PropertyName == nameof (CreateBindingViewModel.ShowObjectSelector)) { + Hidden = !ViewModel.ShowObjectSelector; - return childCount; + if (ViewModel.ShowObjectSelector && ViewModel.ObjectElementRoots != null) { + this.objectOutlineView.ItemsSource = ViewModel.ObjectElementRoots.Value; + }; } + } - public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) - { - object element; - - if (item == null) { - element = this.ViewModel.ElementAt ((int)childIndex); - } else { - var target = (item as NSObjectFacade).Target; - switch (target) { - case KeyValuePair kvp: - element = kvp.Value[(int)childIndex]; - break; - case TypeInfo info: - element = info; - break; - default: - return null; + private void OnObjectOutlineViewSelected (object sender, EventArgs e) + { + if (sender is ObjectOutlineView rov) { + if (rov.SelectedRow != -1) { + if (rov.ItemAtRow (rov.SelectedRow) is NSObjectFacade item) { + if (item.Target is Resource resource) { + ViewModel.SelectedResource = resource; + } } } - - return new NSObjectFacade (element); - } - - public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) - { - var target = (item as NSObjectFacade).Target; - switch (target) { - case KeyValuePair kvp: - return kvp.Value.Count > 0; - case TypeInfo info: - return false; - default: - return false; - } } } - internal ObjectOutlineView objectOutlineView; - - internal BindingObjectSelectorControl (CreateBindingViewModel viewModel) - { - if (viewModel == null) - throw new ArgumentNullException (nameof (viewModel)); - - this.objectOutlineView = new ObjectOutlineView (); - TranslatesAutoresizingMaskIntoConstraints = false; - - viewModel.PropertyChanged += (sender, e) => { - if (e.PropertyName == nameof (CreateBindingViewModel.ShowObjectSelector)) { - Hidden = !viewModel.ShowObjectSelector; - - if (viewModel.ShowObjectSelector && viewModel.ObjectElementRoots != null) { - this.objectOutlineView.ViewModel = viewModel.ObjectElementRoots.Value; - }; - } - }; - } } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingPathSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingPathSelectorControl.cs index 0123d7b..2d84be3 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingPathSelectorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingPathSelectorControl.cs @@ -1,236 +1,66 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; using AppKit; +using CoreGraphics; using Foundation; using Xamarin.PropertyEditing.ViewModels; namespace Xamarin.PropertyEditing.Mac { - internal class PathOutlineView : NSOutlineView + internal class BindingPathSelectorControl + : NotifyingView { - private IReadOnlyCollection viewModel; - private PropertyTreeRoot propertyTreeRoot; - - private IReadOnlyCollection ViewModel - { - get => this.viewModel; - set { - if (this.viewModel != value) { - this.viewModel = value; - - var dataSource = new PathOutlineViewDataSource (this.viewModel, this.targetName); - Delegate = new PathOutlineViewDelegate (dataSource); - DataSource = dataSource; - - ReloadData (); - - ExpandItem (ItemAtRow (0)); - } - } - } + private readonly PathOutlineView pathOutlineView; + internal const string PathSelectorColumnColId = "PathSelectorColumn"; - private string targetName { get; set; } - public PropertyTreeRoot PropertyTreeRoot - { - get { return this.propertyTreeRoot; } - internal set { - if (this.propertyTreeRoot != value) { - this.propertyTreeRoot = value; - if (this.propertyTreeRoot != null) { - targetName = this.propertyTreeRoot.TargetType.Name; - ViewModel = this.propertyTreeRoot.Children; - } else { - targetName = string.Empty; - ViewModel = null; - } - } - } + public string CustomPath { + get { + return this.customPathControl.StringValue; + } } + private NSTextField customPathControl { get; } - public PathOutlineView () - { - Initialize (); - } + private const float HorizontalCustomOffSet = 30.5f; - // Called when created from unmanaged code - public PathOutlineView (IntPtr handle) : base (handle) - { - Initialize (); - } + private HeaderView pathHeader; + private NSView pathBox; - // Called when created directly from a XIB file - [Export ("initWithCoder:")] - public PathOutlineView (NSCoder coder) : base (coder) + public BindingPathSelectorControl (CreateBindingViewModel viewModel) { - Initialize (); - } + if (viewModel == null) + throw new ArgumentNullException (nameof (viewModel)); - [Export ("validateProposedFirstResponder:forEvent:")] - public bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) - { - return true; - } + ViewModel = viewModel; - public void Initialize () - { - AutoresizingMask = NSViewResizingMask.WidthSizable; - HeaderView = null; TranslatesAutoresizingMaskIntoConstraints = false; - } - } - - internal class PathOutlineViewDelegate : NSOutlineViewDelegate - { - private readonly PathOutlineViewDataSource dataSource; - - public PathOutlineViewDelegate (PathOutlineViewDataSource dataSource) - { - this.dataSource = dataSource; - } - - public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) - { - return PropertyEditorControl.DefaultControlHeight; - } - - public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) - { - var labelContainer = (UnfocusableTextField)outlineView.MakeView ("path", this); - if (labelContainer == null) { - labelContainer = new UnfocusableTextField { - Identifier = "path", - }; - } - var target = (item as NSObjectFacade).Target; - - switch (target) { - case PropertyTreeElement propertyTreeElement: - labelContainer.StringValue = string.Format("{0}: ({1})", propertyTreeElement.Property.Name, propertyTreeElement.Property.RealType.Name); - break; - - case string targetName: - labelContainer.StringValue = targetName; - break; - - default: - labelContainer.StringValue = "Type Not Supported"; - break; - } - - return labelContainer; - } - - public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) - { - if (item is NSObjectFacade facade) { - switch (facade.Target) { - case PropertyTreeElement propertyTreeElement: - var propertyTreeResult = propertyTreeElement.Children.Task.Result; - return propertyTreeResult.Count == 0; - - default: - return false; - } - } else { - return false; - } - } - } - - internal class PathOutlineViewDataSource : NSOutlineViewDataSource - { - private readonly IReadOnlyCollection viewModel; - private string targetName; - - internal PathOutlineViewDataSource (IReadOnlyCollection viewModel, string targetName) - { - this.viewModel = viewModel; - this.targetName = targetName; - } - public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) - { - nint childCount; - if (item == null) { - childCount = this.viewModel != null ? this.viewModel.Count () + 1 : 0; - } else { - var target = (item as NSObjectFacade).Target; - switch (target) { - case PropertyTreeElement propertyTreeElement: - IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; - childCount = propertyTrees.Count; - break; - - case string targetName: - childCount = this.viewModel.Count (); - break; - - default: - childCount = 0; - break; - } - } - - return childCount; - } - - public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) - { - if (childIndex == 0 && item == null) { - return new NSObjectFacade (targetName); - } - - if (item is NSObjectFacade objectFacade) { - var target = objectFacade.Target; - switch (target) { - case PropertyTreeElement propertyTreeElement: - IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; - return new NSObjectFacade (propertyTrees.ElementAt ((int)childIndex)); - - case string targetName: - return new NSObjectFacade (this.viewModel.ElementAt ((int)childIndex)); - - default: - return null; - } - - } - return null; - } + this.pathBox = new NSView { + TranslatesAutoresizingMaskIntoConstraints = false, + WantsLayer = true, + + // Layer out of alphabetical order so that WantsLayer creates the layer first + Layer = { + CornerRadius = 1.0f, + BorderColor = new CGColor (.5f, .5f, .5f, 1.0f), + BorderWidth = 1, + }, + }; - public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) - { - if (item is NSObjectFacade objectFacade) { - var target = objectFacade.Target; - switch (target) { - case PropertyTreeElement propertyTreeElement: - IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; - return propertyTrees.Count > 0; - - case string targetName: - return true; - - default: - return false; - } - } + AddSubview (this.pathBox); - return false; - } - } + this.pathHeader = new HeaderView { + Title = Properties.Resources.Path, + }; + this.pathHeader.HorizonalTitleOffset = -HorizontalCustomOffSet; - internal class BindingPathSelectorControl : NSView - { - private PathOutlineView pathOutlineView; - internal const string PathSelectorColumnColId = "PathSelectorColumn"; - private const float spacing = 10f; - private NSTextField customPath; - public NSTextField CustomPath { get; private set; } + this.pathBox.AddSubview (this.pathHeader); - public BindingPathSelectorControl (CreateBindingViewModel viewModel) - { - TranslatesAutoresizingMaskIntoConstraints = false; + this.pathBox.AddConstraints (new[] { + NSLayoutConstraint.Create (this.pathHeader, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.pathHeader, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.pathHeader, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (this.pathHeader, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, BindingEditorWindow.HeaderHeight), + }); var customCheckBox = new NSButton { ControlSize = NSControlSize.Small, @@ -241,26 +71,25 @@ namespace Xamarin.PropertyEditing.Mac customCheckBox.SetButtonType (NSButtonType.Switch); - AddSubview (customCheckBox); - AddConstraints (new[] { - NSLayoutConstraint.Create (customCheckBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 8f), - NSLayoutConstraint.Create (customCheckBox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Right, 1f, -90f), + this.pathHeader.AddSubview (customCheckBox); + this.pathHeader.AddConstraints (new[] { + NSLayoutConstraint.Create (customCheckBox, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this.pathHeader, NSLayoutAttribute.CenterX, 1, HorizontalCustomOffSet), + NSLayoutConstraint.Create (customCheckBox, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this.pathHeader, NSLayoutAttribute.CenterY, 1, 0f), }); - this.customPath = new NSTextField { - ControlSize = NSControlSize.Mini, + this.customPathControl = new NSTextField { + ControlSize = NSControlSize.Regular, Enabled = false, - Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), TranslatesAutoresizingMaskIntoConstraints = false, }; - var customPathHeightConstraint = NSLayoutConstraint.Create (this.customPath, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 0); + var customPathHeightConstraint = NSLayoutConstraint.Create (this.customPathControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 0); - AddSubview (this.customPath); - AddConstraints (new[] { - NSLayoutConstraint.Create (this.customPath, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 45f), - NSLayoutConstraint.Create (this.customPath, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (this.customPath, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0), + this.pathBox.AddSubview (this.customPathControl); + this.pathBox.AddConstraints (new[] { + NSLayoutConstraint.Create (this.customPathControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.Top, 1f, 28f), + NSLayoutConstraint.Create (this.customPathControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (this.customPathControl, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.CenterX, 1, 0), customPathHeightConstraint, }); @@ -269,39 +98,20 @@ namespace Xamarin.PropertyEditing.Mac TranslatesAutoresizingMaskIntoConstraints = false, }; - var outlineViewContainerTopConstraint = NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.customPath, NSLayoutAttribute.Bottom, 1f, 0f); customCheckBox.Activated += (sender, e) => { - this.customPath.Enabled = customCheckBox.State == NSCellStateValue.On; - customPathHeightConstraint.Constant = this.customPath.Enabled ? PropertyEditorControl.DefaultControlHeight : 0; - outlineViewContainerTopConstraint.Constant = this.customPath.Enabled ? 10 : 0; - SetCustomPath (viewModel); + this.customPathControl.Enabled = customCheckBox.State == NSCellStateValue.On; + customPathHeightConstraint.Constant = this.customPathControl.Enabled ? 22 : 0; }; - this.customPath.Changed += (sender, e) => { - viewModel.Path = this.customPath.StringValue; + this.customPathControl.Changed += (sender, e) => { + viewModel.Path = this.customPathControl.StringValue; }; this.pathOutlineView = new PathOutlineView { }; - this.pathOutlineView.Activated += (sender, e) => { - if (sender is PathOutlineView pov) { - if (pov.SelectedRow != -1) { - if (pov.ItemAtRow (pov.SelectedRow) is NSObjectFacade facade) { - switch (facade.Target) { - case PropertyTreeElement propertyTreeElement: - viewModel.SelectedPropertyElement = propertyTreeElement; - break; - - default: - break; - } - SetCustomPath (viewModel); - } - } - } - }; + this.pathOutlineView.Activated += OnPathOutlineViewSelected; var pathColumn = new NSTableColumn (PathSelectorColumnColId); this.pathOutlineView.AddColumn (pathColumn); @@ -311,27 +121,58 @@ namespace Xamarin.PropertyEditing.Mac // add the panel to the window outlineViewContainer.DocumentView = this.pathOutlineView; - AddSubview (outlineViewContainer); + + this.pathBox.AddSubview (outlineViewContainer); + + this.pathBox.AddConstraints (new[] { + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.customPathControl, NSLayoutAttribute.Bottom, 1f, 0f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.CenterX, 1, 0), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this.pathBox, NSLayoutAttribute.Bottom, 1f, 0f), + }); AddConstraints (new[] { - outlineViewContainerTopConstraint, - NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0), - NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal,this, NSLayoutAttribute.Bottom, 1f, -5f), + NSLayoutConstraint.Create (this.pathBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (this.pathBox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.pathBox, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1f, 0f), + NSLayoutConstraint.Create (this.pathBox, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, 0f), }); - viewModel.PropertyChanged += async (sender, e) => { - if (viewModel.PropertyRoot != null) { - this.pathOutlineView.PropertyTreeRoot = await viewModel.PropertyRoot.Task; + viewModel.PropertyChanged += OnPropertyChanged; + } + + public override async void OnPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof (CreateBindingViewModel.PropertyRoot)) { + if (ViewModel.PropertyRoot != null) { + this.pathOutlineView.PropertyTreeRoot = await ViewModel.PropertyRoot.Task; } else { this.pathOutlineView.PropertyTreeRoot = null; } - }; + } + + if (e.PropertyName == nameof (CreateBindingViewModel.Path)) { + this.customPathControl.StringValue = ViewModel.Path ?? string.Empty; + } } - private void SetCustomPath (CreateBindingViewModel viewModel) + private void OnPathOutlineViewSelected (object sender, EventArgs e) { - this.customPath.StringValue = this.customPath.Enabled ? viewModel.Path ?? string.Empty : string.Empty; + if (sender is PathOutlineView pov) { + if (pov.SelectedRow != -1) { + if (pov.ItemAtRow (pov.SelectedRow) is NSObjectFacade facade) { + switch (facade.Target) { + case PropertyTreeElement propertyTreeElement: + ViewModel.SelectedPropertyElement = propertyTreeElement; + break; + + default: + break; + } + } + } + } } + } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceOutlineView.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceOutlineView.cs new file mode 100644 index 0000000..025fcba --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceOutlineView.cs @@ -0,0 +1,139 @@ +using System; +using System.Linq; +using AppKit; +using Foundation; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class BindingResourceOutlineView : BaseSelectorOutlineView + { + private ILookup itemsSource; + public ILookup ItemsSource + { + get => this.itemsSource; + set + { + if (this.itemsSource != value) { + this.itemsSource = value; + + DataSource = new BindingResourceOutlineViewDataSource (this.itemsSource); + Delegate = new BindingResourceOutlineViewDelegate (); + } + + ReloadData (); + + ExpandItem (null, true); + } + } + } + + internal class BindingResourceOutlineViewDelegate : NSOutlineViewDelegate + { + private const string ResourceIdentifier = "resource"; + + public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) + { + var labelContainer = (UnfocusableTextField)outlineView.MakeView (ResourceIdentifier, this); + if (labelContainer == null) { + labelContainer = new UnfocusableTextField { + Identifier = ResourceIdentifier, + }; + } + var target = (item as NSObjectFacade).Target; + + switch (target) { + case IGrouping kvp: + labelContainer.StringValue = kvp.Key.Name; + break; + case Resource resource: + labelContainer.StringValue = resource.Name; + break; + default: + labelContainer.StringValue = Properties.Resources.ResourceNotSupported; + break; + } + + return labelContainer; + } + + public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case IGrouping kvp: + return false; + case Resource resource: + return true; + + default: + return false; + } + } + } + + internal class BindingResourceOutlineViewDataSource : NSOutlineViewDataSource + { + public ILookup ItemsSource { get; } + + internal BindingResourceOutlineViewDataSource (ILookup itemsSource) + { + if (itemsSource == null) + throw new ArgumentNullException (nameof (itemsSource)); + + ItemsSource = itemsSource; + } + + public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) + { + if (item == null) { + return ItemsSource != null ? ItemsSource.Count : 0; + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case IGrouping kvp: + return kvp.Count (); + case Resource resource: + return 0; + default: + return 0; + } + } + } + + public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) + { + object element; + + if (item == null) { + element = ItemsSource.ElementAt ((int)childIndex); + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case IGrouping kvp: + element = kvp.ElementAt ((int)childIndex); + break; + case Resource resource: + element = resource; + break; + default: + return null; + } + } + + return new NSObjectFacade (element); + } + + public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case IGrouping kvp: + return kvp.Any (); + case Resource resource: + return false; + default: + return false; + } + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceSelectorControl.cs index afcf746..54a5e4b 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceSelectorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingResourceSelectorControl.cs @@ -7,209 +7,24 @@ using Xamarin.PropertyEditing.ViewModels; namespace Xamarin.PropertyEditing.Mac { - internal class BindingResourceSelectorControl : NSView + internal class BindingResourceSelectorControl + : NotifyingView { - internal class ResourceOutlineView : NSOutlineView - { - private ILookup viewModel; - public ILookup ViewModel { - get => this.viewModel; - set { - if (this.viewModel != value) { - this.viewModel = value; - var dataSource = new ResourceOutlineViewDataSource (this.viewModel); - Delegate = new ResourceOutlineViewDelegate (dataSource); - DataSource = dataSource; - } - - if (this.viewModel != null) { - ReloadData (); - - ExpandItem (null, true); - } - } - } + private const string ResourceSelectorColId = "ResourceSelectorColumn"; - public ResourceOutlineView () - { - Initialize (); - } - - // Called when created from unmanaged code - public ResourceOutlineView (IntPtr handle) : base (handle) - { - Initialize (); - } - - // Called when created directly from a XIB file - [Export ("initWithCoder:")] - public ResourceOutlineView (NSCoder coder) : base (coder) - { - Initialize (); - } - - [Export ("validateProposedFirstResponder:forEvent:")] - public bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) - { - return true; - } - - public void Initialize () - { - AutoresizingMask = NSViewResizingMask.WidthSizable; - HeaderView = null; - TranslatesAutoresizingMaskIntoConstraints = false; - } - } - - internal class ResourceOutlineViewDelegate : NSOutlineViewDelegate - { - private readonly ResourceOutlineViewDataSource dataSource; + public BindingResourceOutlineView resourceOutlineView; - public ResourceOutlineViewDelegate (ResourceOutlineViewDataSource datasource) - { - this.dataSource = datasource; - } - - public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) - { - return PropertyEditorControl.DefaultControlHeight; - } - - public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) - { - var labelContainer = (UnfocusableTextField)outlineView.MakeView ("resource", this); - if (labelContainer == null) { - labelContainer = new UnfocusableTextField { - Identifier = "resource", - }; - } - var target = (item as NSObjectFacade).Target; - - switch (target) { - case IGrouping kvp: - labelContainer.StringValue = kvp.Key.Name; - break; - case Resource resource: - labelContainer.StringValue = resource.Name; - break; - default: - labelContainer.StringValue = "Resource Not Supported"; - break; - } - - return labelContainer; - } - - public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) - { - var target = (item as NSObjectFacade).Target; - switch (target) { - case IGrouping kvp: - return false; - case Resource resource: - return true; - - default: - return false; - } - } - } - - internal class ResourceOutlineViewDataSource : NSOutlineViewDataSource + internal BindingResourceSelectorControl (CreateBindingViewModel viewModel) { - public ILookup ViewModel { get; } + if (viewModel == null) + throw new ArgumentNullException (nameof (viewModel)); - internal ResourceOutlineViewDataSource (ILookup viewModel) - { - if (viewModel == null) - throw new ArgumentNullException (nameof (viewModel)); - - ViewModel = viewModel; - } + ViewModel = viewModel; - public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) - { - var childCount = 0; - if (item == null) { - childCount = this.ViewModel != null ? this.ViewModel.Count () : 0; - } else { - var target = (item as NSObjectFacade).Target; - switch (target) { - case IGrouping < ResourceSource, Resource> kvp: - childCount = kvp.Count (); - break; - case Resource resource: - childCount = 0; - break; - default: - childCount = 0; - break; - } - } - - return childCount; - } - - public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) - { - object element; - - if (item == null) { - element = this.ViewModel.ElementAt ((int)childIndex); - } else { - var target = (item as NSObjectFacade).Target; - switch (target) { - case IGrouping kvp: - element = kvp.ElementAt ((int)childIndex); - break; - case Resource resource: - element = resource; - break; - default: - return null; - } - } - - return new NSObjectFacade (element); - } - - public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) - { - var target = (item as NSObjectFacade).Target; - switch (target) { - case IGrouping kvp: - return kvp.Count() > 0; - case Resource resource: - return false; - default: - return false; - } - } - } - - internal const string ResourceSelectorColId = "ResourceSelectorColumn"; - - public ResourceOutlineView resourceOutlineView; - - internal BindingResourceSelectorControl (CreateBindingViewModel viewModel) - { TranslatesAutoresizingMaskIntoConstraints = false; - this.resourceOutlineView = new ResourceOutlineView { - - }; - this.resourceOutlineView.Activated += (sender, e) => { - if (sender is ResourceOutlineView rov) { - if (rov.SelectedRow != -1) { - if (rov.ItemAtRow (rov.SelectedRow) is NSObjectFacade item) { - if (item.Target is Resource resource) { - viewModel.SelectedResource = resource; - } - } - } - } - }; + this.resourceOutlineView = new BindingResourceOutlineView (); + this.resourceOutlineView.Activated += OnResourceOutlineViewSelected; var resourceColumn = new NSTableColumn (ResourceSelectorColId); this.resourceOutlineView.AddColumn (resourceColumn); @@ -227,21 +42,37 @@ namespace Xamarin.PropertyEditing.Mac AddSubview (outlineViewContainer); AddConstraints (new[] { - NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 45f), - NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 5f), - NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Height, NSLayoutRelation.Equal,this, NSLayoutAttribute.Height, 1f, -50f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 28f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0), + NSLayoutConstraint.Create (outlineViewContainer, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal,this, NSLayoutAttribute.Bottom, 1f, 0f), }); - viewModel.PropertyChanged += (sender, e) => { - if (e.PropertyName == nameof (CreateBindingViewModel.ShowResourceSelector)) { - Hidden = !viewModel.ShowResourceSelector; + viewModel.PropertyChanged += OnPropertyChanged; + } + + public override void OnPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof (CreateBindingViewModel.ShowResourceSelector)) { + Hidden = !ViewModel.ShowResourceSelector; - if (viewModel.ShowResourceSelector && viewModel.SourceResources != null) { - this.resourceOutlineView.ViewModel = viewModel.SourceResources.Value; - }; + if (ViewModel.ShowResourceSelector && ViewModel.SourceResources != null) { + this.resourceOutlineView.ItemsSource = ViewModel.SourceResources.Value; + }; + } + } + + private void OnResourceOutlineViewSelected (object sender, EventArgs e) + { + if (sender is BindingResourceOutlineView rov) { + if (rov.SelectedRow != -1) { + if (rov.ItemAtRow (rov.SelectedRow) is NSObjectFacade item) { + if (item.Target is Resource resource) { + ViewModel.SelectedResource = resource; + } + } } - }; + } } } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingTypeSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingTypeSelectorControl.cs deleted file mode 100644 index bbfebc9..0000000 --- a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/BindingTypeSelectorControl.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using AppKit; -using Foundation; -using Xamarin.PropertyEditing.ViewModels; - -namespace Xamarin.PropertyEditing.Mac -{ - internal class BindingTypeSelectorControl : NSView - { - internal class TypeOutlineView : NSOutlineView - { - private TypeSelectorViewModel viewModel; - public TypeSelectorViewModel ViewModel { - get => this.viewModel; - set { - if (this.viewModel != null) { - this.viewModel.PropertyChanged -= OnPropertyChanged; - } - - if (this.viewModel != value) { - this.viewModel = value; - var dataSource = new TypeOutlineViewDataSource (this.viewModel); - Delegate = new TypeOutlineViewDelegate (dataSource); - DataSource = dataSource; - } - - OnPropertyChanged (this.viewModel, new PropertyChangedEventArgs (null)); - if (this.viewModel != null) { - this.viewModel.PropertyChanged += OnPropertyChanged; - } - } - } - - private void OnPropertyChanged (object sender, PropertyChangedEventArgs e) - { - ReloadData (); - - ExpandItem (null, true); - } - - public TypeOutlineView () - { - Initialize (); - } - - // Called when created from unmanaged code - public TypeOutlineView (IntPtr handle) : base (handle) - { - Initialize (); - } - - // Called when created directly from a XIB file - [Export ("initWithCoder:")] - public TypeOutlineView (NSCoder coder) : base (coder) - { - Initialize (); - } - - [Export ("validateProposedFirstResponder:forEvent:")] - public bool ValidateProposedFirstResponder (NSResponder responder, NSEvent forEvent) - { - return true; - } - - public void Initialize () - { - AutoresizingMask = NSViewResizingMask.WidthSizable; - HeaderView = null; - TranslatesAutoresizingMaskIntoConstraints = false; - } - } - - internal class TypeOutlineViewDelegate : NSOutlineViewDelegate - { - private TypeOutlineViewDataSource dataSource; - - public TypeOutlineViewDelegate (TypeOutlineViewDataSource dataSource) - { - this.dataSource = dataSource; - } - - public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) - { - return PropertyEditorControl.DefaultControlHeight; - } - - public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) - { - var labelContainer = (UnfocusableTextField)outlineView.MakeView ("type", this); - if (labelContainer == null) { - labelContainer = new UnfocusableTextField { - Identifier = "type", - }; - } - var target = (item as NSObjectFacade).Target; - - switch (target) { - case KeyValuePair kvp: - labelContainer.StringValue = kvp.Key; - break; - case TypeInfo info: - labelContainer.StringValue = info.Name; - break; - default: - labelContainer.StringValue = "Type Not Supported"; - break; - } - - return labelContainer; - } - - public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) - { - var target = (item as NSObjectFacade).Target; - switch (target) { - case KeyValuePair kvp: - return false; - case TypeInfo info: - return true; - - default: - return false; - } - } - } - - internal class TypeOutlineViewDataSource : NSOutlineViewDataSource - { - public TypeSelectorViewModel ViewModel { get; } - - internal TypeOutlineViewDataSource (TypeSelectorViewModel viewModel) - { - if (viewModel == null) - throw new ArgumentNullException (nameof (viewModel)); - - ViewModel = viewModel; - } - - public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) - { - var childCount = 0; - if (item == null) { - childCount = this.ViewModel.Types != null ? this.ViewModel.Types.Count () : 0; - } else { - var target = (item as NSObjectFacade).Target; - switch (target) { - case KeyValuePair kvp: - childCount = kvp.Value.Count; - break; - case TypeInfo info: - childCount = 0; - break; - default: - childCount = 0; - break; - } - } - - return childCount; - } - - public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) - { - object element; - - if (item == null) { - element = this.ViewModel.Types.ElementAt ((int)childIndex); - } else { - var target = (item as NSObjectFacade).Target; - switch (target) { - case KeyValuePair kvp: - element = kvp.Value[(int)childIndex]; - break; - case TypeInfo info: - element = info; - break; - default: - return null; - } - } - - return new NSObjectFacade (element); - } - - public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) - { - var target = (item as NSObjectFacade).Target; - switch (target) { - case KeyValuePair kvp: - return kvp.Value.Count > 0; - case TypeInfo info: - return false; - default: - return false; - } - } - } - internal const string TypeSelectorColId = "TypeSelectorColumn"; - - internal TypeOutlineView typeOutlineView; - private NSButton showAllAssembliesCheckBox; - - public CreateBindingViewModel ViewModel { get; set; } - - public BindingTypeSelectorControl (CreateBindingViewModel viewModel) - { - ViewModel = viewModel; - TranslatesAutoresizingMaskIntoConstraints = false; - - this.typeOutlineView = new TypeOutlineView { - - }; - - this.typeOutlineView.Activated += (sender, e) => { - if (sender is TypeOutlineView tov) { - if (tov.SelectedRow != -1) { - if (tov.ItemAtRow (tov.SelectedRow) is NSObjectFacade item) { - if (item.Target is ITypeInfo typeInfo) { - viewModel.TypeSelector.SelectedType = typeInfo; - } - } - } - } - }; - - var typeColumn = new NSTableColumn (TypeSelectorColId); - this.typeOutlineView.AddColumn (typeColumn); - - // Set OutlineTableColumn or the arrows showing children/expansion will not be drawn - this.typeOutlineView.OutlineTableColumn = typeColumn; - - // create a table view and a scroll view - var tableContainer = new NSScrollView { - TranslatesAutoresizingMaskIntoConstraints = false, - }; - - // add the panel to the window - tableContainer.DocumentView = this.typeOutlineView; - AddSubview (tableContainer); - - var filterObjects = new NSSearchField { - ControlSize = NSControlSize.Mini, - Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), - PlaceholderString = Properties.Resources.SearchObjectsTitle, - TranslatesAutoresizingMaskIntoConstraints = false, - }; - - filterObjects.Changed += (sender, e) => { - viewModel.TypeSelector.FilterText = filterObjects.Cell.Title; - this.typeOutlineView.ReloadData (); - this.typeOutlineView.ExpandItem (null, true); - }; - - AddSubview (filterObjects); - AddConstraints (new[] { - NSLayoutConstraint.Create (filterObjects, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 45f), - NSLayoutConstraint.Create (filterObjects, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 5f), - NSLayoutConstraint.Create (filterObjects, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (filterObjects, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), - }); - - // Position based on filterObjects bottom and All Assemblies Top. - AddConstraints (new[] { - NSLayoutConstraint.Create (tableContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, filterObjects, NSLayoutAttribute.Bottom, 1f, 5f), - NSLayoutConstraint.Create (tableContainer, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 5f), - NSLayoutConstraint.Create (tableContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (tableContainer, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1f, -105f), - }); - - this.showAllAssembliesCheckBox = new NSButton { - ControlSize = NSControlSize.Small, - Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), - Title = Properties.Resources.ShowAllAssemblies, - TranslatesAutoresizingMaskIntoConstraints = false, - }; - - this.showAllAssembliesCheckBox.SetButtonType (NSButtonType.Switch); - this.showAllAssembliesCheckBox.Activated += SelectionChanged; - - AddSubview (this.showAllAssembliesCheckBox); - AddConstraints (new[] { - NSLayoutConstraint.Create (this.showAllAssembliesCheckBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Bottom, 1f, -30f), - NSLayoutConstraint.Create (this.showAllAssembliesCheckBox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 10f), - NSLayoutConstraint.Create (this.showAllAssembliesCheckBox, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (this.showAllAssembliesCheckBox, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), - }); - - viewModel.PropertyChanged += (sender, e) => { - if (e.PropertyName == nameof (CreateBindingViewModel.ShowTypeSelector)) { - Hidden = !viewModel.ShowTypeSelector; - - if (viewModel.ShowTypeSelector && viewModel.TypeSelector != null) { - this.typeOutlineView.ViewModel = viewModel.TypeSelector; - - this.showAllAssembliesCheckBox.State = viewModel.TypeSelector.ShowAllAssemblies ? NSCellStateValue.On : NSCellStateValue.Off; - } - } - }; - } - - private void SelectionChanged (object sender, EventArgs e) - { - if (sender is NSButton button) { - this.typeOutlineView.ViewModel.ShowAllAssemblies = (button.State == NSCellStateValue.On) ? true : false; - } - } - } -} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs index e97a9b4..b8c5c0e 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/CreateValueConverterWindow.cs @@ -10,7 +10,7 @@ namespace Xamarin.PropertyEditing.Mac { internal class CreateValueConverterWindow : NSPanel { - new ModalWindowCloseDelegate Delegate { + private new ModalWindowCloseDelegate Delegate { get => (ModalWindowCloseDelegate)base.Delegate; set => base.Delegate = value; } @@ -42,31 +42,32 @@ namespace Xamarin.PropertyEditing.Mac }; var valueConverterLabel = new UnfocusableTextField { - StringValue = Properties.Resources.ValueConverterName, + Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, 13), + StringValue = Properties.Resources.ValueConverterName + ":", TranslatesAutoresizingMaskIntoConstraints = false, }; container.AddSubview (valueConverterLabel); container.AddConstraints (new[] { - NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, container, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, container, NSLayoutAttribute.Left, 1f, 5f), - NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, container, NSLayoutAttribute.Top, 1f, 18f), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, container, NSLayoutAttribute.Left, 1f, 21f), + NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 20), }); this.valueConverterName = new NSTextField { - ControlSize = NSControlSize.Small, + ControlSize = NSControlSize.Regular, TranslatesAutoresizingMaskIntoConstraints = false, }; container.AddSubview (this.valueConverterName); container.AddConstraints (new[] { NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Top, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Bottom, 1f, 1f), - NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Left, NSLayoutRelation.Equal, container, NSLayoutAttribute.Left, 1f, 5f), - NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Width, NSLayoutRelation.Equal, container, NSLayoutAttribute.Width, 1f, -10f), - NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Left, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (this.valueConverterName, NSLayoutAttribute.Width, NSLayoutRelation.Equal, container, NSLayoutAttribute.Width, 1f, -40f), }); var typeSelectorControl = new TypeSelectorControl { + Flush = true, ViewModel = ViewModel, TranslatesAutoresizingMaskIntoConstraints = false, }; @@ -74,31 +75,53 @@ namespace Xamarin.PropertyEditing.Mac container.AddSubview (typeSelectorControl); container.AddConstraints (new[] { - NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, container, NSLayoutAttribute.Top, 1f, 45f), - NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, container, NSLayoutAttribute.Left, 1f, 0f), - NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, container, NSLayoutAttribute.Width, 1f, 0f), - NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, container, NSLayoutAttribute.Height, 1f, -50f) + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.valueConverterName, NSLayoutAttribute.Bottom, 1f, 8f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Left, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.valueConverterName, NSLayoutAttribute.Width, 1f, 0f), + NSLayoutConstraint.Create (typeSelectorControl, NSLayoutAttribute.Height, NSLayoutRelation.Equal, container, NSLayoutAttribute.Height, 1f, -95f), }); - var buttonDone = new NSButton { + var buttonSelect = new NSButton { BezelStyle = NSBezelStyle.Rounded, + ControlSize = NSControlSize.Regular, + Enabled = false, Highlighted = true, KeyEquivalent = "\r", // Fire when enter pressed - Title = Properties.Resources.DoneTitle, + Title = Properties.Resources.Select, TranslatesAutoresizingMaskIntoConstraints = false, }; - buttonDone.Activated += (sender, e) => { + buttonSelect.Activated += (sender, e) => { Delegate.Response = NSModalResponse.OK; Close (); }; - container.AddSubview (buttonDone); + container.AddSubview (buttonSelect); container.AddConstraints (new[] { - NSLayoutConstraint.Create (buttonDone, NSLayoutAttribute.Top, NSLayoutRelation.Equal, container, NSLayoutAttribute.Bottom, 1f, -32f), - NSLayoutConstraint.Create (buttonDone, NSLayoutAttribute.Right, NSLayoutRelation.Equal, container, NSLayoutAttribute.Right, 1f, -16f), - NSLayoutConstraint.Create (buttonDone, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), + NSLayoutConstraint.Create (buttonSelect, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, container, NSLayoutAttribute.Bottom, 1f, -20f), + NSLayoutConstraint.Create (buttonSelect, NSLayoutAttribute.Right, NSLayoutRelation.Equal, typeSelectorControl, NSLayoutAttribute.Right, 1f, 0f), + NSLayoutConstraint.Create (buttonSelect, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1f, 80f), + }); + + var buttonCancel = new NSButton { + BezelStyle = NSBezelStyle.Rounded, + ControlSize = NSControlSize.Regular, + Title = Properties.Resources.Cancel, + TranslatesAutoresizingMaskIntoConstraints = false, + }; + + buttonCancel.Activated += (sender, e) => { + Delegate.Response = NSModalResponse.Cancel; + Close (); + }; + + container.AddSubview (buttonCancel); + + container.AddConstraints (new[] { + NSLayoutConstraint.Create (buttonCancel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, buttonSelect, NSLayoutAttribute.Top, 1f, 0f), + NSLayoutConstraint.Create (buttonCancel, NSLayoutAttribute.Right, NSLayoutRelation.Equal, buttonSelect, NSLayoutAttribute.Left, 1f, -10f), + NSLayoutConstraint.Create (buttonCancel, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1f, 80f), }); ContentViewController = new NSViewController (null, null) { @@ -106,20 +129,11 @@ namespace Xamarin.PropertyEditing.Mac }; ViewModel.PropertyChanged += (sender, e) => { - if (e.PropertyName == nameof (ViewModel.SelectedType)) { - this.valueConverterName.StringValue = ViewModel.SelectedType.Name; + if (e.PropertyName == nameof (AddValueConverterViewModel.SelectedType)) { + this.valueConverterName.StringValue = ViewModel.SelectedType != null ? ViewModel.SelectedType.Name : string.Empty; + buttonSelect.Enabled = ViewModel.SelectedType != null; } }; } } - - public class ModalWindowCloseDelegate : NSWindowDelegate - { - public NSModalResponse Response { get; set; } = NSModalResponse.Cancel; - - public override void WillClose (NSNotification notification) - { - NSApplication.SharedApplication.StopModalWithCode ((int)Response); - } - } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/HeaderView.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/HeaderView.cs index 5fb377b..833a76b 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/HeaderView.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/HeaderView.cs @@ -7,13 +7,20 @@ namespace Xamarin.PropertyEditing.Mac { internal class HeaderView : NSView { - public string Title - { + public string Title { get { return this.headerText.StringValue; } set { this.headerText.StringValue = value; } } - private UnfocusableTextField headerText = new UnfocusableTextField (); + private NSLayoutConstraint horizonalHeaderTextAlignment; + public nfloat HorizonalTitleOffset { + get { return this.horizonalHeaderTextAlignment.Constant; } + set { this.horizonalHeaderTextAlignment.Constant = value; } + } + + private UnfocusableTextField headerText = new UnfocusableTextField { + TranslatesAutoresizingMaskIntoConstraints = false, + }; internal HeaderView () { @@ -23,19 +30,17 @@ namespace Xamarin.PropertyEditing.Mac // Layer out of alphabetical order so that WantsLayer creates the layer first Layer = new CALayer { CornerRadius = 1.0f, - BorderColor = new CGColor (.5f, .5f, .5f, .5f), + BorderColor = new CGColor (.5f, .5f, .5f, 1.0f), BorderWidth = 1, }; - this.headerText.Alignment = NSTextAlignment.Center; - this.headerText.TranslatesAutoresizingMaskIntoConstraints = false; + this.horizonalHeaderTextAlignment = NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0); AddSubview (this.headerText); AddConstraints (new[] { - NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 0f), - NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, 0f), NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1f, 0f), + this.horizonalHeaderTextAlignment, + NSLayoutConstraint.Create (this.headerText, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1, 0f), }); } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/ObjectOutlineView.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/ObjectOutlineView.cs new file mode 100644 index 0000000..18ee0cf --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/ObjectOutlineView.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using AppKit; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class ObjectOutlineView : BaseSelectorOutlineView + { + private IReadOnlyList itemsSource; + public IReadOnlyList ItemsSource + { + get => this.itemsSource; + set + { + if (this.itemsSource != value) { + this.itemsSource = value; + + DataSource = new ObjectOutlineViewDataSource (this.itemsSource); ; + Delegate = new ObjectOutlineViewDelegate (); + } + + ReloadData (); + + ExpandItem (null, true); + } + } + } + + internal class ObjectOutlineViewDelegate : NSOutlineViewDelegate + { + private const string TypeIdentifier = "type"; + + public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) + { + var labelContainer = (UnfocusableTextField)outlineView.MakeView (TypeIdentifier, this); + if (labelContainer == null) { + labelContainer = new UnfocusableTextField { + Identifier = TypeIdentifier, + }; + } + var target = (item as NSObjectFacade).Target; + + switch (target) { + case KeyValuePair kvp: + labelContainer.StringValue = kvp.Key; + break; + case TypeInfo info: + labelContainer.StringValue = info.Name; + break; + default: + labelContainer.StringValue = Properties.Resources.TypeNotSupported; + break; + } + + return labelContainer; + } + + public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + return false; + case TypeInfo info: + return true; + + default: + return false; + } + } + } + + internal class ObjectOutlineViewDataSource : NSOutlineViewDataSource + { + public IReadOnlyList ItemsSource { get; } + + internal ObjectOutlineViewDataSource (IReadOnlyList itemsSource) + { + if (itemsSource == null) + throw new ArgumentNullException (nameof (itemsSource)); + + ItemsSource = itemsSource; + } + + public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) + { + if (item == null) { + return ItemsSource != null ? ItemsSource.Count : 0; + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + return kvp.Value.Count; + case TypeInfo info: + return 0; + default: + return 0; + } + } + } + + public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) + { + object element; + + if (item == null) { + element = ItemsSource.ElementAt ((int)childIndex); + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + element = kvp.Value[(int)childIndex]; + break; + case TypeInfo info: + element = info; + break; + default: + return null; + } + } + + return new NSObjectFacade (element); + } + + public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) + { + var target = (item as NSObjectFacade).Target; + switch (target) { + case KeyValuePair kvp: + return kvp.Value.Count > 0; + case TypeInfo info: + return false; + default: + return false; + } + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/PathOutlineView.cs b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/PathOutlineView.cs new file mode 100644 index 0000000..124cee6 --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/BindingEditor/PathOutlineView.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using AppKit; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class PathOutlineView : BaseSelectorOutlineView + { + private IReadOnlyCollection itemsSource; + private PropertyTreeRoot propertyTreeRoot; + + private void SetItemsSource (IReadOnlyCollection value, string targetName) + { + if (this.itemsSource != value) { + this.itemsSource = value; + + DataSource = new PathOutlineViewDataSource (this.itemsSource, targetName); ; + Delegate = new PathOutlineViewDelegate (); + + ReloadData (); + + ExpandItem (ItemAtRow (0)); + } + } + + public PropertyTreeRoot PropertyTreeRoot + { + get { return this.propertyTreeRoot; } + internal set + { + if (this.propertyTreeRoot != value) { + this.propertyTreeRoot = value; + if (this.propertyTreeRoot != null) { + SetItemsSource (this.propertyTreeRoot.Children, this.propertyTreeRoot.TargetType.Name); + } else { + SetItemsSource (null, string.Empty); + } + } + } + } + } + + internal class PathOutlineViewDelegate : NSOutlineViewDelegate + { + private const string PathIdentifier = "path"; + + public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) + { + var labelContainer = (UnfocusableTextField)outlineView.MakeView (PathIdentifier, this); + if (labelContainer == null) { + labelContainer = new UnfocusableTextField { + Identifier = PathIdentifier, + }; + } + var target = (item as NSObjectFacade).Target; + + switch (target) { + case PropertyTreeElement propertyTreeElement: + labelContainer.StringValue = string.Format ("{0}: ({1})", propertyTreeElement.Property.Name, propertyTreeElement.Property.RealType.Name); + break; + + case string targetName: + labelContainer.StringValue = targetName; + break; + + default: + labelContainer.StringValue = Properties.Resources.TypeNotSupported; + break; + } + + return labelContainer; + } + + public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item) + { + if (item is NSObjectFacade facade) { + switch (facade.Target) { + case PropertyTreeElement propertyTreeElement: + var propertyTreeResult = propertyTreeElement.Children.Task.Result; + return propertyTreeResult.Count == 0; + + default: + return false; + } + } else { + return false; + } + } + } + + internal class PathOutlineViewDataSource : NSOutlineViewDataSource + { + private readonly IReadOnlyCollection itemsSource; + private readonly string targetName; + + internal PathOutlineViewDataSource (IReadOnlyCollection itemsSource, string targetName) + { + this.itemsSource = itemsSource; + this.targetName = targetName; + } + + public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) + { + if (item == null) { + return this.itemsSource != null ? this.itemsSource.Count + 1 : 0; + } else { + var target = (item as NSObjectFacade).Target; + switch (target) { + case PropertyTreeElement propertyTreeElement: + IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; + return propertyTrees.Count; + + case string targetName: + return this.itemsSource.Count; + + default: + return 0; + } + } + } + + public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) + { + if (childIndex == 0 && item == null) { + return new NSObjectFacade (targetName); + } + + if (item is NSObjectFacade objectFacade) { + var target = objectFacade.Target; + switch (target) { + case PropertyTreeElement propertyTreeElement: + IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; + return new NSObjectFacade (propertyTrees.ElementAt ((int)childIndex)); + + case string targetName: + return new NSObjectFacade (this.itemsSource.ElementAt ((int)childIndex)); + + default: + return null; + } + + } + return null; + } + + public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) + { + if (item is NSObjectFacade objectFacade) { + var target = objectFacade.Target; + switch (target) { + case PropertyTreeElement propertyTreeElement: + IReadOnlyCollection propertyTrees = propertyTreeElement.Children.Task.Result; + return propertyTrees.Count > 0; + + case string targetName: + return true; + + default: + return false; + } + } + + return false; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/BasePanelWindow.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/BasePanelWindow.cs deleted file mode 100644 index d22ea4b..0000000 --- a/Xamarin.PropertyEditing.Mac/Controls/Custom/BasePanelWindow.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using System.Linq; -using AppKit; -using CoreGraphics; -using Foundation; -using Xamarin.PropertyEditing.ViewModels; - -namespace Xamarin.PropertyEditing.Mac -{ - internal class BasePanelWindow : NSPanel - { - protected NSButton ButtonDone; - private NSButton buttonCancel; - private NSButton buttonHelp; - - protected NSPopUpButton BindingTypePopup; - - protected NSPopUpButton ValueConverterPopup; - - protected NSButton AddConverterButton; - - protected NSView AncestorTypeBox; - protected NSView PathBox; - - protected NSView MainContainer; - protected NSView BindingPropertiesView; - protected NSView FlagsPropertiesView; - protected NSButton ButtonMoreSettings; - - protected nfloat MoreSettingsViewHeight { get; set; } - - internal BasePanelWindow () - : base (new CGRect (0, 0, 728, 445), NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Resizable, NSBackingStore.Buffered, true) - { - FloatingPanel = true; - - MaxSize = new CGSize (960, 720); // TODO discuss what the Max/Min Size should be and if we should have one. - MinSize = new CGSize (320, 240); - - this.MainContainer = new NSView { - TranslatesAutoresizingMaskIntoConstraints = false - }; - - this.ButtonDone = new NSButton { - BezelStyle = NSBezelStyle.Rounded, - KeyEquivalent = "\r", // Fire when enter pressed - Highlighted = true, - TranslatesAutoresizingMaskIntoConstraints = false, - }; - - this.MainContainer.AddSubview (this.ButtonDone); - - this.buttonCancel = new NSButton { - BezelStyle = NSBezelStyle.Rounded, - KeyEquivalent = "0x1b", // TODO Need to double check this is right - Title = Properties.Resources.Cancel, - TranslatesAutoresizingMaskIntoConstraints = false, - }; - - this.buttonCancel.Activated += (sender, e) => { - Close (); - }; - - this.MainContainer.AddSubview (this.buttonCancel); - - this.buttonHelp = new NSButton { - BezelStyle = NSBezelStyle.HelpButton, - Title = string.Empty, - TranslatesAutoresizingMaskIntoConstraints = false, - }; - - this.MainContainer.AddSubview (this.buttonHelp); - - var bindingTypeLabel = new UnfocusableTextField { - TranslatesAutoresizingMaskIntoConstraints = false, - Alignment = NSTextAlignment.Right, - }; - - bindingTypeLabel.StringValue = Properties.Resources.BindingType; - this.MainContainer.AddSubview (bindingTypeLabel); - - this.BindingTypePopup = new FocusablePopUpButton { - TranslatesAutoresizingMaskIntoConstraints = false, - StringValue = String.Empty, - ControlSize = NSControlSize.Small, - Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), - }; - - var bindingTypeMenuList = new NSMenu (); - this.BindingTypePopup.Menu = bindingTypeMenuList; - this.MainContainer.AddSubview (this.BindingTypePopup); - - var valueConverterLabel = new UnfocusableTextField { - TranslatesAutoresizingMaskIntoConstraints = false, - Alignment = NSTextAlignment.Right, - }; - - this.AncestorTypeBox = new NSView { - TranslatesAutoresizingMaskIntoConstraints = false, - WantsLayer = true, - - // Layer out of alphabetical order so that WantsLayer creates the layer first - Layer = { - CornerRadius = 1.0f, - BorderColor = new CGColor (.5f, .5f, .5f, .5f), - BorderWidth = 1, - }, - }; - - this.MainContainer.AddSubview (this.AncestorTypeBox); - - this.PathBox = new NSView { - TranslatesAutoresizingMaskIntoConstraints = false, - WantsLayer = true, - - // Layer out of alphabetical order so that WantsLayer creates the layer first - Layer = { - CornerRadius = 1.0f, - BorderColor = new CGColor (.5f, .5f, .5f, .5f), - BorderWidth = 1, - }, - }; - - this.MainContainer.AddSubview (this.PathBox); - - valueConverterLabel.StringValue = Properties.Resources.Converter; - this.MainContainer.AddSubview (valueConverterLabel); - - this.ValueConverterPopup = new FocusablePopUpButton { - TranslatesAutoresizingMaskIntoConstraints = false, - StringValue = String.Empty, - ControlSize = NSControlSize.Small, - Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize), - }; - - var valueConverterMenuList = new NSMenu (); - this.ValueConverterPopup.Menu = valueConverterMenuList; - this.MainContainer.AddSubview (this.ValueConverterPopup); - - this.AddConverterButton = new NSButton { - BezelStyle = NSBezelStyle.Rounded, - Image = NSImage.ImageNamed (NSImageName.AddTemplate), - Title = string.Empty, - ToolTip = Properties.Resources.AddValueConverterEllipsis, - TranslatesAutoresizingMaskIntoConstraints = false, - }; - - this.MainContainer.AddSubview (this.AddConverterButton); - - this.ButtonMoreSettings = new NSButton { - BezelStyle = NSBezelStyle.Disclosure, - Title = string.Empty, - TranslatesAutoresizingMaskIntoConstraints = false, - }; - this.ButtonMoreSettings.SetButtonType (NSButtonType.PushOnPushOff); - - this.MainContainer.AddSubview (this.ButtonMoreSettings); - - var labelOtherSettings = new UnfocusableTextField { - TranslatesAutoresizingMaskIntoConstraints = false, - }; - - this.MainContainer.AddSubview (labelOtherSettings); - - this.BindingPropertiesView = new NSView { - Hidden = true, - TranslatesAutoresizingMaskIntoConstraints = false, - WantsLayer = true, - - // Layer out of alphabetical order so that WantsLayer creates the layer first - Layer = { - CornerRadius = 1.0f, - BorderColor = new CGColor (.5f, .5f, .5f, .5f), - BorderWidth = 1, - }, - }; - - this.MainContainer.AddSubview (this.BindingPropertiesView); - - this.FlagsPropertiesView = new NSView { - Hidden = true, - TranslatesAutoresizingMaskIntoConstraints = false, - WantsLayer = true, - - // Layer out of alphabetical order so that WantsLayer creates the layer first - Layer = { - CornerRadius = 1.0f, - BorderColor = new CGColor (.5f, .5f, .5f, .5f), - BorderWidth = 1, - }, - }; - - this.MainContainer.AddSubview (this.FlagsPropertiesView); - - //Work out the titlebar height - var titleBarHeight = Frame.Size.Height - ContentRectFor (Frame).Size.Height; - - var ancestorTypeBoxHeightConstraint = NSLayoutConstraint.Create (this.AncestorTypeBox, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Bottom, 1f, -100f); - var heightConstant = ancestorTypeBoxHeightConstraint.Constant; - - var bindingPropertiesViewHeightConstraint = NSLayoutConstraint.Create (this.BindingPropertiesView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 124); - - this.ButtonMoreSettings.Activated += (sender, e) => { - if (sender is NSButton moreButton) { - ToggleSettingsLabel (moreButton.State == NSCellStateValue.Off, labelOtherSettings); - - this.BindingPropertiesView.Hidden = moreButton.State == NSCellStateValue.Off; - this.FlagsPropertiesView.Hidden = this.BindingPropertiesView.Hidden; - - bindingPropertiesViewHeightConstraint.Constant = this.MoreSettingsViewHeight; - ancestorTypeBoxHeightConstraint.Constant = this.BindingPropertiesView.Hidden ? heightConstant : heightConstant - (MoreSettingsViewHeight + 20); - this.MainContainer.SetFrameSize (new CGSize (this.MainContainer.Frame.Width, this.BindingPropertiesView.Hidden ? this.MainContainer.Frame.Height - MoreSettingsViewHeight : this.MainContainer.Frame.Height + MoreSettingsViewHeight)); - SetFrame (new CGRect (new CGPoint (Frame.X, Frame.Y), new CGSize (Frame.Width, this.MainContainer.Frame.Height + titleBarHeight)), false, true); - } - }; - - ToggleSettingsLabel (this.ButtonMoreSettings.State == NSCellStateValue.Off, labelOtherSettings); - - this.MainContainer.AddConstraints (new[] { - NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Top, 1f, 10f), - NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20f), - NSLayoutConstraint.Create (bindingTypeLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), - - NSLayoutConstraint.Create (this.BindingTypePopup, NSLayoutAttribute.Top, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (this.BindingTypePopup, NSLayoutAttribute.Left, NSLayoutRelation.Equal, bindingTypeLabel, NSLayoutAttribute.Right, 1f, 5f), - NSLayoutConstraint.Create (this.BindingTypePopup, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), - - NSLayoutConstraint.Create (this.AncestorTypeBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.BindingTypePopup, NSLayoutAttribute.Bottom, 1f, 10f), - NSLayoutConstraint.Create (this.AncestorTypeBox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20f), - NSLayoutConstraint.Create (this.AncestorTypeBox, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.PathBox, NSLayoutAttribute.Left, 1f, -10f), - ancestorTypeBoxHeightConstraint, - - NSLayoutConstraint.Create (this.PathBox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (this.PathBox, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Right, 1f, -20f), - NSLayoutConstraint.Create (this.PathBox, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Width, 1f, 0f), - NSLayoutConstraint.Create (this.PathBox, NSLayoutAttribute.Height, NSLayoutRelation.Equal,this.AncestorTypeBox, NSLayoutAttribute.Height, 1f, 0f), - - NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.AncestorTypeBox, NSLayoutAttribute.Bottom, 1f, 10f), - NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20f), - NSLayoutConstraint.Create (valueConverterLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), - - NSLayoutConstraint.Create (this.ValueConverterPopup, NSLayoutAttribute.Top, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (this.ValueConverterPopup, NSLayoutAttribute.Left, NSLayoutRelation.Equal, valueConverterLabel, NSLayoutAttribute.Right, 1f, 5f), - NSLayoutConstraint.Create (this.ValueConverterPopup, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), - - NSLayoutConstraint.Create (this.AddConverterButton, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ValueConverterPopup, NSLayoutAttribute.Top, 1f, 2f), - NSLayoutConstraint.Create (this.AddConverterButton, NSLayoutAttribute.Left, NSLayoutRelation.Equal,this.ValueConverterPopup, NSLayoutAttribute.Right, 1f, 5f), - NSLayoutConstraint.Create (this.AddConverterButton, NSLayoutAttribute.Width, NSLayoutRelation.Equal, 1f, 20), - NSLayoutConstraint.Create (this.AddConverterButton, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 20), - - NSLayoutConstraint.Create (this.ButtonMoreSettings, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ValueConverterPopup, NSLayoutAttribute.Bottom, 1f, 2f), - NSLayoutConstraint.Create (this.ButtonMoreSettings, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 16f), - NSLayoutConstraint.Create (this.ButtonMoreSettings, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, 24), - NSLayoutConstraint.Create (this.ButtonMoreSettings, NSLayoutAttribute.Width, NSLayoutRelation.Equal, 1f, 24), - - NSLayoutConstraint.Create (labelOtherSettings, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ButtonMoreSettings, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (labelOtherSettings, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.ButtonMoreSettings, NSLayoutAttribute.Right, 1f, -5f), - NSLayoutConstraint.Create (labelOtherSettings, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), - - NSLayoutConstraint.Create (this.BindingPropertiesView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ButtonMoreSettings, NSLayoutAttribute.Bottom, 1f, 10f), - NSLayoutConstraint.Create (this.BindingPropertiesView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20f), - NSLayoutConstraint.Create (this.BindingPropertiesView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.FlagsPropertiesView, NSLayoutAttribute.Left, 1f, -10f), - bindingPropertiesViewHeightConstraint, - - NSLayoutConstraint.Create (this.FlagsPropertiesView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (this.FlagsPropertiesView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Right, 1f, -20f), - NSLayoutConstraint.Create (this.FlagsPropertiesView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Width, 1f, 0f), - NSLayoutConstraint.Create (this.FlagsPropertiesView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.BindingPropertiesView, NSLayoutAttribute.Height, 1f, 0f), - - NSLayoutConstraint.Create (this.buttonHelp, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Bottom, 1f, -40f), - NSLayoutConstraint.Create (this.buttonHelp, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Left, 1f, 20), - NSLayoutConstraint.Create (this.buttonHelp, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), - NSLayoutConstraint.Create (this.buttonHelp, NSLayoutAttribute.Width, NSLayoutRelation.Equal, 1f, 24), - - NSLayoutConstraint.Create (this.ButtonDone, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Bottom, 1f, -40f), - NSLayoutConstraint.Create (this.ButtonDone, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.MainContainer, NSLayoutAttribute.Right, 1f, -20f), - NSLayoutConstraint.Create (this.ButtonDone, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), - - NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.ButtonDone, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.ButtonDone, NSLayoutAttribute.Left, 1f, -10f), - NSLayoutConstraint.Create (this.buttonCancel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, PropertyEditorControl.DefaultControlHeight), - }); - - // put the MainContainer inside this panel's ContentView - ContentView.AddSubview (this.MainContainer); - - ContentView.AddConstraints (new[] { - NSLayoutConstraint.Create (this.MainContainer, NSLayoutAttribute.Top, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Top, 1f, 0f), - NSLayoutConstraint.Create (this.MainContainer, NSLayoutAttribute.Left, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Left, 1f, 0f), - NSLayoutConstraint.Create (this.MainContainer, NSLayoutAttribute.Height, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Height, 1f, 0f), - NSLayoutConstraint.Create (this.MainContainer, NSLayoutAttribute.Width, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Width, 1f, 0f), - - }); - } - - private static void ToggleSettingsLabel (bool show, UnfocusableTextField labelOtherSettings) - { - labelOtherSettings.StringValue = show ? Properties.Resources.ShowSettings : Properties.Resources.HideSettings; - } - } -} diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/CommandButton.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/CommandButton.cs index 4904bd8..4fde65f 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/Custom/CommandButton.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/CommandButton.cs @@ -2,7 +2,7 @@ using System; using System.Windows.Input; using AppKit; -namespace Xamarin.PropertyEditing.Mac.Controls.Custom +namespace Xamarin.PropertyEditing.Mac { internal class CommandButton : NSButton { @@ -11,8 +11,7 @@ namespace Xamarin.PropertyEditing.Mac.Controls.Custom public ICommand Command { get { return this.command; } - set - { + set { if (this.command != null) this.command.CanExecuteChanged -= CanExecuteChanged; diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs index 2533d97..5fc2093 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using AppKit; using CoreGraphics; using Xamarin.PropertyEditing.ViewModels; @@ -19,11 +20,16 @@ namespace Xamarin.PropertyEditing.Mac set { if (this.viewModel != null) { this.viewModel.PropertyChanged -= OnPropertyChanged; + if (this.viewModel.SupportsBindings) + this.viewModel.CreateBindingRequested -= OnBindingRequested; } this.viewModel = value; + if (this.viewModel != null) { this.viewModel.PropertyChanged += OnPropertyChanged; + if (this.viewModel.SupportsBindings) + this.viewModel.CreateBindingRequested += OnBindingRequested; ValueSourceChanged (this.viewModel.ValueSource); } } @@ -110,16 +116,13 @@ namespace Xamarin.PropertyEditing.Mac if (this.viewModel.SupportsBindings) { this.popUpContextMenu.AddItem (NSMenuItem.SeparatorItem); - var mi3 = new NSMenuItem (Properties.Resources.CreateDataBindingMenuItem) { + this.popUpContextMenu.AddItem (new CommandMenuItem (Properties.Resources.CreateDataBindingMenuItem, this.viewModel.RequestCreateBindingCommand) { AttributedTitle = new Foundation.NSAttributedString ( Properties.Resources.CreateDataBindingMenuItem, new CoreText.CTStringAttributes { Font = new CoreText.CTFont (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultFontSize + 1), }) - }; - - mi3.Activated += OnBindingRequested; - this.popUpContextMenu.AddItem (mi3); + }); } this.popUpContextMenu.AddItem (NSMenuItem.SeparatorItem); @@ -252,10 +255,16 @@ namespace Xamarin.PropertyEditing.Mac resourceSelectorPopOver.Show (requestResourceView.Frame, (NSView)this, NSRectEdge.MinYEdge); } - private void OnBindingRequested (object sender, EventArgs e) + private void OnBindingRequested (object sender, CreateBindingRequestedEventArgs e) { - var bindingEditorWindow = new BindingEditorWindow (this.hostResources, this.viewModel); - bindingEditorWindow.MakeKeyAndOrderFront (this); + var bindingEditorWindow = new BindingEditorWindow (this.hostResources, this.viewModel) { + Appearance = EffectiveAppearance, + }; + + var result = (NSModalResponse)(int)NSApplication.SharedApplication.RunModalForWindow (bindingEditorWindow); + if (result == NSModalResponse.OK) { + e.BindingObject = bindingEditorWindow.ViewModel.SelectedObjects.Single (); + } } } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs b/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs index 2175b29..ca1d76b 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs @@ -6,8 +6,8 @@ namespace Xamarin.PropertyEditing.Mac internal class EditorContainer : PropertyContainer { - public EditorContainer (IHostResourceProvider hostResources, IEditorView editorView) - : base (hostResources, editorView, editorView?.NeedsPropertyButton ?? false) + public EditorContainer (IHostResourceProvider hostResources, IEditorView editorView, bool? needsPropertyButton = null) + : base (hostResources, editorView, needsPropertyButton ?? editorView?.NeedsPropertyButton ?? false) { } diff --git a/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs index e58ee32..d5574cb 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs @@ -11,6 +11,25 @@ namespace Xamarin.PropertyEditing.Mac internal class TypeSelectorControl : NotifyingView { + private bool flush; + public bool Flush { + get { return this.flush; } + set { + this.flush = value; + this.filterTop.Constant = this.flush ? 0: 10; + this.filterWidth.Constant = this.flush ? 0 : -20; + this.scrollTop.Constant = this.flush ? 0 : 8; + this.checkBoxBottom.Constant = this.flush ? -28 : -10; + this.checkBoxLeft.Constant = this.flush ? 8 : 0; + } + } + + private NSLayoutConstraint filterTop; + private NSLayoutConstraint filterWidth; + private NSLayoutConstraint scrollTop; + private NSLayoutConstraint checkBoxBottom; + private NSLayoutConstraint checkBoxLeft; + public TypeSelectorControl() { this.checkbox = NSButton.CreateCheckbox (Properties.Resources.ShowAllAssemblies, OnCheckedChanged); @@ -36,7 +55,7 @@ namespace Xamarin.PropertyEditing.Mac AddSubview (scroll); - this.filter = new NSTextField { + this.filter = new NSSearchField { TranslatesAutoresizingMaskIntoConstraints = false, PlaceholderString = "Filter" }; @@ -44,18 +63,24 @@ namespace Xamarin.PropertyEditing.Mac AddSubview (this.filter); + this.filterTop = NSLayoutConstraint.Create (this.filter, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1, 10); + this.filterWidth = NSLayoutConstraint.Create (this.filter, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1, -20); + this.scrollTop = NSLayoutConstraint.Create (scroll, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.filter, NSLayoutAttribute.Bottom, 1, 8); + this.checkBoxBottom = NSLayoutConstraint.Create (this.checkbox, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this, NSLayoutAttribute.Bottom, 1, -10); + this.checkBoxLeft = NSLayoutConstraint.Create (this.checkbox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, scroll, NSLayoutAttribute.Left, 1, 0); + AddConstraints (new[] { - NSLayoutConstraint.Create (this.filter, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1, 10), - NSLayoutConstraint.Create (this.filter, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1, -20), + this.filterTop, + this.filterWidth, NSLayoutConstraint.Create (this.filter, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0), - NSLayoutConstraint.Create (scroll, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.filter, NSLayoutAttribute.Bottom, 1, 8), + this.scrollTop, NSLayoutConstraint.Create (scroll, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.filter, NSLayoutAttribute.Width, 1, 0), NSLayoutConstraint.Create (scroll, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterX, 1, 0), NSLayoutConstraint.Create (scroll, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this.checkbox, NSLayoutAttribute.Top, 1, -8), - NSLayoutConstraint.Create (this.checkbox, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this, NSLayoutAttribute.Bottom, 1, -10), - NSLayoutConstraint.Create (this.checkbox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, scroll, NSLayoutAttribute.Left, 1, 0) + this.checkBoxBottom, + this.checkBoxLeft, }); } diff --git a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs index c5bada9..28071a7 100644 --- a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs +++ b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs @@ -40,19 +40,16 @@ namespace Xamarin.PropertyEditing.Mac int headerCount = (ShowHeader && !Filtering) ? 1 : 0; - nint childCount; if (this.vm.ArrangeMode == PropertyArrangeMode.Name) - childCount = this.vm.ArrangedEditors[0].Editors.Count + headerCount; + return this.vm.ArrangedEditors[0].Editors.Count + headerCount; else { if (item == null) - childCount = this.vm.ArrangedEditors.Count + headerCount; + return this.vm.ArrangedEditors.Count + headerCount; else { var group = (PanelGroupViewModel)((NSObjectFacade)item).Target; - childCount = group.Editors.Count + group.UncommonEditors.Count; + return group.Editors.Count + group.UncommonEditors.Count; } } - - return childCount; } public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) diff --git a/Xamarin.PropertyEditing.Tests/CreateBindingViewModelTests.cs b/Xamarin.PropertyEditing.Tests/CreateBindingViewModelTests.cs index 60f6bbc..9c0fdde 100644 --- a/Xamarin.PropertyEditing.Tests/CreateBindingViewModelTests.cs +++ b/Xamarin.PropertyEditing.Tests/CreateBindingViewModelTests.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Moq; using NUnit.Framework; -using NUnit.Framework.Internal; using Xamarin.PropertyEditing.Drawing; using Xamarin.PropertyEditing.Reflection; using Xamarin.PropertyEditing.Tests.MockControls; @@ -194,9 +193,9 @@ namespace Xamarin.PropertyEditing.Tests await vm.ValueConverters.Task; Assert.That (vm.ValueConverters.Value, Contains.Item (visi)); - if (OSPlatform.CurrentPlatform.IsWindows) { + if (vm.IncludeAddValueConverter) { Assert.That (vm.ValueConverters.Value.Count, Is.EqualTo (3)); // visi, No Converter, Request Converter - } else if (OSPlatform.CurrentPlatform.IsMacOSX) { + } else { Assert.That (vm.ValueConverters.Value.Count, Is.EqualTo (2)); // visi, No Converter } } @@ -620,6 +619,46 @@ namespace Xamarin.PropertyEditing.Tests vm.BindingProperties.Cast ().Select (pvm => pvm.Property)); } + [TestCase (true)] + [TestCase (false)] + public async Task IsAddValueConverterIncluded (bool includeAddValueConverter) + { + var vm = CreateBasicViewModel (includeAddValueConverter: includeAddValueConverter); + Assume.That (vm.ValueConverters, Is.Not.Null); + + await vm.ValueConverters.Task; + + if (includeAddValueConverter) { + Assert.That (vm.ValueConverters.Value.Count, Is.EqualTo (2)); // No Converter, Request Converter + } else { + Assert.That (vm.ValueConverters.Value.Count, Is.EqualTo (1)); // No Converter + } + } + + [Test] + public void RequestAddValueConverterCommand () + { + BindingSource[] sources = new[] { + new BindingSource ("Resource", BindingSourceType.Resource), + new BindingSource ("Type", BindingSourceType.Type), + }; + + var vm = CreateBasicViewModel (sources); + + Assert.That (vm.SelectedBindingSource, Is.EqualTo (sources[0])); + + Assert.That (vm.RequestAddValueConverterCommand.CanExecute (null), Is.True); + + bool changed = false; + vm.RequestAddValueConverterCommand.CanExecuteChanged += (o, e) => { + changed = true; + }; + + vm.SelectedBindingSource = sources[1]; + + Assert.That (changed, Is.True); + } + private TestContext syncContext; private static readonly ResourceSource[] DefaultResourceSources = new[] { MockResourceProvider.SystemResourcesSource, MockResourceProvider.ApplicationResourcesSource }; @@ -683,7 +722,7 @@ namespace Xamarin.PropertyEditing.Tests return bpmock; } - private CreateBindingViewModel CreateBasicViewModel (BindingSource[] sources = null, object target = null) + private CreateBindingViewModel CreateBasicViewModel (BindingSource[] sources = null, object target = null, bool includeAddValueConverter = true) { target = target ?? new object (); Mock property = GetBasicProperty (); @@ -693,7 +732,7 @@ namespace Xamarin.PropertyEditing.Tests Mock resourceProvider = GetBasicResourceProvider (target); Mock bpmock = GetBasicBindingProvider (target, property.Object, sources); - return new CreateBindingViewModel (new TargetPlatform (editorProvider, resourceProvider.Object, bpmock.Object), editor.Object, property.Object); + return new CreateBindingViewModel (new TargetPlatform (editorProvider, resourceProvider.Object, bpmock.Object), editor.Object, property.Object, includeAddValueConverter: includeAddValueConverter); } } } \ No newline at end of file diff --git a/Xamarin.PropertyEditing/Properties/Resources.Designer.cs b/Xamarin.PropertyEditing/Properties/Resources.Designer.cs index f617aab..6f5177b 100644 --- a/Xamarin.PropertyEditing/Properties/Resources.Designer.cs +++ b/Xamarin.PropertyEditing/Properties/Resources.Designer.cs @@ -1169,15 +1169,9 @@ namespace Xamarin.PropertyEditing.Properties { } } - public static string ShowSettings { + public static string OtherSettings { get { - return ResourceManager.GetString("ShowSettings", resourceCulture); - } - } - - public static string HideSettings { - get { - return ResourceManager.GetString("HideSettings", resourceCulture); + return ResourceManager.GetString("OtherSettings", resourceCulture); } } @@ -1198,5 +1192,17 @@ namespace Xamarin.PropertyEditing.Properties { return ResourceManager.GetString("DoneTitle", resourceCulture); } } + + public static string TypeNotSupported { + get { + return ResourceManager.GetString("TypeNotSupported", resourceCulture); + } + } + + public static string ResourceNotSupported { + get { + return ResourceManager.GetString("ResourceNotSupported", resourceCulture); + } + } } } diff --git a/Xamarin.PropertyEditing/Properties/Resources.resx b/Xamarin.PropertyEditing/Properties/Resources.resx index 0e4966b..75b46a3 100644 --- a/Xamarin.PropertyEditing/Properties/Resources.resx +++ b/Xamarin.PropertyEditing/Properties/Resources.resx @@ -327,7 +327,7 @@ OK - Show all assemblies + Show All Assemblies Search properties @@ -510,29 +510,29 @@ Name - Resource dictionary + Resource Dictionary Create New Binding… - Add value converter… + Add Value Converter… Add Value Converter - No value converter + No Value Converter - Value converter name + Value Converter Name Create Data Binding for {0} Create Data Binding for Object.Property - Binding type + Binding Type Path @@ -756,13 +756,9 @@ Create Binding Create Binding for Object.Property - - Show Settings - Show Settings for Binding to Object.Property - - - Hide Settings - Hide Settings for Binding to Object.Property + + Other Settings + Toggle the Other Settings Panel for Binding to Object.Property Converter @@ -774,4 +770,10 @@ Done + + Type Not Supported + + + Resource Not Supported + diff --git a/Xamarin.PropertyEditing/ViewModels/CreateBindingViewModel.cs b/Xamarin.PropertyEditing/ViewModels/CreateBindingViewModel.cs index 6c1dff4..959e425 100644 --- a/Xamarin.PropertyEditing/ViewModels/CreateBindingViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/CreateBindingViewModel.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Windows.Input; using Xamarin.PropertyEditing.Properties; namespace Xamarin.PropertyEditing.ViewModels @@ -34,7 +34,7 @@ namespace Xamarin.PropertyEditing.ViewModels internal class CreateBindingViewModel : PropertiesViewModel, IProvidePath { - public CreateBindingViewModel (TargetPlatform platform, IObjectEditor targetEditor, IPropertyInfo property, PropertyVariation variations = null) + public CreateBindingViewModel (TargetPlatform platform, IObjectEditor targetEditor, IPropertyInfo property, PropertyVariation variations = null, bool includeAddValueConverter = true) : base (platform) { if (platform == null) @@ -51,6 +51,7 @@ namespace Xamarin.PropertyEditing.ViewModels this.property = property; this.provider = platform.BindingProvider; this.variations = variations; + IncludeAddValueConverter = includeAddValueConverter; PropertyDisplay = String.Format (Resources.CreateDataBindingTitle, $"[{this.targetEditor.TargetType.Name}].{property.Name}"); RequestNamedDisplay (); @@ -58,7 +59,9 @@ namespace Xamarin.PropertyEditing.ViewModels BindingSources = new AsyncValue> ( platform.BindingProvider.GetBindingSourcesAsync (targetEditor.Target, property)); - RequestBindingObject(); + this.requestAddValueConverterCommand = new RelayCommand (OnRequestAddValueConverter, CanRequestAddValueConverter); + + RequestBindingObject (); } private async void RequestBindingObject () @@ -235,6 +238,8 @@ namespace Xamarin.PropertyEditing.ViewModels OnPropertyChanged(); UpdateShowProperties(); RequestUpdateSources(); + + ((RelayCommand)RequestAddValueConverterCommand)?.ChangeCanExecute (); } } @@ -452,7 +457,7 @@ namespace Xamarin.PropertyEditing.ViewModels } private static readonly Resource NoValueConverter = new Resource (Resources.NoValueConverter); - internal static readonly Resource AddValueConverter = new Resource ("<" + Resources.AddValueConverterEllipsis + ">"); + private static readonly Resource AddValueConverter = new Resource ("<" + Resources.AddValueConverterEllipsis + ">"); private readonly PropertyVariation variations; private readonly IObjectEditor targetEditor; @@ -483,6 +488,8 @@ namespace Xamarin.PropertyEditing.ViewModels set { GetKnownPropertyViewModel (PropertyBinding.SourceParameterProperty).Value = value; } } + public bool IncludeAddValueConverter { get; private set; } + private void UpdateShowProperties () { OnPropertyChanged (nameof (ShowResourceSelector)); @@ -499,7 +506,7 @@ namespace Xamarin.PropertyEditing.ViewModels this.valueConverters.AddRange (converters); // Don't add the AddValueConverter resource if we are on Mac - if (Environment.OSVersion.Platform != PlatformID.Unix) { + if (IncludeAddValueConverter) { this.valueConverters.Add (AddValueConverter); } @@ -678,5 +685,18 @@ namespace Xamarin.PropertyEditing.ViewModels throw new ArgumentException(); } } + + private readonly RelayCommand requestAddValueConverterCommand; + public ICommand RequestAddValueConverterCommand => this.requestAddValueConverterCommand; + + private bool CanRequestAddValueConverter () + { + return TargetPlatform.BindingProvider != null; + } + + private void OnRequestAddValueConverter () + { + SelectedValueConverter = AddValueConverter; + } } } -- cgit v1.2.3 From 1d8b9f497fa63a975c160946212a009d7fd8b0eb Mon Sep 17 00:00:00 2001 From: Eric Maupin Date: Wed, 19 Jun 2019 14:45:39 -0400 Subject: [mac] Ensure property button view is cleared --- Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs b/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs index ca1d76b..bb2e5f9 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs @@ -57,8 +57,8 @@ namespace Xamarin.PropertyEditing.Mac public override void ViewWillMoveToSuperview (NSView newSuperview) { - if (newSuperview == null && EditorView != null) - EditorView.ViewModel = null; + if (newSuperview == null) + ViewModel = null; base.ViewWillMoveToSuperview (newSuperview); } -- cgit v1.2.3 From a2c5c82cc22edaea46b0eeadeb982a9ac5fe75f7 Mon Sep 17 00:00:00 2001 From: Eric Maupin Date: Wed, 3 Jul 2019 18:21:15 -0400 Subject: [mac] Fix container for no button properties Just checking the editor view if it wants a property button isn't quite correct since there's a parameter in a ctor to override that. --- Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs b/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs index bb2e5f9..c22e438 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs @@ -22,7 +22,7 @@ namespace Xamarin.PropertyEditing.Mac EditorView.ViewModel = value; - if (EditorView.NeedsPropertyButton) + if (PropertyButton != null) PropertyButton.ViewModel = value as PropertyViewModel; } } -- cgit v1.2.3