Skip to content

Commit

Permalink
new theme file structure (#30)
Browse files Browse the repository at this point in the history
* first draft of new file structure

* update readme

* minor fixes

* improve code coverage

* Create SECURITY.md (#25)

* Create SECURITY.md

---------

Signed-off-by: wolframhaussig <13997737+wolframhaussig@users.noreply.github.com>

* added AbstractThemePlugin to make plugin implementations more error proof (#29)

* Add link to JSON schema (only works after merging the PR)

* removed the (possibly wrong) reference to a "V2"

* removed obsolete code

---------

Signed-off-by: wolframhaussig <13997737+wolframhaussig@users.noreply.github.com>
  • Loading branch information
wolframhaussig authored Feb 25, 2024
1 parent 3f842bc commit 86decfc
Show file tree
Hide file tree
Showing 8 changed files with 616 additions and 145 deletions.
51 changes: 18 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,43 +122,28 @@ Out of the box, there are 2 ways you can add custom themes:
- Files with the file ending `.theme.json` stored in a `themes` directory of the working dir.
- Assembly resources in any assembly where the name starts with `CONFIG_THEMING_THEME_`

Both ways use the same JSON format for the theme definition(the version defines the format of the file):
Both ways use the same JSON format for the theme definition(the version defines the format of the file).
A simple example of this could be:
```json
{
"name": "theme-name",
"capabilities": ["DarkMode", "LightMode", "HighContrast"],
"version": 2,
"colors": {
"backColor": "#082a56",
"foreColor": "#082a56",
"buttonBackColor": "#082a56",
"buttonForeColor": "#082a56",
"buttonHoverColor": "#082a56",
"comboBoxItemBackColor": "#082a56",
"comboBoxItemHoverColor": "#082a56",
"controlBackColor": "#082a56",
"controlForeColor": "#082a56",
"controlHighlightColor": "#082a56",
"controlHighlightLightColor": "#082a56",
"controlHighlightDarkColor": "#082a56",
"controlBorderColor": "#082a56",
"controlBorderLightColor": "#082a56",
"listViewHeaderGroupColor": "#082a56",
"tableBackColor": "#082a56",
"tableHeaderBackColor": "#082a56",
"tableHeaderForeColor": "#082a56",
"tableSelectionBackColor": "#082a56",
"tableCellBackColor": "#082a56",
"tableCellForeColor": "#082a56",
"successBackColor": "#082a56",
"successForeColor": "#082a56",
"warningBackColor": "#082a56",
"warningForeColor": "#082a56",
"errorBackColor": "#082a56",
"errorForeColor": "#082a56"
}
"name": "theme-name",
"capabilities": ["DarkMode", "HighContrast"],
"version": 3,
"variables": {
"backColor": "#082a56",
"foreColor": "#082a57"
},
"colors": {
"backColor": "backColor",
"foreColor": "foreColor",
"controls": {
"backColor": "backColor",
"foreColor": "foreColor"
}
}
}
```
For the complete list of available settings please check our JSON schema [here](https://github.com/Assorted-Development/winforms-themes/blob/main/WinFormsThemes/WinFormsThemes/themes.schema.json).

If those 2 ways are not flexible enough, you can implement a theme by yourself and register it using a custom theme source (see below):
The prefered way is to subclass `AbstractTheme` as you just need to implement the base colors and optionally override the extended colors - styling the controls is done by the base class.
Expand Down
156 changes: 149 additions & 7 deletions WinFormsThemes/TestProject/FileThemeTest.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,181 @@
using System.Drawing;
using System.Drawing;
using Microsoft.Extensions.Logging;
using TestProject.Properties;
using WinFormsThemes.Extensions;
using WinFormsThemes.Themes;

namespace TestProject
{
[TestClass]
public class FileThemeTest
public class FileThemeTest : AbstractTestClass
{
[TestMethod]
public void LoadShouldNotThrow_MissingCapabilities()
{
FileTheme? theme = FileTheme.Load(Resources.MISSING_CAPS);
FileTheme? theme = FileTheme.Load(Resources.MISSING_CAPS, getLogger());
Assert.IsNull(theme);
}

[TestMethod]
public void LoadShouldNotThrow_MissingColors()
{
FileTheme? theme = FileTheme.Load(Resources.MISSING_COLORS);
FileTheme? theme = FileTheme.Load(Resources.MISSING_COLORS, getLogger());
Assert.IsNotNull(theme);
Assert.AreEqual(SystemColors.Control, theme.BackgroundColor);
}

[TestMethod]
public void LoadShouldNotThrow_MissingName()
{
FileTheme? theme = FileTheme.Load(Resources.MISSING_NAME);
FileTheme? theme = FileTheme.Load(Resources.MISSING_NAME, getLogger());
Assert.IsNull(theme);
}

[TestMethod]
public void LoadShouldNotThrow_NonJson()
{
FileTheme? theme = FileTheme.Load("abc");
FileTheme? theme = FileTheme.Load("abc", getLogger());
Assert.IsNull(theme);
}

[TestMethod]
public void LoadVersionedSimple()
{
FileTheme? theme = FileTheme.Load(@"
{
'name': 'theme-name',
'capabilities': ['DarkMode', 'HighContrast'],
'version': 3,
'variables': {
},
'colors': {
'backColor': '#082a56',
'foreColor': '#082a56',
'controls': {
'backColor': '#082a56',
'foreColor': '#082a56'
}
}
}".Replace("'", "\"", StringComparison.Ordinal), getLogger());
Assert.IsNotNull(theme);
}

[TestMethod]
public void LoadVersionedWithVariable()
{
FileTheme? theme = FileTheme.Load(@"
{
'name': 'theme-name',
'capabilities': ['DarkMode', 'HighContrast'],
'version': 3,
'variables': {
'backColor': '#082a56',
'foreColor': '#082a57'
},
'colors': {
'backColor': 'backColor',
'foreColor': 'foreColor',
'controls': {
'backColor': 'backColor',
'foreColor': 'foreColor'
}
}
}".Replace("'", "\"", StringComparison.CurrentCulture), getLogger());
Assert.IsNotNull(theme);
Assert.AreEqual("#082a56".ToColor(), theme.ControlBackColor);
Assert.AreEqual("#082a57".ToColor(), theme.ControlForeColor);
}

[TestMethod]
public void LoadVersionedInvalidSchema()
{
FileTheme? theme = FileTheme.Load(@"
{
'name': 'theme-name',
'capabilities': ['DarkMode', 'HighContrast'],
'version': 3,
'variables': {
}
}".Replace("'", "\"", StringComparison.CurrentCulture), getLogger());
Assert.IsNull(theme);
}

[TestMethod]
public void LoadVersionedDefaultValue()
{
FileTheme? theme = FileTheme.Load(@"
{
'name': 'theme-name',
'capabilities': ['DarkMode', 'HighContrast'],
'version': 3,
'variables': {
},
'colors': {
'backColor': '#082a56',
'foreColor': '#082a56',
'controls': {
'backColor': '#082a56',
'foreColor': '#082a56'
}
}
}".Replace("'", "\"", StringComparison.CurrentCulture), getLogger());
Assert.IsNotNull(theme);
Assert.AreEqual("#082a56".ToColor(), theme.TableBackColor);
}

[TestMethod]
public void LoadVersionedInvalidColorOrVariable()
{
FileTheme? theme = FileTheme.Load(@"
{
'name': 'theme-name',
'capabilities': ['DarkMode', 'HighContrast'],
'version': 3,
'variables': {
},
'colors': {
'backColor': 'unknown',
'foreColor': '#082a56',
'controls': {
'backColor': '#082a56',
'foreColor': '#082a56'
}
}
}".Replace("'", "\"", StringComparison.CurrentCulture), getLogger());
Assert.IsNull(theme);
}

[TestMethod]
public void LoadVersionedSkipEmptyCapabilities()
{
FileTheme? theme = FileTheme.Load(@"
{
'name': 'theme-name',
'capabilities': ['DarkMode', 'HighContrast', ''],
'version': 3,
'variables': {
},
'colors': {
'backColor': '#082a56',
'foreColor': '#082a56',
'controls': {
'backColor': '#082a56',
'foreColor': '#082a56'
}
}
}".Replace("'", "\"", StringComparison.Ordinal), getLogger());
Assert.IsNotNull(theme);
Assert.AreEqual(0, theme.AdvancedCapabilities.Count);
}

private ILogger getLogger()
{
return new Logger<FileThemeTest>(LoggerFactory);
}
}
}
}
6 changes: 3 additions & 3 deletions WinFormsThemes/WinFormsThemes/ThemeLookup/FileThemeLookup.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using WinFormsThemes.Themes;

Expand Down Expand Up @@ -33,7 +33,7 @@ public IList<ITheme> Lookup()
_logger.LogDebug("found {count} theme files in {folder}", files.Count(), _folder.FullName);
foreach (FileInfo file in files)
{
ITheme? theme = FileTheme.Load(File.ReadAllText(file.FullName));
ITheme? theme = FileTheme.Load(File.ReadAllText(file.FullName), _logger);
if (theme is not null)
{
results.Add(theme);
Expand All @@ -52,4 +52,4 @@ public void UseLogger(ILoggerFactory loggerFactory)
_logger = new Logger<IThemeLookup>(loggerFactory);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private void handleEmbeddedResource(Stream? stream, string resName)
if (stream is not null)
{
using StreamReader reader = new(stream);
add(FileTheme.Load(reader.ReadToEnd()), resName);
add(FileTheme.Load(reader.ReadToEnd(), _logger), resName);
}
}

Expand All @@ -129,7 +129,7 @@ private void handleResource(string resourceName, Assembly assembly)
if (entry.Key is string key && key.StartsWith(_resThemePrefix, StringComparison.Ordinal) &&
entry.Value is string value)
{
add(FileTheme.Load(value), key);
add(FileTheme.Load(value, _logger), key);
}
}
}
Expand Down
11 changes: 1 addition & 10 deletions WinFormsThemes/WinFormsThemes/Themes/AbstractTheme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ public abstract class AbstractTheme : ITheme
[ExcludeFromCodeCoverage]
public virtual Color ControlBorderColor => ControlHighlightColor;

[ExcludeFromCodeCoverage]
public virtual Color ControlBorderLightColor => ControlBorderColor;

[ExcludeFromCodeCoverage]
public abstract Color ControlErrorBackColor { get; }

Expand All @@ -61,12 +58,6 @@ public abstract class AbstractTheme : ITheme
[ExcludeFromCodeCoverage]
public abstract Color ControlHighlightColor { get; }

[ExcludeFromCodeCoverage]
public virtual Color ControlHighlightDarkColor => GetSoftenedColor(ControlBorderColor);

[ExcludeFromCodeCoverage]
public virtual Color ControlHighlightLightColor => GetSoftenedColor(ControlBorderColor, true);

[ExcludeFromCodeCoverage]
public abstract Color ControlSuccessBackColor { get; }

Expand Down Expand Up @@ -208,7 +199,7 @@ public void Apply(Control control, ThemeOptions options)
{
ts.Renderer = new ThemedToolStripRenderer(
new ThemedColorTable(
Color.Transparent, ControlBorderLightColor, ButtonHoverColor, ControlHighlightColor, ControlBackColor),
Color.Transparent, ControlBorderColor, ButtonHoverColor, ControlHighlightColor, ControlBackColor),
ButtonForeColor,
getForegroundColorForStyle(options, true))
{
Expand Down
Loading

0 comments on commit 86decfc

Please sign in to comment.