Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More Sample Option Attributes #336

Merged
merged 30 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
109ff7f
Adding SliderOption
niels9001 Dec 12, 2022
c260fae
Replacing RadioButtons with ComboBox to save space
niels9001 Dec 12, 2022
bc7899a
Adding TextOption
niels9001 Dec 12, 2022
4e18757
Change order
niels9001 Dec 12, 2022
c4774c7
Fix comment
niels9001 Dec 12, 2022
72530e7
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/…
Arlodotexe Dec 12, 2022
6a57732
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/…
Arlodotexe Dec 12, 2022
de50901
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/…
Arlodotexe Dec 12, 2022
60f29a0
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/…
Arlodotexe Dec 12, 2022
e1a8c94
Address feedback
niels9001 Dec 13, 2022
31946ab
Revert change
niels9001 Dec 13, 2022
c45d2db
Revert
niels9001 Dec 13, 2022
0d4f88f
Delete MyExperimentNameHere.sln
niels9001 Dec 13, 2022
0d0139d
XAML formatting
niels9001 Dec 13, 2022
8153711
Merge branch 'user/niels9001/more-options' of https://github.com/Comm…
niels9001 Dec 13, 2022
747f0aa
Adress feedback
niels9001 Dec 15, 2022
5ad467b
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/To…
Arlodotexe Dec 26, 2022
81b5401
Template updates
niels9001 Dec 27, 2022
2dc79b1
Fix
niels9001 Jan 2, 2023
0812e69
Consistent parameter naming
niels9001 Jan 2, 2023
bd451f7
Merge branch 'main' into user/niels9001/more-options
niels9001 Jan 20, 2023
ff489f5
Update ToolkitSampleMetadataGenerator.Sample.cs
niels9001 Jan 21, 2023
19709c2
CI fixes
niels9001 Jan 25, 2023
db13e4f
Numeric option
niels9001 Jan 25, 2023
ae176e0
Project template
niels9001 Jan 25, 2023
919d0e4
Fix tests
niels9001 Jan 31, 2023
fc82791
Test fixes
niels9001 Jan 31, 2023
ecb6693
Add failing test-case for missing Attribute Property Title
michael-hawker Feb 1, 2023
549c6a1
Fix issue with source generator not generating sample registry to tes…
michael-hawker Feb 1, 2023
4c7528c
Fix issue with extra Title property on Attribute not being setup prop…
michael-hawker Feb 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace CommunityToolkit.Labs.Core.SourceGenerators.Attributes;
public sealed class ToolkitSampleMultiChoiceOptionAttribute : ToolkitSampleOptionBaseAttribute
{
/// <summary>
/// Creates a new instance of <see cref="ToolkitSampleBoolOptionAttribute"/>.
/// Creates a new instance of <see cref="ToolkitSampleMultiChoiceOptionAttribute"/>.
/// </summary>
/// <param name="bindingName">The name of the generated property, which you can bind to in XAML.</param>
/// <param name="choices">A list of the choices to display to the user. Can be literal values, or labeled values. Use a " : " separator (single colon surrounded by at least 1 whitespace) to separate a label from a value.</param>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace CommunityToolkit.Labs.Core.SourceGenerators.Attributes;

/// <summary>
/// Represents a boolean sample option.
/// </summary>
/// <remarks>
/// Using this attribute will automatically generate an <see cref="INotifyPropertyChanged"/>-enabled property
/// that you can bind to in XAML, and displays an options pane alonside your sample which allows the user to manipulate the property.
/// <para/>
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ToolkitSampleSliderOptionAttribute : ToolkitSampleOptionBaseAttribute
{
/// <summary>
/// Creates a new instance of <see cref="ToolkitSampleSliderOptionAttribute"/>.
/// </summary>
/// <param name="bindingName">The name of the generated property, which you can bind to in XAML.</param>
/// <param name="choices">A list of the choices to display to the user. Can be literal values, or labeled values. Use a " : " separator (single colon surrounded by at least 1 whitespace) to separate a label from a value.</param>
niels9001 marked this conversation as resolved.
Show resolved Hide resolved
/// <param name="title">A title to display on top of this option.</param>
public ToolkitSampleSliderOptionAttribute(string bindingName, double initial = 0, double min = 0, double max = 10, double step = 1, string? title = null)
: base(bindingName, null, title)
{
Initial = initial;
Min = min;
Max = max;
Step = step;
}

/// <summary>
/// The default start value
Arlodotexe marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public double? Initial { get; }

/// <summary>
/// The minimal value
Arlodotexe marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public double? Min { get; }

/// <summary>
/// The maximum value
Arlodotexe marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public double? Max { get; }

/// <summary>
/// The step value
Arlodotexe marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public double? Step { get; }

/// <summary>
/// The source generator-friendly type name used for casting.
/// </summary>
internal override string TypeName { get; } = "double";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace CommunityToolkit.Labs.Core.SourceGenerators.Attributes;

/// <summary>
/// Represents a boolean sample option that the user can manipulate and the XAML can bind to.
/// </summary>
/// <remarks>
/// Using this attribute will automatically generate an <see cref="INotifyPropertyChanged"/>-enabled property
/// that you can bind to in XAML, and displays an options pane alonside your sample which allows the user to manipulate the property.
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ToolkitSampleTextOptionAttribute : ToolkitSampleOptionBaseAttribute
{
/// <summary>
/// Creates a new instance of <see cref="ToolkitSampleTextOptionAttribute"/>.
/// </summary>
/// <param name="bindingName">The name of the generated property, which you can bind to in XAML.</param>
/// <param name="defaultState">The initial value for the bound property.</param>
/// <param name="title">A title to display on top of this option.</param>
public ToolkitSampleTextOptionAttribute(string bindingName, string placeholderText = null, string? title = null)
: base(bindingName, placeholderText, title)
{
PlaceholderText = placeholderText;
}

/// <summary>
/// The source generator-friendly type name used for casting.
/// </summary>
internal override string TypeName { get; } = "string";

/// <summary>
/// A label to display along the boolean option.
niels9001 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public string PlaceholderText { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.Labs.Core.SourceGenerators.Attributes;

namespace CommunityToolkit.Labs.Core.SourceGenerators.Metadata;

/// <summary>
/// An INPC-enabled metadata container for data defined in an <see cref="ToolkitSampleSliderOptionAttribute"/>.
/// </summary>
public class ToolkitSampleSliderOptionMetadataViewModel : IGeneratedToolkitSampleOptionViewModel
{
private string? _title;
private object? _value;

/// <summary>
/// Creates a new instance of <see cref="ToolkitSampleSliderOptionMetadataViewModel"/>.
/// </summary>
public ToolkitSampleSliderOptionMetadataViewModel(string name, double initial = 0, double min = 0, double max = 10, double step = 1, string? title = null)
{
Name = name;
_title = title;
_value = initial;
Max = max;
Min = min;
Step = step;;
}

/// <inheritdoc cref="INotifyPropertyChanged.PropertyChanged"/>
public event PropertyChangedEventHandler? PropertyChanged;

/// <summary>
/// A unique identifier for this option.
/// </summary>
/// <remarks>
/// Used by the sample system to match up <see cref="ToolkitSampleSliderOptionMetadataViewModel"/> to the original <see cref="ToolkitSampleSliderOptionAttribute"/> and the control that declared it.
/// </remarks>
public string Name { get; }

/// <summary>
/// The available options presented to the user.
/// </summary>
/// <summary>
/// The initial double value.
/// </summary>
public double Initial
{
get => (double)_value;
set
{
_value = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name));
}
}

/// <inheritdoc/>
public object? Value
{
get => Initial;
set
{
Initial = (double)(value ?? 0.0);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name));
}
}

/// <summary>
/// The Minimum value of the slider.
/// </summary>
public double Min { get; }

/// <summary>
/// The Maximum value of the slider.
/// </summary>
public double Max { get; }

/// <summary>
/// The StepFrequency value of the slider.
/// </summary>
public double Step { get; }

/// <summary>
/// A label to display along the slider.
/// </summary>
public string? Title
{
get => _title;
set
{
_title = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.Labs.Core.SourceGenerators.Attributes;

namespace CommunityToolkit.Labs.Core.SourceGenerators.Metadata;

/// <summary>
/// An INPC-enabled metadata container for data defined in an <see cref="ToolkitSampleTextOptionAttribute"/>.
/// </summary>
/// <remarks>
/// Instances of these are generated by the <see cref="ToolkitSampleMetadataGenerator"/> and
/// provided to the app alongside the sample registration.
/// </remarks>
public class ToolkitSampleTextOptionMetadataViewModel : IGeneratedToolkitSampleOptionViewModel
{
private string? _title;
private object _value;

/// <summary>
/// Creates a new instance of <see cref="ToolkitSampleTextOptionAttribute"/>.
/// </summary>
public ToolkitSampleTextOptionMetadataViewModel(string id, string placeholderText = null, string? title = null)
Arlodotexe marked this conversation as resolved.
Show resolved Hide resolved
{
Name = id;
_title = title;
_value = placeholderText;
}

/// <inheritdoc cref="INotifyPropertyChanged.PropertyChanged"/>
public event PropertyChangedEventHandler? PropertyChanged;

/// <summary>
/// A unique identifier for this option.
/// </summary>
/// <remarks>
/// Used by the sample system to match up <see cref="ToolkitSampleTextOptionMetadataViewModel"/> to the original <see cref="ToolkitSampleTextOptionAttribute"/> and the control that declared it.
/// </remarks>
public string Name { get; }

/// <summary>
/// The current boolean value.
/// </summary>
/// <remarks>
/// Provided to accomodate binding to a property that is a non-nullable <see cref="bool"/>.
/// </remarks>
public string PlaceholderText
{
get => (string)_value;
set
{
_value = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name));
}
}

/// <inheritdoc/>
public object? Value
{
get => PlaceholderText;
set
{
PlaceholderText = (string)(value ?? string.Empty);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name));
}
}

