Browse Source

max-width, min-height, max-height support.

pull/7938/head
Dan Walmsley 4 years ago
parent
commit
f0e3bf4e35
  1. 18
      samples/ControlCatalog/MainView.xaml
  2. 36
      src/Avalonia.Styling/Styling/Activators/ScreenActivator.cs
  3. 101
      src/Avalonia.Styling/Styling/ScreenSelector.cs
  4. 15
      src/Avalonia.Styling/Styling/Selectors.cs
  5. 64
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
  6. 27
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
  7. 9
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs

18
samples/ControlCatalog/MainView.xaml

@ -6,10 +6,24 @@
xmlns:models="clr-namespace:ControlCatalog.Models"> xmlns:models="clr-namespace:ControlCatalog.Models">
<Grid> <Grid>
<Grid.Styles> <Grid.Styles>
<Style Selector=":screen(test) TextBlock.h2"> <Style Selector=":min-width(600) TextBlock#MinWidth">
<Setter Property="Foreground" Value="Red" />
</Style>
<Style Selector=":max-width(800) TextBlock#MaxWidth">
<Setter Property="Foreground" Value="Red" />
</Style>
<Style Selector=":min-height(600) TextBlock#MinHeight">
<Setter Property="Foreground" Value="Red" />
</Style>
<Style Selector=":max-height(800) TextBlock#MaxHeight">
<Setter Property="Foreground" Value="Red" /> <Setter Property="Foreground" Value="Red" />
</Style> </Style>
</Grid.Styles> </Grid.Styles>
<TextBlock Classes="h2" Text="Testing" /> <StackPanel Spacing="10">
<TextBlock Name="MinWidth" Classes="h2" Text="min-width" />
<TextBlock Name="MaxWidth" Classes="h2" Text="max-width" />
<TextBlock Name="MinHeight" Classes="h2" Text="min-height" />
<TextBlock Name="MaxHeight" Classes="h2" Text="max-height" />
</StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

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

