|
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Xml;
using Microsoft.Maui.Controls.Xaml;
namespace Microsoft.Maui.Controls.StyleSheets
{
/// <include file="../../../docs/Microsoft.Maui.Controls.StyleSheets/StyleSheet.xml" path="Type[@FullName='Microsoft.Maui.Controls.StyleSheets.StyleSheet']/Docs/*" />
public sealed class StyleSheet : IStyle
{
StyleSheet()
{
}
internal IDictionary<Selector, Style> Styles { get; set; } = new Dictionary<Selector, Style>();
//used by code generated by XamlC. Has to stay public
/// <include file="../../../docs/Microsoft.Maui.Controls.StyleSheets/StyleSheet.xml" path="//Member[@MemberName='FromResource']/Docs/*" />
[EditorBrowsable(EditorBrowsableState.Never)]
public static StyleSheet FromResource(string resourcePath, Assembly assembly, IXmlLineInfo lineInfo = null)
{
var styleSheet = new StyleSheet();
var resString = GetResource(resourcePath, assembly, styleSheet, lineInfo);
using (var textReader = new StringReader(resString))
using (var cssReader = new CssReader(textReader))
Parse(styleSheet, cssReader);
return styleSheet;
}
//used by code generated by XamlC. Has to stay public
/// <include file="../../../docs/Microsoft.Maui.Controls.StyleSheets/StyleSheet.xml" path="//Member[@MemberName='FromString']/Docs/*" />
[EditorBrowsable(EditorBrowsableState.Never)]
public static StyleSheet FromString(string stylesheet)
{
if (stylesheet == null)
throw new ArgumentNullException(nameof(stylesheet));
using (var reader = new StringReader(stylesheet))
return FromReader(reader);
}
/// <include file="../../../docs/Microsoft.Maui.Controls.StyleSheets/StyleSheet.xml" path="//Member[@MemberName='FromReader']/Docs/*" />
public static StyleSheet FromReader(TextReader reader)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));
var sheet = new StyleSheet();
using (var cssReader = new CssReader(reader))
Parse(sheet, cssReader);
return sheet;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void Parse(StyleSheet sheet, CssReader reader)
{
Style style = null;
var selector = Selector.All;
int p;
bool inStyle = false;
reader.SkipWhiteSpaces();
while ((p = reader.Peek()) > 0)
{
switch ((char)p)
{
case '@':
throw new NotSupportedException("AT-rules not supported");
case '{':
reader.Read();
style = Style.Parse(reader, '}');
inStyle = true;
break;
case '}':
reader.Read();
if (!inStyle)
throw new Exception();
inStyle = false;
sheet.Styles.Add(selector, style);
style = null;
selector = Selector.All;
break;
default:
selector = Selector.Parse(reader, '{');
break;
}
}
}
Type IStyle.TargetType => typeof(VisualElement);
void IStyle.Apply(BindableObject bindable, SetterSpecificity setterspecificity)
{
if (!(bindable is Element styleable))
return;
Apply(styleable);
}
void Apply(Element styleable)
{
if (!(styleable is VisualElement visualStylable))
return;
foreach (var kvp in Styles)
{
var selector = kvp.Key;
var style = kvp.Value;
if (!selector.Matches(styleable))
continue;
style.Apply(visualStylable);
}
}
void IStyle.UnApply(BindableObject bindable) => throw new NotImplementedException();
private static string GetResource(string resourcePath, Assembly assembly, object target, IXmlLineInfo lineInfo)
{
var resourceLoadingResponse = Maui.Controls.Internals.ResourceLoader.ResourceProvider2?.Invoke(new Maui.Controls.Internals.ResourceLoader.ResourceLoadingQuery
{
AssemblyName = assembly.GetName(),
ResourcePath = resourcePath,
Instance = target
});
var alternateResource = resourceLoadingResponse?.ResourceContent;
if (alternateResource != null)
return alternateResource;
var resourceId = XamlResourceIdAttribute.GetResourceIdForPath(assembly, resourcePath);
if (resourceId == null)
throw new XamlParseException($"Resource '{resourcePath}' not found.", lineInfo);
using (var stream = assembly.GetManifestResourceStream(resourceId))
{
if (stream == null)
throw new XamlParseException($"No resource found for '{resourceId}'.", lineInfo);
using (var reader = new StreamReader(stream))
return reader.ReadToEnd();
}
}
}
}
|