Browse Source

Set pseudoclasses manually.

It makes creating new controls a _lot_ faster.
pull/3292/head
Steven Kirk 7 years ago
parent
commit
b89305d708
  1. 8
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  2. 25
      src/Avalonia.Controls/Button.cs
  3. 28
      src/Avalonia.Controls/ButtonSpinner.cs
  4. 20
      src/Avalonia.Controls/ControlExtensions.cs
  5. 41
      src/Avalonia.Controls/Expander.cs
  6. 30
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  7. 24
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  8. 21
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  9. 28
      src/Avalonia.Controls/Primitives/Track.cs
  10. 44
      src/Avalonia.Controls/ProgressBar.cs
  11. 24
      src/Avalonia.Controls/Slider.cs
  12. 42
      src/Avalonia.Input/InputElement.cs
  13. 28
      src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs
  14. 120
      src/Avalonia.Styling/PseudoclassEngine.cs
  15. 70
      src/Avalonia.Styling/StyledElement.cs

8
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -373,7 +373,11 @@ namespace Avalonia.Controls
public bool IsValid
{
get { return _isValid; }
internal set { SetAndRaise(IsValidProperty, ref _isValid, value); }
internal set
{
SetAndRaise(IsValidProperty, ref _isValid, value);
PseudoClasses.Set(":invalid", !value);
}
}
public static readonly StyledProperty<double> MaxColumnWidthProperty =
@ -656,8 +660,6 @@ namespace Avalonia.Controls
HorizontalScrollBarVisibilityProperty,
VerticalScrollBarVisibilityProperty);
PseudoClass<DataGrid, bool>(IsValidProperty, x => !x, ":invalid");
ItemsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnItemsPropertyChanged(e));
CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnCanUserResizeColumnsChanged(e));
ColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnColumnWidthChanged(e));

25
src/Avalonia.Controls/Button.cs

@ -91,7 +91,11 @@ namespace Avalonia.Controls
CommandProperty.Changed.Subscribe(CommandChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
IsCancelProperty.Changed.Subscribe(IsCancelChanged);
PseudoClass<Button>(IsPressedProperty, ":pressed");
}
public Button()
{
UpdatePseudoClasses(IsPressed);
}
/// <summary>
@ -312,6 +316,20 @@ namespace Avalonia.Controls
IsPressed = false;
}
protected override void OnPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == IsPressedProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<bool>());
}
}
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
{
base.UpdateDataValidation(property, value);
@ -474,5 +492,10 @@ namespace Avalonia.Controls
OnClick();
}
}
private void UpdatePseudoClasses(bool isPressed)
{
PseudoClasses.Set(":pressed", isPressed);
}
}
}

28
src/Avalonia.Controls/ButtonSpinner.cs

@ -1,5 +1,6 @@
using System;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
@ -34,6 +35,11 @@ namespace Avalonia.Controls
public static readonly StyledProperty<Location> ButtonSpinnerLocationProperty =
AvaloniaProperty.Register<ButtonSpinner, Location>(nameof(ButtonSpinnerLocation), Location.Right);
public ButtonSpinner()
{
UpdatePseudoClasses(ButtonSpinnerLocation);
}
private Button _decreaseButton;
/// <summary>
/// Gets or sets the DecreaseButton template part.
@ -85,8 +91,6 @@ namespace Avalonia.Controls
static ButtonSpinner()
{
AllowSpinProperty.Changed.Subscribe(AllowSpinChanged);
PseudoClass<ButtonSpinner, Location>(ButtonSpinnerLocationProperty, location => location == Location.Left, ":left");
PseudoClass<ButtonSpinner, Location>(ButtonSpinnerLocationProperty, location => location == Location.Right, ":right");
}
/// <summary>
@ -201,6 +205,20 @@ namespace Avalonia.Controls
}
}
protected override void OnPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == ButtonSpinnerLocationProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<Location>());
}
}
protected override void OnValidSpinDirectionChanged(ValidSpinDirections oldValue, ValidSpinDirections newValue)
{
SetButtonUsage();
@ -259,5 +277,11 @@ namespace Avalonia.Controls
OnSpin(new SpinEventArgs(SpinEvent, direction));
}
}
private void UpdatePseudoClasses(Location location)
{
PseudoClasses.Set(":left", location == Location.Left);
PseudoClasses.Set(":right", location == Location.Right);
}
}
}

