Browse Source

Merge branch 'master' into dataobject-storage-file

pull/10389/head
Max Katz 4 years ago
committed by GitHub
parent
commit
46ac6567a4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 39
      src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
  2. 6
      src/Avalonia.Base/Layout/Layoutable.cs
  3. 2
      src/Avalonia.Base/Media/FormattedText.cs
  4. 4
      src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs
  5. 13
      src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
  6. 21
      src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
  7. 9
      src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
  8. 191
      src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
  9. 25
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  10. 7
      src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
  11. 47
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  12. 7
      src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
  13. 7
      src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs
  14. 2
      src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs
  15. 4
      src/Avalonia.Base/Media/TextTrailingTrimming.cs
  16. 48
      src/Avalonia.Base/StyledElement.cs
  17. 22
      src/Avalonia.Base/Styling/IGlobalThemeVariantProvider.cs
  18. 26
      src/Avalonia.Base/Styling/IThemeVariantHost.cs
  19. 17
      src/Avalonia.Base/Styling/ThemeVariant.cs
  20. 71
      src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
  21. 19
      src/Avalonia.Controls/Application.cs
  22. 2
      src/Avalonia.Controls/Control.cs
  23. 9
      src/Avalonia.Controls/Primitives/Popup.cs
  24. 8
      src/Avalonia.Controls/ThemeVariantScope.cs
  25. 14
      src/Avalonia.Controls/TopLevel.cs
  26. 2
      src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs
  27. 104
      src/Avalonia.Themes.Simple/Controls/SplitButton.xaml
  28. 6
      src/Avalonia.Themes.Simple/Controls/SplitView.xaml
  29. 91
      src/Avalonia.Themes.Simple/Controls/ToggleSwitch.xaml
  30. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  31. 40
      tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs
  32. 77
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ThemeDictionariesTests.cs
  33. 2
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
  34. 2
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

39
src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

@ -138,18 +138,18 @@ namespace Avalonia.Controls
protected override void Initialize()
{
_target.ResourcesChanged += ResourcesChanged;
if (_target is StyledElement themeStyleable)
if (_target is IThemeVariantHost themeVariantHost)
{
themeStyleable.PropertyChanged += PropertyChanged;
themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged;
}
}
protected override void Deinitialize()
{
_target.ResourcesChanged -= ResourcesChanged;
if (_target is StyledElement themeStyleable)
if (_target is IThemeVariantHost themeVariantHost)
{
themeStyleable.PropertyChanged -= PropertyChanged;
themeVariantHost.ActualThemeVariantChanged -= ActualThemeVariantChanged;
}
}
@ -163,18 +163,15 @@ namespace Avalonia.Controls
PublishNext(GetValue());
}
private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
private void ActualThemeVariantChanged(object? sender, EventArgs e)
{
if (e.Property == StyledElement.ActualThemeVariantProperty)
{
PublishNext(GetValue());
}
PublishNext(GetValue());
}
private object? GetValue()
{
if (_target is not StyledElement themeStyleable
|| !_target.TryFindResource(_key, themeStyleable.ActualThemeVariant, out var value))
if (_target is not IThemeVariantHost themeVariantHost
|| !_target.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value))
{
value = _target.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
}
@ -236,9 +233,9 @@ namespace Avalonia.Controls
{
_owner.ResourcesChanged -= ResourcesChanged;
}
if (_owner is StyledElement styleable)
if (_owner is IThemeVariantHost themeVariantHost)
{
styleable.PropertyChanged += PropertyChanged;
themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged;
}
_owner = _target.Owner;
@ -247,20 +244,18 @@ namespace Avalonia.Controls
{
_owner.ResourcesChanged += ResourcesChanged;
}
if (_owner is StyledElement styleable2)
if (_owner is IThemeVariantHost themeVariantHost2)
{
styleable2.PropertyChanged += PropertyChanged;
themeVariantHost2.ActualThemeVariantChanged -= ActualThemeVariantChanged;
}
PublishNext();
}
private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
private void ActualThemeVariantChanged(object? sender, EventArgs e)
{
if (e.Property == StyledElement.ActualThemeVariantProperty)
{
PublishNext();
}
PublishNext();
}
private void ResourcesChanged(object? sender, ResourcesChangedEventArgs e)
@ -270,8 +265,8 @@ namespace Avalonia.Controls
private object? GetValue()
{
if (!(_target.Owner is StyledElement themeStyleable)
|| !_target.Owner.TryFindResource(_key, themeStyleable.ActualThemeVariant, out var value))
if (!(_target.Owner is IThemeVariantHost themeVariantHost)
|| !_target.Owner.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value))
{
value = _target.Owner?.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
}

6
src/Avalonia.Base/Layout/Layoutable.cs

@ -798,6 +798,12 @@ namespace Avalonia.Layout
InvalidateMeasure();
}
internal override void OnTemplatedParentControlThemeChanged()
{
base.OnTemplatedParentControlThemeChanged();
InvalidateMeasure();
}
/// <summary>
/// Called when the layout manager raises a LayoutUpdated event.
/// </summary>

2
src/Avalonia.Base/Media/FormattedText.cs

@ -877,7 +877,7 @@ namespace Avalonia.Media
var lastRunProps = (GenericTextRunProperties)thatFormatRider.CurrentElement!;
TextCollapsingProperties collapsingProperties = _that._trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(maxLineLength, lastRunProps));
TextCollapsingProperties collapsingProperties = _that._trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(maxLineLength, lastRunProps, paraProps.FlowDirection));
var collapsedLine = line.Collapse(collapsingProperties);

4
src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs

@ -6,11 +6,13 @@ namespace Avalonia.Media
{
public readonly double Width;
public readonly TextRunProperties TextRunProperties;
public readonly FlowDirection FlowDirection;
public TextCollapsingCreateInfo(double width, TextRunProperties textRunProperties)
public TextCollapsingCreateInfo(double width, TextRunProperties textRunProperties, FlowDirection flowDirection)
{
Width = width;
TextRunProperties = textRunProperties;
FlowDirection = flowDirection;
}
}
}

13
src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs

@ -27,16 +27,6 @@ namespace Avalonia.Media.TextFormatting
return;
}
if (lineImpl.NewLineLength > 0)
{
return;
}
if (lineImpl.TextLineBreak is { TextEndOfLine: not null, IsSplit: false })
{
return;
}
var breakOportunities = new Queue<int>();
var currentPosition = textLine.FirstTextSourceIndex;
@ -97,7 +87,8 @@ namespace Avalonia.Media.TextFormatting
continue;
}
var glyphIndex = glyphRun.FindGlyphIndex(characterIndex);
var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster);
var glyphIndex = glyphRun.FindGlyphIndex(characterIndex - offset);
var glyphInfo = shapedBuffer.GlyphInfos[glyphIndex];
shapedBuffer.GlyphInfos[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex,

21
src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs

@ -148,33 +148,38 @@ namespace Avalonia.Media.TextFormatting
internal SplitResult<ShapedTextRun> Split(int length)
{
if (IsReversed)
var isReversed = IsReversed;
if (isReversed)
{
Reverse();
}
length = Length - length;
}
#if DEBUG
if(length == 0)
if (length == 0)
{
throw new ArgumentOutOfRangeException(nameof(length), "length must be greater than zero.");
}
#endif
#endif
var splitBuffer = ShapedBuffer.Split(length);
var first = new ShapedTextRun(splitBuffer.First, Properties);
#if DEBUG
#if DEBUG
if (first.Length != length)
{
throw new InvalidOperationException("Split length mismatch.");
}
#endif
var second = new ShapedTextRun(splitBuffer.Second!, Properties);
if (isReversed)
{
return new SplitResult<ShapedTextRun>(second, first);
}
return new SplitResult<ShapedTextRun>(first, second);
}