/// <summary>
/// A title to display on top of the boolean option.
/// </summary>
public string? Title
{
get => _title;
set
{
_title = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ void Execute(IncrementalValuesProvider<ISymbol> types, bool skipDiagnostics = fa
if (x.Item2.TryReconstructAs<ToolkitSampleMultiChoiceOptionAttribute>() is ToolkitSampleMultiChoiceOptionAttribute multiChoiceOptionAttribute)
return (x.Item1, (ToolkitSampleOptionBaseAttribute)multiChoiceOptionAttribute);

if (x.Item2.TryReconstructAs<ToolkitSampleSliderOptionAttribute>() is ToolkitSampleSliderOptionAttribute sliderOptionAttribute)
return (x.Item1, (ToolkitSampleSliderOptionAttribute)sliderOptionAttribute);

if (x.Item2.TryReconstructAs<ToolkitSampleTextOptionAttribute>() is ToolkitSampleTextOptionAttribute textOptionAttribute)
return (x.Item1, (ToolkitSampleOptionBaseAttribute)textOptionAttribute);

return default;
})
.Where(static x => x != default)
Expand Down Expand Up @@ -287,6 +293,14 @@ private static IEnumerable<string> BuildNewGeneratedSampleOptionMetadataSource(T
{
yield return $@"new {typeof(ToolkitSampleBoolOptionMetadataViewModel).FullName}(id: ""{boolAttribute.Name}"", label: ""{boolAttribute.Label}"", defaultState: {boolAttribute.DefaultState?.ToString().ToLower()}, title: ""{boolAttribute.Title}"")";
}
else if (item is ToolkitSampleSliderOptionAttribute sliderAttribute)
{
yield return $@"new {typeof(ToolkitSampleSliderOptionMetadataViewModel).FullName}(name: ""{sliderAttribute.Name}"", initial: {sliderAttribute.Initial}, min: {sliderAttribute.Min}, max: {sliderAttribute.Max}, step: {sliderAttribute.Step}, title: ""{sliderAttribute.Title}"")";
}
else if (item is ToolkitSampleTextOptionAttribute textAttribute)
{
yield return $@"new {typeof(ToolkitSampleTextOptionMetadataViewModel).FullName}(id: ""{textAttribute.Name}"", placeholderText: ""{textAttribute.PlaceholderText}"", title: ""{textAttribute.Title}"")";
}
else
{
throw new NotSupportedException($"Unsupported or unhandled type {item.GetType()}.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
if (x.Item2.TryReconstructAs<ToolkitSampleMultiChoiceOptionAttribute>() is ToolkitSampleMultiChoiceOptionAttribute multiChoiceOptionAttribute)
return (Attribute: (ToolkitSampleOptionBaseAttribute)multiChoiceOptionAttribute, ContainingClassSymbol: x.Item1, Type: typeof(ToolkitSampleMultiChoiceOptionMetadataViewModel));

if(x.Item2.TryReconstructAs<ToolkitSampleSliderOptionAttribute>() is ToolkitSampleSliderOptionAttribute sliderOptionAttribute)
return (Attribute: (ToolkitSampleOptionBaseAttribute)sliderOptionAttribute, ContainingClassSymbol: x.Item1, Type: typeof(ToolkitSampleSliderOptionMetadataViewModel));

if (x.Item2.TryReconstructAs<ToolkitSampleTextOptionAttribute>() is ToolkitSampleTextOptionAttribute textOptionAttribute)
return (Attribute: (ToolkitSampleOptionBaseAttribute)textOptionAttribute, ContainingClassSymbol: x.Item1, Type: typeof(ToolkitSampleTextOptionMetadataViewModel));

return default;
})
.Where(x => x != default);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ internal class GeneratedSampleOptionTemplateSelector : DataTemplateSelector

public DataTemplate? MultiChoiceOptionTemplate { get; set; }

public DataTemplate? SliderOptionTemplate { get; set; }

public DataTemplate? TextOptionTemplate { get; set; }

protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return item switch
{
ToolkitSampleBoolOptionMetadataViewModel => BoolOptionTemplate ?? base.SelectTemplateCore(item, container),
ToolkitSampleMultiChoiceOptionMetadataViewModel => MultiChoiceOptionTemplate ?? base.SelectTemplateCore(item, container),
ToolkitSampleSliderOptionMetadataViewModel => SliderOptionTemplate ?? base.SelectTemplateCore(item, container),
ToolkitSampleTextOptionMetadataViewModel => TextOptionTemplate ?? base.SelectTemplateCore(item, container),
_ => base.SelectTemplateCore(item, container),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,27 @@
</local:GeneratedSampleOptionTemplateSelector.BoolOptionTemplate>
<local:GeneratedSampleOptionTemplateSelector.MultiChoiceOptionTemplate>
<DataTemplate x:DataType="metadata:ToolkitSampleMultiChoiceOptionMetadataViewModel">
<StackPanel>
<TextBlock Text="{x:Bind Title}"
Visibility="{x:Bind local:GeneratedSampleOptionsRenderer.NullOrWhiteSpaceToVisibility(Title)}" />
<muxc:RadioButtons ItemsSource="{x:Bind Options}"
SelectedIndex="0"
SelectedItem="{x:Bind Value, Mode=TwoWay}" />
</StackPanel>
<ComboBox Header="{x:Bind Title}"
ItemsSource="{x:Bind Options}"
SelectedIndex="0"
SelectedItem="{x:Bind Value, Mode=TwoWay}" />
</DataTemplate>
</local:GeneratedSampleOptionTemplateSelector.MultiChoiceOptionTemplate>
<local:GeneratedSampleOptionTemplateSelector.SliderOptionTemplate>
<DataTemplate x:DataType="metadata:ToolkitSampleSliderOptionMetadataViewModel">
<Slider Header="{x:Bind Title}"
Maximum="{x:Bind Max, Mode=OneWay}"
Minimum="{x:Bind Min, Mode=OneWay}"
StepFrequency="{x:Bind Step, Mode=OneWay}"
Value="{x:Bind Initial, Mode=TwoWay}" />
</DataTemplate>
</local:GeneratedSampleOptionTemplateSelector.SliderOptionTemplate>
<local:GeneratedSampleOptionTemplateSelector.TextOptionTemplate>
<DataTemplate x:DataType="metadata:ToolkitSampleTextOptionMetadataViewModel">
<TextBox Header="{x:Bind Title}"
Text="{x:Bind PlaceholderText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</local:GeneratedSampleOptionTemplateSelector.TextOptionTemplate>
</local:GeneratedSampleOptionTemplateSelector>
</UserControl.Resources>

Expand Down