@ -15,6 +15,42 @@ internal sealed class MinWidthActivator : MediaQueryActivatorBase
protected override bool IsMatching() => CurrentMediaInfoProvider != null && MinWidthMediaSelector.Evaluate(CurrentMediaInfoProvider, _argument).IsMatch; protected override bool IsMatching() => CurrentMediaInfoProvider != null && MinWidthMediaSelector.Evaluate(CurrentMediaInfoProvider, _argument).IsMatch;
} }
internal sealed class MaxWidthActivator : MediaQueryActivatorBase
{
private readonly double _argument;
public MaxWidthActivator(ITopLevelScreenSizeProvider provider, double argument) : base(provider)
{
_argument = argument;
}
protected override bool IsMatching() => CurrentMediaInfoProvider != null && MaxWidthMediaSelector.Evaluate(CurrentMediaInfoProvider, _argument).IsMatch;
}
internal sealed class MinHeightActivator : MediaQueryActivatorBase
{
private readonly double _argument;
public MinHeightActivator(ITopLevelScreenSizeProvider provider, double argument) : base(provider)
{
_argument = argument;
}
protected override bool IsMatching() => CurrentMediaInfoProvider != null && MinHeightMediaSelector.Evaluate(CurrentMediaInfoProvider, _argument).IsMatch;
}
internal sealed class MaxHeightActivator : MediaQueryActivatorBase
{
private readonly double _argument;
public MaxHeightActivator(ITopLevelScreenSizeProvider provider, double argument) : base(provider)
{
_argument = argument;
}
protected override bool IsMatching() => CurrentMediaInfoProvider != null && MaxHeightMediaSelector.Evaluate(CurrentMediaInfoProvider, _argument).IsMatch;
}
internal abstract class MediaQueryActivatorBase : StyleActivatorBase internal abstract class MediaQueryActivatorBase : StyleActivatorBase
{ {
private readonly ITopLevelScreenSizeProvider _provider; private readonly ITopLevelScreenSizeProvider _provider;

101
src/Avalonia.Styling/Styling/ScreenSelector.cs

@ -32,11 +32,110 @@ public sealed class MinWidthMediaSelector : MediaSelector<double>
internal static SelectorMatch Evaluate(IScreenSizeProvider screenSizeProvider, double argument) internal static SelectorMatch Evaluate(IScreenSizeProvider screenSizeProvider, double argument)
{ {
return screenSizeProvider.GetScreenWidth() > argument ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; return screenSizeProvider.GetScreenWidth() >= argument ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
} }
public override string ToString() => "min-width"; public override string ToString() => "min-width";
} }
public sealed class MaxWidthMediaSelector : MediaSelector<double>
{
public MaxWidthMediaSelector(Selector? previous, double argument) : base(previous, argument)
{
}
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
if (!(control is ITopLevelScreenSizeProvider logical))
{
return SelectorMatch.NeverThisType;
}
if (subscribe)
{
return new SelectorMatch(new MaxWidthActivator(logical, Argument));
}
if (logical.GetScreenSizeProvider() is { } screenSizeProvider)
{
return Evaluate(screenSizeProvider, Argument);
}
return SelectorMatch.NeverThisInstance;
}
internal static SelectorMatch Evaluate(IScreenSizeProvider screenSizeProvider, double argument)
{
return screenSizeProvider.GetScreenWidth() <= argument ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
}
public override string ToString() => "max-width";
}
public sealed class MinHeightMediaSelector : MediaSelector<double>
{
public MinHeightMediaSelector(Selector? previous, double argument) : base(previous, argument)
{
}
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
if (!(control is ITopLevelScreenSizeProvider logical))
{
return SelectorMatch.NeverThisType;
}
if (subscribe)
{
return new SelectorMatch(new MinHeightActivator(logical, Argument));
}
if (logical.GetScreenSizeProvider() is { } screenSizeProvider)
{
return Evaluate(screenSizeProvider, Argument);
}
return SelectorMatch.NeverThisInstance;
}
internal static SelectorMatch Evaluate(IScreenSizeProvider screenSizeProvider, double argument)
{
return screenSizeProvider.GetScreenHeight() >= argument ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
}
public override string ToString() => "min-height";
}
public sealed class MaxHeightMediaSelector : MediaSelector<double>
{
public MaxHeightMediaSelector(Selector? previous, double argument) : base(previous, argument)
{
}
protected override SelectorMatch Evaluate(IStyleable control, bool subscribe)
{
if (!(control is ITopLevelScreenSizeProvider logical))
{
return SelectorMatch.NeverThisType;
}
if (subscribe)
{
return new SelectorMatch(new MaxHeightActivator(logical, Argument));
}
if (logical.GetScreenSizeProvider() is { } screenSizeProvider)
{
return Evaluate(screenSizeProvider, Argument);
}
return SelectorMatch.NeverThisInstance;
}
internal static SelectorMatch Evaluate(IScreenSizeProvider screenSizeProvider, double argument)
{
return screenSizeProvider.GetScreenHeight() <= argument ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
}
public override string ToString() => "max-height";
}
public abstract class MediaSelector<T> : Selector public abstract class MediaSelector<T> : Selector
{ {
private readonly Selector? _previous; private readonly Selector? _previous;

15
src/Avalonia.Styling/Styling/Selectors.cs

@ -135,6 +135,21 @@ namespace Avalonia.Styling
{ {
return new MinWidthMediaSelector(previous, argument); return new MinWidthMediaSelector(previous, argument);
} }
public static Selector MaxWidth(this Selector? previous, double argument)
{
return new MaxWidthMediaSelector(previous, argument);
}
public static Selector MinHeight(this Selector? previous, double argument)
{
return new MinHeightMediaSelector(previous, argument);
}
public static Selector MaxHeight(this Selector? previous, double argument)
{
return new MaxHeightMediaSelector(previous, argument);
}
/// <inheritdoc cref="NthChildSelector"/> /// <inheritdoc cref="NthChildSelector"/>
/// <inheritdoc cref="NthChildSelector(Selector?, int, int)"/> /// <inheritdoc cref="NthChildSelector(Selector?, int, int)"/>

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

@ -142,6 +142,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
case SelectorGrammar.MinWidthSyntax minWidth: case SelectorGrammar.MinWidthSyntax minWidth:
result = new XamlIlMinWidthSelector(result, minWidth.Argument); result = new XamlIlMinWidthSelector(result, minWidth.Argument);
break; break;
case SelectorGrammar.MaxWidthSyntax maxWidth:
result = new XamlIlMaxWidthSelector(result, maxWidth.Argument);
break;
case SelectorGrammar.MinHeightSyntax minHeight:
result = new XamlIlMinHeightSelector(result, minHeight.Argument);
break;
case SelectorGrammar.MaxHeightSyntax maxHeight:
result = new XamlIlMaxHeightSelector(result, maxHeight.Argument);
break;
case SelectorGrammar.NthChildSyntax nth: case SelectorGrammar.NthChildSyntax nth:
result = new XamlIlNthChildSelector(result, nth.Step, nth.Offset, XamlIlNthChildSelector.SelectorType.NthChild); result = new XamlIlNthChildSelector(result, nth.Step, nth.Offset, XamlIlNthChildSelector.SelectorType.NthChild);
break; break;
@ -370,6 +379,61 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
m => m.Name == "MinWidth" && m.Parameters.Count == 2); m => m.Name == "MinWidth" && m.Parameters.Count == 2);
} }
} }
class XamlIlMaxWidthSelector : XamlIlSelectorNode
{
private double _argument;
public XamlIlMaxWidthSelector(XamlIlSelectorNode previous, double argument) : base(previous)
{
_argument = argument;
}
public override IXamlType TargetType => Previous?.TargetType;
protected override void DoEmit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen.Ldc_R8(_argument);
EmitCall(context, codeGen,
m => m.Name == "MaxWidth" && m.Parameters.Count == 2);
}
}
class XamlIlMinHeightSelector : XamlIlSelectorNode
{
private double _argument;
public XamlIlMinHeightSelector(XamlIlSelectorNode previous, double argument) : base(previous)
{
_argument = argument;
}
public override IXamlType TargetType => Previous?.TargetType;
protected override void DoEmit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen.Ldc_R8(_argument);
EmitCall(context, codeGen,
m => m.Name == "MinHeight" && m.Parameters.Count == 2);
}
}
class XamlIlMaxHeightSelector : XamlIlSelectorNode
{
private double _argument;
public XamlIlMaxHeightSelector(XamlIlSelectorNode previous, double argument) : base(previous)
{
_argument = argument;
}
public override IXamlType TargetType => Previous?.TargetType;
protected override void DoEmit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen.Ldc_R8(_argument);
EmitCall(context, codeGen,
m => m.Name == "MaxHeight" && m.Parameters.Count == 2);
}
}
class XamlIlPropertyEqualsSelector : XamlIlSelectorNode class XamlIlPropertyEqualsSelector : XamlIlSelectorNode
{ {

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

@ -171,6 +171,9 @@ namespace Avalonia.Markup.Parsers
const string IsKeyword = "is"; const string IsKeyword = "is";
const string NotKeyword = "not"; const string NotKeyword = "not";
const string MinWidthKeyword = "min-width"; const string MinWidthKeyword = "min-width";
const string MaxWidthKeyword = "max-width";
const string MinHeightKeyword = "min-height";
const string MaxHeightKeyword = "max-height";
const string NthChildKeyword = "nth-child"; const string NthChildKeyword = "nth-child";
const string NthLastChildKeyword = "nth-last-child"; const string NthLastChildKeyword = "nth-last-child";
@ -197,6 +200,30 @@ namespace Avalonia.Markup.Parsers
var syntax = new MinWidthSyntax { Argument = argument }; var syntax = new MinWidthSyntax { Argument = argument };
return (State.Middle, syntax); return (State.Middle, syntax);
} }
if(identifier.SequenceEqual(MaxWidthKeyword.AsSpan()) && r.TakeIf('('))
{
var argument = ParseDecimal(ref r);
Expect(ref r, ')');
var syntax = new MaxWidthSyntax { Argument = argument };
return (State.Middle, syntax);
}
if(identifier.SequenceEqual(MinHeightKeyword.AsSpan()) && r.TakeIf('('))
{
var argument = ParseDecimal(ref r);
Expect(ref r, ')');
var syntax = new MinHeightSyntax { Argument = argument };
return (State.Middle, syntax);
}
if(identifier.SequenceEqual(MaxHeightKeyword.AsSpan()) && r.TakeIf('('))
{
var argument = ParseDecimal(ref r);
Expect(ref r, ')');
var syntax = new MaxHeightSyntax { Argument = argument };
return (State.Middle, syntax);
}
if (identifier.SequenceEqual(NthChildKeyword.AsSpan()) && r.TakeIf('(')) if (identifier.SequenceEqual(NthChildKeyword.AsSpan()) && r.TakeIf('('))
{ {
var (step, offset) = ParseNthChildArguments(ref r); var (step, offset) = ParseNthChildArguments(ref r);

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

@ -152,6 +152,15 @@ namespace Avalonia.Markup.Parsers
case SelectorGrammar.MinWidthSyntax minWidth: case SelectorGrammar.MinWidthSyntax minWidth:
result = result.MinWidth(minWidth.Argument); result = result.MinWidth(minWidth.Argument);
break; break;
case SelectorGrammar.MaxWidthSyntax maxWidth:
result = result.MaxWidth(maxWidth.Argument);
break;
case SelectorGrammar.MinHeightSyntax minHeight:
result = result.MinHeight(minHeight.Argument);
break;
case SelectorGrammar.MaxHeightSyntax maxHeight:
result = result.MaxHeight(maxHeight.Argument);
break;
case SelectorGrammar.NthChildSyntax nth: case SelectorGrammar.NthChildSyntax nth:
result = result.NthChild(nth.Step, nth.Offset); result = result.NthChild(nth.Step, nth.Offset);
break; break;

Loading…
Cancel
Save