Browse Source

prototype for screen style keyword.

pull/7938/head
Dan Walmsley 4 years ago
parent
commit
7cd3d6df80
  1. 31
      src/Avalonia.Controls/TopLevel.cs
  2. 12
      src/Avalonia.Styling/LogicalTree/IScreenSizeProvider.cs
  3. 10
      src/Avalonia.Styling/LogicalTree/ITopLevelScreenSizeProvider.cs
  4. 64
      src/Avalonia.Styling/Styling/Activators/ScreenActivator.cs
  5. 51
      src/Avalonia.Styling/Styling/ScreenSelector.cs
  6. 5
      src/Avalonia.Styling/Styling/Selectors.cs
  7. 11
      src/Avalonia.Visuals/Visual.cs
  8. 18
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
  9. 19
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
  10. 3
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs

31
src/Avalonia.Controls/TopLevel.cs

@ -36,7 +36,8 @@ namespace Avalonia.Controls
IStyleHost,
ILogicalRoot,
ITextInputMethodRoot,
IWeakEventSubscriber<ResourcesChangedEventArgs>
IWeakEventSubscriber<ResourcesChangedEventArgs>,
IScreenSizeProvider
{
/// <summary>
/// Defines the <see cref="ClientSize"/> property.
@ -529,7 +530,35 @@ namespace Avalonia.Controls
KeyboardDevice.Instance?.SetFocusedElement(null, NavigationMethod.Unspecified, KeyModifiers.None);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == BoundsProperty)
{
var oldValue = change.OldValue.GetValueOrDefault<Rect>();
var newValue = change.NewValue.GetValueOrDefault<Rect>();
if (oldValue.Size != newValue.Size)
{
ScreenSizeChanged?.Invoke(this, EventArgs.Empty);
}
}
}
ITextInputMethodImpl? ITextInputMethodRoot.InputMethod =>
(PlatformImpl as ITopLevelImplWithTextInputMethod)?.TextInputMethod;
public double GetScreenWidth()
{
return Bounds.Size.Width;
}
public double GetScreenHeight()
{
return Bounds.Size.Height;
}
public event EventHandler? ScreenSizeChanged;
}
}

12
src/Avalonia.Styling/LogicalTree/IScreenSizeProvider.cs

@ -0,0 +1,12 @@
using System;
namespace Avalonia.LogicalTree;
public interface IScreenSizeProvider
{
double GetScreenWidth();
double GetScreenHeight();
event EventHandler? ScreenSizeChanged;
}

10
src/Avalonia.Styling/LogicalTree/ITopLevelScreenSizeProvider.cs

@ -0,0 +1,10 @@
using System;
namespace Avalonia.LogicalTree;
public interface ITopLevelScreenSizeProvider
{
IScreenSizeProvider? GetScreenSizeProvider();
event EventHandler? ScreenSizeProviderChanged;
}

64
src/Avalonia.Styling/Styling/Activators/ScreenActivator.cs

@ -0,0 +1,64 @@
using System;
using Avalonia.LogicalTree;
namespace Avalonia.Styling.Activators;
internal sealed class ScreenActivator : StyleActivatorBase
{
private readonly ITopLevelScreenSizeProvider _provider;
private IScreenSizeProvider? _currentScreenSizeProvider;
public ScreenActivator(
ITopLevelScreenSizeProvider provider)
{
_provider = provider;
}
protected override void Initialize()
{
InitialiseScreenSizeProvider();
PublishNext(IsMatching());
_provider.ScreenSizeProviderChanged += ScreenSizeProviderChanged;
}
protected override void Deinitialize()
{
_provider.ScreenSizeProviderChanged -= ScreenSizeProviderChanged;
if (_currentScreenSizeProvider is { })
{
_currentScreenSizeProvider.ScreenSizeChanged -= ScreenSizeChanged;
_currentScreenSizeProvider = null;
}
}
private void ScreenSizeProviderChanged(object? sender, EventArgs e)
{
if (_currentScreenSizeProvider is { })
{
_currentScreenSizeProvider.ScreenSizeChanged -= ScreenSizeChanged;
_currentScreenSizeProvider = null;
}
InitialiseScreenSizeProvider();
}
private void InitialiseScreenSizeProvider()
{
if (_provider.GetScreenSizeProvider() is { } screenSizeProvider)
{
_currentScreenSizeProvider = screenSizeProvider;
_currentScreenSizeProvider.ScreenSizeChanged += ScreenSizeChanged;
}
PublishNext(IsMatching());
}
private void ScreenSizeChanged(object? sender, EventArgs e)
{
PublishNext(IsMatching());
}
private bool IsMatching() => _currentScreenSizeProvider != null && ScreenSelector.Evaluate(_currentScreenSizeProvider).IsMatch;
}

51
src/Avalonia.Styling/Styling/ScreenSelector.cs