20
src/Avalonia.Controls/ControlExtensions.cs

@ -69,26 +69,6 @@ namespace Avalonia.Controls
return nameScope.Find<T>(name);
}
/// <summary>
/// Adds or removes a pseudoclass depending on a boolean value.
/// </summary>
/// <param name="classes">The pseudoclasses collection.</param>
/// <param name="name">The name of the pseudoclass to set.</param>
/// <param name="value">True to add the pseudoclass or false to remove.</param>
public static void Set(this IPseudoClasses classes, string name, bool value)
{
Contract.Requires<ArgumentNullException>(classes != null);
if (value)
{
classes.Add(name);
}
else
{
classes.Remove(name);
}
}
/// <summary>
/// Sets a pseudoclass depending on an observable trigger.
/// </summary>

41
src/Avalonia.Controls/Expander.cs

@ -1,5 +1,6 @@
using Avalonia.Animation;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
namespace Avalonia.Controls
{
@ -30,16 +31,14 @@ namespace Avalonia.Controls
static Expander()
{
PseudoClass<Expander, ExpandDirection>(ExpandDirectionProperty, d => d == ExpandDirection.Down, ":down");
PseudoClass<Expander, ExpandDirection>(ExpandDirectionProperty, d => d == ExpandDirection.Up, ":up");
PseudoClass<Expander, ExpandDirection>(ExpandDirectionProperty, d => d == ExpandDirection.Left, ":left");
PseudoClass<Expander, ExpandDirection>(ExpandDirectionProperty, d => d == ExpandDirection.Right, ":right");
PseudoClass<Expander>(IsExpandedProperty, ":expanded");
IsExpandedProperty.Changed.AddClassHandler<Expander>((x, e) => x.OnIsExpandedChanged(e));
}
public Expander()
{
UpdatePseudoClasses(ExpandDirection);
}
public IPageTransition ContentTransition
{
get => GetValue(ContentTransitionProperty);
@ -55,7 +54,11 @@ namespace Avalonia.Controls
public bool IsExpanded
{
get { return _isExpanded; }
set { SetAndRaise(IsExpandedProperty, ref _isExpanded, value); }
set
{
SetAndRaise(IsExpandedProperty, ref _isExpanded, value);
PseudoClasses.Set(":expanded", value);
}
}
protected virtual void OnIsExpandedChanged(AvaloniaPropertyChangedEventArgs e)
@ -74,5 +77,27 @@ namespace Avalonia.Controls
}
}
}
protected override void OnPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == ExpandDirectionProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<ExpandDirection>());
}
}
private void UpdatePseudoClasses(ExpandDirection d)
{
PseudoClasses.Set(":up", d == ExpandDirection.Up);
PseudoClasses.Set(":down", d == ExpandDirection.Down);
PseudoClasses.Set(":left", d == ExpandDirection.Left);
PseudoClasses.Set(":right", d == ExpandDirection.Right);
}
}
}

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

