diff options
author | Dominique Louis <dominique@nirsingh-lap.fareast.corp.microsoft.com> | 2019-01-10 13:08:49 +0300 |
---|---|---|
committer | CartBlanche <savagesoftware@gmail.com> | 2019-06-26 21:12:35 +0300 |
commit | 820c53ae01fa95075fd3990f56a81a30e38e8312 (patch) | |
tree | 276edd87d93972381f5d6d245f1c7ceab68e3100 /Xamarin.PropertyEditing.Mac | |
parent | 50cbb497b21e77c752a808ff406db9ba47cff84f (diff) |
[Mac] Initial Binding Editor Dialog.
Diffstat (limited to 'Xamarin.PropertyEditing.Mac')
12 files changed, 1886 insertions, 20 deletions
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<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> (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<ObjectTreeElement> viewModel; + public IReadOnlyList<ObjectTreeElement> 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<string, SimpleCollectionView> 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<string, SimpleCollectionView> kvp: + return false; + case TypeInfo info: + return true; + + default: + return false; + } + } + } + + internal class ObjectOutlineViewDataSource : NSOutlineViewDataSource + { + public IReadOnlyList<ObjectTreeElement> ViewModel { get; } + + internal ObjectOutlineViewDataSource (IReadOnlyList<ObjectTreeElement> 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<string, SimpleCollectionView> 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<string, SimpleCollectionView> 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<string, SimpleCollectionView> 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<object> viewModel; + private PropertyTreeRoot propertyTreeRoot; + + private IReadOnlyCollection<object> 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<object> viewModel; + private string targetName; + + internal PathOutlineViewDataSource (IReadOnlyCollection<object> 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<PropertyTreeElement> 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<PropertyTreeElement> 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<PropertyTreeElement> 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<ResourceSource, Resource> viewModel; + public ILookup<ResourceSource, Resource> 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<ResourceSource, Resource> 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<ResourceSource, Resource> kvp: + return false; + case Resource resource: + return true; + + default: + return false; + } + } + } + + internal class ResourceOutlineViewDataSource : NSOutlineViewDataSource + { + public ILookup<ResourceSource, Resource> ViewModel { get; } + + internal ResourceOutlineViewDataSource (ILookup<ResourceSource, Resource> 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<ResourceSource, Resource> 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<ResourceSource, Resource> 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<string, SimpleCollectionView> 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<string, SimpleCollectionView> 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<string, SimpleCollectionView> 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<string, SimpleCollectionView> 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<string, SimpleCollectionView> 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<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> 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 { |