diff --git a/src/Controls/src/Core/StyleSheets/Selector.cs b/src/Controls/src/Core/StyleSheets/Selector.cs
index 231fbc77d7f4..b3c8b79b2c4a 100644
--- a/src/Controls/src/Core/StyleSheets/Selector.cs
+++ b/src/Controls/src/Core/StyleSheets/Selector.cs
@@ -1,5 +1,6 @@
#nullable disable
using System;
+using System.Diagnostics;
namespace Microsoft.Maui.Controls.StyleSheets
{
@@ -313,5 +314,65 @@ public override bool Matches(IStyleSelectable styleable)
//return siblingIndex != -1;
}
}
+
+ [DebuggerDisplay("{id} - {class} = {type}")]
+ public struct SelectorSpecificity
+ {
+ int id;
+ int @class;
+ int type;
+
+ public int Id { get => id; }
+ public int Class { get => @class; }
+ public int Type { get => type; }
+
+ public static SelectorSpecificity FromSelector(Selector selector)
+ {
+ switch(selector)
+ {
+ case Selector.Class c:
+ return new SelectorSpecificity { @class = 1 };
+ case Selector.Id i:
+ return new SelectorSpecificity { id = 1 };
+ case Selector.Element e:
+ return new SelectorSpecificity { type = 1 };
+ case Selector.Base b:
+ return new SelectorSpecificity { type = 1 };
+ case Selector.And a:
+ return FromSelector(a.Left) + FromSelector(a.Right);
+ case Selector.Or o:
+ return FromSelector(o.Left) + FromSelector(o.Right);
+ case Selector.Child c:
+ return FromSelector(c.Left) + FromSelector(c.Right);
+ case Selector.Descendent d:
+ return FromSelector(d.Left) + FromSelector(d.Right);
+ case Selector.Adjacent a:
+ return FromSelector(a.Left) + FromSelector(a.Right);
+ case Selector.Sibling s:
+ return FromSelector(s.Left) + FromSelector(s.Right);
+ default:
+ return default;
+ }
+ }
+
+ public static SelectorSpecificity operator +(SelectorSpecificity left, SelectorSpecificity right)
+ {
+ return new SelectorSpecificity
+ {
+ id = left.id + right.id,
+ @class = left.@class + right.@class,
+ type = left.type + right.type
+ };
+ }
+ public SelectorSpecificity Add(SelectorSpecificity other)
+ {
+ return new SelectorSpecificity
+ {
+ id = id + other.id,
+ @class = @class + other.@class,
+ type = type + other.type
+ };
+ }
+ }
}
}
diff --git a/src/Controls/src/Core/StyleSheets/Style.cs b/src/Controls/src/Core/StyleSheets/Style.cs
index 2b14357b455f..8c8634ee4ea6 100644
--- a/src/Controls/src/Core/StyleSheets/Style.cs
+++ b/src/Controls/src/Core/StyleSheets/Style.cs
@@ -60,7 +60,7 @@ public static Style Parse(CssReader reader, char stopChar = '\0')
return style;
}
- public void Apply(VisualElement styleable, bool inheriting = false)
+ public void Apply(VisualElement styleable, Selector.SelectorSpecificity selectorSpecificity = default, bool inheriting = false)
{
if (styleable == null)
throw new ArgumentNullException(nameof(styleable));
@@ -71,15 +71,14 @@ public void Apply(VisualElement styleable, bool inheriting = false)
if (property == null)
continue;
if (string.Equals(decl.Value, "initial", StringComparison.OrdinalIgnoreCase))
- //FIXME
- styleable.ClearValue(property, new SetterSpecificity(SetterSpecificity.StyleImplicit, 0, 0, 0));
+ styleable.SetValue(property, property.DefaultValue, new SetterSpecificity(SetterSpecificity.StyleImplicit, (byte)selectorSpecificity.Id, (byte)selectorSpecificity.Class, (byte)selectorSpecificity.Type));
else
{
object value;
if (!convertedValues.TryGetValue(decl, out value))
convertedValues[decl] = (value = Convert(styleable, decl.Value, property));
//FIXME: compute distance
- styleable.SetValue(property, value, new SetterSpecificity(SetterSpecificity.StyleImplicit, 0, 0, 0));
+ styleable.SetValue(property, value, new SetterSpecificity(SetterSpecificity.StyleImplicit, (byte)selectorSpecificity.Id, (byte)selectorSpecificity.Class, (byte)selectorSpecificity.Type));
}
}
diff --git a/src/Controls/src/Core/StyleSheets/StyleSheet.cs b/src/Controls/src/Core/StyleSheets/StyleSheet.cs
index 69bf4d6dbb55..5ed7218206fb 100644
--- a/src/Controls/src/Core/StyleSheets/StyleSheet.cs
+++ b/src/Controls/src/Core/StyleSheets/StyleSheet.cs
@@ -111,7 +111,8 @@ void Apply(Element styleable)
var style = kvp.Value;
if (!selector.Matches(styleable))
continue;
- style.Apply(visualStylable);
+
+ style.Apply(visualStylable, Selector.SelectorSpecificity.FromSelector(selector));
}
}
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980.xaml
new file mode 100644
index 000000000000..f1b628671a9b
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980.xaml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980.xaml.cs
new file mode 100644
index 000000000000..b71b1bbc782e
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980.xaml.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using Microsoft.Maui.ApplicationModel;
+using Microsoft.Maui.Controls.Core.UnitTests;
+using Microsoft.Maui.Controls.Shapes;
+using Microsoft.Maui.Devices;
+using Microsoft.Maui.Dispatching;
+
+using Microsoft.Maui.Graphics;
+using Microsoft.Maui.UnitTests;
+using NUnit.Framework;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests
+{
+ public partial class Maui18980 : ContentPage
+ {
+ public Maui18980()
+ {
+ InitializeComponent();
+ }
+ public Maui18980(bool useCompiledXaml)
+ {
+ //this stub will be replaced at compile time
+ }
+
+ [TestFixture]
+ class Test
+ {
+ [SetUp]
+ public void Setup()
+ {
+ Application.SetCurrentApplication(new MockApplication());
+ DispatcherProvider.SetCurrent(new DispatcherProviderStub());
+ }
+
+
+ [TearDown] public void TearDown() => AppInfo.SetCurrent(null);
+
+ [Test]
+ public void CSSnotOverridenbyImplicitStyle([Values(false, true)] bool useCompiledXaml)
+ {
+ // var app = new MockApplication();
+ // app.Resources.Add(new Maui18980Style(useCompiledXaml));
+ // Application.SetCurrentApplication(app);
+
+ var page = new Maui18980(useCompiledXaml);
+ Assert.That(page.button.BackgroundColor, Is.EqualTo(Colors.Red));
+ }
+ }
+ }
+}
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.css b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.css
new file mode 100644
index 000000000000..0e02220a569c
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.css
@@ -0,0 +1 @@
+.buttonClass{ background-color: red;}
\ No newline at end of file
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.xaml
new file mode 100644
index 000000000000..f1236b6bb6f4
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.xaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.xaml.cs
new file mode 100644
index 000000000000..b5becc1ad51d
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui18980Style.xaml.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using Microsoft.Maui.ApplicationModel;
+using Microsoft.Maui.Controls.Core.UnitTests;
+using Microsoft.Maui.Controls.Shapes;
+using Microsoft.Maui.Devices;
+using Microsoft.Maui.Dispatching;
+
+using Microsoft.Maui.Graphics;
+using Microsoft.Maui.UnitTests;
+using NUnit.Framework;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests
+{
+ public partial class Maui18980Style : ResourceDictionary
+ {
+ public Maui18980Style()
+ {
+ InitializeComponent();
+ }
+
+ public Maui18980Style(bool useCompiledXaml)
+ {
+ //this stub will be replaced at compile time
+ }
+ }
+}