@ -7,6 +7,7 @@ using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Notifications
@ -67,15 +68,12 @@ namespace Avalonia.Controls.Notifications
Install(host);
});
}
UpdatePseudoClasses(Position);
}
static WindowNotificationManager()
{
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopLeft, ":topleft");
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopRight, ":topright");
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomLeft, ":bottomleft");
PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomRight, ":bottomright");
HorizontalAlignmentProperty.OverrideDefaultValue<WindowNotificationManager>(Layout.HorizontalAlignment.Stretch);
VerticalAlignmentProperty.OverrideDefaultValue<WindowNotificationManager>(Layout.VerticalAlignment.Stretch);
}
@ -142,6 +140,20 @@ namespace Avalonia.Controls.Notifications
notificationControl.Close();
}
protected override void OnPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == PositionProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<NotificationPosition>());
}
}
/// <summary>
/// Installs the <see cref="WindowNotificationManager"/> within the <see cref="AdornerLayer"/>
/// of the host <see cref="Window"/>.
@ -159,5 +171,13 @@ namespace Avalonia.Controls.Notifications
adornerLayer.Children.Add(this);
}
}
private void UpdatePseudoClasses(NotificationPosition position)
{
PseudoClasses.Set(":topleft", position == NotificationPosition.TopLeft);
PseudoClasses.Set(":topright", position == NotificationPosition.TopRight);
PseudoClasses.Set(":bottomleft", position == NotificationPosition.BottomLeft);
PseudoClasses.Set(":bottomright", position == NotificationPosition.BottomRight);
}
}
}

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

@ -55,9 +55,6 @@ namespace Avalonia.Controls.Primitives
/// </summary>
static ScrollBar()
{
PseudoClass<ScrollBar, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<ScrollBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>((x, e) => x.OnThumbDragDelta(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>((x, e) => x.OnThumbDragComplete(e), RoutingStrategies.Bubble);
}
@ -74,6 +71,7 @@ namespace Avalonia.Controls.Primitives
this.GetObservable(VisibilityProperty).Select(_ => Unit.Default))
.Select(_ => CalculateIsVisible());
this.Bind(IsVisibleProperty, isVisible, BindingPriority.Style);
UpdatePseudoClasses(Orientation);
}
/// <summary>
@ -143,6 +141,20 @@ namespace Avalonia.Controls.Primitives
}
}
protected override void OnPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == OrientationProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<Orientation>());
}
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
@ -252,5 +264,11 @@ namespace Avalonia.Controls.Primitives
{
Scroll?.Invoke(this, new ScrollEventArgs(scrollEventType, Value));
}
private void UpdatePseudoClasses(Orientation o)
{
PseudoClasses.Set(":vertical", o == Orientation.Vertical);
PseudoClasses.Set(":horizontal", o == Orientation.Horizontal);
}
}
}

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

@ -1,8 +1,6 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Interactivity;
using Avalonia.Data;
namespace Avalonia.Controls.Primitives
@ -22,17 +20,19 @@ namespace Avalonia.Controls.Primitives
private bool? _isChecked = false;
static ToggleButton()
public ToggleButton()
{
PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == true, ":checked");
PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == false, ":unchecked");
PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == null, ":indeterminate");
UpdatePseudoClasses(IsChecked);
}
public bool? IsChecked
{
get { return _isChecked; }
set { SetAndRaise(IsCheckedProperty, ref _isChecked, value); }
set
{
SetAndRaise(IsCheckedProperty, ref _isChecked, value);
UpdatePseudoClasses(value);
}
}
public bool IsThreeState
@ -60,5 +60,12 @@ namespace Avalonia.Controls.Primitives
else
IsChecked = false;
}
private void UpdatePseudoClasses(bool? isChecked)
{
PseudoClasses.Set(":checked", isChecked == true);
PseudoClasses.Set(":unchecked", isChecked == false);
PseudoClasses.Set(":indeterminate", isChecked == null);
}
}
}

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