9
src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs

@ -1,4 +1,6 @@
namespace Avalonia.Media.TextFormatting
using System.Collections.Generic;
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// Properties of text collapsing.
@ -15,6 +17,11 @@
/// </summary>
public abstract TextRun Symbol { get; }
/// <summary>
/// Gets the flow direction that is used for collapsing.
/// </summary>
public abstract FlowDirection FlowDirection { get; }
/// <summary>
/// Collapses given text line.
/// </summary>

191
src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using Avalonia.Media.TextFormatting.Unicode;
namespace Avalonia.Media.TextFormatting
@ -28,97 +27,191 @@ namespace Avalonia.Media.TextFormatting
var availableWidth = properties.Width - shapedSymbol.Size.Width;
while (runIndex < textRuns.Count)
if(properties.FlowDirection== FlowDirection.LeftToRight)
{
var currentRun = textRuns[runIndex];
switch (currentRun)
while (runIndex < textRuns.Count)
{
case ShapedTextRun shapedRun:
{
currentWidth += shapedRun.Size.Width;
var currentRun = textRuns[runIndex];
if (currentWidth > availableWidth)
switch (currentRun)
{
case ShapedTextRun shapedRun:
{
if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength))
currentWidth += shapedRun.Size.Width;
if (currentWidth > availableWidth)
{
if (isWordEllipsis && measuredLength < textLine.Length)
if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength))
{
var currentBreakPosition = 0;
var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak))
if (isWordEllipsis && measuredLength < textLine.Length)
{
var nextBreakPosition = lineBreak.PositionMeasure;
var currentBreakPosition = 0;
if (nextBreakPosition == 0)
{
break;
}
var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
if (nextBreakPosition >= measuredLength)
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak))
{
break;
var nextBreakPosition = lineBreak.PositionMeasure;
if (nextBreakPosition == 0)
{
break;
}
if (nextBreakPosition >= measuredLength)
{
break;
}
currentBreakPosition = nextBreakPosition;
}
currentBreakPosition = nextBreakPosition;
measuredLength = currentBreakPosition;
}
measuredLength = currentBreakPosition;
}
collapsedLength += measuredLength;
return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol);
}
collapsedLength += measuredLength;
availableWidth -= shapedRun.Size.Width;
return CreateCollapsedRuns(textRuns, collapsedLength, shapedSymbol);
break;
}
availableWidth -= shapedRun.Size.Width;
case DrawableTextRun drawableRun:
{
//The whole run needs to fit into available space
if (currentWidth + drawableRun.Size.Width > availableWidth)
{
return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol);
}
break;
}
availableWidth -= drawableRun.Size.Width;
case DrawableTextRun drawableRun:
{
//The whole run needs to fit into available space
if (currentWidth + drawableRun.Size.Width > availableWidth)
{
return CreateCollapsedRuns(textRuns, collapsedLength, shapedSymbol);
break;
}
}
availableWidth -= drawableRun.Size.Width;
collapsedLength += currentRun.Length;
break;
}
runIndex++;
}
}
else
{
runIndex = textRuns.Count - 1;
while (runIndex >= 0)
{
var currentRun = textRuns[runIndex];
collapsedLength += currentRun.Length;
switch (currentRun)
{
case ShapedTextRun shapedRun:
{
currentWidth += shapedRun.Size.Width;
runIndex++;
}
if (currentWidth > availableWidth)
{
if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength))
{
if (isWordEllipsis && measuredLength < textLine.Length)
{
var currentBreakPosition = 0;
var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak))
{
var nextBreakPosition = lineBreak.PositionMeasure;
if (nextBreakPosition == 0)
{
break;
}
if (nextBreakPosition >= measuredLength)
{
break;
}
currentBreakPosition = nextBreakPosition;
}
measuredLength = currentBreakPosition;
}
}
collapsedLength += measuredLength;
return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol);
}
availableWidth -= shapedRun.Size.Width;
break;
}
case DrawableTextRun drawableRun:
{
//The whole run needs to fit into available space
if (currentWidth + drawableRun.Size.Width > availableWidth)
{
return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol);
}
availableWidth -= drawableRun.Size.Width;
break;
}
}
collapsedLength += currentRun.Length;
runIndex--;
}
}
return null;
}
private static TextRun[] CreateCollapsedRuns(IReadOnlyList<TextRun> textRuns, int collapsedLength,
TextRun shapedSymbol)
private static TextRun[] CreateCollapsedRuns(TextLine textLine, int collapsedLength,
FlowDirection flowDirection, TextRun shapedSymbol)
{
var textRuns = textLine.TextRuns;
if (collapsedLength <= 0)
{
return new[] { shapedSymbol };
}
if(flowDirection == FlowDirection.RightToLeft)
{
collapsedLength = textLine.Length - collapsedLength;
}
var objectPool = FormattingObjectPool.Instance;
var (preSplitRuns, postSplitRuns) = TextFormatterImpl.SplitTextRuns(textRuns, collapsedLength, objectPool);
try
{
var collapsedRuns = new TextRun[preSplitRuns.Count + 1];
preSplitRuns.CopyTo(collapsedRuns);
collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol;
return collapsedRuns;
if (flowDirection == FlowDirection.RightToLeft)
{
var collapsedRuns = new TextRun[postSplitRuns!.Count + 1];
postSplitRuns.CopyTo(collapsedRuns, 1);
collapsedRuns[0] = shapedSymbol;
return collapsedRuns;
}
else
{
var collapsedRuns = new TextRun[preSplitRuns!.Count + 1];
preSplitRuns.CopyTo(collapsedRuns);
collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol;
return collapsedRuns;
}
}
finally
{

25
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

@ -352,7 +352,7 @@ namespace Avalonia.Media.TextFormatting
var lastTrailingIndex = 0;
if(_paragraphProperties.FlowDirection== FlowDirection.LeftToRight)
if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight)
{
lastTrailingIndex = textLine.FirstTextSourceIndex + textLine.Length;
@ -377,7 +377,7 @@ namespace Avalonia.Media.TextFormatting
{
lastTrailingIndex += textEndOfLine.Length;
}
}
}
var textPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
@ -553,26 +553,18 @@ namespace Avalonia.Media.TextFormatting
if (_paragraphProperties.TextAlignment == TextAlignment.Justify)
{
var whitespaceWidth = 0d;
var justificationWidth = MaxWidth;
for (var i = 0; i < textLines.Count; i++)
if (_paragraphProperties.TextWrapping != TextWrapping.NoWrap)
{
var line = textLines[i];
var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace;
if (lineWhitespaceWidth > whitespaceWidth)
{
whitespaceWidth = lineWhitespaceWidth;
}
justificationWidth = width;
}
var justificationWidth = width - whitespaceWidth;
if (justificationWidth > 0)
{
var justificationProperties = new InterWordJustification(justificationWidth);
for (var i = 0; i < textLines.Count - 1; i++)
for (var i = 0; i < textLines.Count; i++)
{
var line = textLines[i];
@ -597,12 +589,13 @@ namespace Avalonia.Media.TextFormatting
/// <returns>The <see cref="TextCollapsingProperties"/>.</returns>
private TextCollapsingProperties? GetCollapsingProperties(double width)
{
if(_textTrimming == TextTrimming.None)
if (_textTrimming == TextTrimming.None)
{
return null;
}
return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties));
return _textTrimming.CreateCollapsingProperties(
new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties, _paragraphProperties.FlowDirection));
}
public void Dispose()

