-
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from JiayiSoftware/feature/live-theming
Feature/live theming
- Loading branch information
Showing
39 changed files
with
1,893 additions
and
758 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,161 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace JiayiLauncher.Appearance; | ||
|
||
|
||
|
||
public class CssProperty | ||
{ | ||
public string Property { get; set; } | ||
public string? Value { get; set; } | ||
|
||
public CssProperty(string property, string? value) | ||
{ | ||
Property = property; | ||
Value = value; | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return $"{Property}: {Value};"; | ||
} | ||
} | ||
|
||
public class CssSelector | ||
{ | ||
public string Selector { get; set; } | ||
public List<CssProperty> Properties { get; set; } | ||
|
||
public CssSelector(string selector, List<CssProperty> properties) | ||
{ | ||
Selector = selector; | ||
Properties = properties; | ||
} | ||
|
||
public CssSelector UpdateProperty(CssProperty prop) | ||
{ | ||
var idx = Properties.FindIndex(x => x.Property == prop.Property); | ||
if (idx >= 0) | ||
{ | ||
Properties[idx] = prop; | ||
} | ||
else | ||
{ | ||
Properties.Add(prop); | ||
} | ||
return this; | ||
} | ||
|
||
public CssProperty? GetProperty(string prop) | ||
{ | ||
var idx = Properties.FindIndex(x => x.Property == prop); | ||
return idx >= 0 ? Properties[idx] : null; | ||
} | ||
public string? GetPropertyValue(string prop) | ||
{ | ||
return GetProperty(prop)?.Value; | ||
} | ||
|
||
public string ToStringNoSelector() | ||
{ | ||
return string.Join("\n", Properties.Select(prop => "\t" + prop)); | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return $"{Selector} {{\n{ToStringNoSelector()}\n}}"; | ||
} | ||
} | ||
|
||
public class CssBuilder | ||
{ | ||
private readonly List<string> _properties = new(); | ||
private readonly string _selector; | ||
|
||
public CssBuilder(string selector) | ||
{ | ||
_selector = selector; | ||
} | ||
|
||
public static CssBuilder FromFile(string path, string selector) | ||
{ | ||
var styles = File.ReadAllText(path); | ||
|
||
if (!styles.Contains(selector)) | ||
throw new ArgumentException($"Selector '{selector}' was not found in this file."); | ||
|
||
// i hate windows newlines | ||
var lines = styles.Split('\n'); | ||
|
||
if (lines.Any(x => x.Contains('\r'))) | ||
lines = styles.Split("\r\n"); | ||
|
||
var start = Array.IndexOf(lines, selector + " {"); | ||
var end = Array.IndexOf(lines, "}", start); | ||
|
||
var builder = new CssBuilder(selector); | ||
for (var i = start + 1; i < end; i++) | ||
{ | ||
var line = lines[i]; | ||
if (string.IsNullOrWhiteSpace(line)) continue; | ||
var property = line.Split(':')[0].Trim(); | ||
|
||
//var value = line.Split(':')[1].Trim().TrimEnd(';'); | ||
// in case the value here contains a url we need to join the rest of the line | ||
var value = string.Join(':', line.Split(':').Skip(1)).Trim().TrimEnd(';'); | ||
builder.AddProperty(property, value); | ||
} | ||
|
||
return builder; | ||
} | ||
|
||
public CssBuilder AddProperty(string property, string value) | ||
{ | ||
_properties.Add($"{property}: {value};"); | ||
private readonly List<CssSelector> _selectors; | ||
|
||
public CssBuilder(List<CssSelector>? selectors = null) | ||
{ | ||
_selectors = selectors ?? new List<CssSelector>(); | ||
} | ||
|
||
public CssSelector? GetSelector(string selector) | ||
{ | ||
var idx = _selectors.FindIndex(x => x.Selector == selector); | ||
return idx >= 0 ? _selectors[idx] : null; | ||
} | ||
|
||
public CssProperty? GetProperty(string selector, string prop) | ||
{ | ||
return GetSelector(selector)?.GetProperty(prop); | ||
} | ||
|
||
public CssBuilder UpdateProperty(string selector, CssProperty prop) | ||
{ | ||
GetSelector(selector)?.UpdateProperty(prop); | ||
|
||
return this; | ||
} | ||
public string GetPropertyValue(string property) | ||
|
||
public static CssBuilder FromFile(string path) | ||
{ | ||
foreach (var prop in _properties.Where(prop => prop.StartsWith(property))) | ||
{ | ||
return string.Join(':', prop.Split(':').Skip(1)).Trim().TrimEnd(';'); | ||
} | ||
var css = File.ReadAllText(path); | ||
|
||
return string.Empty; | ||
return Parse(css); | ||
} | ||
|
||
// i like linq extension methods | ||
public List<string> GetAllPropertyValues() => _properties.Select(GetPropertyValue).ToList(); | ||
public static CssBuilder Parse(string cssString) | ||
{ | ||
cssString = RemoveCssComments(cssString); | ||
|
||
var result = new List<CssSelector>(); | ||
|
||
public string Build() | ||
const string pattern = @"(?<selector>[^{]+)\s*{\s*(?<properties>[^}]+)\s*}"; | ||
var regex = new Regex(pattern, RegexOptions.IgnorePatternWhitespace); | ||
|
||
var matches = regex.Matches(cssString); | ||
|
||
foreach (Match match in matches) | ||
{ | ||
var selector = match.Groups["selector"].Value.Trim(); | ||
var propertyString = match.Groups["properties"].Value; | ||
|
||
var properties = ParseProperties(propertyString); | ||
|
||
result.Add(new CssSelector(selector, properties)); | ||
} | ||
|
||
return new CssBuilder(result); | ||
} | ||
|
||
private static List<CssProperty> ParseProperties(string propertyString) | ||
{ | ||
var css = $"{_selector} {{\n"; | ||
var properties = new List<CssProperty>(); | ||
|
||
css = _properties.Aggregate(css, (current, property) => current + $"\t{property}\n"); | ||
const string propertyPattern = @"(?<property>[^:]+)\s*:\s*(?<value>[^;]+);+"; | ||
var propertyRegex = new Regex(propertyPattern, RegexOptions.IgnorePatternWhitespace); | ||
|
||
css += "}\n"; | ||
return css; | ||
var propertyMatches = propertyRegex.Matches(propertyString); | ||
|
||
foreach (Match propertyMatch in propertyMatches) | ||
{ | ||
var property = propertyMatch.Groups["property"].Value.Trim(); | ||
var value = propertyMatch.Groups["value"].Value.Trim(); | ||
|
||
properties.Add(new CssProperty(property, value)); | ||
} | ||
|
||
return properties; | ||
} | ||
|
||
public override string ToString() => Build(); | ||
private static string RemoveCssComments(string cssString) | ||
{ | ||
// Use regular expression to remove CSS comments | ||
const string commentPattern = @"/\*.*?\*/"; | ||
return Regex.Replace(cssString, commentPattern, string.Empty, RegexOptions.Singleline); | ||
} | ||
public override string ToString() | ||
{ | ||
return string.Join("\n\n", _selectors.Select(selector => selector.ToString())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Text; | ||
using JiayiLauncher.Settings; | ||
|
||
namespace JiayiLauncher.Appearance; | ||
|
||
public class LocalTheme | ||
{ | ||
private static readonly string _themeRoot = Path.Combine(ThemeState.RootPath, "themes"); | ||
|
||
public string Name; | ||
|
||
public LocalTheme(string name) | ||
{ | ||
Name = name; | ||
} | ||
|
||
public static LocalTheme[] GetAllThemes() | ||
{ | ||
var localThemes = new List<LocalTheme>(); | ||
|
||
var path = Path.Combine(_themeRoot, ".local"); | ||
Directory.CreateDirectory(path); // Ensure directory exists | ||
var directories = Directory.GetDirectories(Path.Combine(_themeRoot, ".local")); | ||
foreach (var d in directories) | ||
{ | ||
var name = new DirectoryInfo(d).Name; | ||
var theme = new LocalTheme(name); | ||
localThemes.Add(theme); | ||
} | ||
|
||
if (localThemes.Count <= 0) | ||
{ | ||
var theme = CreateTheme("default"); | ||
if (theme != null) localThemes.Add(theme); | ||
} | ||
|
||
return localThemes.ToArray(); | ||
} | ||
|
||
public static LocalTheme? CreateTheme(string name) | ||
{ | ||
var path = Path.Combine(_themeRoot, $".local\\{name}"); | ||
if (Directory.Exists(path)) | ||
{ | ||
return null; | ||
} | ||
|
||
Directory.CreateDirectory(path); | ||
|
||
var buffer = File.Create(Path.Combine(path, "theme.css")); | ||
var defaultTheme = new ThemeState().ThemeStyles.ToString(); | ||
var byteArray = Encoding.UTF8.GetBytes(defaultTheme); | ||
buffer.Write(byteArray, 0, byteArray.Length); | ||
buffer.Close(); | ||
|
||
return new LocalTheme(name); | ||
} | ||
|
||
public static void SaveCurrentTheme() | ||
{ | ||
var buffer = File.OpenWrite(Path.Combine(_themeRoot, JiayiSettings.Instance.Theme, "theme.css")); | ||
var themeStyles = ThemeState.Instance.ThemeStyles.ToString(); | ||
var byteArray = Encoding.UTF8.GetBytes(themeStyles); | ||
buffer.Write(byteArray, 0, byteArray.Length); | ||
buffer.Close(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System; | ||
using JiayiLauncher.Utils; | ||
using Newtonsoft.Json; | ||
|
||
namespace JiayiLauncher.Appearance; | ||
|
||
public class PublicTheme : ThemeMetadata | ||
{ | ||
[JsonProperty("Name")] | ||
public string Name; | ||
|
||
[JsonProperty("bg")] | ||
public Uri? Background; | ||
|
||
[JsonProperty("meta")] | ||
public Uri Metadata; | ||
|
||
[JsonProperty("theme")] | ||
public Uri Theme; | ||
|
||
public static PublicTheme[]? GetAllThemes() | ||
{ | ||
const string url = "https://raw.githubusercontent.com/JiayiSoftware/jiayi-themes/main/all_themes.json"; | ||
var response = InternetManager.Client.GetStringAsync(url).Result; | ||
var data = JsonConvert.DeserializeObject<PublicTheme[]>(response); | ||
|
||
if (data != null) | ||
{ | ||
return data; | ||
} | ||
|
||
Log.Write("Theme", "Failed to retrieve public themes", Log.LogLevel.Error); | ||
return null; | ||
} | ||
} |
Oops, something went wrong.