@ -4,6 +4,7 @@
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Metadata;
@ -46,14 +47,17 @@ namespace Avalonia.Controls.Primitives
static Track()
{
PseudoClass<Track, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<Track, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
ThumbProperty.Changed.AddClassHandler<Track>((x,e) => x.ThumbChanged(e));
IncreaseButtonProperty.Changed.AddClassHandler<Track>((x, e) => x.ButtonChanged(e));
DecreaseButtonProperty.Changed.AddClassHandler<Track>((x, e) => x.ButtonChanged(e));
AffectsArrange<Track>(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty);
}
public Track()
{
UpdatePseudoClasses(Orientation);
}
public double Minimum
{
get { return _minimum; }
@ -276,6 +280,20 @@ namespace Avalonia.Controls.Primitives
return arrangeSize;
}
protected override void OnPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == OrientationProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<Orientation>());
}
}
private static void CoerceLength(ref double componentLength, double trackLength)
{
if (componentLength < 0)
@ -433,5 +451,11 @@ namespace Avalonia.Controls.Primitives
DecreaseButton.IsVisible = visible;
}
}
private void UpdatePseudoClasses(Orientation o)
{
PseudoClasses.Set(":vertical", o == Orientation.Vertical);
PseudoClasses.Set(":horizontal", o == Orientation.Horizontal);
}
}
}

44
src/Avalonia.Controls/ProgressBar.cs

@ -4,6 +4,7 @@
using System;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Layout;
namespace Avalonia.Controls
@ -35,14 +36,15 @@ namespace Avalonia.Controls
static ProgressBar()
{
PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
PseudoClass<ProgressBar>(IsIndeterminateProperty, ":indeterminate");
ValueProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e));
IsIndeterminateProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e));
}
public ProgressBar()
{
UpdatePseudoClasses(IsIndeterminate, Orientation);
}
public bool IsIndeterminate
{
get => GetValue(IsIndeterminateProperty);
@ -75,6 +77,24 @@ namespace Avalonia.Controls
return base.ArrangeOverride(finalSize);
}
protected override void OnPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == IsIndeterminateProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<bool>(), null);
}
else if (property == OrientationProperty)
{
UpdatePseudoClasses(null, newValue.ValueOrDefault<Orientation>());
}
}
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
@ -121,5 +141,21 @@ namespace Avalonia.Controls
{
UpdateIndicator(Bounds.Size);
}
private void UpdatePseudoClasses(
bool? isIndeterminate,
Orientation? o)
{
if (isIndeterminate.HasValue)
{
PseudoClasses.Set(":indeterminate", isIndeterminate.Value);
}
if (o.HasValue)
{
PseudoClasses.Set(":vertical", o == Orientation.Vertical);
PseudoClasses.Set(":horizontal", o == Orientation.Horizontal);
}
}
}
}

24
src/Avalonia.Controls/Slider.cs

@ -3,6 +3,7 @@
using System;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
@ -43,8 +44,6 @@ namespace Avalonia.Controls
static Slider()
{
OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal);
PseudoClass<Slider, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<Slider, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragDeltaEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragDelta(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e), RoutingStrategies.Bubble);
@ -55,6 +54,7 @@ namespace Avalonia.Controls
/// </summary>
public Slider()
{
UpdatePseudoClasses(Orientation);
}
/// <summary>
@ -137,6 +137,20 @@ namespace Avalonia.Controls
}
}
protected override void OnPropertyChanged<T>(
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == OrientationProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<Orientation>());
}
}
/// <summary>
/// Called when user start dragging the <see cref="Thumb"/>.
/// </summary>
@ -190,5 +204,11 @@ namespace Avalonia.Controls
return value;
}
private void UpdatePseudoClasses(Orientation o)
{
PseudoClasses.Set(":vertical", o == Orientation.Vertical);
PseudoClasses.Set(":horizontal", o == Orientation.Horizontal);
}
}
}

42
src/Avalonia.Input/InputElement.cs

