mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Store Themes as JSON files (#4471)
Co-authored-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
7a286480d6
commit
4e3433e966
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
|
@ -26,3 +26,7 @@ jobs:
|
||||||
- name: Show diff
|
- name: Show diff
|
||||||
run: git --no-pager diff --exit-code --color=never
|
run: git --no-pager diff --exit-code --color=never
|
||||||
shell: bash
|
shell: bash
|
||||||
|
- name: Check Theme files
|
||||||
|
run: |
|
||||||
|
npm i ajv-cli
|
||||||
|
npx -- ajv validate -s docs/ChatterinoTheme.schema.json -d "resources/themes/*.json"
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# JSON resources should not be prettified
|
# JSON resources should not be prettified...
|
||||||
resources/*.json
|
resources/*.json
|
||||||
|
# ...themes should be prettified for readability.
|
||||||
|
!resources/themes/*.json
|
||||||
|
|
||||||
# Ignore submodule files
|
# Ignore submodule files
|
||||||
lib/*/
|
lib/*/
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
- Bugfix: Fixed search popup ignoring setting for message scrollback limit. (#4496)
|
- Bugfix: Fixed search popup ignoring setting for message scrollback limit. (#4496)
|
||||||
- Bugfix: Fixed a memory leak that occurred when loading message history. This was mostly noticeable with unstable internet connections where reconnections were frequent or long-running instances of Chatterino. (#4499)
|
- Bugfix: Fixed a memory leak that occurred when loading message history. This was mostly noticeable with unstable internet connections where reconnections were frequent or long-running instances of Chatterino. (#4499)
|
||||||
- Dev: Disabling precompiled headers on Windows is now tested in CI. (#4472)
|
- Dev: Disabling precompiled headers on Windows is now tested in CI. (#4472)
|
||||||
|
- Dev: Themes are now stored as JSON files in `resources/themes`. (#4471)
|
||||||
- Dev: Ignore unhandled BTTV user-events. (#4438)
|
- Dev: Ignore unhandled BTTV user-events. (#4438)
|
||||||
- Dev: Only log debug messages when NDEBUG is not defined. (#4442)
|
- Dev: Only log debug messages when NDEBUG is not defined. (#4442)
|
||||||
- Dev: Cleaned up theme related code. (#4450)
|
- Dev: Cleaned up theme related code. (#4450)
|
||||||
|
|
|
@ -7,6 +7,7 @@ set(
|
||||||
resources.qrc
|
resources.qrc
|
||||||
resources_autogenerated.qrc
|
resources_autogenerated.qrc
|
||||||
windows.rc
|
windows.rc
|
||||||
|
themes/ChatterinoTheme.schema.json
|
||||||
)
|
)
|
||||||
set(RES_IMAGE_EXCLUDE_FILTER ^linuxinstall/)
|
set(RES_IMAGE_EXCLUDE_FILTER ^linuxinstall/)
|
||||||
|
|
||||||
|
|
398
docs/ChatterinoTheme.schema.json
Normal file
398
docs/ChatterinoTheme.schema.json
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema",
|
||||||
|
"title": "Chatterino Theme",
|
||||||
|
"description": "Colors and metadata for a Chatterino 2 theme",
|
||||||
|
"definitions": {
|
||||||
|
"qt-color": {
|
||||||
|
"type": "string",
|
||||||
|
"$comment": "https://doc.qt.io/qt-5/qcolor.html#setNamedColor",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"title": "#RGB",
|
||||||
|
"pattern": "^#[a-fA-F0-9]{3}$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "#RRGGBB",
|
||||||
|
"pattern": "^#[a-fA-F0-9]{6}$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "#AARRGGBB",
|
||||||
|
"$comment": "Note that this isn't identical to the CSS Color Moudle Level 4 <hex-color> where the alpha value is at the end.",
|
||||||
|
"pattern": "^#[a-fA-F0-9]{8}$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "#RRRGGGBBB",
|
||||||
|
"pattern": "^#[a-fA-F0-9]{9}$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "#RRRRGGGGBBBB",
|
||||||
|
"pattern": "^#[a-fA-F0-9]{12}$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "SVG Color",
|
||||||
|
"description": "This is stricter than Qt. You could theoretically put tabs an spaces between characters in a named color and capitalize the color.",
|
||||||
|
"$comment": "https://www.w3.org/TR/SVG11/types.html#ColorKeywords",
|
||||||
|
"enum": [
|
||||||
|
"aliceblue",
|
||||||
|
"antiquewhite",
|
||||||
|
"aqua",
|
||||||
|
"aquamarine",
|
||||||
|
"azure",
|
||||||
|
"beige",
|
||||||
|
"bisque",
|
||||||
|
"black",
|
||||||
|
"blanchedalmond",
|
||||||
|
"blue",
|
||||||
|
"blueviolet",
|
||||||
|
"brown",
|
||||||
|
"burlywood",
|
||||||
|
"cadetblue",
|
||||||
|
"chartreuse",
|
||||||
|
"chocolate",
|
||||||
|
"coral",
|
||||||
|
"cornflowerblue",
|
||||||
|
"cornsilk",
|
||||||
|
"crimson",
|
||||||
|
"cyan",
|
||||||
|
"darkblue",
|
||||||
|
"darkcyan",
|
||||||
|
"darkgoldenrod",
|
||||||
|
"darkgray",
|
||||||
|
"darkgreen",
|
||||||
|
"darkgrey",
|
||||||
|
"darkkhaki",
|
||||||
|
"darkmagenta",
|
||||||
|
"darkolivegreen",
|
||||||
|
"darkorange",
|
||||||
|
"darkorchid",
|
||||||
|
"darkred",
|
||||||
|
"darksalmon",
|
||||||
|
"darkseagreen",
|
||||||
|
"darkslateblue",
|
||||||
|
"darkslategray",
|
||||||
|
"darkslategrey",
|
||||||
|
"darkturquoise",
|
||||||
|
"darkviolet",
|
||||||
|
"deeppink",
|
||||||
|
"deepskyblue",
|
||||||
|
"dimgray",
|
||||||
|
"dimgrey",
|
||||||
|
"dodgerblue",
|
||||||
|
"firebrick",
|
||||||
|
"floralwhite",
|
||||||
|
"forestgreen",
|
||||||
|
"fuchsia",
|
||||||
|
"gainsboro",
|
||||||
|
"ghostwhite",
|
||||||
|
"gold",
|
||||||
|
"goldenrod",
|
||||||
|
"gray",
|
||||||
|
"grey",
|
||||||
|
"green",
|
||||||
|
"greenyellow",
|
||||||
|
"honeydew",
|
||||||
|
"hotpink",
|
||||||
|
"indianred",
|
||||||
|
"indigo",
|
||||||
|
"ivory",
|
||||||
|
"khaki",
|
||||||
|
"lavender",
|
||||||
|
"lavenderblush",
|
||||||
|
"lawngreen",
|
||||||
|
"lemonchiffon",
|
||||||
|
"lightblue",
|
||||||
|
"lightcoral",
|
||||||
|
"lightcyan",
|
||||||
|
"lightgoldenrodyellow",
|
||||||
|
"lightgray",
|
||||||
|
"lightgreen",
|
||||||
|
"lightgrey",
|
||||||
|
"lightpink",
|
||||||
|
"lightsalmon",
|
||||||
|
"lightseagreen",
|
||||||
|
"lightskyblue",
|
||||||
|
"lightslategray",
|
||||||
|
"lightslategrey",
|
||||||
|
"lightsteelblue",
|
||||||
|
"lightyellow",
|
||||||
|
"lime",
|
||||||
|
"limegreen",
|
||||||
|
"linen",
|
||||||
|
"magenta",
|
||||||
|
"maroon",
|
||||||
|
"mediumaquamarine",
|
||||||
|
"mediumblue",
|
||||||
|
"mediumorchid",
|
||||||
|
"mediumpurple",
|
||||||
|
"mediumseagreen",
|
||||||
|
"mediumslateblue",
|
||||||
|
"mediumspringgreen",
|
||||||
|
"mediumturquoise",
|
||||||
|
"mediumvioletred",
|
||||||
|
"midnightblue",
|
||||||
|
"mintcream",
|
||||||
|
"mistyrose",
|
||||||
|
"moccasin",
|
||||||
|
"navajowhite",
|
||||||
|
"navy",
|
||||||
|
"oldlace",
|
||||||
|
"olive",
|
||||||
|
"olivedrab",
|
||||||
|
"orange",
|
||||||
|
"orangered",
|
||||||
|
"orchid",
|
||||||
|
"palegoldenrod",
|
||||||
|
"palegreen",
|
||||||
|
"paleturquoise",
|
||||||
|
"palevioletred",
|
||||||
|
"papayawhip",
|
||||||
|
"peachpuff",
|
||||||
|
"peru",
|
||||||
|
"pink",
|
||||||
|
"plum",
|
||||||
|
"powderblue",
|
||||||
|
"purple",
|
||||||
|
"red",
|
||||||
|
"rosybrown",
|
||||||
|
"royalblue",
|
||||||
|
"saddlebrown",
|
||||||
|
"salmon",
|
||||||
|
"sandybrown",
|
||||||
|
"seagreen",
|
||||||
|
"seashell",
|
||||||
|
"sienna",
|
||||||
|
"silver",
|
||||||
|
"skyblue",
|
||||||
|
"slateblue",
|
||||||
|
"slategray",
|
||||||
|
"slategrey",
|
||||||
|
"snow",
|
||||||
|
"springgreen",
|
||||||
|
"steelblue",
|
||||||
|
"tan",
|
||||||
|
"teal",
|
||||||
|
"thistle",
|
||||||
|
"tomato",
|
||||||
|
"turquoise",
|
||||||
|
"violet",
|
||||||
|
"wheat",
|
||||||
|
"white",
|
||||||
|
"whitesmoke",
|
||||||
|
"yellow",
|
||||||
|
"yellowgreen"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "transparent",
|
||||||
|
"enum": ["transparent"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tab-colors": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"backgrounds": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"hover": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"regular": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"unfocused": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": ["hover", "regular", "unfocused"]
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"hover": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"regular": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"unfocused": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": ["hover", "regular", "unfocused"]
|
||||||
|
},
|
||||||
|
"text": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": ["backgrounds", "line", "text"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"colors": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"accent": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"messages": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"backgrounds": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"alternate": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"regular": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": ["alternate", "regular"]
|
||||||
|
},
|
||||||
|
"disabled": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"highlightAnimationEnd": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"highlightAnimationStart": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"selection": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"textColors": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"caret": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"chatPlaceholder": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"link": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"regular": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"system": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"caret",
|
||||||
|
"chatPlaceholder",
|
||||||
|
"link",
|
||||||
|
"regular",
|
||||||
|
"system"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"backgrounds",
|
||||||
|
"disabled",
|
||||||
|
"highlightAnimationEnd",
|
||||||
|
"highlightAnimationStart",
|
||||||
|
"selection",
|
||||||
|
"textColors"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"scrollbars": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"background": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"thumb": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"thumbSelected": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": ["background", "thumb", "thumbSelected"]
|
||||||
|
},
|
||||||
|
"splits": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"background": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"dropPreview": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"dropPreviewBorder": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"dropTargetRect": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"dropTargetRectBorder": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"header": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"background": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"border": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"focusedBackground": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"focusedBorder": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"focusedText": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"text": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"background",
|
||||||
|
"border",
|
||||||
|
"focusedBackground",
|
||||||
|
"focusedBorder",
|
||||||
|
"focusedText",
|
||||||
|
"text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"background": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"text": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": ["background", "text"]
|
||||||
|
},
|
||||||
|
"messageSeperator": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"resizeHandle": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"resizeHandleBackground": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"background",
|
||||||
|
"dropPreview",
|
||||||
|
"dropPreviewBorder",
|
||||||
|
"dropTargetRect",
|
||||||
|
"dropTargetRectBorder",
|
||||||
|
"header",
|
||||||
|
"input",
|
||||||
|
"messageSeperator",
|
||||||
|
"resizeHandle",
|
||||||
|
"resizeHandleBackground"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"dividerLine": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"highlighted": {
|
||||||
|
"$ref": "#/definitions/tab-colors"
|
||||||
|
},
|
||||||
|
"newMessage": {
|
||||||
|
"$ref": "#/definitions/tab-colors"
|
||||||
|
},
|
||||||
|
"regular": {
|
||||||
|
"$ref": "#/definitions/tab-colors"
|
||||||
|
},
|
||||||
|
"selected": {
|
||||||
|
"$ref": "#/definitions/tab-colors"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"dividerLine",
|
||||||
|
"highlighted",
|
||||||
|
"newMessage",
|
||||||
|
"regular",
|
||||||
|
"selected"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"window": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"background": { "$ref": "#/definitions/qt-color" },
|
||||||
|
"text": { "$ref": "#/definitions/qt-color" }
|
||||||
|
},
|
||||||
|
"required": ["background", "text"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"accent",
|
||||||
|
"messages",
|
||||||
|
"scrollbars",
|
||||||
|
"splits",
|
||||||
|
"tabs",
|
||||||
|
"window"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"iconTheme": {
|
||||||
|
"$comment": "Determines which icons to use. 'dark' will use dark icons (best for a light theme). 'light' will use light icons.",
|
||||||
|
"enum": ["light", "dark"],
|
||||||
|
"default": "light"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["iconTheme"]
|
||||||
|
},
|
||||||
|
"$schema": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["colors", "metadata"]
|
||||||
|
}
|
112
resources/themes/Black.json
Normal file
112
resources/themes/Black.json
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
{
|
||||||
|
"$schema": "../../docs/ChatterinoTheme.schema.json",
|
||||||
|
"metadata": {
|
||||||
|
"iconTheme": "light"
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"accent": "#00aeef",
|
||||||
|
"messages": {
|
||||||
|
"backgrounds": {
|
||||||
|
"alternate": "#0a0a0a",
|
||||||
|
"regular": "#000000"
|
||||||
|
},
|
||||||
|
"disabled": "#99000000",
|
||||||
|
"highlightAnimationEnd": "#00e6e6e6",
|
||||||
|
"highlightAnimationStart": "#6ee6e6e6",
|
||||||
|
"selection": "#40ffffff",
|
||||||
|
"textColors": {
|
||||||
|
"caret": "#ffffff",
|
||||||
|
"chatPlaceholder": "#5d5555",
|
||||||
|
"link": "#4286f4",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"system": "#8c7f7f"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scrollbars": {
|
||||||
|
"background": "#00000000",
|
||||||
|
"thumb": "#4d4d4d",
|
||||||
|
"thumbSelected": "#595959"
|
||||||
|
},
|
||||||
|
"splits": {
|
||||||
|
"background": "#000000",
|
||||||
|
"dropPreview": "#300094ff",
|
||||||
|
"dropPreviewBorder": "#0094ff",
|
||||||
|
"dropTargetRect": "#000094ff",
|
||||||
|
"dropTargetRectBorder": "#000094ff",
|
||||||
|
"header": {
|
||||||
|
"background": "#191919",
|
||||||
|
"border": "#262626",
|
||||||
|
"focusedBackground": "#363636",
|
||||||
|
"focusedBorder": "#383838",
|
||||||
|
"focusedText": "#84c1ff",
|
||||||
|
"text": "#ffffff"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"background": "#0d0d0d",
|
||||||
|
"text": "#ffffff"
|
||||||
|
},
|
||||||
|
"messageSeperator": "#3c3c3c",
|
||||||
|
"resizeHandle": "#700094ff",
|
||||||
|
"resizeHandleBackground": "#200094ff"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"dividerLine": "#555555",
|
||||||
|
"highlighted": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#252525",
|
||||||
|
"regular": "#252525",
|
||||||
|
"unfocused": "#252525"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#ee6166",
|
||||||
|
"regular": "#ee6166",
|
||||||
|
"unfocused": "#ee6166"
|
||||||
|
},
|
||||||
|
"text": "#eeeeee"
|
||||||
|
},
|
||||||
|
"newMessage": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#252525",
|
||||||
|
"regular": "#252525",
|
||||||
|
"unfocused": "#252525"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#888888",
|
||||||
|
"regular": "#888888",
|
||||||
|
"unfocused": "#888888"
|
||||||
|
},
|
||||||
|
"text": "#eeeeee"
|
||||||
|
},
|
||||||
|
"regular": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#252525",
|
||||||
|
"regular": "#252525",
|
||||||
|
"unfocused": "#252525"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#444444",
|
||||||
|
"regular": "#444444",
|
||||||
|
"unfocused": "#444444"
|
||||||
|
},
|
||||||
|
"text": "#aaaaaa"
|
||||||
|
},
|
||||||
|
"selected": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#555555",
|
||||||
|
"regular": "#555555",
|
||||||
|
"unfocused": "#555555"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#00aeef",
|
||||||
|
"regular": "#00aeef",
|
||||||
|
"unfocused": "#00aeef"
|
||||||
|
},
|
||||||
|
"text": "#ffffff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"window": {
|
||||||
|
"background": "#111111",
|
||||||
|
"text": "#eeeeee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
resources/themes/Dark.json
Normal file
112
resources/themes/Dark.json
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
{
|
||||||
|
"$schema": "../../docs/ChatterinoTheme.schema.json",
|
||||||
|
"metadata": {
|
||||||
|
"iconTheme": "light"
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"accent": "#00aeef",
|
||||||
|
"messages": {
|
||||||
|
"backgrounds": {
|
||||||
|
"alternate": "#222222",
|
||||||
|
"regular": "#191919"
|
||||||
|
},
|
||||||
|
"disabled": "#99191919",
|
||||||
|
"highlightAnimationEnd": "#00e6e6e6",
|
||||||
|
"highlightAnimationStart": "#6ee6e6e6",
|
||||||
|
"selection": "#40ffffff",
|
||||||
|
"textColors": {
|
||||||
|
"caret": "#ffffff",
|
||||||
|
"chatPlaceholder": "#5d5555",
|
||||||
|
"link": "#4286f4",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"system": "#8c7f7f"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scrollbars": {
|
||||||
|
"background": "#00000000",
|
||||||
|
"thumb": "#575757",
|
||||||
|
"thumbSelected": "#616161"
|
||||||
|
},
|
||||||
|
"splits": {
|
||||||
|
"background": "#191919",
|
||||||
|
"dropPreview": "#300094ff",
|
||||||
|
"dropPreviewBorder": "#0094ff",
|
||||||
|
"dropTargetRect": "#000094ff",
|
||||||
|
"dropTargetRectBorder": "#000094ff",
|
||||||
|
"header": {
|
||||||
|
"background": "#2e2e2e",
|
||||||
|
"border": "#383838",
|
||||||
|
"focusedBackground": "#444444",
|
||||||
|
"focusedBorder": "#464646",
|
||||||
|
"focusedText": "#84c1ff",
|
||||||
|
"text": "#ffffff"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"background": "#242424",
|
||||||
|
"text": "#ffffff"
|
||||||
|
},
|
||||||
|
"messageSeperator": "#3c3c3c",
|
||||||
|
"resizeHandle": "#700094ff",
|
||||||
|
"resizeHandleBackground": "#200094ff"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"dividerLine": "#555555",
|
||||||
|
"highlighted": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#252525",
|
||||||
|
"regular": "#252525",
|
||||||
|
"unfocused": "#252525"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#ee6166",
|
||||||
|
"regular": "#ee6166",
|
||||||
|
"unfocused": "#ee6166"
|
||||||
|
},
|
||||||
|
"text": "#eeeeee"
|
||||||
|
},
|
||||||
|
"newMessage": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#252525",
|
||||||
|
"regular": "#252525",
|
||||||
|
"unfocused": "#252525"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#888888",
|
||||||
|
"regular": "#888888",
|
||||||
|
"unfocused": "#888888"
|
||||||
|
},
|
||||||
|
"text": "#eeeeee"
|
||||||
|
},
|
||||||
|
"regular": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#252525",
|
||||||
|
"regular": "#252525",
|
||||||
|
"unfocused": "#252525"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#444444",
|
||||||
|
"regular": "#444444",
|
||||||
|
"unfocused": "#444444"
|
||||||
|
},
|
||||||
|
"text": "#aaaaaa"
|
||||||
|
},
|
||||||
|
"selected": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#555555",
|
||||||
|
"regular": "#555555",
|
||||||
|
"unfocused": "#555555"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#00aeef",
|
||||||
|
"regular": "#00aeef",
|
||||||
|
"unfocused": "#00aeef"
|
||||||
|
},
|
||||||
|
"text": "#ffffff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"window": {
|
||||||
|
"background": "#111111",
|
||||||
|
"text": "#eeeeee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
resources/themes/Light.json
Normal file
112
resources/themes/Light.json
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
{
|
||||||
|
"$schema": "../../docs/ChatterinoTheme.schema.json",
|
||||||
|
"metadata": {
|
||||||
|
"iconTheme": "dark"
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"accent": "#00aeef",
|
||||||
|
"messages": {
|
||||||
|
"backgrounds": {
|
||||||
|
"alternate": "#dddddd",
|
||||||
|
"regular": "#e6e6e6"
|
||||||
|
},
|
||||||
|
"disabled": "#99e6e6e6",
|
||||||
|
"highlightAnimationEnd": "#00141414",
|
||||||
|
"highlightAnimationStart": "#6e141414",
|
||||||
|
"selection": "#40000000",
|
||||||
|
"textColors": {
|
||||||
|
"caret": "#000000",
|
||||||
|
"chatPlaceholder": "#af9f9f",
|
||||||
|
"link": "#4286f4",
|
||||||
|
"regular": "#000000",
|
||||||
|
"system": "#8c7f7f"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scrollbars": {
|
||||||
|
"background": "#00000000",
|
||||||
|
"thumb": "#a8a8a8",
|
||||||
|
"thumbSelected": "#9e9e9e"
|
||||||
|
},
|
||||||
|
"splits": {
|
||||||
|
"background": "#e6e6e6",
|
||||||
|
"dropPreview": "#300094ff",
|
||||||
|
"dropPreviewBorder": "#0094ff",
|
||||||
|
"dropTargetRect": "#00ffffff",
|
||||||
|
"dropTargetRectBorder": "#000094ff",
|
||||||
|
"header": {
|
||||||
|
"background": "#e6e6e6",
|
||||||
|
"border": "#e6e6e6",
|
||||||
|
"focusedBackground": "#dbdbdb",
|
||||||
|
"focusedBorder": "#d1d1d1",
|
||||||
|
"focusedText": "#0051a3",
|
||||||
|
"text": "#000000"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"background": "#dbdbdb",
|
||||||
|
"text": "#000000"
|
||||||
|
},
|
||||||
|
"messageSeperator": "#7f7f7f",
|
||||||
|
"resizeHandle": "#0094ff",
|
||||||
|
"resizeHandleBackground": "#500094ff"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"dividerLine": "#b4d7ff",
|
||||||
|
"highlighted": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#eeeeee",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"unfocused": "#ffffff"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#ff0000",
|
||||||
|
"regular": "#ff0000",
|
||||||
|
"unfocused": "#ff0000"
|
||||||
|
},
|
||||||
|
"text": "#000000"
|
||||||
|
},
|
||||||
|
"newMessage": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#eeeeee",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"unfocused": "#ffffff"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#bbbbbb",
|
||||||
|
"regular": "#bbbbbb",
|
||||||
|
"unfocused": "#bbbbbb"
|
||||||
|
},
|
||||||
|
"text": "#222222"
|
||||||
|
},
|
||||||
|
"regular": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#eeeeee",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"unfocused": "#ffffff"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#ffffff",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"unfocused": "#ffffff"
|
||||||
|
},
|
||||||
|
"text": "#444444"
|
||||||
|
},
|
||||||
|
"selected": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#b4d7ff",
|
||||||
|
"regular": "#b4d7ff",
|
||||||
|
"unfocused": "#b4d7ff"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#00aeef",
|
||||||
|
"regular": "#00aeef",
|
||||||
|
"unfocused": "#00aeef"
|
||||||
|
},
|
||||||
|
"text": "#000000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"window": {
|
||||||
|
"background": "#ffffff",
|
||||||
|
"text": "#000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
resources/themes/White.json
Normal file
112
resources/themes/White.json
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
{
|
||||||
|
"$schema": "../../docs/ChatterinoTheme.schema.json",
|
||||||
|
"metadata": {
|
||||||
|
"iconTheme": "dark"
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"accent": "#00aeef",
|
||||||
|
"messages": {
|
||||||
|
"backgrounds": {
|
||||||
|
"alternate": "#f5f5f5",
|
||||||
|
"regular": "#ffffff"
|
||||||
|
},
|
||||||
|
"disabled": "#99ffffff",
|
||||||
|
"highlightAnimationEnd": "#00141414",
|
||||||
|
"highlightAnimationStart": "#6e141414",
|
||||||
|
"selection": "#40000000",
|
||||||
|
"textColors": {
|
||||||
|
"caret": "#000000",
|
||||||
|
"chatPlaceholder": "#af9f9f",
|
||||||
|
"link": "#4286f4",
|
||||||
|
"regular": "#000000",
|
||||||
|
"system": "#8c7f7f"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scrollbars": {
|
||||||
|
"background": "#00000000",
|
||||||
|
"thumb": "#b3b3b3",
|
||||||
|
"thumbSelected": "#a6a6a6"
|
||||||
|
},
|
||||||
|
"splits": {
|
||||||
|
"background": "#ffffff",
|
||||||
|
"dropPreview": "#300094ff",
|
||||||
|
"dropPreviewBorder": "#0094ff",
|
||||||
|
"dropTargetRect": "#00ffffff",
|
||||||
|
"dropTargetRectBorder": "#000094ff",
|
||||||
|
"header": {
|
||||||
|
"background": "#ffffff",
|
||||||
|
"border": "#ffffff",
|
||||||
|
"focusedBackground": "#f2f2f2",
|
||||||
|
"focusedBorder": "#e6e6e6",
|
||||||
|
"focusedText": "#0051a3",
|
||||||
|
"text": "#000000"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"background": "#f2f2f2",
|
||||||
|
"text": "#000000"
|
||||||
|
},
|
||||||
|
"messageSeperator": "#7f7f7f",
|
||||||
|
"resizeHandle": "#0094ff",
|
||||||
|
"resizeHandleBackground": "#500094ff"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"dividerLine": "#b4d7ff",
|
||||||
|
"highlighted": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#eeeeee",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"unfocused": "#ffffff"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#ff0000",
|
||||||
|
"regular": "#ff0000",
|
||||||
|
"unfocused": "#ff0000"
|
||||||
|
},
|
||||||
|
"text": "#000000"
|
||||||
|
},
|
||||||
|
"newMessage": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#eeeeee",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"unfocused": "#ffffff"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#bbbbbb",
|
||||||
|
"regular": "#bbbbbb",
|
||||||
|
"unfocused": "#bbbbbb"
|
||||||
|
},
|
||||||
|
"text": "#222222"
|
||||||
|
},
|
||||||
|
"regular": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#eeeeee",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"unfocused": "#ffffff"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#ffffff",
|
||||||
|
"regular": "#ffffff",
|
||||||
|
"unfocused": "#ffffff"
|
||||||
|
},
|
||||||
|
"text": "#444444"
|
||||||
|
},
|
||||||
|
"selected": {
|
||||||
|
"backgrounds": {
|
||||||
|
"hover": "#b4d7ff",
|
||||||
|
"regular": "#b4d7ff",
|
||||||
|
"unfocused": "#b4d7ff"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"hover": "#00aeef",
|
||||||
|
"regular": "#00aeef",
|
||||||
|
"unfocused": "#00aeef"
|
||||||
|
},
|
||||||
|
"text": "#000000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"window": {
|
||||||
|
"background": "#ffffff",
|
||||||
|
"text": "#000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ Q_LOGGING_CATEGORY(chatterinoSound, "chatterino.sound", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoStreamerMode, "chatterino.streamermode",
|
Q_LOGGING_CATEGORY(chatterinoStreamerMode, "chatterino.streamermode",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoStreamlink, "chatterino.streamlink", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoStreamlink, "chatterino.streamlink", logThreshold);
|
||||||
|
Q_LOGGING_CATEGORY(chatterinoTheme, "chatterino.theme", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoTokenizer, "chatterino.tokenizer", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoTokenizer, "chatterino.tokenizer", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoTwitch, "chatterino.twitch", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoTwitch, "chatterino.twitch", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoUpdate, "chatterino.update", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoUpdate, "chatterino.update", logThreshold);
|
||||||
|
|
|
@ -34,6 +34,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoSeventvEventAPI);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoSound);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoSound);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoStreamerMode);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoStreamerMode);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoStreamlink);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoStreamlink);
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoTheme);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoTokenizer);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoTokenizer);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoTwitch);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoTwitch);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoUpdate);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoUpdate);
|
||||||
|
|
|
@ -2,34 +2,153 @@
|
||||||
#include "singletons/Theme.hpp"
|
#include "singletons/Theme.hpp"
|
||||||
|
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
#include "singletons/Resources.hpp"
|
#include "common/QLogging.hpp"
|
||||||
|
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
double getMultiplierByTheme(const QString &themeName)
|
void parseInto(const QJsonObject &obj, const QLatin1String &key, QColor &color)
|
||||||
{
|
{
|
||||||
if (themeName == "Light")
|
const auto &jsonValue = obj[key];
|
||||||
|
if (!jsonValue.isString()) [[unlikely]]
|
||||||
{
|
{
|
||||||
return 0.8;
|
qCWarning(chatterinoTheme) << key
|
||||||
|
<< "was expected but not found in the "
|
||||||
|
"current theme - using previous value.";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (themeName == "White")
|
QColor parsed = {jsonValue.toString()};
|
||||||
|
if (!parsed.isValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
return 1.0;
|
qCWarning(chatterinoTheme).nospace()
|
||||||
|
<< "While parsing " << key << ": '" << jsonValue.toString()
|
||||||
|
<< "' isn't a valid color.";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (themeName == "Black")
|
color = parsed;
|
||||||
{
|
|
||||||
return -1.0;
|
|
||||||
}
|
|
||||||
if (themeName == "Dark")
|
|
||||||
{
|
|
||||||
return -0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -0.8; // default: Dark
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||||
|
#define parseColor(to, from, key) \
|
||||||
|
parseInto(from, QLatin1String(#key), (to).from.key)
|
||||||
|
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||||
|
|
||||||
|
void parseWindow(const QJsonObject &window, chatterino::Theme &theme)
|
||||||
|
{
|
||||||
|
parseColor(theme, window, background);
|
||||||
|
parseColor(theme, window, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseTabs(const QJsonObject &tabs, chatterino::Theme &theme)
|
||||||
|
{
|
||||||
|
const auto parseTabColors = [](auto json, auto &tab) {
|
||||||
|
parseInto(json, QLatin1String("text"), tab.text);
|
||||||
|
{
|
||||||
|
const auto backgrounds = json["backgrounds"].toObject();
|
||||||
|
parseColor(tab, backgrounds, regular);
|
||||||
|
parseColor(tab, backgrounds, hover);
|
||||||
|
parseColor(tab, backgrounds, unfocused);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto line = json["line"].toObject();
|
||||||
|
parseColor(tab, line, regular);
|
||||||
|
parseColor(tab, line, hover);
|
||||||
|
parseColor(tab, line, unfocused);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
parseColor(theme, tabs, dividerLine);
|
||||||
|
parseTabColors(tabs["regular"].toObject(), theme.tabs.regular);
|
||||||
|
parseTabColors(tabs["newMessage"].toObject(), theme.tabs.newMessage);
|
||||||
|
parseTabColors(tabs["highlighted"].toObject(), theme.tabs.highlighted);
|
||||||
|
parseTabColors(tabs["selected"].toObject(), theme.tabs.selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseMessages(const QJsonObject &messages, chatterino::Theme &theme)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const auto textColors = messages["textColors"].toObject();
|
||||||
|
parseColor(theme.messages, textColors, regular);
|
||||||
|
parseColor(theme.messages, textColors, caret);
|
||||||
|
parseColor(theme.messages, textColors, link);
|
||||||
|
parseColor(theme.messages, textColors, system);
|
||||||
|
parseColor(theme.messages, textColors, chatPlaceholder);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto backgrounds = messages["backgrounds"].toObject();
|
||||||
|
parseColor(theme.messages, backgrounds, regular);
|
||||||
|
parseColor(theme.messages, backgrounds, alternate);
|
||||||
|
}
|
||||||
|
parseColor(theme, messages, disabled);
|
||||||
|
parseColor(theme, messages, selection);
|
||||||
|
parseColor(theme, messages, highlightAnimationStart);
|
||||||
|
parseColor(theme, messages, highlightAnimationEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseScrollbars(const QJsonObject &scrollbars, chatterino::Theme &theme)
|
||||||
|
{
|
||||||
|
parseColor(theme, scrollbars, background);
|
||||||
|
parseColor(theme, scrollbars, thumb);
|
||||||
|
parseColor(theme, scrollbars, thumbSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseSplits(const QJsonObject &splits, chatterino::Theme &theme)
|
||||||
|
{
|
||||||
|
parseColor(theme, splits, messageSeperator);
|
||||||
|
parseColor(theme, splits, background);
|
||||||
|
parseColor(theme, splits, dropPreview);
|
||||||
|
parseColor(theme, splits, dropPreviewBorder);
|
||||||
|
parseColor(theme, splits, dropTargetRect);
|
||||||
|
parseColor(theme, splits, dropTargetRectBorder);
|
||||||
|
parseColor(theme, splits, resizeHandle);
|
||||||
|
parseColor(theme, splits, resizeHandleBackground);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto header = splits["header"].toObject();
|
||||||
|
parseColor(theme.splits, header, border);
|
||||||
|
parseColor(theme.splits, header, focusedBorder);
|
||||||
|
parseColor(theme.splits, header, background);
|
||||||
|
parseColor(theme.splits, header, focusedBackground);
|
||||||
|
parseColor(theme.splits, header, text);
|
||||||
|
parseColor(theme.splits, header, focusedText);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto input = splits["input"].toObject();
|
||||||
|
parseColor(theme.splits, input, background);
|
||||||
|
parseColor(theme.splits, input, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseColors(const QJsonObject &root, chatterino::Theme &theme)
|
||||||
|
{
|
||||||
|
const auto colors = root["colors"].toObject();
|
||||||
|
|
||||||
|
parseInto(colors, QLatin1String("accent"), theme.accent);
|
||||||
|
|
||||||
|
parseWindow(colors["window"].toObject(), theme);
|
||||||
|
parseTabs(colors["tabs"].toObject(), theme);
|
||||||
|
parseMessages(colors["messages"].toObject(), theme);
|
||||||
|
parseScrollbars(colors["scrollbars"].toObject(), theme);
|
||||||
|
parseSplits(colors["splits"].toObject(), theme);
|
||||||
|
}
|
||||||
|
#undef parseColor
|
||||||
|
|
||||||
|
QString getThemePath(const QString &name)
|
||||||
|
{
|
||||||
|
static QSet<QString> knownThemes = {"White", "Light", "Dark", "Black"};
|
||||||
|
|
||||||
|
if (knownThemes.contains(name))
|
||||||
|
{
|
||||||
|
return QStringLiteral(":/themes/%1.json").arg(name);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -52,142 +171,45 @@ Theme::Theme()
|
||||||
|
|
||||||
void Theme::update()
|
void Theme::update()
|
||||||
{
|
{
|
||||||
this->actuallyUpdate(getMultiplierByTheme(this->themeName.getValue()));
|
this->parse();
|
||||||
|
|
||||||
this->updated.invoke();
|
this->updated.invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiplier: 1 = white, 0.8 = light, -0.8 dark, -1 black
|
void Theme::parse()
|
||||||
void Theme::actuallyUpdate(double multiplier)
|
|
||||||
{
|
{
|
||||||
this->isLight_ = multiplier > 0;
|
QFile file(getThemePath(this->themeName));
|
||||||
|
if (!file.open(QFile::ReadOnly))
|
||||||
const auto isLight = this->isLightTheme();
|
|
||||||
|
|
||||||
auto getGray = [multiplier](double l, double a = 1.0) {
|
|
||||||
return QColor::fromHslF(0, 0, ((l - 0.5) * multiplier) + 0.5, a);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// WINDOW
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
this->window.background = isLight ? "#fff" : QColor(61, 60, 56);
|
|
||||||
#else
|
|
||||||
this->window.background = isLight ? "#fff" : "#111";
|
|
||||||
#endif
|
|
||||||
this->window.text = isLight ? "#000" : "#eee";
|
|
||||||
|
|
||||||
/// TABSs
|
|
||||||
if (isLight)
|
|
||||||
{
|
{
|
||||||
this->tabs.regular = {.text = "#444",
|
qCWarning(chatterinoTheme) << "Failed to open" << file.fileName();
|
||||||
.backgrounds = {"#fff", "#eee", "#fff"},
|
return;
|
||||||
.line = {"#fff", "#fff", "#fff"}};
|
|
||||||
this->tabs.newMessage = {.text = "#222",
|
|
||||||
.backgrounds = {"#fff", "#eee", "#fff"},
|
|
||||||
.line = {"#bbb", "#bbb", "#bbb"}};
|
|
||||||
this->tabs.highlighted = {.text = "#000",
|
|
||||||
.backgrounds = {"#fff", "#eee", "#fff"},
|
|
||||||
.line = {"#f00", "#f00", "#f00"}};
|
|
||||||
this->tabs.selected = {
|
|
||||||
.text = "#000",
|
|
||||||
.backgrounds = {"#b4d7ff", "#b4d7ff", "#b4d7ff"},
|
|
||||||
.line = {this->accent, this->accent, this->accent}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->tabs.regular = {.text = "#aaa",
|
|
||||||
.backgrounds{"#252525", "#252525", "#252525"},
|
|
||||||
.line = {"#444", "#444", "#444"}};
|
|
||||||
this->tabs.newMessage = {.text = "#eee",
|
|
||||||
.backgrounds{"#252525", "#252525", "#252525"},
|
|
||||||
.line = {"#888", "#888", "#888"}};
|
|
||||||
this->tabs.highlighted = {.text = "#eee",
|
|
||||||
.backgrounds{"#252525", "#252525", "#252525"},
|
|
||||||
.line = {"#ee6166", "#ee6166", "#ee6166"}};
|
|
||||||
this->tabs.selected = {
|
|
||||||
.text = "#fff",
|
|
||||||
.backgrounds{"#555", "#555", "#555"},
|
|
||||||
.line = {this->accent, this->accent, this->accent}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->tabs.dividerLine = this->tabs.selected.backgrounds.regular;
|
QJsonParseError error{};
|
||||||
|
auto json = QJsonDocument::fromJson(file.readAll(), &error);
|
||||||
// Message
|
if (json.isNull())
|
||||||
this->messages.textColors.caret = isLight ? "#000" : "#fff";
|
|
||||||
this->messages.textColors.regular = isLight ? "#000" : "#fff";
|
|
||||||
this->messages.textColors.link = QColor(66, 134, 244);
|
|
||||||
this->messages.textColors.system = QColor(140, 127, 127);
|
|
||||||
this->messages.textColors.chatPlaceholder =
|
|
||||||
isLight ? QColor(175, 159, 159) : QColor(93, 85, 85);
|
|
||||||
|
|
||||||
this->messages.backgrounds.regular = getGray(1);
|
|
||||||
this->messages.backgrounds.alternate = getGray(0.96);
|
|
||||||
|
|
||||||
this->messages.disabled = getGray(1, 0.6);
|
|
||||||
|
|
||||||
int complementaryGray = isLight ? 20 : 230;
|
|
||||||
this->messages.highlightAnimationStart =
|
|
||||||
QColor(complementaryGray, complementaryGray, complementaryGray, 110);
|
|
||||||
this->messages.highlightAnimationEnd =
|
|
||||||
QColor(complementaryGray, complementaryGray, complementaryGray, 0);
|
|
||||||
|
|
||||||
// Scrollbar
|
|
||||||
this->scrollbars.background = QColor(0, 0, 0, 0);
|
|
||||||
this->scrollbars.thumb = getGray(0.70);
|
|
||||||
this->scrollbars.thumbSelected = getGray(0.65);
|
|
||||||
|
|
||||||
// Selection
|
|
||||||
this->messages.selection =
|
|
||||||
isLight ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64);
|
|
||||||
|
|
||||||
// Splits
|
|
||||||
if (isLight)
|
|
||||||
{
|
{
|
||||||
this->splits.dropTargetRect = QColor(255, 255, 255, 0);
|
qCWarning(chatterinoTheme) << "Failed to parse" << file.fileName()
|
||||||
|
<< "error:" << error.errorString();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
this->splits.dropTargetRect = QColor(0, 148, 255, 0);
|
|
||||||
}
|
|
||||||
this->splits.dropTargetRectBorder = QColor(0, 148, 255, 0);
|
|
||||||
this->splits.dropPreview = QColor(0, 148, 255, 48);
|
|
||||||
this->splits.dropPreviewBorder = QColor(0, 148, 255);
|
|
||||||
this->splits.resizeHandle = QColor(0, 148, 255, isLight ? 255 : 112);
|
|
||||||
this->splits.resizeHandleBackground =
|
|
||||||
QColor(0, 148, 255, isLight ? 80 : 32);
|
|
||||||
|
|
||||||
this->splits.header.background = getGray(isLight ? 1 : 0.9);
|
this->parseFrom(json.object());
|
||||||
this->splits.header.border = getGray(isLight ? 1 : 0.85);
|
}
|
||||||
this->splits.header.text = this->messages.textColors.regular;
|
|
||||||
this->splits.header.focusedBackground = getGray(isLight ? 0.95 : 0.79);
|
void Theme::parseFrom(const QJsonObject &root)
|
||||||
this->splits.header.focusedBorder = getGray(isLight ? 0.90 : 0.78);
|
{
|
||||||
this->splits.header.focusedText = QColor::fromHsvF(
|
parseColors(root, *this);
|
||||||
0.58388, isLight ? 1.0 : 0.482, isLight ? 0.6375 : 1.0);
|
|
||||||
|
this->isLight_ =
|
||||||
|
root["metadata"]["iconTheme"].toString() == QStringLiteral("dark");
|
||||||
|
|
||||||
this->splits.input.background = getGray(0.95);
|
|
||||||
this->splits.input.text = this->messages.textColors.regular;
|
|
||||||
this->splits.input.styleSheet =
|
this->splits.input.styleSheet =
|
||||||
"background:" + this->splits.input.background.name() + ";" +
|
"background:" + this->splits.input.background.name() + ";" +
|
||||||
"border:" + this->tabs.selected.backgrounds.regular.name() + ";" +
|
"border:" + this->tabs.selected.backgrounds.regular.name() + ";" +
|
||||||
"color:" + this->messages.textColors.regular.name() + ";" +
|
"color:" + this->messages.textColors.regular.name() + ";" +
|
||||||
"selection-background-color:" +
|
"selection-background-color:" +
|
||||||
(isLight ? "#68B1FF" : this->tabs.selected.backgrounds.regular.name());
|
(this->isLightTheme() ? "#68B1FF"
|
||||||
|
: this->tabs.selected.backgrounds.regular.name());
|
||||||
this->splits.messageSeperator =
|
|
||||||
isLight ? QColor(127, 127, 127) : QColor(60, 60, 60);
|
|
||||||
this->splits.background = getGray(1);
|
|
||||||
|
|
||||||
// Copy button
|
|
||||||
if (isLight)
|
|
||||||
{
|
|
||||||
this->buttons.copy = getResources().buttons.copyDark;
|
|
||||||
this->buttons.pin = getResources().buttons.pinDisabledDark;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->buttons.copy = getResources().buttons.copyLight;
|
|
||||||
this->buttons.pin = getResources().buttons.pinDisabledLight;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Theme::normalizeColor(QColor &color) const
|
void Theme::normalizeColor(QColor &color) const
|
||||||
|
|
|
@ -120,7 +120,9 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isLight_ = false;
|
bool isLight_ = false;
|
||||||
void actuallyUpdate(double multiplier);
|
|
||||||
|
void parse();
|
||||||
|
void parseFrom(const QJsonObject &root);
|
||||||
|
|
||||||
pajlada::Signals::NoArgSignal repaintVisibleChatWidgets_;
|
pajlada::Signals::NoArgSignal repaintVisibleChatWidgets_;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue