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

Commit 188bdd4

Browse files
authored
Merge pull request #541 from xamarin/ermau-type-selector-editor
Type selector editor
2 parents 74cd11c + af9ca3b commit 188bdd4

File tree

18 files changed

+390
-54
lines changed

18 files changed

+390
-54
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
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 static class ControlExtensions
12+
{
13+
public static Task<ITypeInfo> RequestAt (this TypeRequestedEventArgs self, IHostResourceProvider hostResources, NSView source, AsyncValue<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> assignableTypes)
14+
{
15+
var tcs = new TaskCompletionSource<ITypeInfo> ();
16+
17+
var vm = new TypeSelectorViewModel (assignableTypes);
18+
var selector = new TypeSelectorControl {
19+
ViewModel = vm,
20+
Appearance = source.EffectiveAppearance
21+
};
22+
23+
vm.PropertyChanged += (vms, ve) => {
24+
if (ve.PropertyName == nameof (TypeSelectorViewModel.SelectedType)) {
25+
tcs.TrySetResult (vm.SelectedType);
26+
}
27+
};
28+
29+
var popover = new NSPopover {
30+
Behavior = NSPopoverBehavior.Transient,
31+
Delegate = new PopoverDelegate<ITypeInfo> (tcs),
32+
ContentViewController = new NSViewController {
33+
View = selector,
34+
PreferredContentSize = new CoreGraphics.CGSize (360, 335)
35+
},
36+
};
37+
popover.SetAppearance (hostResources.GetVibrantAppearance (source.EffectiveAppearance));
38+
39+
tcs.Task.ContinueWith (t => {
40+
popover.PerformClose (popover);
41+
popover.Dispose ();
42+
}, TaskScheduler.FromCurrentSynchronizationContext ());
43+
44+
popover.Show (source.Frame, source.Superview, NSRectEdge.MinYEdge);
45+
return tcs.Task;
46+
}
47+
48+
private class PopoverDelegate<T>
49+
: NSPopoverDelegate
50+
{
51+
public PopoverDelegate (TaskCompletionSource<T> tcs)
52+
{
53+
this.tcs = tcs;
54+
}
55+
56+
public override void WillClose (NSNotification notification)
57+
{
58+
this.tcs.TrySetCanceled ();
59+
}
60+
61+
private readonly TaskCompletionSource<T> tcs;
62+
}
63+
}
64+
}

Xamarin.PropertyEditing.Mac/Controls/ObjectEditorControl.cs

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -110,38 +110,7 @@ private void OnCreateInstanceExecutableChanged (object sender, EventArgs e)
110110

111111
private void OnTypeRequested (object sender, TypeRequestedEventArgs e)
112112
{
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);
113+
e.SelectedType = e.RequestAt (HostResources, this.createObject, ViewModel.AssignableTypes);
145114
}
146115

147116
private void UpdateTypeLabel ()
@@ -164,21 +133,5 @@ private void OnNewPressed (object sender, EventArgs e)
164133
{
165134
ViewModel.CreateInstanceCommand.Execute (null);
166135
}
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-
}
183136
}
184137
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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 TypeEditorControl
12+
: PropertyEditorControl<TypePropertyViewModel>
13+
{
14+
public TypeEditorControl (IHostResourceProvider hostResources)
15+
: base (hostResources)
16+
{
17+
this.typeLabel = new UnfocusableTextField {
18+
TranslatesAutoresizingMaskIntoConstraints = false
19+
};
20+
AddSubview (this.typeLabel);
21+
22+
this.selectType = new NSButton {
23+
Title = Properties.Resources.Select,
24+
TranslatesAutoresizingMaskIntoConstraints = false,
25+
BezelStyle = NSBezelStyle.Rounded
26+
};
27+
this.selectType.Activated += OnSelectPressed;
28+
AddSubview (this.selectType);
29+
30+
this.buttonConstraint = NSLayoutConstraint.Create (this.selectType, 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.selectType, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this, NSLayoutAttribute.Leading, 1, 0).WithPriority (NSLayoutPriority.DefaultLow),
38+
NSLayoutConstraint.Create (this.selectType, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this, NSLayoutAttribute.CenterY, 1f, 0f),
39+
NSLayoutConstraint.Create (this.selectType, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1f, 70f),
40+
});
41+
}
42+
43+
public override NSView FirstKeyView => this.selectType;
44+
45+
public override NSView LastKeyView => this.selectType;
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.selectType.Enabled = ViewModel.Property.CanWrite;
62+
}
63+
64+
protected override void UpdateAccessibilityValues ()
65+
{
66+
this.selectType.AccessibilityTitle = String.Format (Properties.Resources.SelectTypeForProperty, ViewModel.Property.Name);
67+
}
68+
69+
protected override void OnViewModelChanged (PropertyViewModel oldModel)
70+
{
71+
base.OnViewModelChanged (oldModel);
72+
73+
if (oldModel is TypePropertyViewModel tvm) {
74+
tvm.TypeRequested -= OnTypeRequested;
75+
}
76+
77+
if (ViewModel != null) {
78+
ViewModel.TypeRequested += OnTypeRequested;
79+
80+
OnPropertyChanged (ViewModel, new PropertyChangedEventArgs (null));
81+
}
82+
}
83+
84+
protected override void OnPropertyChanged (object sender, PropertyChangedEventArgs e)
85+
{
86+
switch (e.PropertyName) {
87+
case nameof (TypePropertyViewModel.Value):
88+
UpdateTypeLabel ();
89+
break;
90+
case null:
91+
case "":
92+
UpdateTypeLabel ();
93+
UpdateCreateInstanceCommand ();
94+
break;
95+
}
96+
97+
base.OnPropertyChanged (sender, e);
98+
}
99+
100+
private readonly UnfocusableTextField typeLabel;
101+
private readonly NSButton selectType;
102+
private readonly NSLayoutConstraint buttonConstraint;
103+
104+
private void OnCreateInstanceExecutableChanged (object sender, EventArgs e)
105+
{
106+
UpdateCreateInstanceCommand ();
107+
}
108+
109+
private void OnTypeRequested (object sender, TypeRequestedEventArgs e)
110+
{
111+
e.SelectedType = e.RequestAt (HostResources, this.selectType, ViewModel.AssignableTypes);
112+
}
113+
114+
private void UpdateTypeLabel ()
115+
{
116+
if (ViewModel.Value == null) {
117+
this.typeLabel.StringValue = String.Empty;
118+
this.buttonConstraint.Active = false;
119+
} else {
120+
this.typeLabel.StringValue = $"({ViewModel.Value.Name})";
121+
this.buttonConstraint.Active = true;
122+
}
123+
}
124+
125+
private void UpdateCreateInstanceCommand ()
126+
{
127+
this.selectType.Enabled = ViewModel.SelectTypeCommand.CanExecute (null);
128+
}
129+
130+
private void OnSelectPressed (object sender, EventArgs e)
131+
{
132+
ViewModel.SelectTypeCommand.Execute (null);
133+
}
134+
135+
private class PopoverDelegate<T>
136+
: NSPopoverDelegate
137+
{
138+
public PopoverDelegate (TaskCompletionSource<T> tcs)
139+
{
140+
this.tcs = tcs;
141+
}
142+
143+
public override void WillClose (NSNotification notification)
144+
{
145+
this.tcs.TrySetCanceled ();
146+
}
147+
148+
private readonly TaskCompletionSource<T> tcs;
149+
}
150+
}
151+
}

Xamarin.PropertyEditing.Mac/PropertyEditorSelector.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public virtual IEditorView GetEditor (IHostResourceProvider hostResources, Edito
5555
{typeof (ThicknessPropertyViewModel), typeof (CommonThicknessEditorControl) },
5656
{typeof (PropertyGroupViewModel), typeof (GroupEditorControl)},
5757
{typeof (ObjectPropertyViewModel), typeof (ObjectEditorControl)},
58+
{typeof (TypePropertyViewModel), typeof (TypeEditorControl)},
5859

5960
};
6061
}