@ -4,6 +4,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input.GestureRecognizers;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
@ -181,10 +183,11 @@ namespace Avalonia.Input
PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerReleased(e));
PointerCaptureLostEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerCaptureLost(e));
PointerWheelChangedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerWheelChanged(e));
}
PseudoClass<InputElement, bool>(IsEffectivelyEnabledProperty, x => !x, ":disabled");
PseudoClass<InputElement>(IsFocusedProperty, ":focus");
PseudoClass<InputElement>(IsPointerOverProperty, ":pointerover");
public InputElement()
{
UpdatePseudoClasses(IsFocused, IsPointerOver);
}
/// <summary>
@ -372,7 +375,11 @@ namespace Avalonia.Input
public bool IsEffectivelyEnabled
{
get => _isEffectivelyEnabled;
private set => SetAndRaise(IsEffectivelyEnabledProperty, ref _isEffectivelyEnabled, value);
private set
{
SetAndRaise(IsEffectivelyEnabledProperty, ref _isEffectivelyEnabled, value);
PseudoClasses.Set(":disabled", !value);
}
}
public List<KeyBinding> KeyBindings { get; } = new List<KeyBinding>();
@ -522,6 +529,20 @@ namespace Avalonia.Input
{
}
protected override void OnPropertyChanged<T>(AvaloniaProperty<T> property, Optional<T> oldValue, BindingValue<T> newValue, BindingPriority priority)
{
base.OnPropertyChanged(property, oldValue, newValue, priority);
if (property == IsFocusedProperty)
{
UpdatePseudoClasses(newValue.ValueOrDefault<bool>(), null);
}
else if (property == IsPointerOverProperty)
{
UpdatePseudoClasses(null, newValue.ValueOrDefault<bool>());
}
}
/// <summary>
/// Updates the <see cref="IsEffectivelyEnabled"/> property value according to the parent
/// control's enabled state and <see cref="IsEnabledCore"/>.
@ -578,5 +599,18 @@ namespace Avalonia.Input
child?.UpdateIsEffectivelyEnabled(this);
}
}
private void UpdatePseudoClasses(bool? isFocused, bool? isPointerOver)
{
if (isFocused.HasValue)
{
PseudoClasses.Set(":focus", isFocused.Value);
}
if (isPointerOver.HasValue)
{
PseudoClasses.Set(":pointerover", isPointerOver.Value);
}
}
}
}

28
src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs

@ -0,0 +1,28 @@
using System;
namespace Avalonia.Controls
{
public static class PseudolassesExtensions
{
/// <summary>
/// Adds or removes a pseudoclass depending on a boolean value.
/// </summary>
/// <param name="classes">The pseudoclasses collection.</param>
/// <param name="name">The name of the pseudoclass to set.</param>
/// <param name="value">True to add the pseudoclass or false to remove.</param>
public static void Set(this IPseudoClasses classes, string name, bool value)
{
Contract.Requires<ArgumentNullException>(classes != null);
if (value)
{
classes.Add(name);
}
else
{
classes.Remove(name);
}
}
}
}

120
src/Avalonia.Styling/PseudoclassEngine.cs

