Browse Source

Merge branch 'master' into fixes/make-fluent-buttons-animation-consistent

pull/4710/head
Jumar Macato 6 years ago
committed by GitHub
parent
commit
a171465c91
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  2. 58
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  3. 2
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  4. 2
      src/Avalonia.Controls.DataGrid/DataGridCell.cs
  5. 2
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  6. 2
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  7. 2
      src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
  8. 2
      src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
  9. 96
      src/Avalonia.Controls/AutoCompleteBox.cs
  10. 2
      src/Avalonia.Controls/Button.cs
  11. 2
      src/Avalonia.Controls/ButtonSpinner.cs
  12. 2
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  13. 2
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  14. 2
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  15. 2
      src/Avalonia.Controls/Chrome/CaptionButtons.cs
  16. 2
      src/Avalonia.Controls/Chrome/TitleBar.cs
  17. 2
      src/Avalonia.Controls/DataValidationErrors.cs
  18. 4
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  19. 4
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  20. 3
      src/Avalonia.Controls/Expander.cs
  21. 2
      src/Avalonia.Controls/ItemsControl.cs
  22. 2
      src/Avalonia.Controls/ListBoxItem.cs
  23. 2
      src/Avalonia.Controls/MenuItem.cs
  24. 2
      src/Avalonia.Controls/Notifications/NotificationCard.cs
  25. 2
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  26. 2
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  27. 2
      src/Avalonia.Controls/Primitives/Thumb.cs
  28. 2
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  29. 2
      src/Avalonia.Controls/Primitives/Track.cs
  30. 2
      src/Avalonia.Controls/ProgressBar.cs
  31. 2
      src/Avalonia.Controls/Slider.cs
  32. 7
      src/Avalonia.Controls/SplitView.cs
  33. 2
      src/Avalonia.Controls/TabItem.cs
  34. 2
      src/Avalonia.Controls/TextBox.cs
  35. 4
      src/Avalonia.Controls/ToggleSwitch.cs
  36. 2
      src/Avalonia.Controls/ToolTip.cs
  37. 2
      src/Avalonia.Controls/TreeViewItem.cs
  38. 2
      src/Avalonia.Input/InputElement.cs
  39. 18
      src/Avalonia.Styling/Controls/Metadata/PseudoClassesAttribute.cs
  40. 34
      tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

5
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml

@ -51,6 +51,11 @@
Width="200" Width="200"
Margin="0,0,0,8" Margin="0,0,0,8"
FilterMode="None"/> FilterMode="None"/>
<TextBlock Text="Custom Autocomplete"/>
<AutoCompleteBox Name="CustomAutocompleteBox"
Width="200"
Margin="0,0,0,8"
FilterMode="None"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

58
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs

@ -92,13 +92,28 @@ namespace ControlCatalog.Pages
} }
public StateData[] States { get; private set; } public StateData[] States { get; private set; }
private LinkedList<string>[] BuildAllSentences()
{
return new string[]
{
"Hello world",
"No this is Patrick",
"Never gonna give you up",
"How does one patch KDE2 under FreeBSD"
}
.Select(x => new LinkedList<string>(x.Split(' ')))
.ToArray();
}
public LinkedList<string>[] Sentences { get; private set; }
public AutoCompleteBoxPage() public AutoCompleteBoxPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
States = BuildAllStates(); States = BuildAllStates();
Sentences = BuildAllSentences();
foreach (AutoCompleteBox box in GetAllAutoCompleteBox()) foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox"))
{ {
box.Items = States; box.Items = States;
} }
@ -116,6 +131,11 @@ namespace ControlCatalog.Pages
var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox"); var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox");
asyncBox.AsyncPopulator = PopulateAsync; asyncBox.AsyncPopulator = PopulateAsync;
var customAutocompleteBox = this.FindControl<AutoCompleteBox>("CustomAutocompleteBox");
customAutocompleteBox.Items = Sentences.SelectMany(x => x);
customAutocompleteBox.TextFilter = LastWordContains;
customAutocompleteBox.TextSelector = AppendWord;
} }
private IEnumerable<AutoCompleteBox> GetAllAutoCompleteBox() private IEnumerable<AutoCompleteBox> GetAllAutoCompleteBox()
{ {
@ -137,6 +157,42 @@ namespace ControlCatalog.Pages
.ToList(); .ToList();
} }
private bool LastWordContains(string searchText, string item)
{
var words = searchText.Split(' ');
var options = Sentences.Select(x => x.First).ToArray();
for (var i = 0; i < words.Length; ++i)
{
var word = words[i];
for (var j = 0; j < options.Length; ++j)
{
var option = options[j];
if (option == null)
continue;
if (i == words.Length - 1)
{
options[j] = option.Value.ToLower().Contains(word.ToLower()) ? option : null;
}
else
{
options[j] = option.Value.Equals(word, StringComparison.InvariantCultureIgnoreCase) ? option.Next : null;
}
}
}
return options.Any(x => x != null && x.Value == item);
}
private string AppendWord(string text, string item)
{
string[] parts = text.Split(' ');
if (parts.Length == 0)
return item;
parts[parts.Length - 1] = item;
return string.Join(" ", parts);
}
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);