7
src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs

@ -19,11 +19,13 @@ namespace Avalonia.Media.TextFormatting
/// <param name="prefixLength">Length of leading prefix.</param>
/// <param name="width">width in which collapsing is constrained to</param>
/// <param name="textRunProperties">text run properties of ellipsis symbol</param>
/// <param name="flowDirection">the flow direction of the collapes line.</param>
public TextLeadingPrefixCharacterEllipsis(
string ellipsis,
int prefixLength,
double width,
TextRunProperties textRunProperties)
TextRunProperties textRunProperties,
FlowDirection flowDirection)
{
if (_prefixLength < 0)
{
@ -33,6 +35,7 @@ namespace Avalonia.Media.TextFormatting
_prefixLength = prefixLength;
Width = width;
Symbol = new TextCharacters(ellipsis, textRunProperties);
FlowDirection = flowDirection;
}
/// <inheritdoc/>
@ -41,6 +44,8 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/>
public override TextRun Symbol { get; }
public override FlowDirection FlowDirection { get; }
/// <inheritdoc />
public override TextRun[]? Collapse(TextLine textLine)
{

47
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -658,7 +658,7 @@ namespace Avalonia.Media.TextFormatting
currentX += drawableTextRun.Size.Width;
}
if(lastRunIndex - 1 < 0)
if (lastRunIndex - 1 < 0)
{
break;
}
@ -685,7 +685,7 @@ namespace Avalonia.Media.TextFormatting
directionalWidth -= drawableTextRun.Size.Width;
}
if(firstRunIndex + 1 == _textRuns.Length)
if (firstRunIndex + 1 == _textRuns.Length)
{
break;
}
@ -1097,7 +1097,7 @@ namespace Avalonia.Media.TextFormatting
var runWidth = endX - startX;
return new TextRunBounds(new Rect(Start + startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
return new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
}
public override void Dispose()
@ -1439,13 +1439,6 @@ namespace Avalonia.Media.TextFormatting
}
}
if (index == lastRunIndex)
{
width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width;
trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength;
newLineLength += textRun.GlyphRun.Metrics.NewLineLength;
}
widthIncludingWhitespace += textRun.Size.Width;
break;
@ -1455,12 +1448,6 @@ namespace Avalonia.Media.TextFormatting
{
widthIncludingWhitespace += drawableTextRun.Size.Width;
if (index == lastRunIndex)
{
width = widthIncludingWhitespace;
trailingWhitespaceLength = 0;
}
if (drawableTextRun.Size.Height > height)
{
height = drawableTextRun.Size.Height;
@ -1476,6 +1463,32 @@ namespace Avalonia.Media.TextFormatting
}
}
width = widthIncludingWhitespace;
for (var i = _textRuns.Length - 1; i >= 0; i--)
{
var currentRun = _textRuns[i];
if(currentRun is ShapedTextRun shapedText)
{
var glyphRun = shapedText.GlyphRun;
var glyphRunMetrics = glyphRun.Metrics;
newLineLength += glyphRunMetrics.NewLineLength;
if (glyphRunMetrics.TrailingWhitespaceLength == 0)
{
break;
}
trailingWhitespaceLength += glyphRunMetrics.TrailingWhitespaceLength;
var whitespaceWidth = glyphRun.Size.Width - glyphRunMetrics.Width;
width -= whitespaceWidth;
}
}
var start = GetParagraphOffsetX(width, widthIncludingWhitespace);
if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight))
@ -1543,7 +1556,7 @@ namespace Avalonia.Media.TextFormatting
return Math.Max(0, start);
case TextAlignment.Right:
return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace);
return Math.Max(0, _paragraphWidth - width);
default:
return 0;

7
src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs

@ -12,10 +12,13 @@
/// <param name="ellipsis">Text used as collapsing symbol.</param>
/// <param name="width">Width in which collapsing is constrained to.</param>
/// <param name="textRunProperties">Text run properties of ellipsis symbol.</param>
public TextTrailingCharacterEllipsis(string ellipsis, double width, TextRunProperties textRunProperties)
/// <param name="flowDirection">The flow direction of the collapsed line.</param>
public TextTrailingCharacterEllipsis(string ellipsis, double width,
TextRunProperties textRunProperties, FlowDirection flowDirection)
{
Width = width;
Symbol = new TextCharacters(ellipsis, textRunProperties);
FlowDirection = flowDirection;
}
/// <inheritdoc/>
@ -24,6 +27,8 @@
/// <inheritdoc/>
public override TextRun Symbol { get; }
public override FlowDirection FlowDirection { get; }
/// <inheritdoc />
public override TextRun[]? Collapse(TextLine textLine)
{

7
src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs

@ -12,14 +12,17 @@
/// <param name="ellipsis">Text used as collapsing symbol.</param>
/// <param name="width">width in which collapsing is constrained to.</param>
/// <param name="textRunProperties">text run properties of ellipsis symbol.</param>
/// <param name="flowDirection">flow direction of the collapsed line.</param>
public TextTrailingWordEllipsis(
string ellipsis,
double width,
TextRunProperties textRunProperties
TextRunProperties textRunProperties,
FlowDirection flowDirection
)
{
Width = width;
Symbol = new TextCharacters(ellipsis, textRunProperties);
FlowDirection = flowDirection;
}
/// <inheritdoc/>
@ -28,6 +31,8 @@
/// <inheritdoc/>
public override TextRun Symbol { get; }
public override FlowDirection FlowDirection { get; }
/// <inheritdoc />
public override TextRun[]? Collapse(TextLine textLine)
{

2
src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs

@ -15,7 +15,7 @@ namespace Avalonia.Media
public override TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo)
{
return new TextLeadingPrefixCharacterEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties);
return new TextLeadingPrefixCharacterEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection);
}
public override string ToString()

4
src/Avalonia.Base/Media/TextTrailingTrimming.cs