@ -1,120 +0,0 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
namespace Avalonia
{
internal static class PseudoclassEngine
{
private static Dictionary<Type, List<IEntry>> _entries = new Dictionary<Type, List<IEntry>>();
private static Dictionary<AvaloniaProperty, List<IEntry>> _properties = new Dictionary<AvaloniaProperty, List<IEntry>>();
private static Dictionary<Type, List<IEntry>> _cache = new Dictionary<Type, List<IEntry>>();
public static void Created(StyledElement element)
{
var t = element.GetType();
if (!_cache.TryGetValue(t, out var list))
{
list = new List<IEntry>();
foreach (var i in _entries)
{
if (i.Key.IsAssignableFrom(t))
{
list.AddRange(i.Value);
}
}
_cache.Add(t, list);
}
foreach (var i in list)
{
i.Update(element);
}
}
public static void Register<T>(
Type type,
AvaloniaProperty<T> property,
Func<T, bool> selector,
string className)
{
if (!_entries.TryGetValue(type, out var list))
{
list = new List<IEntry>();
_entries.Add(type, list);
}
var entry = new Entry<T>(type, property, selector, className);
list.Add(entry);
if (!_properties.TryGetValue(property, out list))
{
list = new List<IEntry>();
_properties.Add(property, list);
property.Changed.Subscribe(ValueChanged);
}
list.Add(entry);
}
private static void ValueChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is StyledElement element &&
_properties.TryGetValue(e.Property, out var list))
{
var t = e.Sender.GetType();
foreach (var i in list)
{
if (i.Type.IsAssignableFrom(t))
{
i.Update(element);
}
}
}
}
private interface IEntry
{
Type Type { get; }
void Update(StyledElement element);
}
private class Entry<T> : IEntry
{
public Entry(
Type type,
AvaloniaProperty<T> property,
Func<T, bool> selector,
string className)
{
Type = type;
Property = property;
Selector = selector;
ClassName = className;
}
public Type Type { get; }
public AvaloniaProperty<T> Property { get; }
public Func<T, bool> Selector { get; }
public string ClassName { get; }
public void Update(StyledElement element)
{
var value = element.GetValue(Property);
if (Selector(value))
{
((IPseudoClasses)element.Classes).Add(ClassName);
}
else
{
((IPseudoClasses)element.Classes).Remove(ClassName);
}
}
}
}
}

70
src/Avalonia.Styling/StyledElement.cs

@ -82,7 +82,6 @@ namespace Avalonia
public StyledElement()
{
_isAttachedToLogicalTree = this is IStyleRoot;
PseudoclassEngine.Created(this);
}
/// <summary>
@ -496,75 +495,6 @@ namespace Avalonia
InheritanceParent = parent;
}
/// <summary>
/// Adds a pseudo-class to be set when a property is true.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="className">The pseudo-class.</param>
[Obsolete("Use PseudoClass<TOwner> and specify the control type.")]
protected static void PseudoClass(AvaloniaProperty<bool> property, string className)
{
PseudoClass<StyledElement>(property, className);
}
/// <summary>
/// Adds a pseudo-class to be set when a property is true.
/// </summary>
/// <typeparam name="TOwner">The type to apply the pseudo-class to.</typeparam>
/// <param name="property">The property.</param>
/// <param name="className">The pseudo-class.</param>
protected static void PseudoClass<TOwner>(AvaloniaProperty<bool> property, string className)
where TOwner : class, IStyledElement
{
PseudoClass<TOwner, bool>(property, x => x, className);
}
/// <summary>
/// Adds a pseudo-class to be set when a property equals a certain value.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="selector">Returns a boolean value based on the property value.</param>
/// <param name="className">The pseudo-class.</param>
[Obsolete("Use PseudoClass<TOwner, TProperty> and specify the control type.")]
protected static void PseudoClass<TProperty>(
AvaloniaProperty<TProperty> property,
Func<TProperty, bool> selector,
string className)
{
PseudoClass<StyledElement, TProperty>(property, selector, className);
}
/// <summary>
/// Adds a pseudo-class to be set when a property equals a certain value.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <typeparam name="TOwner">The type to apply the pseudo-class to.</typeparam>
/// <param name="property">The property.</param>
/// <param name="selector">Returns a boolean value based on the property value.</param>
/// <param name="className">The pseudo-class.</param>
protected static void PseudoClass<TOwner, TProperty>(
AvaloniaProperty<TProperty> property,
Func<TProperty, bool> selector,
string className)
where TOwner : class, IStyledElement
{
Contract.Requires<ArgumentNullException>(property != null);
Contract.Requires<ArgumentNullException>(selector != null);
Contract.Requires<ArgumentNullException>(className != null);
if (string.IsNullOrWhiteSpace(className))
{
throw new ArgumentException("Cannot supply an empty className.");
}
PseudoclassEngine.Register(
typeof(TOwner),
property,
selector,
className);
}
protected virtual void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)

Loading…
Cancel
Save