@ -0,0 +1,51 @@
using System;
using Avalonia.LogicalTree;
using Avalonia.Styling.Activators;
namespace Avalonia.Styling;
public class ScreenSelector : Selector
{
private readonly Selector? _previous;
public ScreenSelector(Selector? previous)
{
_previous = previous;
}
public override bool InTemplate => _previous?.InTemplate ?? false;
public override bool IsCombinator => false;
public override Type? TargetType => _previous?.TargetType;
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
if (!(control is ITopLevelScreenSizeProvider logical))
{
return SelectorMatch.NeverThisType;
}
if (subscribe)
{
return new SelectorMatch(new ScreenActivator(logical));
}
if (logical.GetScreenSizeProvider() is { } screenSizeProvider)
{
return Evaluate(screenSizeProvider);
}
return SelectorMatch.NeverThisInstance;
}
internal static SelectorMatch Evaluate(IScreenSizeProvider screenSizeProvider)
{
var match = screenSizeProvider.GetScreenWidth() > 600 && screenSizeProvider.GetScreenHeight() > 600;
return match ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
}
protected override Selector? MovePrevious() => _previous;
public override string ToString() => "screen";
}

5
src/Avalonia.Styling/Styling/Selectors.cs

@ -131,6 +131,11 @@ namespace Avalonia.Styling
return new NotSelector(previous, argument);
}
public static Selector Screen(this Selector? previous)
{
return new ScreenSelector(previous);
}
/// <inheritdoc cref="NthChildSelector"/>
/// <inheritdoc cref="NthChildSelector(Selector?, int, int)"/>
/// <returns>The selector.</returns>

11
src/Avalonia.Visuals/Visual.cs

@ -25,7 +25,7 @@ namespace Avalonia
/// extension methods defined in <see cref="VisualExtensions"/>.
/// </remarks>
[UsableDuringInitialization]
public class Visual : StyledElement, IVisual
public class Visual : StyledElement, IVisual, ITopLevelScreenSizeProvider
{
/// <summary>
/// Defines the <see cref="Bounds"/> property.
@ -418,6 +418,8 @@ namespace Avalonia
}
}
}
ScreenSizeProviderChanged?.Invoke(this, EventArgs.Empty);
}
/// <summary>
@ -645,5 +647,12 @@ namespace Avalonia
visual.SetVisualParent(parent);
}
}
public IScreenSizeProvider? GetScreenSizeProvider()
{
return VisualRoot as IScreenSizeProvider;
}
public event EventHandler? ScreenSizeProviderChanged;
}
}

18
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs

@ -139,6 +139,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
case SelectorGrammar.NotSyntax not:
result = new XamlIlNotSelector(result, Create(not.Argument, typeResolver));
break;
case SelectorGrammar.ScreenSyntax screen:
result = new XamlIlScreenSelector(result);
break;
case SelectorGrammar.NthChildSyntax nth:
result = new XamlIlNthChildSelector(result, nth.Step, nth.Offset, XamlIlNthChildSelector.SelectorType.NthChild);
break;
@ -349,6 +352,21 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
m => m.Name == _type.ToString() && m.Parameters.Count == 3);
}
}
class XamlIlScreenSelector : XamlIlSelectorNode
{
public XamlIlScreenSelector(XamlIlSelectorNode previous) : base(previous)
{
}
public override IXamlType TargetType => Previous?.TargetType;
protected override void DoEmit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
EmitCall(context, codeGen,
m => m.Name == "Screen" && m.Parameters.Count == 1);
}
}
class XamlIlPropertyEqualsSelector : XamlIlSelectorNode
{

19
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs

@ -169,6 +169,7 @@ namespace Avalonia.Markup.Parsers
const string IsKeyword = "is";
const string NotKeyword = "not";
const string ScreenKeyword = "screen";
const string NthChildKeyword = "nth-child";
const string NthLastChildKeyword = "nth-last-child";
@ -187,6 +188,14 @@ namespace Avalonia.Markup.Parsers
var syntax = new NotSyntax { Argument = argument };
return (State.Middle, syntax);
}
if(identifier.SequenceEqual(ScreenKeyword.AsSpan()) && r.TakeIf('('))
{
var argument = Parse(ref r, ')');
Expect(ref r, ')');
var syntax = new ScreenSyntax { Argument = argument };
return (State.Middle, syntax);
}
if (identifier.SequenceEqual(NthChildKeyword.AsSpan()) && r.TakeIf('('))
{
var (step, offset) = ParseNthChildArguments(ref r);
@ -605,6 +614,16 @@ namespace Avalonia.Markup.Parsers
return (obj is NotSyntax not) && Argument.SequenceEqual(not.Argument);
}
}
public class ScreenSyntax : ISyntax
{
public IEnumerable<ISyntax> Argument { get; set; } = Enumerable.Empty<ISyntax>();
public override bool Equals(object? obj)
{
return (obj is ScreenSyntax screen) && Argument.SequenceEqual(screen.Argument);
}
}
public class NthChildSyntax : ISyntax
{

3
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs

@ -149,6 +149,9 @@ namespace Avalonia.Markup.Parsers
case SelectorGrammar.NotSyntax not:
result = result.Not(x => Create(not.Argument)!);
break;
case SelectorGrammar.ScreenSyntax screen:
result = result.Screen();
break;
case SelectorGrammar.NthChildSyntax nth:
result = result.NthChild(nth.Step, nth.Offset);
break;

Loading…
Cancel
Save