Skip to content
This repository was archived by the owner on Sep 25, 2024. It is now read-only.

Commit f3fb78d

Browse files
authored
Merge pull request #520 from xamarin/ermau-macobjecteditor3
Mac object editor
2 parents 936a723 + 355df8e commit f3fb78d

21 files changed

+712
-96
lines changed

Xamarin.PropertyEditing.Mac/Controls/CombinablePropertyEditor.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ protected override void UpdateErrorsDisplayed (IEnumerable errors)
5858

5959
protected override void OnViewModelChanged (PropertyViewModel oldModel)
6060
{
61+
base.OnViewModelChanged (oldModel);
62+
63+
if (ViewModel == null)
64+
return;
65+
6166
nint rowHeight = GetHeight (ViewModel);
6267

6368
float top = checkHeight;
@@ -99,9 +104,6 @@ protected override void OnViewModelChanged (PropertyViewModel oldModel)
99104
// Set our tabable order
100105
this.firstKeyView = this.combinableList.KeyAt (0);
101106
this.lastKeyView = this.combinableList.KeyAt (this.combinableList.Count - 1);
102-
103-
base.OnViewModelChanged (oldModel);
104-
105107
}
106108

107109
protected override void UpdateValue ()

Xamarin.PropertyEditing.Mac/Controls/Custom/BrushTabViewController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public override void OnViewModelChanged (BrushPropertyViewModel oldModel)
5858
this.inhibitSelection = true;
5959
base.OnViewModelChanged (oldModel);
6060

61-
var existing = new HashSet<CommonBrushType> (ViewModel?.BrushTypes?.Values ?? Array.Empty<CommonBrushType> ());
61+
var existing = new HashSet<CommonBrushType> (oldModel?.BrushTypes?.Values ?? Array.Empty<CommonBrushType> ());
6262
existing.IntersectWith (this.brushTypeTable.Keys);
6363

6464
var removed = new HashSet<CommonBrushType> (this.brushTypeTable.Keys);

Xamarin.PropertyEditing.Mac/Controls/Custom/PropertyButton.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ internal PropertyViewModel ViewModel
1818
{
1919
get { return viewModel; }
2020
set {
21-
if (viewModel != null) {
22-
viewModel.PropertyChanged -= OnPropertyChanged;
21+
if (this.viewModel != null) {
22+
this.viewModel.PropertyChanged -= OnPropertyChanged;
2323
}
2424

25-
viewModel = value;
26-
viewModel.PropertyChanged += OnPropertyChanged;
27-
28-
ValueSourceChanged (viewModel.ValueSource);
25+
this.viewModel = value;
26+
if (this.viewModel != null) {
27+
this.viewModel.PropertyChanged += OnPropertyChanged;
28+
ValueSourceChanged (this.viewModel.ValueSource);
29+
}
2930
}
3031
}
3132

Xamarin.PropertyEditing.Mac/Controls/EditorContainer.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,42 @@ public string Label {
4343
set { this.label.StringValue = value; }
4444
}
4545

46+
public NSView LeftEdgeView
47+
{
48+
get { return this.leftEdgeView; }
49+
set
50+
{
51+
if (this.leftEdgeView != null) {
52+
this.leftEdgeView.RemoveFromSuperview ();
53+
RemoveConstraints (new[] { this.leftEdgeLeftConstraint, this.leftEdgeVCenterConstraint });
54+
this.leftEdgeLeftConstraint.Dispose ();
55+
this.leftEdgeLeftConstraint = null;
56+
this.leftEdgeVCenterConstraint.Dispose ();
57+
this.leftEdgeVCenterConstraint = null;
58+
}
59+
60+
this.leftEdgeView = value;
61+
62+
if (value != null) {
63+
AddSubview (value);
64+
65+
value.TranslatesAutoresizingMaskIntoConstraints = false;
66+
this.leftEdgeLeftConstraint = NSLayoutConstraint.Create (this.leftEdgeView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1, 4);
67+
this.leftEdgeVCenterConstraint = NSLayoutConstraint.Create (this.leftEdgeView, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1, 0);
68+
69+
AddConstraints (new[] { this.leftEdgeLeftConstraint, this.leftEdgeVCenterConstraint });
70+
}
71+
}
72+
}
73+
74+
public override void ViewWillMoveToSuperview (NSView newSuperview)
75+
{
76+
if (newSuperview == null && EditorView != null)
77+
EditorView.ViewModel = null;
78+
79+
base.ViewWillMoveToSuperview (newSuperview);
80+
}
81+
4682
private UnfocusableTextField label = new UnfocusableTextField {
4783
Alignment = NSTextAlignment.Right,
4884
TranslatesAutoresizingMaskIntoConstraints = false
@@ -54,6 +90,7 @@ public NSColor LabelTextColor {
5490
}
5591
#endif
5692

57-
private readonly IHostResourceProvider hostResources;
93+
private NSView leftEdgeView;
94+
private NSLayoutConstraint leftEdgeLeftConstraint, leftEdgeVCenterConstraint;
5895
}
5996
}

