diff options
Diffstat (limited to 'Xamarin.PropertyEditing.Mac')
20 files changed, 753 insertions, 117 deletions
diff --git a/Xamarin.PropertyEditing.Mac/Controls/CombinablePropertyEditor.cs b/Xamarin.PropertyEditing.Mac/Controls/CombinablePropertyEditor.cs index cf58bdc..29fd55f 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/CombinablePropertyEditor.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/CombinablePropertyEditor.cs @@ -31,7 +31,7 @@ namespace Xamarin.PropertyEditing.Mac public override nint GetHeight (EditorViewModel vm) { var realVm = (CombinablePropertyViewModel<T>)vm; - return checkHeight * realVm.Choices.Count; + return DefaultControlHeight * realVm.Choices.Count; } protected override void HandleErrorsChanged (object sender, DataErrorsChangedEventArgs e) @@ -58,9 +58,12 @@ namespace Xamarin.PropertyEditing.Mac protected override void OnViewModelChanged (PropertyViewModel oldModel) { - nint rowHeight = GetHeight (ViewModel); + base.OnViewModelChanged (oldModel); + + if (ViewModel == null) + return; - float top = checkHeight; + float top = 0; while (this.combinableList.Count > ViewModel.Choices.Count) { var child = this.combinableList.KeyAt (ViewModel.Choices.Count); @@ -75,7 +78,12 @@ namespace Xamarin.PropertyEditing.Mac NSButton checkbox; if (i >= this.combinableList.Count) { checkbox = new NSButton { + AllowsExpansionToolTips = true, AllowsMixedState = true, + Cell = { + LineBreakMode = NSLineBreakMode.TruncatingTail, + UsesSingleLineMode = true, + }, ControlSize = NSControlSize.Small, Font = NSFont.FromFontName (DefaultFontName, DefaultFontSize), TranslatesAutoresizingMaskIntoConstraints = false, @@ -85,23 +93,26 @@ namespace Xamarin.PropertyEditing.Mac checkbox.Activated += SelectionChanged; AddSubview (checkbox); + + this.AddConstraints (new[] { + NSLayoutConstraint.Create (checkbox, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1f, top), + NSLayoutConstraint.Create (checkbox, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1f, 0f), + NSLayoutConstraint.Create (checkbox, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1f, -33f), + NSLayoutConstraint.Create (checkbox, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1f, DefaultControlHeight), + }); } else { checkbox = this.combinableList.KeyAt (i); } checkbox.Title = choice.Name; - checkbox.Frame = new CGRect (0, rowHeight - top, Frame.Width, checkHeight); this.combinableList[checkbox] = choice; - top += checkHeight; + top += DefaultControlHeight; } // Set our tabable order this.firstKeyView = this.combinableList.KeyAt (0); this.lastKeyView = this.combinableList.KeyAt (this.combinableList.Count - 1); - - base.OnViewModelChanged (oldModel); - } protected override void UpdateValue () @@ -125,7 +136,6 @@ namespace Xamarin.PropertyEditing.Mac } } - private const int checkHeight = 22; private readonly OrderedDictionary<NSButton, FlaggableChoiceViewModel<T>> combinableList = new OrderedDictionary<NSButton, FlaggableChoiceViewModel<T>> (); private NSView firstKeyView; private NSView lastKeyView; diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/BrushTabViewController.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/BrushTabViewController.cs index 651d716..6fa2dd5 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/Custom/BrushTabViewController.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/BrushTabViewController.cs @@ -58,7 +58,7 @@ namespace Xamarin.PropertyEditing.Mac this.inhibitSelection = true; base.OnViewModelChanged (oldModel);
- var existing = new HashSet<CommonBrushType> (ViewModel?.BrushTypes?.Values ?? Array.Empty<CommonBrushType> ());
+ var existing = new HashSet<CommonBrushType> (oldModel?.BrushTypes?.Values ?? Array.Empty<CommonBrushType> ());
existing.IntersectWith (this.brushTypeTable.Keys);
var removed = new HashSet<CommonBrushType> (this.brushTypeTable.Keys);
diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/NumericTextField.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/NumericTextField.cs index 5347283..22211fa 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/Custom/NumericTextField.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/NumericTextField.cs @@ -1,4 +1,4 @@ -using System; +using System; using AppKit; using CoreGraphics; using Foundation; @@ -6,13 +6,13 @@ using ObjCRuntime; namespace Xamarin.PropertyEditing.Mac { - public class NumericTextField : NSTextField + internal class NumericTextField : PropertyTextField { - NSText CachedCurrentEditor { + private NSText CachedCurrentEditor { get; set; } - string cachedValueString; + private string cachedValueString; public bool AllowNegativeValues { get; set; @@ -54,7 +54,7 @@ namespace Xamarin.PropertyEditing.Mac public override bool ShouldBeginEditing (NSText textObject) { CachedCurrentEditor = textObject; - cachedValueString = textObject.Value; + this.cachedValueString = textObject.Value; if (AllowRatios) CachedCurrentEditor.Delegate = new RatioValidateDelegate (this); @@ -81,7 +81,7 @@ namespace Xamarin.PropertyEditing.Mac public virtual void ResetInvalidInput () { - this.StringValue = cachedValueString; + this.StringValue = this.cachedValueString; } public static bool CheckIfNumber (string finalString, ValidationType mode, bool allowNegativeValues) @@ -93,9 +93,8 @@ namespace Xamarin.PropertyEditing.Mac public static bool ValidateDecimal (string finalString, bool allowNegativeValues) { - double value; //Checks parsing to number - if (!double.TryParse (finalString, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentUICulture, out value)) + if (!double.TryParse (finalString, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentUICulture, out var value)) return false; //Checks if needs to be possitive value if (!allowNegativeValues && value < 0) @@ -106,9 +105,8 @@ namespace Xamarin.PropertyEditing.Mac public static bool ValidateInteger (string finalString, bool allowNegativeValues) { - int value; //Checks parsing to number - if (!int.TryParse (finalString, out value)) + if (!int.TryParse (finalString, out var value)) return false; //Checks if needs to be possitive value if (!allowNegativeValues && value < 0) @@ -156,7 +154,7 @@ namespace Xamarin.PropertyEditing.Mac } } - class KeyUpDownDelegate : NSTextFieldDelegate + internal class KeyUpDownDelegate : NSTextFieldDelegate { public event EventHandler<bool> KeyArrowUp; public event EventHandler<bool> KeyArrowDown; @@ -194,7 +192,7 @@ namespace Xamarin.PropertyEditing.Mac } } - public abstract class TextViewValidationDelegate : NSTextViewDelegate + internal abstract class TextViewValidationDelegate : NSTextViewDelegate { protected NumericTextField TextField { get; set; @@ -233,7 +231,7 @@ namespace Xamarin.PropertyEditing.Mac protected abstract bool ValidateFinalString (string value); } - public class NumericValidationDelegate : TextViewValidationDelegate + internal class NumericValidationDelegate : TextViewValidationDelegate { public NumericValidationDelegate (NumericTextField textField) : base (textField) @@ -249,7 +247,7 @@ namespace Xamarin.PropertyEditing.Mac } } - public class RatioValidateDelegate : TextViewValidationDelegate + internal class RatioValidateDelegate : TextViewValidationDelegate { public RatioValidateDelegate (NumericTextField textField) : base (textField) diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs index 17581f1..012fbc7 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs @@ -18,14 +18,15 @@ namespace Xamarin.PropertyEditing.Mac { get { return viewModel; } set { - if (viewModel != null) { - viewModel.PropertyChanged -= OnPropertyChanged; + if (this.viewModel != null) { + this.viewModel.PropertyChanged -= OnPropertyChanged; } - viewModel = value; - viewModel.PropertyChanged += OnPropertyChanged; - - ValueSourceChanged (viewModel.ValueSource); + this.viewModel = value; + if (this.viewModel != null) { + this.viewModel.PropertyChanged += OnPropertyChanged; + ValueSourceChanged (this.viewModel.ValueSource); + } } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyTextField.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyTextField.cs new file mode 100644 index 0000000..2e7e158 --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyTextField.cs @@ -0,0 +1,15 @@ +using System; +using AppKit; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class PropertyTextField : NSTextField + { + public PropertyTextField () + { + AllowsExpansionToolTips = true; + Cell.LineBreakMode = NSLineBreakMode.TruncatingTail; + Cell.UsesSingleLineMode = true; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/Custom/UnfocusableTextField.cs b/Xamarin.PropertyEditing.Mac/Controls/Custom/UnfocusableTextField.cs index a55341e..5f9dfab 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/Custom/UnfocusableTextField.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/Custom/UnfocusableTextField.cs @@ -1,4 +1,4 @@ -using System; +using System; using AppKit; using CoreGraphics; using Foundation; @@ -59,14 +59,10 @@ namespace Xamarin.PropertyEditing.Mac private void SetDefaultTextProperties () { - this.label = new NSTextField { + this.label = new PropertyTextField { AccessibilityElement = false, BackgroundColor = NSColor.Clear, Bordered = false, - Cell = { - LineBreakMode = NSLineBreakMode.TruncatingTail, - UsesSingleLineMode = true, - }, ControlSize = NSControlSize.Small, Editable = false, Font = NSFont.FromFontName (PropertyEditorControl.DefaultFontName, PropertyEditorControl.DefaultPropertyLabelFontSize), diff --git a/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs b/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs index e5eec20..075cfd1 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs @@ -1,5 +1,7 @@ using System; using AppKit; +using CoreGraphics; +using Foundation; namespace Xamarin.PropertyEditing.Mac { @@ -27,7 +29,7 @@ namespace Xamarin.PropertyEditing.Mac this.AddConstraints (new[] { NSLayoutConstraint.Create (EditorView.NativeView, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1f, 0f), - NSLayoutConstraint.Create (EditorView.NativeView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.label, NSLayoutAttribute.Right, 1f, 5f), + NSLayoutConstraint.Create (EditorView.NativeView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.label, NSLayoutAttribute.Right, 1f, LabelToControlSpacing), NSLayoutConstraint.Create (EditorView.NativeView, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this, NSLayoutAttribute.Right, 1f, 0f), NSLayoutConstraint.Create (EditorView.NativeView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1f, 0f) }); @@ -40,12 +42,54 @@ namespace Xamarin.PropertyEditing.Mac public string Label { get { return this.label.StringValue; } - set { this.label.StringValue = value; } + set { + this.label.StringValue = value + ":"; + this.label.ToolTip = value; + } + } + + public NSView LeftEdgeView + { + get { return this.leftEdgeView; } + set + { + if (this.leftEdgeView != null) { + this.leftEdgeView.RemoveFromSuperview (); + RemoveConstraints (new[] { this.leftEdgeLeftConstraint, this.leftEdgeVCenterConstraint }); + this.leftEdgeLeftConstraint.Dispose (); + this.leftEdgeLeftConstraint = null; + this.leftEdgeVCenterConstraint.Dispose (); + this.leftEdgeVCenterConstraint = null; + } + + this.leftEdgeView = value; + + if (value != null) { + AddSubview (value); + + value.TranslatesAutoresizingMaskIntoConstraints = false; + this.leftEdgeLeftConstraint = NSLayoutConstraint.Create (this.leftEdgeView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1, 4); + this.leftEdgeVCenterConstraint = NSLayoutConstraint.Create (this.leftEdgeView, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1, 0); + + AddConstraints (new[] { this.leftEdgeLeftConstraint, this.leftEdgeVCenterConstraint }); + } + } + } + + public override void ViewWillMoveToSuperview (NSView newSuperview) + { + if (newSuperview == null && EditorView != null) + EditorView.ViewModel = null; + + base.ViewWillMoveToSuperview (newSuperview); } private UnfocusableTextField label = new UnfocusableTextField { Alignment = NSTextAlignment.Right, - TranslatesAutoresizingMaskIntoConstraints = false + Cell = { + LineBreakMode = NSLineBreakMode.TruncatingHead, + }, + TranslatesAutoresizingMaskIntoConstraints = false, }; #if DEBUG // Currently only used to highlight which controls haven't been implemented @@ -54,6 +98,9 @@ namespace Xamarin.PropertyEditing.Mac } #endif + private NSView leftEdgeView; + private NSLayoutConstraint leftEdgeLeftConstraint, leftEdgeVCenterConstraint; private readonly IHostResourceProvider hostResources; + private const float LabelToControlSpacing = 5f; } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/NumericEditorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/NumericEditorControl.cs index e6b1277..e15e206 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/NumericEditorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/NumericEditorControl.cs @@ -133,6 +133,11 @@ namespace Xamarin.PropertyEditing.Mac protected override void OnViewModelChanged (PropertyViewModel oldModel) { + base.OnViewModelChanged (oldModel); + + if (ViewModel == null) + return; + if (ViewModel.HasInputModes) { if (this.inputModePopup == null) { this.inputModePopup = new NSPopUpButton { @@ -169,8 +174,6 @@ namespace Xamarin.PropertyEditing.Mac // If we are reusing the control we'll have to hid the inputMode if this doesn't have InputMode. if (this.inputModePopup != null) this.inputModePopup.Hidden = !ViewModel.HasInputModes; - - base.OnViewModelChanged (oldModel); } } } diff --git a/Xamarin.PropertyEditing.Mac/Controls/ObjectEditorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/ObjectEditorControl.cs new file mode 100644 index 0000000..ab9749b --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/ObjectEditorControl.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Threading.Tasks; +using AppKit; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class ObjectEditorControl + : PropertyEditorControl<ObjectPropertyViewModel> + { + public ObjectEditorControl (IHostResourceProvider hostResources) + : base (hostResources) + { + this.typeLabel = new UnfocusableTextField { + TranslatesAutoresizingMaskIntoConstraints = false + }; + AddSubview (this.typeLabel); + + this.createObject = new NSButton { + Title = Properties.Resources.New, + TranslatesAutoresizingMaskIntoConstraints = false, + BezelStyle = NSBezelStyle.Rounded + }; + this.createObject.Activated += OnNewPressed; + AddSubview (this.createObject); + + this.buttonConstraint = NSLayoutConstraint.Create (this.createObject, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this.typeLabel, NSLayoutAttribute.Trailing, 1f, 12); + + AddConstraints (new[] { + NSLayoutConstraint.Create (this.typeLabel, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this, NSLayoutAttribute.Leading, 1f, 0f), + NSLayoutConstraint.Create (this.typeLabel, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1f, 0f), + NSLayoutConstraint.Create (this.typeLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1, 0), + this.buttonConstraint, + NSLayoutConstraint.Create (this.createObject, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this, NSLayoutAttribute.Leading, 1, 0).WithPriority (NSLayoutPriority.DefaultLow), + NSLayoutConstraint.Create (this.createObject, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1f, 0f), + NSLayoutConstraint.Create (this.createObject, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1f, 70f), + }); + } + + public override NSView FirstKeyView => this.createObject; + + public override NSView LastKeyView => this.createObject; + + protected override void UpdateValue () + { + } + + protected override void UpdateErrorsDisplayed (IEnumerable errors) + { + } + + protected override void HandleErrorsChanged (object sender, DataErrorsChangedEventArgs e) + { + } + + protected override void SetEnabled () + { + this.createObject.Enabled = ViewModel.Property.CanWrite; + } + + protected override void UpdateAccessibilityValues () + { + this.createObject.AccessibilityTitle = String.Format (Properties.Resources.NewInstanceForProperty, ViewModel.Property.Name); + } + + protected override void OnViewModelChanged (PropertyViewModel oldModel) + { + base.OnViewModelChanged (oldModel); + + if (oldModel is ObjectPropertyViewModel ovm) { + ovm.TypeRequested -= OnTypeRequested; + ovm.CreateInstanceCommand.CanExecuteChanged -= OnCreateInstanceExecutableChanged; + } + + if (ViewModel != null) { + ViewModel.TypeRequested += OnTypeRequested; + ViewModel.CreateInstanceCommand.CanExecuteChanged += OnCreateInstanceExecutableChanged; + + OnPropertyChanged (ViewModel, new PropertyChangedEventArgs (null)); + } + } + + protected override void OnPropertyChanged (object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) { + case nameof (ObjectPropertyViewModel.ValueType): + UpdateTypeLabel (); + break; + case null: + case "": + UpdateTypeLabel (); + UpdateCreateInstanceCommand (); + break; + } + + base.OnPropertyChanged (sender, e); + } + + private readonly UnfocusableTextField typeLabel; + private readonly NSButton createObject; + private readonly NSLayoutConstraint buttonConstraint; + + private void OnCreateInstanceExecutableChanged (object sender, EventArgs e) + { + UpdateCreateInstanceCommand (); + } + + private void OnTypeRequested (object sender, TypeRequestedEventArgs e) + { + var tcs = new TaskCompletionSource<ITypeInfo> (); + e.SelectedType = tcs.Task; + + var vm = new TypeSelectorViewModel (ViewModel.AssignableTypes); + var selector = new TypeSelectorControl { + ViewModel = vm, + Appearance = EffectiveAppearance + }; + + vm.PropertyChanged += (vms, ve) => { + if (ve.PropertyName == nameof (TypeSelectorViewModel.SelectedType)) { + tcs.TrySetResult (vm.SelectedType); + } + }; + + var popover = new NSPopover { + Behavior = NSPopoverBehavior.Transient, + Delegate = new PopoverDelegate<ITypeInfo> (tcs), + ContentViewController = new NSViewController { + View = selector, + PreferredContentSize = new CoreGraphics.CGSize (360, 335) + }, + }; + + popover.SetAppearance (HostResources.GetVibrantAppearance (EffectiveAppearance)); + + tcs.Task.ContinueWith (t => { + popover.PerformClose (popover); + popover.Dispose (); + }, TaskScheduler.FromCurrentSynchronizationContext()); + + popover.Show (this.createObject.Frame, this, NSRectEdge.MinYEdge); + } + + private void UpdateTypeLabel () + { + if (ViewModel.ValueType == null) { + this.typeLabel.StringValue = String.Empty; + this.buttonConstraint.Active = false; + } else { + this.typeLabel.StringValue = $"({ViewModel.ValueType.Name})"; + this.buttonConstraint.Active = true; + } + } + + private void UpdateCreateInstanceCommand() + { + this.createObject.Enabled = ViewModel.CreateInstanceCommand.CanExecute (null); + } + + private void OnNewPressed (object sender, EventArgs e) + { + ViewModel.CreateInstanceCommand.Execute (null); + } + + private class PopoverDelegate<T> + : NSPopoverDelegate + { + public PopoverDelegate (TaskCompletionSource<T> tcs) + { + this.tcs = tcs; + } + + public override void WillClose (NSNotification notification) + { + this.tcs.TrySetCanceled (); + } + + private readonly TaskCompletionSource<T> tcs; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/PanelHeaderEditorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/PanelHeaderEditorControl.cs index 0c76c5d..13ecdaf 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/PanelHeaderEditorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/PanelHeaderEditorControl.cs @@ -22,7 +22,7 @@ namespace Xamarin.PropertyEditing.Mac NSControlSize controlSize = NSControlSize.Small; TranslatesAutoresizingMaskIntoConstraints = false; - this.propertyObjectName = new NSTextField { + this.propertyObjectName = new PropertyTextField { ControlSize = controlSize, PlaceholderString = LocalizationResources.ObjectNamePlaceholder, TranslatesAutoresizingMaskIntoConstraints = false, diff --git a/Xamarin.PropertyEditing.Mac/Controls/PredefinedValuesEditor.cs b/Xamarin.PropertyEditing.Mac/Controls/PredefinedValuesEditor.cs index cc7b4b0..b38237f 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/PredefinedValuesEditor.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/PredefinedValuesEditor.cs @@ -21,22 +21,32 @@ namespace Xamarin.PropertyEditing.Mac base.TranslatesAutoresizingMaskIntoConstraints = false; this.comboBox = new FocusableComboBox { - TranslatesAutoresizingMaskIntoConstraints = false, + AllowsExpansionToolTips = true, BackgroundColor = NSColor.Clear, - StringValue = String.Empty, + Cell = { + LineBreakMode = NSLineBreakMode.TruncatingTail, + UsesSingleLineMode = true, + }, ControlSize = NSControlSize.Small, - Font = NSFont.FromFontName(DefaultFontName, DefaultFontSize), + Font = NSFont.FromFontName (DefaultFontName, DefaultFontSize), + TranslatesAutoresizingMaskIntoConstraints = false, + StringValue = String.Empty, }; this.comboBox.SelectionChanged += (sender, e) => { - ViewModel.ValueName = comboBox.SelectedValue.ToString (); + ViewModel.ValueName = this.comboBox.SelectedValue.ToString (); }; this.popUpButton = new NSPopUpButton { - TranslatesAutoresizingMaskIntoConstraints = false, - StringValue = String.Empty, + AllowsExpansionToolTips = true, + Cell = { + LineBreakMode = NSLineBreakMode.TruncatingTail, + UsesSingleLineMode = true, + }, ControlSize = NSControlSize.Small, Font = NSFont.FromFontName (DefaultFontName, DefaultFontSize), + TranslatesAutoresizingMaskIntoConstraints = false, + StringValue = String.Empty, }; popupButtonList = new NSMenu (); diff --git a/Xamarin.PropertyEditing.Mac/Controls/PropertyEditorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/PropertyEditorControl.cs index efa2171..7106b79 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/PropertyEditorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/PropertyEditorControl.cs @@ -20,7 +20,6 @@ namespace Xamarin.PropertyEditing.Mac public abstract NSView FirstKeyView { get; } public abstract NSView LastKeyView { get; } - public nint TableRow { get; set; } = -1; public NSTableView TableView { get; set; } public const int DefaultControlHeight = 22; @@ -32,9 +31,9 @@ namespace Xamarin.PropertyEditing.Mac PropertyViewModel viewModel; public PropertyViewModel ViewModel { - get { return viewModel; } + get { return this.viewModel; } set { - if (viewModel == value) + if (this.ViewModel == value) return; PropertyViewModel oldModel = this.viewModel; @@ -45,10 +44,10 @@ namespace Xamarin.PropertyEditing.Mac this.viewModel = value; OnViewModelChanged (oldModel); - viewModel.PropertyChanged += OnPropertyChanged; - - // FIXME: figure out what we want errors to display as (tooltip, etc.) - viewModel.ErrorsChanged += HandleErrorsChanged; + if (this.viewModel != null) { + this.viewModel.PropertyChanged += OnPropertyChanged; + this.viewModel.ErrorsChanged += HandleErrorsChanged; + } } } @@ -70,26 +69,23 @@ namespace Xamarin.PropertyEditing.Mac public void UpdateKeyViews () { - if (TableRow < 0) + nint row = TableView.RowForView (this); + if (row <= 0) return; + NSView view; PropertyEditorControl ctrl = null; - - var tr = TableRow; - if (tr > 0) { - NSView view; - do { - tr--; - view = TableView.GetView (0, tr, false); - ctrl = (view as EditorContainer)?.EditorView?.NativeView as PropertyEditorControl; - } while (tr > 0 && ctrl == null); - - if (ctrl != null) { - ctrl.LastKeyView.NextKeyView = FirstKeyView; - ctrl.UpdateKeyViews (); - } else if (tr == 0 && view is PanelHeaderEditorControl header) { - header.SetNextKeyView (FirstKeyView); - } + 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); } } @@ -103,11 +99,12 @@ namespace Xamarin.PropertyEditing.Mac protected virtual void OnViewModelChanged (PropertyViewModel oldModel) { - SetEnabled (); - UpdateValue (); - UpdateAccessibilityValues (); + if (ViewModel != null) { + SetEnabled (); + UpdateValue (); + UpdateAccessibilityValues (); + } - // Hook this up so we know when to reset values PropertyButton.ViewModel = viewModel; } diff --git a/Xamarin.PropertyEditing.Mac/Controls/StringEditorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/StringEditorControl.cs index 75f46d4..b3b2efe 100644 --- a/Xamarin.PropertyEditing.Mac/Controls/StringEditorControl.cs +++ b/Xamarin.PropertyEditing.Mac/Controls/StringEditorControl.cs @@ -27,7 +27,7 @@ namespace Xamarin.PropertyEditing.Mac public StringEditorControl (IHostResourceProvider hostResource) : base (hostResource) { - this.stringEditor = new NSTextField { + this.stringEditor = new PropertyTextField { BackgroundColor = NSColor.Clear, ControlSize = NSControlSize.Small, Font = NSFont.FromFontName (DefaultFontName, DefaultFontSize), @@ -65,6 +65,11 @@ namespace Xamarin.PropertyEditing.Mac protected override void OnViewModelChanged (PropertyViewModel oldModel) { + base.OnViewModelChanged (oldModel); + + if (ViewModel == null) + return; + if (ViewModel.HasInputModes) { if (this.inputModePopup == null) { this.inputModePopup = new NSPopUpButton { @@ -101,8 +106,6 @@ namespace Xamarin.PropertyEditing.Mac // If we are reusing the control we'll have to hid the inputMode if this doesn't have InputMode. if (this.inputModePopup != null) this.inputModePopup.Hidden = !ViewModel.HasInputModes; - - base.OnViewModelChanged (oldModel); } protected override void UpdateErrorsDisplayed (IEnumerable errors) diff --git a/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs b/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs new file mode 100644 index 0000000..747ca64 --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorControl.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading; +using AppKit; +using Foundation; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class TypeSelectorControl + : NotifyingView<TypeSelectorViewModel> + { + public TypeSelectorControl() + { + this.checkbox = NSButton.CreateCheckbox (Properties.Resources.ShowAllAssemblies, OnCheckedChanged); + this.checkbox.TranslatesAutoresizingMaskIntoConstraints = false; + AddSubview (this.checkbox); + + var scroll = new NSScrollView { + TranslatesAutoresizingMaskIntoConstraints = false + }; + + var d = new TypeSelectorDelegate (); + this.outlineView = new NSOutlineView { + Delegate = d, + AutoresizingMask = NSViewResizingMask.WidthSizable, + HeaderView = null, + Action = new ObjCRuntime.Selector ("onActivatedItem"), + Target = this + }; + var col = new NSTableColumn (); + this.outlineView.AddColumn (col); + this.outlineView.OutlineTableColumn = col; + scroll.DocumentView = this.outlineView; + + AddSubview (scroll); + + this.filter = new NSTextField { + TranslatesAutoresizingMaskIntoConstraints = false, + PlaceholderString = "Filter" + }; + this.filter.Changed += OnFilterChanged; + + AddSubview (this.filter); + + 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), + 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), + 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) + }); + } + + public override void OnViewModelChanged (TypeSelectorViewModel oldModel) + { + base.OnViewModelChanged (oldModel); + + this.outlineView.DataSource = new TypeSelectorDataSource (ViewModel); + OnPropertyChanged (ViewModel, new PropertyChangedEventArgs (null)); + } + + public override void OnPropertyChanged (object sender, PropertyChangedEventArgs e) + { + base.OnPropertyChanged (sender, e); + + switch (e.PropertyName) { + case nameof (TypeSelectorViewModel.Types): + Reload (); + break; + case nameof (TypeSelectorViewModel.FilterText): + default: + UpdateFilter (); + break; + } + } + + public override void KeyDown (NSEvent theEvent) + { + if (theEvent.KeyCode == 76 || theEvent.KeyCode == 36) { + if (this.outlineView.SelectedRow >= 0) { + var facade = (NSObjectFacade)this.outlineView.ItemAtRow (this.outlineView.SelectedRow); + if (facade.Target is ITypeInfo) + OnActivatedItem (); + } else { + OnActivatedItem (); + } + } + + base.KeyDown (theEvent); + } + + private readonly NSOutlineView outlineView; + private readonly NSTextField filter; + private readonly NSButton checkbox; + + private void Reload() + { + this.outlineView.ReloadData (); + + if (!String.IsNullOrWhiteSpace (ViewModel?.FilterText)) { + for (int i = 0; i < this.outlineView.RowCount; i++) { + this.outlineView.ExpandItem (this.outlineView.ItemAtRow (i)); + } + } + } + + [Export ("onActivatedItem")] + private void OnActivatedItem() + { + if (this.outlineView.SelectedRow >= 0) { + var facade = (NSObjectFacade)this.outlineView.ItemAtRow (this.outlineView.SelectedRow); + ViewModel.SelectedType = facade.Target as ITypeInfo; + } else { + ViewModel.SelectedType = null; + } + } + + private void OnFilterChanged (object sender, EventArgs e) + { + if (ViewModel == null) + return; + + ViewModel.FilterText = this.filter.StringValue; + Reload (); + } + + private void UpdateFilter() + { + this.filter.StringValue = ViewModel?.FilterText ?? String.Empty; + } + + private void OnCheckedChanged () + { + ViewModel.ShowAllAssemblies = this.checkbox.State == NSCellStateValue.On; + Reload (); + } + + private class TypeSelectorDataSource + : NSOutlineViewDataSource + { + public TypeSelectorDataSource (TypeSelectorViewModel viewModel) + { + this.viewModel = viewModel; + } + + public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item) + { + if (item == null) { + return this.viewModel.Types?.Count ?? 0; + } else if (((NSObjectFacade)item).Target is KeyValuePair<string, SimpleCollectionView> kvp) { + return kvp.Value.Count; + } + + return base.GetChildrenCount (outlineView, item); + } + + public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item) + { + if (item == null) { + return new NSObjectFacade (this.viewModel.Types[(int)childIndex]); + } else if (((NSObjectFacade)item).Target is KeyValuePair<string, SimpleCollectionView> kvp) { + return new NSObjectFacade (kvp.Value[(int)childIndex]); + } + + return base.GetChild (outlineView, childIndex, item); + } + + public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) + { + return !(((NSObjectFacade)item).Target is ITypeInfo); + } + + private TypeSelectorViewModel viewModel; + } + + private class TypeSelectorDelegate + : NSOutlineViewDelegate + { + + public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item) + { + var label = (UnfocusableTextField)outlineView.MakeView (LabelId, outlineView); + if (label == null) { + label = new UnfocusableTextField { + Identifier = LabelId + }; + } + + string text = String.Empty; + var facade = (NSObjectFacade)item; + if (facade.Target is KeyValuePair<string, SimpleCollectionView> kvp) + text = kvp.Key; + else if (facade.Target is ITypeInfo type) + text = type.Name; + + label.StringValue = text; + return label; + } + + private const string LabelId = "label"; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorWindow.cs b/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorWindow.cs new file mode 100644 index 0000000..de4da3c --- /dev/null +++ b/Xamarin.PropertyEditing.Mac/Controls/TypeSelectorWindow.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using AppKit; +using CoreGraphics; + +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Mac +{ + internal class TypeSelectorWindow + : NSWindow + { + public TypeSelectorWindow (TypeSelectorViewModel viewModel) + : base (new CGRect (0, 0, 300, 300), NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Resizable, NSBackingStore.Buffered, true) + { + Title = Properties.Resources.SelectObjectTitle; + + this.selector = new TypeSelectorControl { + ViewModel = viewModel, + TranslatesAutoresizingMaskIntoConstraints = false + }; + + ContentView.AddSubview (this.selector); + + this.ok = NSButton.CreateButton (Properties.Resources.OK, OnOked); + this.ok.TranslatesAutoresizingMaskIntoConstraints = false; + ContentView.AddSubview (this.ok); + + this.cancel = NSButton.CreateButton (Properties.Resources.Cancel, OnCanceled); + this.cancel.TranslatesAutoresizingMaskIntoConstraints = false; + ContentView.AddSubview (this.cancel); + + ContentView.AddConstraints (new[] { + NSLayoutConstraint.Create (this.selector, NSLayoutAttribute.Width, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Width, 1, -20), + NSLayoutConstraint.Create (this.selector, NSLayoutAttribute.Top, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Top, 1, 0), + NSLayoutConstraint.Create (this.selector, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.CenterX, 1, 0), + NSLayoutConstraint.Create (this.selector, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this.ok, NSLayoutAttribute.Top, 1, -10), + + NSLayoutConstraint.Create (this.ok, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Bottom, 1, -10), + NSLayoutConstraint.Create (this.ok, NSLayoutAttribute.Right, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Right, 1, -10), + + NSLayoutConstraint.Create (this.cancel, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.ok, NSLayoutAttribute.Left, 1, -10), + NSLayoutConstraint.Create (this.cancel, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Bottom, 1, -10), + }); + } + + private TypeSelectorControl selector; + private NSButton ok, cancel; + + private void OnOked () + { + NSApplication.SharedApplication.StopModalWithCode ((int)NSModalResponse.OK); + Close (); + } + + private void OnCanceled () + { + NSApplication.SharedApplication.StopModalWithCode ((int)NSModalResponse.Cancel); + Close (); + } + + public static ITypeInfo RequestType (AsyncValue<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> assignableTypes) + { + var w = new TypeSelectorWindow (new TypeSelectorViewModel (assignableTypes)); + + var result = (NSModalResponse)(int)NSApplication.SharedApplication.RunModalForWindow (w); + if (result != NSModalResponse.OK) + return null; + + return w.selector.ViewModel.SelectedType; + } + } +} diff --git a/Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs b/Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs index 1b6d71a..1494549 100644 --- a/Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs +++ b/Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs @@ -175,17 +175,16 @@ namespace Xamarin.PropertyEditing.Mac ((NSView)this.header.ContentView).AddSubview (this.tabStack); this.propertyTable = new FirstResponderOutlineView { + IndentationPerLevel = 0, RefusesFirstResponder = true, SelectionHighlightStyle = NSTableViewSelectionHighlightStyle.None, HeaderView = null, + IntercellSpacing = new CGSize (0, 0) }; var propertyEditors = new NSTableColumn (PropertyEditorColId); this.propertyTable.AddColumn (propertyEditors); - // Set OutlineTableColumn or the arrows showing children/expansion will not be drawn - this.propertyTable.OutlineTableColumn = propertyEditors; - var tableContainer = new NSScrollView { TranslatesAutoresizingMaskIntoConstraints = false, }; @@ -278,18 +277,6 @@ namespace Xamarin.PropertyEditing.Mac { return true; } - - public override CGRect GetCellFrame (nint column, nint row) - { - var super = base.GetCellFrame (column, row); - if (column == 0) { - var obj = (NSObjectFacade)ItemAtRow (row); - if (obj.Target is PropertyGroupViewModel) - return new CGRect (0, super.Top, super.Right - (super.Left / 2), super.Height); - } - - return super; - } } } }
\ No newline at end of file diff --git a/Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs b/Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs index d3a7398..cb96191 100644 --- a/Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs +++ b/Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs @@ -54,6 +54,8 @@ namespace Xamarin.PropertyEditing.Mac {typeof (RatioViewModel), typeof (RatioEditorControl<CommonRatio>)}, {typeof (ThicknessPropertyViewModel), typeof (CommonThicknessEditorControl) }, {typeof (PropertyGroupViewModel), typeof (GroupEditorControl)}, + {typeof (ObjectPropertyViewModel), typeof (ObjectEditorControl)}, + }; } } diff --git a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs index da40034..2c0e403 100644 --- a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs +++ b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using AppKit; using Foundation; @@ -27,8 +28,11 @@ namespace Xamarin.PropertyEditing.Mac if (this.vm.ArrangedEditors.Count == 0) return 0; - var childCount = 0; + var facade = (NSObjectFacade)item; + if (facade?.Target is ObjectPropertyViewModel ovm) + return ovm.ValueModel.Properties.Count; + var childCount = 0; if (this.vm.ArrangeMode == PropertyArrangeMode.Name) childCount = Filtering ? this.vm.ArrangedEditors[0].Editors.Count : this.vm.ArrangedEditors[0].Editors.Count + 1; else { @@ -47,17 +51,20 @@ namespace Xamarin.PropertyEditing.Mac { object element; + var f = ((NSObjectFacade)item); // We only want the Header to appear at the top of both Category and Name Modes, which means item is null in both. if (childIndex == 0 && item == null && !Filtering) element = null; - else { + else if (f?.Target is ObjectPropertyViewModel ovm) { + element = ovm.ValueModel.Properties[(int)childIndex]; + } else { if (this.vm.ArrangeMode == PropertyArrangeMode.Name) element = Filtering ? this.vm.ArrangedEditors[0].Editors[(int)childIndex] : this.vm.ArrangedEditors[0].Editors[(int)childIndex - 1]; else { if (item == null) element = Filtering ? this.vm.ArrangedEditors[(int)childIndex] : this.vm.ArrangedEditors[(int)childIndex - 1]; else { - var group = (PanelGroupViewModel)((NSObjectFacade)item).Target; + var group = (PanelGroupViewModel)f.Target; var list = group.Editors; if (childIndex >= list.Count) { childIndex -= list.Count; @@ -74,10 +81,25 @@ namespace Xamarin.PropertyEditing.Mac public override bool ItemExpandable (NSOutlineView outlineView, NSObject item) { + var f = (NSObjectFacade)item; + if (f.Target is ObjectPropertyViewModel ovm) { + PropertyChangedEventHandler changed = null; + changed = (o, e) => { + if (e.PropertyName != nameof (ObjectPropertyViewModel.CanDelve)) + return; + + ovm.PropertyChanged -= changed; + outlineView.ReloadItem (item); + }; + ovm.PropertyChanged += changed; + + return ovm.CanDelve; + } + if (this.vm.ArrangeMode == PropertyArrangeMode.Name) return false; - return ((NSObjectFacade)item).Target is PanelGroupViewModel; + return f.Target is PanelGroupViewModel; } public NSObject GetFacade (object element) diff --git a/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs b/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs index 92e4382..bce86c0 100644 --- a/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs +++ b/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs @@ -53,14 +53,30 @@ namespace Xamarin.PropertyEditing.Mac GetVMGroupCellItendifiterFromFacade (item, out evm, out group, out cellIdentifier); if (group != null) { - var labelContainer = (UnfocusableTextField)outlineView.MakeView (LabelIdentifier, this); + var labelContainer = (NSView)outlineView.MakeView (CategoryIdentifier, this); if (labelContainer == null) { - labelContainer = new UnfocusableTextField { - Identifier = LabelIdentifier, + labelContainer = new NSView { + Identifier = CategoryIdentifier, }; + + var disclosure = outlineView.MakeView ("NSOutlineViewDisclosureButtonKey", outlineView); + disclosure.TranslatesAutoresizingMaskIntoConstraints = false; + labelContainer.AddSubview (disclosure); + + var label = new UnfocusableTextField { + TranslatesAutoresizingMaskIntoConstraints = false + }; + labelContainer.AddSubview (label); + + labelContainer.AddConstraints (new[] { + NSLayoutConstraint.Create (disclosure, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, labelContainer, NSLayoutAttribute.CenterY, 1, 0), + NSLayoutConstraint.Create (disclosure, NSLayoutAttribute.Left, NSLayoutRelation.Equal, labelContainer, NSLayoutAttribute.Left, 1, 4), + NSLayoutConstraint.Create (label, NSLayoutAttribute.Left, NSLayoutRelation.Equal, disclosure, NSLayoutAttribute.Right, 1, 0), + NSLayoutConstraint.Create (label, NSLayoutAttribute.Height, NSLayoutRelation.Equal, labelContainer, NSLayoutAttribute.Height, 1, 0), + }); } - labelContainer.StringValue = group.Category; + ((UnfocusableTextField)labelContainer.Subviews[1]).StringValue = group.Category; if (this.dataSource.DataContext.GetIsExpanded (group.Category)) { SynchronizationContext.Current.Post (s => { @@ -74,14 +90,14 @@ namespace Xamarin.PropertyEditing.Mac NSView editorOrContainer = null; if (this.firstCache.TryGetValue (cellIdentifier, out IEditorView editor)) { this.firstCache.Remove (cellIdentifier); - editorOrContainer = (editor.NativeView is PropertyEditorControl) ? new EditorContainer (this.hostResources, editor) : editor.NativeView; + editorOrContainer = (editor.NativeView is PropertyEditorControl) ? new EditorContainer (this.hostResources, editor) { Identifier = cellIdentifier } : editor.NativeView; } else { editorOrContainer = GetEditor (cellIdentifier, evm, outlineView); editor = ((editorOrContainer as EditorContainer)?.EditorView) ?? editorOrContainer as IEditorView; } if (editorOrContainer is EditorContainer ec) { - ec.Label = evm.Name + ":"; + ec.Label = evm.Name; #if DEBUG // Currently only used to highlight which controls haven't been implemented if (editor == null) @@ -90,15 +106,29 @@ namespace Xamarin.PropertyEditing.Mac } if (editor != null) { - nint index = outlineView.RowForItem (item); - if (editor.NativeView is PropertyEditorControl pec) { - pec.TableRow = index; + editor.ViewModel = evm; + + var ovm = evm as ObjectPropertyViewModel; + if (ovm != null && editorOrContainer is EditorContainer container) { + if (container.LeftEdgeView == null) { + if (ovm.CanDelve) + container.LeftEdgeView = outlineView.MakeView ("NSOutlineViewDisclosureButtonKey", outlineView); + } else if (!ovm.CanDelve) { + container.LeftEdgeView = null; + } } - editor.ViewModel = evm; + bool openObjectRow = ovm != null && outlineView.IsItemExpanded (item); + if (!openObjectRow) { + var parent = outlineView.GetParent (item); + openObjectRow = (parent != null && ((NSObjectFacade)parent).Target is ObjectPropertyViewModel); + } + + SetRowValueBackground (editorOrContainer, openObjectRow); // Force a row update due to new height, but only when we are non-default if (editor.IsDynamicallySized) { + nint index = outlineView.RowForItem (item); outlineView.NoteHeightOfRowsWithIndexesChanged (new NSIndexSet (index)); } } else if (editorOrContainer is PanelHeaderEditorControl header) { @@ -115,24 +145,46 @@ namespace Xamarin.PropertyEditing.Mac public override void ItemDidExpand (NSNotification notification) { - if (this.isExpanding) + NSObjectFacade facade = notification.UserInfo.Values[0] as NSObjectFacade; + var outline = (NSOutlineView)notification.Object; + nint row = outline.RowForItem (facade); + + if (this.isExpanding) { + NSView view = outline.GetView (0, row, makeIfNecessary: true); + if (view.Subviews[0] is NSButton expander) + expander.State = NSCellStateValue.On; + return; + } - NSObjectFacade facade = notification.UserInfo.Values[0] as NSObjectFacade; - var group = facade.Target as PanelGroupViewModel; - if (group != null) + if (facade.Target is PanelGroupViewModel group) this.dataSource.DataContext.SetIsExpanded (group.Category, isExpanded: true); + else if (facade.Target is ObjectPropertyViewModel ovm) { + NSView view = outline.GetView (0, row, makeIfNecessary: false); + SetRowValueBackground (view, valueBackground: true); + } } public override void ItemDidCollapse (NSNotification notification) { - if (this.isExpanding) + NSObjectFacade facade = notification.UserInfo.Values[0] as NSObjectFacade; + var outline = (NSOutlineView)notification.Object; + nint row = outline.RowForItem (facade); + + if (this.isExpanding) { + NSView view = outline.GetView (0, row, makeIfNecessary: true); + if (view.Subviews[0] is NSButton expander) + expander.State = NSCellStateValue.Off; + return; + } - NSObjectFacade facade = notification.UserInfo.Values[0] as NSObjectFacade; - var group = facade.Target as PanelGroupViewModel; - if (group != null) + if (facade.Target is PanelGroupViewModel group) this.dataSource.DataContext.SetIsExpanded (group.Category, isExpanded: false); + else if (facade.Target is ObjectPropertyViewModel ovm) { + NSView view = outline.GetView (0, row, makeIfNecessary: false); + SetRowValueBackground (view, valueBackground: false); + } } public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item) @@ -187,7 +239,7 @@ namespace Xamarin.PropertyEditing.Mac } } - public const string LabelIdentifier = "label"; + public const string CategoryIdentifier = "label"; private PropertyTableDataSource dataSource; private bool isExpanding; @@ -197,6 +249,25 @@ namespace Xamarin.PropertyEditing.Mac private readonly Dictionary<string, EditorRegistration> registrations = new Dictionary<string, EditorRegistration> (); private readonly Dictionary<string, IEditorView> firstCache = new Dictionary<string, IEditorView> (); + private NSView GetViewForItem (NSOutlineView outline, NSObjectFacade facade) + { + nint row = outline.RowForItem (facade); + return outline.GetView (0, row, false); + } + + private void SetRowValueBackground (NSView view, bool valueBackground) + { + if (view == null) + return; + + if (valueBackground) { + var c = this.hostResources.GetNamedColor (NamedResources.ValueBlockBackgroundColor); + view.SetValueForKey (c, new NSString ("backgroundColor")); + } else { + view.SetValueForKey (NSColor.Clear, new NSString ("backgroundColor")); + } + } + private NSView GetEditor (string identifier, EditorViewModel vm, NSOutlineView outlineView) { var view = outlineView.MakeView (identifier, this); diff --git a/Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj b/Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj index 8277ff5..ef68417 100644 --- a/Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj +++ b/Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj @@ -145,6 +145,10 @@ <Compile Include="Controls\Custom\DynamicFillBox.cs" />
<Compile Include="Controls\Custom\TabButton.cs" />
<Compile Include="Controls\Custom\FocusableComboBox.cs" />
+ <Compile Include="Controls\ObjectEditorControl.cs" />
+ <Compile Include="Controls\TypeSelectorControl.cs" />
+ <Compile Include="Controls\TypeSelectorWindow.cs" />
+ <Compile Include="Controls\Custom\PropertyTextField.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controls\" />
|