@ -17,10 +17,10 @@ namespace Avalonia.Media
{
if (_isWordBased)
{
return new TextTrailingWordEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties);
return new TextTrailingWordEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection);
}
return new TextTrailingCharacterEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties);
return new TextTrailingCharacterEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection);
}
public override string ToString()

48
src/Avalonia.Base/StyledElement.cs

@ -28,7 +28,7 @@ namespace Avalonia
public class StyledElement : Animatable,
IDataContextProvider,
ILogical,
IResourceHost,
IThemeVariantHost,
IStyleHost,
IStyleable,
ISetLogicalParent,
@ -75,23 +75,6 @@ namespace Avalonia
public static readonly StyledProperty<ControlTheme?> ThemeProperty =
AvaloniaProperty.Register<StyledElement, ControlTheme?>(nameof(Theme));
/// <summary>
/// Defines the <see cref="ActualThemeVariant"/> property.
/// </summary>
public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
AvaloniaProperty.Register<StyledElement, ThemeVariant>(
nameof(ThemeVariant),
inherits: true,
defaultValue: ThemeVariant.Light);
/// <summary>
/// Defines the RequestedThemeVariant property.
/// </summary>
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
AvaloniaProperty.Register<StyledElement, ThemeVariant?>(
nameof(ThemeVariant),
defaultValue: ThemeVariant.Default);
private static readonly ControlTheme s_invalidTheme = new ControlTheme();
private int _initCount;
private string? _name;
@ -160,6 +143,9 @@ namespace Avalonia
/// </summary>
public event EventHandler<ResourcesChangedEventArgs>? ResourcesChanged;
/// <inheritdoc />
public event EventHandler? ActualThemeVariantChanged;
/// <summary>
/// Gets or sets the name of the styled element.
/// </summary>
@ -278,15 +264,6 @@ namespace Avalonia
set => SetValue(ThemeProperty, value);
}
/// <summary>
/// Gets the UI theme that is currently used by the element, which might be different than the <see cref="RequestedThemeVariantProperty"/>.
/// </summary>
/// <returns>
/// If current control is contained in the ThemeVariantScope, TopLevel or Application with non-default RequestedThemeVariant, that value will be returned.
/// Otherwise, current OS theme variant is returned.
/// </returns>
public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
/// <summary>
/// Gets the styled element's logical children.
/// </summary>
@ -325,6 +302,9 @@ namespace Avalonia
/// </summary>
public StyledElement? Parent { get; private set; }
/// <inheritdoc />
public ThemeVariant ActualThemeVariant => GetValue(ThemeVariant.ActualThemeVariantProperty);
/// <summary>
/// Gets the styled element's logical parent.
/// </summary>
@ -394,7 +374,7 @@ namespace Avalonia
/// </returns>
public bool ApplyStyling()
{
if (_initCount == 0 && (!_stylesApplied || !_themeApplied))
if (_initCount == 0 && (!_stylesApplied || !_themeApplied || !_templatedParentThemeApplied))
{
GetValueStore().BeginStyling();
@ -644,13 +624,19 @@ namespace Avalonia
base.OnPropertyChanged(change);
if (change.Property == ThemeProperty)
{
OnControlThemeChanged();
else if (change.Property == RequestedThemeVariantProperty)
}
else if (change.Property == ThemeVariant.RequestedThemeVariantProperty)
{
if (change.GetNewValue<ThemeVariant>() is {} themeVariant && themeVariant != ThemeVariant.Default)
SetValue(ActualThemeVariantProperty, themeVariant);
SetValue(ThemeVariant.ActualThemeVariantProperty, themeVariant);
else
ClearValue(ActualThemeVariantProperty);
ClearValue(ThemeVariant.ActualThemeVariantProperty);
}
else if (change.Property == ThemeVariant.ActualThemeVariantProperty)
{
ActualThemeVariantChanged?.Invoke(this, EventArgs.Empty);
}
}

22
src/Avalonia.Base/Styling/IGlobalThemeVariantProvider.cs

@ -1,22 +0,0 @@
using System;
using Avalonia.Controls;
using Avalonia.Metadata;
namespace Avalonia.Styling;
/// <summary>
/// Interface for an application host element with a root theme variant.
/// </summary>
[Unstable]
public interface IGlobalThemeVariantProvider : IResourceHost
{
/// <summary>
/// Gets the UI theme variant that is used by the control (and its child elements) for resource determination.
/// </summary>
ThemeVariant ActualThemeVariant { get; }
/// <summary>
/// Raised when the theme variant is changed on the element or an ancestor of the element.
/// </summary>
event EventHandler? ActualThemeVariantChanged;
}

26
src/Avalonia.Base/Styling/IThemeVariantHost.cs

@ -0,0 +1,26 @@
using System;
using Avalonia.Controls;
using Avalonia.Metadata;
namespace Avalonia.Styling;
/// <summary>
/// Interface for the host element with a theme variant.
/// </summary>
[Unstable]
public interface IThemeVariantHost : IResourceHost
{
/// <summary>
/// Gets the UI theme that is currently used by the element, which might be different than the RequestedThemeVariantProperty.
/// </summary>
/// <returns>
/// If current control is contained in the ThemeVariantScope, TopLevel or Application with non-default RequestedThemeVariant, that value will be returned.
/// Otherwise, current OS theme variant is returned.
/// </returns>
ThemeVariant ActualThemeVariant { get; }
/// <summary>
/// Raised when the theme variant is changed on the element or an ancestor of the element.
/// </summary>
event EventHandler? ActualThemeVariantChanged;
}

17
src/Avalonia.Base/Styling/ThemeVariant.cs

@ -6,11 +6,26 @@ using Avalonia.Platform;
namespace Avalonia.Styling;
/// <summary>
/// Specifies a UI theme variant that should be used for the
/// Specifies a UI theme variant that should be used for the Control and Application types.
/// </summary>
[TypeConverter(typeof(ThemeVariantTypeConverter))]
public sealed record ThemeVariant
{
/// <summary>
/// Defines the ActualThemeVariant property.
/// </summary>
internal static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
AvaloniaProperty.Register<StyledElement, ThemeVariant>(
"ActualThemeVariant",
inherits: true);
/// <summary>
/// Defines the RequestedThemeVariant property.
/// </summary>
internal static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
AvaloniaProperty.Register<StyledElement, ThemeVariant?>(
"RequestedThemeVariant", defaultValue: Default);
/// <summary>
/// Creates a new instance of the <see cref="ThemeVariant"/>
/// </summary>

71
src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml

@ -1,6 +1,48 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:collections="using:Avalonia.Collections">
<Styles.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
<SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
<SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
<SolidColorBrush x:Key="DataGridGridLinesBrush" Opacity="0.4" Color="{DynamicResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
</ResourceDictionary>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
<SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
<SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
<SolidColorBrush x:Key="DataGridGridLinesBrush" Opacity="0.4" Color="{DynamicResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<x:Double x:Key="ListAccentLowOpacity">0.6</x:Double>
<x:Double x:Key="ListAccentMediumOpacity">0.8</x:Double>
@ -9,19 +51,6 @@
<StreamGeometry x:Key="DataGridRowGroupHeaderIconClosedPath">M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z</StreamGeometry>
<StreamGeometry x:Key="DataGridRowGroupHeaderIconOpenedPath">M109 486 19 576 1024 1581 2029 576 1939 486 1024 1401z</StreamGeometry>
<SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush"
Color="{DynamicResource SystemChromeMediumLowColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush"
Color="{DynamicResource SystemListMediumColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
<StaticResource x:Key="DataGridRowBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
<SolidColorBrush x:Key="DataGridRowSelectedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="DataGridRowSelectedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
@ -29,23 +58,10 @@
<StaticResource x:Key="DataGridRowSelectedHoveredBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
<SolidColorBrush x:Key="DataGridRowSelectedUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
<SolidColorBrush x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundBrush"
Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
<SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
<StaticResource x:Key="DataGridCellBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
<SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
<SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
<SolidColorBrush x:Key="DataGridGridLinesBrush"
Opacity="0.4"
Color="{DynamicResource SystemBaseMediumLowColor}" />
<StaticResource x:Key="DataGridCurrencyVisualPrimaryBrush" ResourceKey="SystemControlTransparentBrush" />
<SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush"
Color="{DynamicResource SystemChromeMediumLowColor}" />
<StaticResource x:Key="DataGridFillerColumnGridLinesBrush" ResourceKey="SystemControlTransparentBrush" />
<ControlTheme x:Key="DataGridCellTextBlockTheme" TargetType="TextBlock">
@ -565,5 +581,6 @@
</Style>
</Style>
</ControlTheme>
</ResourceDictionary>
</Styles.Resources>
</Styles>

19
src/Avalonia.Controls/Application.cs

@ -29,7 +29,7 @@ namespace Avalonia
/// method.
/// - Tracks the lifetime of the application.
/// </remarks>
public class Application : AvaloniaObject, IDataContextProvider, IGlobalDataTemplates, IGlobalStyles, IGlobalThemeVariantProvider, IApplicationPlatformEvents
public class Application : AvaloniaObject, IDataContextProvider, IGlobalDataTemplates, IGlobalStyles, IThemeVariantHost, IApplicationPlatformEvents
{
/// <summary>
/// The application-global data templates.
@ -50,13 +50,13 @@ namespace Avalonia
public static readonly StyledProperty<object?> DataContextProperty =
StyledElement.DataContextProperty.AddOwner<Application>();
/// <inheritdoc cref="StyledElement.ActualThemeVariantProperty" />
/// <inheritdoc cref="ThemeVariantScope.ActualThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
StyledElement.ActualThemeVariantProperty.AddOwner<Application>();
ThemeVariantScope.ActualThemeVariantProperty.AddOwner<Application>();
/// <inheritdoc cref="StyledElement.RequestedThemeVariantProperty" />
/// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
StyledElement.RequestedThemeVariantProperty.AddOwner<Application>();
ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<Application>();
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs>? ResourcesChanged;
@ -95,11 +95,8 @@ namespace Avalonia
set => SetValue(RequestedThemeVariantProperty, value);
}
/// <inheritdoc cref="StyledElement.ActualThemeVariant"/>
public ThemeVariant ActualThemeVariant
{
get => GetValue(ActualThemeVariantProperty);
}
/// <inheritdoc />
public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
/// <summary>
/// Gets the current instance of the <see cref="Application"/> class.
@ -256,7 +253,7 @@ namespace Avalonia
.Bind<IAccessKeyHandler>().ToTransient<AccessKeyHandler>()
.Bind<IGlobalDataTemplates>().ToConstant(this)
.Bind<IGlobalStyles>().ToConstant(this)
.Bind<IGlobalThemeVariantProvider>().ToConstant(this)
.Bind<IThemeVariantHost>().ToConstant(this)
.Bind<IFocusManager>().ToConstant(FocusManager)
.Bind<IInputManager>().ToConstant(InputManager)
.Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()

2
src/Avalonia.Controls/Control.cs

@ -161,7 +161,7 @@ namespace Avalonia.Controls
get => GetValue(TagProperty);
set => SetValue(TagProperty, value);
}
/// <summary>
/// Occurs when the user has completed a context input gesture, such as a right-click.
/// </summary>

9
src/Avalonia.Controls/Primitives/Popup.cs

@ -387,6 +387,15 @@ namespace Avalonia.Controls.Primitives
popupHost.Transform = null;
}
if (popupHost is PopupRoot topLevelPopup)
{
topLevelPopup
.Bind(
ThemeVariantScope.ActualThemeVariantProperty,
this.GetBindingObservable(ThemeVariantScope.ActualThemeVariantProperty))
.DisposeWith(handlerCleanup);
}
UpdateHostPosition(popupHost, placementTarget);
SubscribeToEventHandler<IPopupHost, EventHandler<TemplateAppliedEventArgs>>(popupHost, RootTemplateApplied,

8
src/Avalonia.Controls/ThemeVariantScope.cs

@ -7,6 +7,14 @@ namespace Avalonia.Controls
/// </summary>
public class ThemeVariantScope : Decorator
{
/// <inheritdoc cref="ThemeVariant.ActualThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
ThemeVariant.ActualThemeVariantProperty.AddOwner<ThemeVariantScope>();
/// <inheritdoc cref="ThemeVariant.RequestedThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
ThemeVariant.RequestedThemeVariantProperty.AddOwner<ThemeVariantScope>();
/// <summary>
/// Gets or sets the UI theme variant that is used by the control (and its child elements) for resource determination.
/// The UI theme you specify with ThemeVariant can override the app-level ThemeVariant.

14
src/Avalonia.Controls/TopLevel.cs

@ -80,6 +80,14 @@ namespace Avalonia.Controls
public static readonly StyledProperty<IBrush> TransparencyBackgroundFallbackProperty =
AvaloniaProperty.Register<TopLevel, IBrush>(nameof(TransparencyBackgroundFallback), Brushes.White);
/// <inheritdoc cref="ThemeVariantScope.ActualThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
ThemeVariantScope.ActualThemeVariantProperty.AddOwner<Application>();
/// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<Application>();
/// <summary>
/// Defines the <see cref="BackRequested"/> event.
/// </summary>
@ -96,7 +104,7 @@ namespace Avalonia.Controls
private readonly IAccessKeyHandler? _accessKeyHandler;
private readonly IKeyboardNavigationHandler? _keyboardNavigationHandler;
private readonly IGlobalStyles? _globalStyles;
private readonly IGlobalThemeVariantProvider? _applicationThemeHost;
private readonly IThemeVariantHost? _applicationThemeHost;
private readonly PointerOverPreProcessor? _pointerOverPreProcessor;
private readonly IDisposable? _pointerOverPreProcessorSubscription;
private readonly IDisposable? _backGestureSubscription;
@ -147,7 +155,7 @@ namespace Avalonia.Controls
_inputManager = TryGetService<IInputManager>(dependencyResolver);
_keyboardNavigationHandler = TryGetService<IKeyboardNavigationHandler>(dependencyResolver);
_globalStyles = TryGetService<IGlobalStyles>(dependencyResolver);
_applicationThemeHost = TryGetService<IGlobalThemeVariantProvider>(dependencyResolver);
_applicationThemeHost = TryGetService<IThemeVariantHost>(dependencyResolver);
Renderer = impl.CreateRenderer(this);
Renderer.SceneInvalidated += SceneInvalidated;
@ -642,7 +650,7 @@ namespace Avalonia.Controls
private void GlobalActualThemeVariantChanged(object? sender, EventArgs e)
{
SetValue(ActualThemeVariantProperty, ((IGlobalThemeVariantProvider)sender!).ActualThemeVariant, BindingPriority.Template);
SetValue(ActualThemeVariantProperty, ((IThemeVariantHost)sender!).ActualThemeVariant, BindingPriority.Template);
}
private void SceneInvalidated(object? sender, SceneInvalidatedEventArgs e)

2
src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs

@ -14,7 +14,7 @@ namespace Avalonia.Diagnostics.Controls
public event EventHandler? Closed;
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
StyledElement.RequestedThemeVariantProperty.AddOwner<Application>();
ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<Application>();
public Application(Avalonia.Application application)
{

104
src/Avalonia.Themes.Simple/Controls/SplitButton.xaml

@ -2,8 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:Avalonia.Controls.Converters">
<!-- This style is heavily based on the Fluent v1 style.
The base resources are switched out along with a few minor changes such as Padding, CornerRadius. -->
<!-- This style is based on the Fluent v1 style. -->
<Design.PreviewWith>
<Border Padding="20">
@ -26,37 +25,7 @@
<converters:CornerRadiusFilterConverter x:Key="RightCornerRadiusFilterConverter" Filter="TopRight, BottomRight" />
<converters:CornerRadiusFilterConverter x:Key="BottomCornerRadiusFilterConverter" Filter="BottomLeft, BottomRight" />
<converters:CornerRadiusFilterConverter x:Key="LeftCornerRadiusFilterConverter" Filter="TopLeft, BottomLeft" />
<SolidColorBrush x:Key="SplitButtonBackground" Color="{DynamicResource ThemeControlMidColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundPointerOver" Color="{DynamicResource ThemeControlMidColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundPressed" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundDisabled" Color="{DynamicResource ThemeControlMidColor}"
Opacity="{DynamicResource ThemeDisabledOpacity}" />
<SolidColorBrush x:Key="SplitButtonBackgroundChecked" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPointerOver" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedPressed" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled" Color="{DynamicResource ThemeControlHighColor}"
Opacity="{DynamicResource ThemeDisabledOpacity}" />
<SolidColorBrush x:Key="SplitButtonForeground" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundPointerOver" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundPressed" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundDisabled" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundChecked" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundCheckedPointerOver" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundCheckedPressed" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonForegroundCheckedDisabled" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrush" Color="{DynamicResource ThemeBorderLowColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushPointerOver" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushPressed" Color="{DynamicResource ThemeBorderLowColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushDisabled" Color="{DynamicResource ThemeBorderLowColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushChecked" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushCheckedPointerOver" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushCheckedPressed" Color="{DynamicResource ThemeControlHighColor}" />
<SolidColorBrush x:Key="SplitButtonBorderBrushCheckedDisabled" Color="{DynamicResource ThemeControlMidColor}"
Opacity="{DynamicResource ThemeDisabledOpacity}" />
<ControlTheme x:Key="SimpleSplitButtonComponent" TargetType="Button">
<Setter Property="Template">
<ControlTemplate>
@ -75,66 +44,75 @@
</Setter>
<Style Selector="^:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
<Setter Property="Background" Value="{DynamicResource ThemeControlMidColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
<Style Selector="^:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="Background" Value="{DynamicResource ThemeBorderHighColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
<Style Selector="^:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
<Setter Property="Background">
<SolidColorBrush Color="{DynamicResource ThemeControlMidColor}"
Opacity="{DynamicResource ThemeDisabledOpacity}" />
</Setter>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
<Style Selector="^[Tag=flyout-open] /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="Background" Value="{DynamicResource ThemeBorderHighColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
<Style Selector="^[Tag=checked]">
<Style Selector="^ /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
<Setter Property="Background" Value="{DynamicResource ThemeBorderHighColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
<Style Selector="^:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
<Setter Property="Background" Value="{DynamicResource ThemeBorderHighColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
<Style Selector="^:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="Background" Value="{DynamicResource ThemeBorderHighColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
<Style Selector="^:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
<Setter Property="Background">
<SolidColorBrush Color="{DynamicResource ThemeControlHighColor}"
Opacity="{DynamicResource ThemeDisabledOpacity}" />
</Setter>
<Setter Property="BorderBrush">
<SolidColorBrush Color="{DynamicResource ThemeControlMidColor}"
Opacity="{DynamicResource ThemeDisabledOpacity}" />
</Setter>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
</Style>
<Style Selector="^[Tag=checked-flyout-open] /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="Background" Value="{DynamicResource ThemeBorderHighColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type SplitButton}" TargetType="SplitButton">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrush}" />
<Setter Property="Background" Value="{DynamicResource ThemeControlMidColor}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowColor}" />
<Setter Property="BorderThickness" Value="{DynamicResource SplitButtonBorderThemeThickness}" />
<Setter Property="MinHeight" Value="{DynamicResource SplitButtonMinHeight}" />
<Setter Property="HorizontalAlignment" Value="Left" />

6
src/Avalonia.Themes.Simple/Controls/SplitView.xaml

@ -17,8 +17,6 @@
<x:Double x:Key="SplitViewOpenPaneThemeLength">320</x:Double>
<x:Double x:Key="SplitViewCompactPaneThemeLength">48</x:Double>
<SolidColorBrush x:Key="SplitViewLightDismissOverlayBackground" Color="{DynamicResource ThemeControlLowColor}"
Opacity="0.6" />
<sys:TimeSpan x:Key="SplitViewPaneAnimationOpenDuration">00:00:00.2</sys:TimeSpan>
<sys:TimeSpan x:Key="SplitViewPaneAnimationCloseDuration">00:00:00.1</sys:TimeSpan>
<Easing x:Key="SplitViewPaneAnimationEasing">0.1,0.9,0.2,1.0</Easing>
@ -240,7 +238,9 @@
<Setter Property="Fill" Value="Transparent" />
</Style>
<Style Selector="^:lightdismiss /template/ Rectangle#LightDismissLayer">
<Setter Property="Fill" Value="{DynamicResource SplitViewLightDismissOverlayBackground}" />
<Setter Property="Fill">
<SolidColorBrush Color="{DynamicResource ThemeControlLowColor}" Opacity="0.6" />
</Setter>
</Style>
<Style Selector="^:overlay:open /template/ Rectangle#LightDismissLayer">

91
src/Avalonia.Themes.Simple/Controls/ToggleSwitch.xaml

@ -36,43 +36,10 @@
<x:Double x:Key="ToggleSwitchThemeMinWidth">0</x:Double>
<Thickness x:Key="ToggleSwitchOnStrokeThickness">0</Thickness>
<Thickness x:Key="ToggleSwitchOuterBorderStrokeThickness">1</Thickness>
<SolidColorBrush x:Key="ToggleSwitchContentForeground" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchContentForegroundDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchHeaderForeground" Color="{DynamicResource ThemeForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchHeaderForegroundDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchContainerBackground" Color="Transparent" />
<SolidColorBrush x:Key="ToggleSwitchContainerBackgroundPointerOver" Color="Transparent" />
<SolidColorBrush x:Key="ToggleSwitchContainerBackgroundPressed" Color="Transparent" />
<SolidColorBrush x:Key="ToggleSwitchContainerBackgroundDisabled" Color="Transparent" />
<SolidColorBrush x:Key="ToggleSwitchFillOff" Color="Transparent" />
<SolidColorBrush x:Key="ToggleSwitchFillOffPointerOver" Color="Transparent" />
<SolidColorBrush x:Key="ToggleSwitchFillOffPressed" Color="{DynamicResource ThemeControlMidHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOffDisabled" Color="Transparent" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOff" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOffPointerOver" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOffPressed" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOffDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOn" Color="{DynamicResource ThemeAccentColor}" />
<SolidColorBrush x:Key="ToggleSwitchFillOnPointerOver" Color="{DynamicResource ThemeAccentColor2}" />
<SolidColorBrush x:Key="ToggleSwitchFillOnPressed" Color="{DynamicResource ThemeAccentColor3}" />
<SolidColorBrush x:Key="ToggleSwitchFillOnDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOn" Color="{DynamicResource ThemeAccentColor}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOnPointerOver" Color="{DynamicResource ThemeAccentColor2}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOnPressed" Color="{DynamicResource ThemeAccentColor3}" />
<SolidColorBrush x:Key="ToggleSwitchStrokeOnDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOff" Color="{DynamicResource ThemeBorderMidColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOffPointerOver" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOffPressed" Color="{DynamicResource ThemeBorderHighColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOffDisabled" Color="{DynamicResource ThemeForegroundLowColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOn" Color="{DynamicResource HighlightForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOnPointerOver" Color="{DynamicResource HighlightForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOnPressed" Color="{DynamicResource HighlightForegroundColor}" />
<SolidColorBrush x:Key="ToggleSwitchKnobFillOnDisabled" Color="{DynamicResource HighlightForegroundColor}" />
<ControlTheme x:Key="{x:Type ToggleSwitch}"
TargetType="ToggleSwitch">
<Setter Property="Foreground" Value="{DynamicResource ToggleSwitchContentForeground}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
@ -111,7 +78,7 @@
<Grid x:Name="SwitchAreaGrid"
Grid.RowSpan="3"
Grid.ColumnSpan="3"
Background="{DynamicResource ToggleSwitchContainerBackground}"
Background="Transparent"
TemplatedControl.IsTemplateFocusTarget="True"
Margin="0,5" />
@ -136,8 +103,8 @@
Height="20"
Width="40"
CornerRadius="10"
Background="{DynamicResource ToggleSwitchFillOff}"
BorderBrush="{DynamicResource ToggleSwitchStrokeOff}"
Background="Transparent"
BorderBrush="{DynamicResource ThemeBorderMidColor}"
BorderThickness="{DynamicResource ToggleSwitchOuterBorderStrokeThickness}" />
<Border x:Name="SwitchKnobBounds"
@ -145,8 +112,8 @@
Height="20"
Width="40"
CornerRadius="10"
Background="{DynamicResource ToggleSwitchFillOn}"
BorderBrush="{DynamicResource ToggleSwitchStrokeOn}"
Background="{DynamicResource ThemeAccentColor}"
BorderBrush="{DynamicResource ThemeAccentColor}"
BorderThickness="{DynamicResource ToggleSwitchOnStrokeThickness}" />
<Canvas x:Name="PART_SwitchKnob" Grid.Row="1"
@ -162,11 +129,11 @@
</Grid.Transitions>
<Ellipse x:Name="SwitchKnobOn"
Fill="{DynamicResource ToggleSwitchKnobFillOn}"
Fill="{DynamicResource HighlightForegroundColor}"
Width="10" Height="10" />
<Ellipse x:Name="SwitchKnobOff"
Fill="{DynamicResource ToggleSwitchKnobFillOff}"
Fill="{DynamicResource ThemeBorderMidColor}"
Width="10" Height="10" />
</Grid>
</Canvas>
@ -181,71 +148,71 @@
<!-- PointerOverState -->
<Style Selector="^:pointerover /template/ Border#OuterBorder">
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffPointerOver}" />
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighColor}" />
<Setter Property="Background" Value="Transparent" />
</Style>
<Style Selector="^:pointerover /template/ Ellipse#SwitchKnobOff">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffPointerOver}" />
<Setter Property="Fill" Value="{DynamicResource ThemeBorderHighColor}" />
</Style>
<Style Selector="^:pointerover /template/ Ellipse#SwitchKnobOn">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnPointerOver}" />
<Setter Property="Fill" Value="{DynamicResource HighlightForegroundColor}" />
</Style>
<Style Selector="^:pointerover /template/ Border#SwitchKnobBounds">
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnPointerOver}" />
<Setter Property="Background" Value="{DynamicResource ThemeAccentColor2}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeAccentColor2}" />
</Style>
<Style Selector="^:pointerover /template/ Grid#SwitchAreaGrid">
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackgroundPointerOver}" />
<Setter Property="Background" Value="Transparent" />
</Style>
<!-- PressedState -->
<Style Selector="^:pressed /template/ Border#OuterBorder">
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffPressed}" />
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighColor}" />
<Setter Property="Background" Value="{DynamicResource ThemeControlMidHighColor}" />
</Style>
<Style Selector="^:pressed /template/ Border#SwitchKnobBounds">
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnPressed}" />
<Setter Property="Background" Value="{DynamicResource ThemeAccentColor3}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeAccentColor3}" />
</Style>
<Style Selector="^:pressed /template/ Ellipse#SwitchKnobOff">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffPressed}" />
<Setter Property="Fill" Value="{DynamicResource ThemeBorderHighColor}" />
</Style>
<Style Selector="^:pressed /template/ Ellipse#SwitchKnobOn">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnPressed}" />
<Setter Property="Fill" Value="{DynamicResource HighlightForegroundColor}" />
</Style>
<Style Selector="^:pressed /template/ Grid#SwitchAreaGrid">
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackgroundPressed}" />
<Setter Property="Background" Value="Transparent" />
</Style>
<!-- DisabledState -->
<Style Selector="^:disabled">
<Setter Property="Foreground" Value="{DynamicResource ToggleSwitchHeaderForegroundDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowColor}" />
</Style>
<Style Selector="^:disabled /template/ Border#OuterBorder">
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffDisabled}" />
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeForegroundLowColor}" />
<Setter Property="Background" Value="Transparent" />
</Style>
<Style Selector="^:disabled /template/ Ellipse#SwitchKnobOff">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffDisabled}" />
<Setter Property="Fill" Value="{DynamicResource ThemeForegroundLowColor}" />
</Style>
<Style Selector="^:disabled /template/ Ellipse#SwitchKnobOn">
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnDisabled}" />
<Setter Property="Fill" Value="{DynamicResource HighlightForegroundColor}" />
</Style>
<Style Selector="^:disabled /template/ Border#SwitchKnobBounds">
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnDisabled}" />
<Setter Property="Background" Value="{DynamicResource ThemeForegroundLowColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeForegroundLowColor}" />
</Style>
<!-- CheckedState -->

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@ -34,7 +34,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
var stack = serviceProvider.GetService<IAvaloniaXamlIlParentStackProvider>();
var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
var themeVariant = (provideTarget.TargetObject as StyledElement)?.ActualThemeVariant;
var themeVariant = (provideTarget.TargetObject as IThemeVariantHost)?.ActualThemeVariant;
var targetType = provideTarget.TargetProperty switch
{

40
tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs

@ -104,7 +104,7 @@ public class StyledElementTests_Theming
target.Theme = null;
Assert.Equal("style", target.Tag);
}
[Fact]
public void TemplatedParent_Theme_Is_Detached_From_Template_Controls_When_Theme_Property_Cleared()
{
@ -539,12 +539,42 @@ public class StyledElementTests_Theming
Assert.Same(target.Theme, theme3);
}
[Fact]
public void TemplatedParent_Theme_Change_Applies_To_Children()
{
var theme = CreateDerivedTheme();
var target = CreateTarget();
Assert.Null(target.Theme);
Assert.Null(target.Template);
var root = CreateRoot(target, theme.BasedOn);
Assert.NotNull(target.Theme);
Assert.NotNull(target.Template);
root.Styles.Add(new Style(x => x.OfType<ThemedControl>().Class("foo"))
{
Setters = { new Setter(StyledElement.ThemeProperty, theme) }
});
root.LayoutManager.ExecuteLayoutPass();
var border = Assert.IsType<Border>(target.VisualChild);
Assert.Equal(Brushes.Red, border.Background);
target.Classes.Add("foo");
root.LayoutManager.ExecuteLayoutPass();
Assert.Equal(Brushes.Green, border.Background);
}
private static ThemedControl CreateTarget()
{
return new ThemedControl();
}
private static TestRoot CreateRoot(Control child)
private static TestRoot CreateRoot(Control child, ControlTheme? theme = null)
{
var result = new TestRoot()
{
@ -552,7 +582,7 @@ public class StyledElementTests_Theming
{
new Style(x => x.OfType<ThemedControl>())
{
Setters = { new Setter(StyledElement.ThemeProperty, CreateTheme()) }
Setters = { new Setter(StyledElement.ThemeProperty, theme ?? CreateTheme()) }
}
}
};
@ -580,8 +610,8 @@ public class StyledElementTests_Theming
{
new Style(x => x.Nesting().Template().OfType<Border>())
{
Setters =
{
Setters =
{
new Setter(Border.BackgroundProperty, Brushes.Red),
new Setter(Control.TagProperty, tag),
}

77
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ThemeDictionariesTests.cs

@ -1,8 +1,12 @@
using Avalonia.Controls;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Moq;
using Xunit;
@ -185,9 +189,9 @@ public class ThemeDictionariesTests : XamlTestBase
{
using (AvaloniaLocator.EnterScope())
{
var applicationThemeHost = new Mock<IGlobalThemeVariantProvider>();
var applicationThemeHost = new Mock<IThemeVariantHost>();
applicationThemeHost.SetupGet(h => h.ActualThemeVariant).Returns(ThemeVariant.Dark);
AvaloniaLocator.CurrentMutable.Bind<IGlobalThemeVariantProvider>().ToConstant(applicationThemeHost.Object);
AvaloniaLocator.CurrentMutable.Bind<IThemeVariantHost>().ToConstant(applicationThemeHost.Object);
var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
<ThemeVariantScope xmlns='https://github.com/avaloniaui'
@ -444,4 +448,71 @@ public class ThemeDictionariesTests : XamlTestBase
Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
}
[Fact]
public void Theme_Switch_Works_In_Nested_Scope()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = (Window)AvaloniaRuntimeXamlLoader.Load(@"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
RequestedThemeVariant='Dark'>
<ThemeVariantScope Name='Scope'>
<TextBlock Name='Text' />
</ThemeVariantScope>
</Window>");
window.ApplyTemplate();
var scope = window.FindControl<ThemeVariantScope>("Scope")!;
var text = window.FindControl<TextBlock>("Text")!;
Assert.Equal(ThemeVariant.Dark, text.ActualThemeVariant);
Assert.Equal(Color.Parse("#dedede"), ((ISolidColorBrush)text.Foreground!).Color);
scope.RequestedThemeVariant = ThemeVariant.Light;
Assert.Equal(ThemeVariant.Light, text.ActualThemeVariant);
Assert.Equal(Colors.Black, ((ISolidColorBrush)text.Foreground!).Color);
}
}
[Fact]
public void Theme_Switch_Works_In_With_Popup()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = (Window)AvaloniaRuntimeXamlLoader.Load(@"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<ThemeVariantScope Name='Scope' RequestedThemeVariant='Dark'>
<Popup Name='Popup'>
<Border Width='100' Height='100'
Background='{DynamicResource ThemeBackgroundBrush}'>
</Border>
</Popup>
</ThemeVariantScope>
</Window>");
window.Show();
var scope = window.FindControl<ThemeVariantScope>("Scope")!;
var popup = window.FindControl<Popup>("Popup")!;
popup.IsOpen = true;
var border = (Border)popup.Child!;
Assert.Equal(ThemeVariant.Dark, popup.ActualThemeVariant);
Assert.Equal(ThemeVariant.Dark, border.ActualThemeVariant);
Assert.Equal(Color.Parse("#282828"), ((ISolidColorBrush)border.Background!).Color);
scope.RequestedThemeVariant = ThemeVariant.Light;
Assert.Equal(ThemeVariant.Light, popup.ActualThemeVariant);
Assert.Equal(ThemeVariant.Light, border.ActualThemeVariant);
Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background!).Color);
}
}
}
}

2
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -457,7 +457,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
if (textLine.Width > 300 || currentHeight + textLine.Height > 240)
{
textLine = textLine.Collapse(new TextTrailingWordEllipsis(TextTrimming.DefaultEllipsisChar, 300, defaultProperties));
textLine = textLine.Collapse(new TextTrailingWordEllipsis(TextTrimming.DefaultEllipsisChar, 300, defaultProperties, FlowDirection.LeftToRight));
}
currentHeight += textLine.Height;

2
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

@ -407,7 +407,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
Assert.False(textLine.HasCollapsed);
TextCollapsingProperties collapsingProperties = trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, defaultProperties));
TextCollapsingProperties collapsingProperties = trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, defaultProperties, FlowDirection.LeftToRight));
var collapsedLine = textLine.Collapse(collapsingProperties);

Loading…
Cancel
Save