Xamarin.PropertyEditing.Mac/Controls/NumericEditorControl.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ protected override void UpdateAccessibilityValues ()
133133

134134
protected override void OnViewModelChanged (PropertyViewModel oldModel)
135135
{
136+
base.OnViewModelChanged (oldModel);
137+
138+
if (ViewModel == null)
139+
return;
140+
136141
if (ViewModel.HasInputModes) {
137142
if (this.inputModePopup == null) {
138143
this.inputModePopup = new NSPopUpButton {
@@ -169,8 +174,6 @@ protected override void OnViewModelChanged (PropertyViewModel oldModel)
169174
// If we are reusing the control we'll have to hid the inputMode if this doesn't have InputMode.
170175
if (this.inputModePopup != null)
171176
this.inputModePopup.Hidden = !ViewModel.HasInputModes;
172-
173-
base.OnViewModelChanged (oldModel);
174177
}
175178
}
176179
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
using System;
2+
using System.Collections;
3+
using System.ComponentModel;
4+
using System.Threading.Tasks;
5+
using AppKit;
6+
using Foundation;
7+
using Xamarin.PropertyEditing.ViewModels;
8+
9+
namespace Xamarin.PropertyEditing.Mac
10+
{
11+
internal class ObjectEditorControl
12+
: PropertyEditorControl<ObjectPropertyViewModel>
13+
{
14+
public ObjectEditorControl (IHostResourceProvider hostResources)
15+
: base (hostResources)
16+
{
17+
this.typeLabel = new UnfocusableTextField {
18+
TranslatesAutoresizingMaskIntoConstraints = false
19+
};
20+
AddSubview (this.typeLabel);
21+
22+
this.createObject = new NSButton {
23+
Title = Properties.Resources.New,
24+
TranslatesAutoresizingMaskIntoConstraints = false,
25+
BezelStyle = NSBezelStyle.Rounded
26+
};
27+
this.createObject.Activated += OnNewPressed;
28+
AddSubview (this.createObject);
29+
30+
this.buttonConstraint = NSLayoutConstraint.Create (this.createObject, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this.typeLabel, NSLayoutAttribute.Trailing, 1f, 12);
31+
32+
AddConstraints (new[] {
33+
NSLayoutConstraint.Create (this.typeLabel, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this, NSLayoutAttribute.Leading, 1f, 0f),
34+
NSLayoutConstraint.Create (this.typeLabel, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1f, 0f),
35+
NSLayoutConstraint.Create (this.typeLabel, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this, NSLayoutAttribute.Height, 1, 0),
36+
this.buttonConstraint,
37+
NSLayoutConstraint.Create (this.createObject, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this, NSLayoutAttribute.Leading, 1, 0).WithPriority (NSLayoutPriority.DefaultLow),
38+
NSLayoutConstraint.Create (this.createObject, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1f, 0f),
39+
NSLayoutConstraint.Create (this.createObject, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1f, 70f),
40+
});
41+
}
42+
43+
public override NSView FirstKeyView => this.createObject;
44+
45+
public override NSView LastKeyView => this.createObject;
46+
47+
protected override void UpdateValue ()
48+
{
49+
}
50+
51+
protected override void UpdateErrorsDisplayed (IEnumerable errors)
52+
{
53+
}
54+
55+
protected override void HandleErrorsChanged (object sender, DataErrorsChangedEventArgs e)
56+
{
57+
}
58+
59+
protected override void SetEnabled ()
60+
{
61+
this.createObject.Enabled = ViewModel.Property.CanWrite;
62+
}
63+
64+
protected override void UpdateAccessibilityValues ()
65+
{
66+
this.createObject.AccessibilityTitle = String.Format (Properties.Resources.NewInstanceForProperty, ViewModel.Property.Name);
67+
}
68+
69+
protected override void OnViewModelChanged (PropertyViewModel oldModel)
70+
{
71+
base.OnViewModelChanged (oldModel);
72+
73+
if (oldModel is ObjectPropertyViewModel ovm) {
74+
ovm.TypeRequested -= OnTypeRequested;
75+
ovm.CreateInstanceCommand.CanExecuteChanged -= OnCreateInstanceExecutableChanged;
76+
}
77+
78+
if (ViewModel != null) {
79+
ViewModel.TypeRequested += OnTypeRequested;
80+
ViewModel.CreateInstanceCommand.CanExecuteChanged += OnCreateInstanceExecutableChanged;
81+
82+
OnPropertyChanged (ViewModel, new PropertyChangedEventArgs (null));
83+
}
84+
}
85+
86+
protected override void OnPropertyChanged (object sender, PropertyChangedEventArgs e)
87+
{
88+
switch (e.PropertyName) {
89+
case nameof (ObjectPropertyViewModel.ValueType):
90+
UpdateTypeLabel ();
91+
break;
92+
case null:
93+
case "":
94+
UpdateTypeLabel ();
95+
UpdateCreateInstanceCommand ();
96+
break;
97+
}
98+
99+
base.OnPropertyChanged (sender, e);
100+
}
101+
102+
private readonly UnfocusableTextField typeLabel;
103+
private readonly NSButton createObject;
104+
private readonly NSLayoutConstraint buttonConstraint;
105+
106+
private void OnCreateInstanceExecutableChanged (object sender, EventArgs e)
107+
{
108+
UpdateCreateInstanceCommand ();
109+
}
110+
111+
private void OnTypeRequested (object sender, TypeRequestedEventArgs e)
112+
{
113+
var tcs = new TaskCompletionSource<ITypeInfo> ();
114+
e.SelectedType = tcs.Task;
115+
116+
var vm = new TypeSelectorViewModel (ViewModel.AssignableTypes);
117+
var selector = new TypeSelectorControl {
118+
ViewModel = vm,
119+
Appearance = EffectiveAppearance
120+
};
121+
122+
vm.PropertyChanged += (vms, ve) => {
123+
if (ve.PropertyName == nameof (TypeSelectorViewModel.SelectedType)) {
124+
tcs.TrySetResult (vm.SelectedType);
125+
}
126+
};
127+
128+
var popover = new NSPopover {
129+
Behavior = NSPopoverBehavior.Transient,
130+
Delegate = new PopoverDelegate<ITypeInfo> (tcs),
131+
ContentViewController = new NSViewController {
132+
View = selector,
133+
PreferredContentSize = new CoreGraphics.CGSize (360, 335)
134+
},
135+
};
136+
137+
popover.SetAppearance (HostResources.GetVibrantAppearance (EffectiveAppearance));
138+
139+
tcs.Task.ContinueWith (t => {
140+
popover.PerformClose (popover);
141+
popover.Dispose ();
142+
}, TaskScheduler.FromCurrentSynchronizationContext());
143+
144+
popover.Show (this.createObject.Frame, this, NSRectEdge.MinYEdge);
145+
}
146+
147+
private void UpdateTypeLabel ()
148+
{
149+
if (ViewModel.ValueType == null) {
150+
this.typeLabel.StringValue = String.Empty;
151+
this.buttonConstraint.Active = false;
152+
} else {
153+
this.typeLabel.StringValue = $"({ViewModel.ValueType.Name})";
154+
this.buttonConstraint.Active = true;
155+
}
156+
}
157+
158+
private void UpdateCreateInstanceCommand()
159+
{
160+
this.createObject.Enabled = ViewModel.CreateInstanceCommand.CanExecute (null);
161+
}
162+
163+
private void OnNewPressed (object sender, EventArgs e)
164+
{
165+
ViewModel.CreateInstanceCommand.Execute (null);
166+
}
167+
168+
private class PopoverDelegate<T>
169+
: NSPopoverDelegate
170+
{
171+
public PopoverDelegate (TaskCompletionSource<T> tcs)
172+
{
173+
this.tcs = tcs;
174+
}
175+
176+
public override void WillClose (NSNotification notification)
177+
{
178+
this.tcs.TrySetCanceled ();
179+
}
180+
181+
private readonly TaskCompletionSource<T> tcs;
182+
}
183+
}
184+
}

0 commit comments

Comments
 (0)