Xamarin.PropertyEditing.Mac/Xamarin.PropertyEditing.Mac.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@
149149
<Compile Include="Controls\TypeSelectorControl.cs" />
150150
<Compile Include="Controls\TypeSelectorWindow.cs" />
151151
<Compile Include="Controls\Custom\PropertyTextField.cs" />
152+
<Compile Include="Controls\ControlExtensions.cs" />
153+
<Compile Include="Controls\TypeEditorControl.cs" />
152154
</ItemGroup>
153155
<ItemGroup>
154156
<Folder Include="Controls\" />

Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public void AddProperty<T> (string name, string category = null,
1515
bool canWrite = true, bool flag = false,
1616
IEnumerable<Type> converterTypes = null,
1717
string description = null, bool constrained = true, ValueSources valueSources = ValueSources.Local | ValueSources.Default | ValueSources.Binding,
18-
IReadOnlyList<InputMode> inputModes = null, PropertyVariationOption[] options = null, bool isUncommon = false)
18+
IReadOnlyList<InputMode> inputModes = null, PropertyVariationOption[] options = null, bool isUncommon = false, ITypeInfo realType = null)
1919
{
2020
IPropertyInfo propertyInfo;
2121
if (typeof(T).IsEnum) {
@@ -26,7 +26,7 @@ public void AddProperty<T> (string name, string category = null,
2626
} else if (inputModes != null) {
2727
propertyInfo = new MockPropertyInfoWithInputTypes<T> (name, inputModes, description, category, canWrite, converterTypes, valueSources, options);
2828
} else {
29-
propertyInfo = new MockPropertyInfo<T> (name, description, category, canWrite, converterTypes, valueSources, options, isUncommon);
29+
propertyInfo = new MockPropertyInfo<T> (name, description, category, canWrite, converterTypes, valueSources, options, isUncommon, realType);
3030
}
3131

3232
AddProperty<T> (propertyInfo);

Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public MockSampleControl ()
3737
AddProperty<CommonThickness> ("Thickness", ReadWrite);
3838
AddProperty<object> ("Object", ReadWrite);
3939
AddProperty<IList> ("Collection", ReadWrite);
40+
AddProperty<ITypeInfo> ("Type", ReadWrite, realType: typeof(Type).ToTypeInfo());
4041

4142
AddReadOnlyProperty<bool> ("ReadOnlyBoolean", ReadOnly);
4243
AddReadOnlyProperty<int> ("ReadOnlyInteger", ReadOnly);

Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public IReadOnlyList<InputMode> InputModes
2323

2424
public class MockPropertyInfo<T> : IPropertyInfo, IPropertyConverter, IEquatable<MockPropertyInfo<T>>
2525
{
26-
public MockPropertyInfo (string name, string description = null, string category = null, bool canWrite = true, IEnumerable<Type> converterTypes = null, ValueSources valueSources = ValueSources.Local | ValueSources.Default, PropertyVariationOption[] options = null, bool isUncommon = false)
26+
public MockPropertyInfo (string name, string description = null, string category = null, bool canWrite = true, IEnumerable<Type> converterTypes = null, ValueSources valueSources = ValueSources.Local | ValueSources.Default, PropertyVariationOption[] options = null, bool isUncommon = false, ITypeInfo realType = null)
2727
{
2828
Name = name;
2929
Description = description;
@@ -43,13 +43,18 @@ public MockPropertyInfo (string name, string description = null, string category
4343
}
4444

4545
Variations = options ?? EmptyVariationOptions;
46+
RealType = realType ?? typeof (T).ToTypeInfo ();
4647
}
4748

4849
public string Name { get; }
4950
public string Description { get; }
5051
public virtual Type Type => typeof (T);
5152

52-
public virtual ITypeInfo RealType => typeof(T).ToTypeInfo ();
53+
public ITypeInfo RealType
54+
{
55+
get;
56+
private set;
57+
}
5358

5459
public string Category { get; }
5560
public bool CanWrite { get; }

Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ private bool TryGetTemplate (Type type, out DataTemplate template)
108108
{ typeof(BrushPropertyViewModel), typeof(BrushEditorControl) },
109109
{ typeof(PropertyGroupViewModel), typeof(GroupEditorControl) },
110110
{ typeof(ObjectPropertyViewModel), typeof(ObjectEditorControl) },
111+
{ typeof(TypePropertyViewModel), typeof(TypeEditorControl) },
111112
{ typeof(CollectionPropertyViewModel), typeof(CollectionEditor) },
112113
{ typeof(RatioViewModel), typeof(RatioEditorControl) },
113114
};

Xamarin.PropertyEditing.Windows/Themes/Resources.xaml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,25 @@
417417
</Grid.ColumnDefinitions>
418418

419419
<TextBlock Text="{Binding ValueType.Name,StringFormat=({0})}" Grid.Column="0" VerticalAlignment="Center" />
420-
<Button AutomationProperties.Name="{Binding Property.Name,Mode=OneTime,StringFormat={x:Static prop:Resources.NewInstanceForProperty}}" MinHeight="20" MinWidth="40" Grid.Column="1" HorizontalAlignment="Right" Content="{x:Static prop:Resources.New}" Command="{Binding CreateInstanceCommand}" />
420+
<Button AutomationProperties.Name="{Binding Property.Name,Mode=OneTime,StringFormat={x:Static prop:Resources.NewInstanceForProperty}}" MinHeight="20" MinWidth="40" Grid.Column="1" HorizontalAlignment="Right" Content="{x:Static prop:Resources.New}" Command="{Binding CreateInstanceCommand,Mode=OneTime}" />
421+
</Grid>
422+
</ControlTemplate>
423+
</Setter.Value>
424+
</Setter>
425+
</Style>
426+
427+
<Style TargetType="local:TypeEditorControl">
428+
<Setter Property="Template">
429+
<Setter.Value>
430+
<ControlTemplate TargetType="local:TypeEditorControl">
431+
<Grid>
432+
<Grid.ColumnDefinitions>
433+
<ColumnDefinition Width="*" />
434+
<ColumnDefinition Width="Auto" />
435+
</Grid.ColumnDefinitions>
436+
437+
<TextBlock Text="{Binding Value.Name,StringFormat=({0})}" Grid.Column="0" VerticalAlignment="Center" />
438+
<Button AutomationProperties.Name="{Binding Property.Name,Mode=OneTime,StringFormat={x:Static prop:Resources.SelectTypeForProperty}}" MinHeight="20" MinWidth="40" Grid.Column="1" HorizontalAlignment="Right" Content="{x:Static prop:Resources.Select}" Command="{Binding SelectTypeCommand,Mode=OneTime}" />
421439
</Grid>
422440
</ControlTemplate>
423441
</Setter.Value>

0 commit comments

Comments
 (0)