2
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -24,12 +24,14 @@ using Avalonia.Input.Platform;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Displays data in a customizable grid. /// Displays data in a customizable grid.
/// </summary> /// </summary>
[PseudoClasses(":invalid")]
public partial class DataGrid : TemplatedControl public partial class DataGrid : TemplatedControl
{ {
private const string DATAGRID_elementRowsPresenterName = "PART_RowsPresenter"; private const string DATAGRID_elementRowsPresenterName = "PART_RowsPresenter";

2
src/Avalonia.Controls.DataGrid/DataGridCell.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Input; using Avalonia.Input;
@ -12,6 +13,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> cell. /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> cell.
/// </summary> /// </summary>
[PseudoClasses(":selected", ":current", ":edited", ":invalid")]
public class DataGridCell : ContentControl public class DataGridCell : ContentControl
{ {
private const string DATAGRIDCELL_elementRightGridLine = "PART_RightGridLine"; private const string DATAGRIDCELL_elementRightGridLine = "PART_RightGridLine";

2
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -14,12 +14,14 @@ using Avalonia.Utilities;
using System; using System;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> column header. /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> column header.
/// </summary> /// </summary>
[PseudoClasses(":dragIndicator", ":pressed", ":sortascending", ":sortdescending")]
public class DataGridColumnHeader : ContentControl public class DataGridColumnHeader : ContentControl
{ {
private enum DragMode private enum DragMode

2
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
@ -20,6 +21,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> row. /// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> row.
/// </summary> /// </summary>
[PseudoClasses(":selected", ":editing", ":invalid")]
public class DataGridRow : TemplatedControl public class DataGridRow : TemplatedControl
{ {

2
src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
@ -13,6 +14,7 @@ using System.Reactive.Linq;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
[PseudoClasses(":pressed", ":current", ":expanded")]
public class DataGridRowGroupHeader : TemplatedControl public class DataGridRowGroupHeader : TemplatedControl
{ {
private const string DATAGRIDROWGROUPHEADER_expanderButton = "ExpanderButton"; private const string DATAGRIDROWGROUPHEADER_expanderButton = "ExpanderButton";

2
src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using System.Diagnostics; using System.Diagnostics;
@ -12,6 +13,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> row header. /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> row header.
/// </summary> /// </summary>
[PseudoClasses(":invalid", ":selected", ":editing", ":current")]
public class DataGridRowHeader : ContentControl public class DataGridRowHeader : ContentControl
{ {
private const string DATAGRIDROWHEADER_elementRootName = "PART_Root"; private const string DATAGRIDROWHEADER_elementRootName = "PART_Root";

96
src/Avalonia.Controls/AutoCompleteBox.cs

@ -14,6 +14,7 @@ using System.Reactive.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
@ -30,6 +31,7 @@ namespace Avalonia.Controls
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" /> /// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
/// event. /// event.
/// </summary> /// </summary>
[PseudoClasses(":dropdownopen")]
public class PopulatedEventArgs : EventArgs public class PopulatedEventArgs : EventArgs
{ {
/// <summary> /// <summary>
@ -225,6 +227,27 @@ namespace Avalonia.Controls
Custom = 13, Custom = 13,
} }
/// <summary>
/// Represents the selector used by the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control to
/// determine how the specified text should be modified with an item.
/// </summary>
/// <returns>
/// Modified text that will be used by the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.
/// </returns>
/// <param name="search">The string used as the basis for filtering.</param>
/// <param name="item">
/// The selected item that should be combined with the
/// <paramref name="search" /> parameter.
/// </param>
/// <typeparam name="T">
/// The type used for filtering the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.
/// This type can be either a string or an object.
/// </typeparam>
public delegate string AutoCompleteSelector<T>(string search, T item);
/// <summary> /// <summary>
/// Represents a control that provides a text box for user input and a /// Represents a control that provides a text box for user input and a
/// drop-down that contains possible matches based on the input in the text /// drop-down that contains possible matches based on the input in the text
@ -362,6 +385,9 @@ namespace Avalonia.Controls
private AutoCompleteFilterPredicate<object> _itemFilter; private AutoCompleteFilterPredicate<object> _itemFilter;
private AutoCompleteFilterPredicate<string> _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith); private AutoCompleteFilterPredicate<string> _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith);
private AutoCompleteSelector<object> _itemSelector;
private AutoCompleteSelector<string> _textSelector;
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent = public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox)); RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox));
@ -528,6 +554,34 @@ namespace Avalonia.Controls
(o, v) => o.TextFilter = v, (o, v) => o.TextFilter = v,
unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith)); unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>> ItemSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>>(
nameof(ItemSelector),
o => o.ItemSelector,
(o, v) => o.ItemSelector = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string>> TextSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string>>(
nameof(TextSelector),
o => o.TextSelector,
(o, v) => o.TextSelector = v);
/// <summary> /// <summary>
/// Identifies the /// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" /> /// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
@ -1061,6 +1115,40 @@ namespace Avalonia.Controls
set { SetAndRaise(TextFilterProperty, ref _textFilter, value); } set { SetAndRaise(TextFilterProperty, ref _textFilter, value); }
} }
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </value>
public AutoCompleteSelector<object> ItemSelector
{
get { return _itemSelector; }
set { SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); }
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way.
/// </value>
public AutoCompleteSelector<string> TextSelector
{
get { return _textSelector; }
set { SetAndRaise(TextSelectorProperty, ref _textSelector, value); }
}
public Func<string, CancellationToken, Task<IEnumerable<object>>> AsyncPopulator public Func<string, CancellationToken, Task<IEnumerable<object>>> AsyncPopulator
{ {
get { return _asyncPopulator; } get { return _asyncPopulator; }
@ -2329,6 +2417,14 @@ namespace Avalonia.Controls
{ {
text = SearchText; text = SearchText;
} }
else if (TextSelector != null)
{
text = TextSelector(SearchText, FormatValue(newItem, true));
}
else if (ItemSelector != null)
{
text = ItemSelector(SearchText, newItem);
}
else else
{ {
text = FormatValue(newItem, true); text = FormatValue(newItem, true);

2
src/Avalonia.Controls/Button.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -28,6 +29,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A button control. /// A button control.
/// </summary> /// </summary>
[PseudoClasses(":pressed")]
public class Button : ContentControl public class Button : ContentControl
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/ButtonSpinner.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
@ -15,6 +16,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents a spinner control that includes two Buttons. /// Represents a spinner control that includes two Buttons.
/// </summary> /// </summary>
[PseudoClasses(":left", ":right")]
public class ButtonSpinner : Spinner public class ButtonSpinner : Spinner
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Calendar/CalendarButton.cs

@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
using System; using System;
@ -12,6 +13,7 @@ namespace Avalonia.Controls.Primitives
/// Represents a button on a /// Represents a button on a
/// <see cref="T:Avalonia.Controls.Calendar" />. /// <see cref="T:Avalonia.Controls.Calendar" />.
/// </summary> /// </summary>
[PseudoClasses(":selected", ":inactive", ":btnfocused")]
public sealed class CalendarButton : Button public sealed class CalendarButton : Button
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Calendar/CalendarDayButton.cs

@ -5,10 +5,12 @@
using System; using System;
using System.Globalization; using System.Globalization;
using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
[PseudoClasses(":pressed", ":disabled", ":selected", ":inactive", ":today", ":blackout", ":dayfocused")]
public sealed class CalendarDayButton : Button public sealed class CalendarDayButton : Button
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -18,6 +19,7 @@ namespace Avalonia.Controls.Primitives
/// Represents the currently displayed month or year on a /// Represents the currently displayed month or year on a
/// <see cref="T:Avalonia.Controls.Calendar" />. /// <see cref="T:Avalonia.Controls.Calendar" />.
/// </summary> /// </summary>
[PseudoClasses(":calendardisabled")]
public sealed class CalendarItem : TemplatedControl public sealed class CalendarItem : TemplatedControl
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Chrome/CaptionButtons.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
#nullable enable #nullable enable
@ -9,6 +10,7 @@ namespace Avalonia.Controls.Chrome
/// <summary> /// <summary>
/// Draws window minimize / maximize / close buttons in a <see cref="TitleBar"/> when managed client decorations are enabled. /// Draws window minimize / maximize / close buttons in a <see cref="TitleBar"/> when managed client decorations are enabled.
/// </summary> /// </summary>
[PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")]
public class CaptionButtons : TemplatedControl public class CaptionButtons : TemplatedControl
{ {
private CompositeDisposable? _disposables; private CompositeDisposable? _disposables;

2
src/Avalonia.Controls/Chrome/TitleBar.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
#nullable enable #nullable enable
@ -9,6 +10,7 @@ namespace Avalonia.Controls.Chrome
/// <summary> /// <summary>
/// Draws a titlebar when managed client decorations are enabled. /// Draws a titlebar when managed client decorations are enabled.
/// </summary> /// </summary>
[PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")]
public class TitleBar : TemplatedControl public class TitleBar : TemplatedControl
{ {
private CompositeDisposable? _disposables; private CompositeDisposable? _disposables;

2
src/Avalonia.Controls/DataValidationErrors.cs

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Data; using Avalonia.Data;
@ -14,6 +15,7 @@ namespace Avalonia.Controls
/// <remarks> /// <remarks>
/// You will probably only want to create instances inside of control templates. /// You will probably only want to create instances inside of control templates.
/// </remarks> /// </remarks>
[PseudoClasses(":error")]
public class DataValidationErrors : ContentControl public class DataValidationErrors : ContentControl
{ {
/// <summary> /// <summary>

4
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -11,6 +12,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control to allow the user to select a date /// A control to allow the user to select a date
/// </summary> /// </summary>
[PseudoClasses(":hasnodate")]
public class DatePicker : TemplatedControl public class DatePicker : TemplatedControl
{ {
/// <summary> /// <summary>

4
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using System; using System;
@ -9,6 +10,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control to allow the user to select a time /// A control to allow the user to select a time
/// </summary> /// </summary>
[PseudoClasses(":hasnotime")]
public class TimePicker : TemplatedControl public class TimePicker : TemplatedControl
{ {
/// <summary> /// <summary>

3
src/Avalonia.Controls/Expander.cs

@ -1,6 +1,6 @@
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Data;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -12,6 +12,7 @@ namespace Avalonia.Controls
Right Right
} }
[PseudoClasses(":expanded", ":up", ":down", ":left", ":right")]
public class Expander : HeaderedContentControl public class Expander : HeaderedContentControl
{ {
public static readonly StyledProperty<IPageTransition> ContentTransitionProperty = public static readonly StyledProperty<IPageTransition> ContentTransitionProperty =

2
src/Avalonia.Controls/ItemsControl.cs

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
@ -18,6 +19,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Displays a collection of items. /// Displays a collection of items.
/// </summary> /// </summary>
[PseudoClasses(":empty", ":singleitem")]
public class ItemsControl : TemplatedControl, IItemsPresenterHost, ICollectionChangedListener public class ItemsControl : TemplatedControl, IItemsPresenterHost, ICollectionChangedListener
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/ListBoxItem.cs

@ -1,3 +1,4 @@
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Input; using Avalonia.Input;
@ -6,6 +7,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A selectable item in a <see cref="ListBox"/>. /// A selectable item in a <see cref="ListBox"/>.
/// </summary> /// </summary>
[PseudoClasses(":pressed", ":selected")]
public class ListBoxItem : ContentControl, ISelectable public class ListBoxItem : ContentControl, ISelectable
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/MenuItem.cs

@ -4,6 +4,7 @@ using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
@ -20,6 +21,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A menu item control. /// A menu item control.
/// </summary> /// </summary>
[PseudoClasses(":separator", ":icon", ":open", ":pressed", ":selected")]
public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Notifications/NotificationCard.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls.Metadata;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
@ -9,6 +10,7 @@ namespace Avalonia.Controls.Notifications
/// <summary> /// <summary>
/// Control that represents and displays a notification. /// Control that represents and displays a notification.
/// </summary> /// </summary>
[PseudoClasses(":error", ":information", ":success", ":warning")]
public class NotificationCard : ContentControl public class NotificationCard : ContentControl
{ {
private bool _isClosed; private bool _isClosed;

2
src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@ -7,12 +7,14 @@ using Avalonia.Controls.Primitives;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls.Notifications namespace Avalonia.Controls.Notifications
{ {
/// <summary> /// <summary>
/// An <see cref="INotificationManager"/> that displays notifications in a <see cref="Window"/>. /// An <see cref="INotificationManager"/> that displays notifications in a <see cref="Window"/>.
/// </summary> /// </summary>
[PseudoClasses(":topleft", ":topright", ":bottomleft", ":bottomright")]
public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest
{ {
private IList _items; private IList _items;

2
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -4,6 +4,7 @@ using Avalonia.Interactivity;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
@ -21,6 +22,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// A scrollbar control. /// A scrollbar control.
/// </summary> /// </summary>
[PseudoClasses(":vertical", ":horizontal")]
public class ScrollBar : RangeBase public class ScrollBar : RangeBase
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Primitives/Thumb.cs

@ -1,9 +1,11 @@
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
[PseudoClasses(":pressed")]
public class Thumb : TemplatedControl public class Thumb : TemplatedControl
{ {
public static readonly RoutedEvent<VectorEventArgs> DragStartedEvent = public static readonly RoutedEvent<VectorEventArgs> DragStartedEvent =

2
src/Avalonia.Controls/Primitives/ToggleButton.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -7,6 +8,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Represents a control that a user can select (check) or clear (uncheck). Base class for controls that can switch states. /// Represents a control that a user can select (check) or clear (uncheck). Base class for controls that can switch states.
/// </summary> /// </summary>
[PseudoClasses(":checked", ":unchecked", ":indeterminate")]
public class ToggleButton : Button public class ToggleButton : Button
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/Primitives/Track.cs

@ -4,6 +4,7 @@
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
@ -12,6 +13,7 @@ using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
[PseudoClasses(":vertical", ":horizontal")]
public class Track : Control public class Track : Control
{ {
public static readonly DirectProperty<Track, double> MinimumProperty = public static readonly DirectProperty<Track, double> MinimumProperty =

2
src/Avalonia.Controls/ProgressBar.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
@ -8,6 +9,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control used to indicate the progress of an operation. /// A control used to indicate the progress of an operation.
/// </summary> /// </summary>
[PseudoClasses(":vertical", ":horizontal", ":indeterminate")]
public class ProgressBar : RangeBase public class ProgressBar : RangeBase
{ {
public class ProgressBarTemplateProperties : AvaloniaObject public class ProgressBarTemplateProperties : AvaloniaObject

2
src/Avalonia.Controls/Slider.cs

@ -1,5 +1,6 @@
using System; using System;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
@ -39,6 +40,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control that lets the user select from a range of values by moving a Thumb control along a Track. /// A control that lets the user select from a range of values by moving a Thumb control along a Track.
/// </summary> /// </summary>
[PseudoClasses(":vertical", ":horizontal", ":pressed")]
public class Slider : RangeBase public class Slider : RangeBase
{ {
/// <summary> /// <summary>

7
src/Avalonia.Controls/SplitView.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -73,6 +74,10 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A control with two views: A collapsible pane and an area for content /// A control with two views: A collapsible pane and an area for content
/// </summary> /// </summary>
[PseudoClasses(":open", ":closed")]
[PseudoClasses(":compactoverlay", ":compactinline", ":overlay", ":inline")]
[PseudoClasses(":left", ":right")]
[PseudoClasses(":lightdismiss")]
public class SplitView : TemplatedControl public class SplitView : TemplatedControl
{ {
/* /*

2
src/Avalonia.Controls/TabItem.cs

@ -1,3 +1,4 @@
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
@ -6,6 +7,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// An item in a <see cref="TabStrip"/> or <see cref="TabControl"/>. /// An item in a <see cref="TabStrip"/> or <see cref="TabControl"/>.
/// </summary> /// </summary>
[PseudoClasses(":pressed", ":selected")]
public class TabItem : HeaderedContentControl, ISelectable public class TabItem : HeaderedContentControl, ISelectable
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/TextBox.cs

@ -13,9 +13,11 @@ using Avalonia.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Controls.Metadata;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
[PseudoClasses(":empty")]
public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost
{ {
public static KeyGesture CutGesture { get; } = AvaloniaLocator.Current public static KeyGesture CutGesture { get; } = AvaloniaLocator.Current

4
src/Avalonia.Controls/ToggleSwitch.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Presenters; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
@ -8,6 +9,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// A Toggle Switch control. /// A Toggle Switch control.
/// </summary> /// </summary>
[PseudoClasses(":dragging")]
public class ToggleSwitch : ToggleButton public class ToggleSwitch : ToggleButton
{ {
private Panel _knobsPanel; private Panel _knobsPanel;

2
src/Avalonia.Controls/ToolTip.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -14,6 +15,7 @@ namespace Avalonia.Controls
/// To add a tooltip to a control, use the <see cref="TipProperty"/> attached property, /// To add a tooltip to a control, use the <see cref="TipProperty"/> attached property,
/// assigning the content that you want displayed. /// assigning the content that you want displayed.
/// </remarks> /// </remarks>
[PseudoClasses(":open")]
public class ToolTip : ContentControl public class ToolTip : ContentControl
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/TreeViewItem.cs

@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
@ -11,6 +12,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// An item in a <see cref="TreeView"/>. /// An item in a <see cref="TreeView"/>.
/// </summary> /// </summary>
[PseudoClasses(":pressed", ":selected")]
public class TreeViewItem : HeaderedItemsControl, ISelectable public class TreeViewItem : HeaderedItemsControl, ISelectable
{ {
/// <summary> /// <summary>

2
src/Avalonia.Input/InputElement.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input.GestureRecognizers; using Avalonia.Input.GestureRecognizers;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -12,6 +13,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Implements input-related functionality for a control. /// Implements input-related functionality for a control.
/// </summary> /// </summary>
[PseudoClasses(":disabled", ":focus", ":focus-visible", ":pointerover")]
public class InputElement : Interactive, IInputElement public class InputElement : Interactive, IInputElement
{ {
/// <summary> /// <summary>

18
src/Avalonia.Styling/Controls/Metadata/PseudoClassesAttribute.cs

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
#nullable enable
namespace Avalonia.Controls.Metadata
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class PseudoClassesAttribute : Attribute
{
public PseudoClassesAttribute(params string[] pseudoClasses)
{
PseudoClasses = pseudoClasses;
}
public IReadOnlyList<string> PseudoClasses { get; }
}
}

34
tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

@ -363,6 +363,40 @@ namespace Avalonia.Controls.UnitTests
}); });
} }
[Fact]
public void Custom_TextSelector()
{
RunTest((control, textbox) =>
{
object selectedItem = control.Items.Cast<object>().First();
string input = "42";
control.TextSelector = (text, item) => text + item;
Assert.Equal(control.TextSelector("4", "2"), "42");
control.Text = input;
control.SelectedItem = selectedItem;
Assert.Equal(control.Text, control.TextSelector(input, selectedItem.ToString()));
});
}
[Fact]
public void Custom_ItemSelector()
{
RunTest((control, textbox) =>
{
object selectedItem = control.Items.Cast<object>().First();
string input = "42";
control.ItemSelector = (text, item) => text + item;
Assert.Equal(control.ItemSelector("4", 2), "42");
control.Text = input;
control.SelectedItem = selectedItem;
Assert.Equal(control.Text, control.ItemSelector(input, selectedItem));
});
}
/// <summary> /// <summary>
/// Retrieves a defined predicate filter through a new AutoCompleteBox /// Retrieves a defined predicate filter through a new AutoCompleteBox
/// control instance. /// control instance.

Loading…
Cancel
Save