Skip to content

Commit

Permalink
feat: Ability to override system accent color
Browse files Browse the repository at this point in the history
  • Loading branch information
shdwmtr committed Oct 27, 2024
1 parent 747ccb8 commit a850936
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 8 deletions.
15 changes: 14 additions & 1 deletion assets/core/api/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Millennium, json, os # type: ignore

from api.css_analyzer import ColorTypes, convert_from_hex, convert_to_hex, parse_root
from api.themes import is_valid
from api.themes import Colors, is_valid
from api.watchdog import SteamUtils
from util.webkit_handler import WebkitStack, add_browser_css, add_browser_js
from watchdog.observers import Observer
Expand Down Expand Up @@ -32,6 +32,7 @@ def __init__(self):
self.create_default("scripts", True, bool)
self.create_default("styles", True, bool)
self.create_default("updateNotifications", True, bool)
self.create_default("accentColor", "DEFAULT_ACCENT_COLOR", str)

# Check if the active them
self.validate_theme()
Expand Down Expand Up @@ -200,6 +201,18 @@ def change_color(self, color_name: str, new_color: str, type: int) -> None:
self.config["colors"][self.name][color] = parsed_color
self.set_config(json.dumps(self.config, indent=4))
return parsed_color

def change_accent_color(self, new_color: str) -> None:
self.config["accentColor"] = new_color
self.set_config(json.dumps(self.config, indent=4))

return Colors.get_accent_color(self.config["accentColor"])

def reset_accent_color(self) -> None:
self.config["accentColor"] = "DEFAULT_ACCENT_COLOR"
self.set_config(json.dumps(self.config, indent=4))

return Colors.get_accent_color(self.config["accentColor"])


def set_theme_cb(self):
Expand Down
87 changes: 83 additions & 4 deletions assets/core/api/themes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def rgb(accent):
'dark1': hex(UIColorType.ACCENT_DARK1), 'dark1Rgb': rgb(UIColorType.ACCENT_DARK1),
'dark2': hex(UIColorType.ACCENT_DARK2), 'dark2Rgb': rgb(UIColorType.ACCENT_DARK2),
'dark3': hex(UIColorType.ACCENT_DARK3), 'dark3Rgb': rgb(UIColorType.ACCENT_DARK3),
'systemAccent': True
}
return json.dumps(color_dictionary)

Expand All @@ -36,15 +37,93 @@ def get_accent_color_posix():
'accent': '#000',
'light1': '#000', 'light2': '#000', 'light3': '#000',
'dark1': '#000', 'dark2': '#000', 'dark3': '#000',
'accentRgb': '0, 0, 0', 'light1Rgb': '0, 0, 0', 'light2Rgb': '0, 0, 0', 'light3Rgb': '0, 0, 0',
'dark1Rgb': '0, 0, 0', 'dark2Rgb': '0, 0, 0', 'dark3Rgb': '0, 0, 0',
'systemAccent': True
}
return json.dumps(color_dictionary)

@staticmethod
def extrap_custom_color(accent_color: str) -> str:

def adjust_hex_color(hex_color, percent=15):
# Remove the '#' character if present
hex_color = hex_color.lstrip('#')

# Convert hex to RGB
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)

# Calculate the adjusted color
if percent < 0: # Darken the color
r = max(0, int(r * (1 + percent / 100)))
g = max(0, int(g * (1 + percent / 100)))
b = max(0, int(b * (1 + percent / 100)))
else: # Lighten the color
r = min(255, int(r + (255 - r) * (percent / 100)))
g = min(255, int(g + (255 - g) * (percent / 100)))
b = min(255, int(b + (255 - b) * (percent / 100)))

# Convert back to hex
adjusted_hex = f'#{r:02x}{g:02x}{b:02x}'
return adjusted_hex

def hex_to_rgba(hex_color):
# Remove the '#' character if present
hex_color = hex_color.lstrip('#')

# Check if the input has an alpha channel (8 characters)
if len(hex_color) == 8:
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
a = int(hex_color[6:8], 16) / 255 # Normalize alpha to [0, 1]
elif len(hex_color) == 6:
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
a = 1.0 # Fully opaque
else:
raise ValueError("Invalid hex color format. Use 6 or 8 characters.")

return (r, g, b, a)

original_accent = Colors.get_accent_color_win32() if os.name == 'nt' else Colors.get_accent_color_posix()

return json.dumps({
'accent': accent_color,
'light1': adjust_hex_color(accent_color, 15),
'light2': adjust_hex_color(accent_color, 30),
'light3': adjust_hex_color(accent_color, 45),
'dark1': adjust_hex_color(accent_color, -15),
'dark2': adjust_hex_color(accent_color, -30),
'dark3': adjust_hex_color(accent_color, -45),
'accentRgb': hex_to_rgba(accent_color),
'light1Rgb': hex_to_rgba(adjust_hex_color(accent_color, 15)),
'light2Rgb': hex_to_rgba(adjust_hex_color(accent_color, 30)),
'light3Rgb': hex_to_rgba(adjust_hex_color(accent_color, 45)),
'dark1Rgb': hex_to_rgba(adjust_hex_color(accent_color, -15)),
'dark2Rgb': hex_to_rgba(adjust_hex_color(accent_color, -30)),
'dark3Rgb': hex_to_rgba(adjust_hex_color(accent_color, -45)),
'systemAccent': False,
'originalAccent': original_accent
})

@staticmethod
def get_accent_color():
if os.name == 'nt':
return Colors.get_accent_color_win32()
def get_accent_color(accent_color):

if accent_color == "DEFAULT_ACCENT_COLOR":

print("Using default accent color")
if os.name == 'nt':
return Colors.get_accent_color_win32()
else:
return Colors.get_accent_color_posix()

else:
return Colors.get_accent_color_posix()
print("Using custom accent color")
return Colors.extrap_custom_color(accent_color)


def is_valid(theme_native_name: str) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion assets/core/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def get_load_config():
config = cfg.get_config()

return json.dumps({
"accent_color": json.loads(Colors.get_accent_color()),
"accent_color": json.loads(Colors.get_accent_color(config["accentColor"])),
"conditions": config["conditions"] if "conditions" in config else None,
"active_theme": json.loads(cfg.get_active_theme()),
"settings": config,
Expand Down
6 changes: 4 additions & 2 deletions assets/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,18 @@ const InitializePatcher = (startTime: number, result: SettingsProps) => {

const theme: ThemeItem = result.active_theme
const systemColors: SystemAccentColor = result.accent_color


ParseLocalTheme(theme)
DispatchSystemColors(systemColors)

const themeV1: ThemeItemV1 = result?.active_theme?.data as ThemeItemV1

if (themeV1?.GlobalsColors) {
DispatchGlobalColors(themeV1?.GlobalsColors)
}

pluginSelf.systemColors = systemColors
pluginSelf.conditionals = result?.conditions as ConditionsStore
pluginSelf.scriptsAllowed = result?.settings?.scripts as boolean ?? true
pluginSelf.stylesAllowed = result?.settings?.styles as boolean ?? true
Expand Down
2 changes: 2 additions & 0 deletions assets/src/locales/locales/english.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"themePanelInjectJavascriptToolTip": "Decide whether themes are allowed to insert JavaScript into Steam. Disabling JavaScript may break Steam interface as a byproduct (requires reload)",
"themePanelInjectCSS": "Inject StyleSheets",
"themePanelInjectCSSToolTip": "Decide whether themes are allowed to insert stylesheets into Steam. (requires reload)",
"themePanelCustomAccentColor": "Custom Accent Color",
"themePanelCustomAccentColorToolTip": "Set a custom accent color for themes that support it (requires reload)",
"updatePanelHasUpdates": "Updates Available!",
"updatePanelHasUpdatesSub": "Millennium found the following updates for your themes.",
"updatePanelReleasedTag": "Released:",
Expand Down
52 changes: 52 additions & 0 deletions assets/src/tabs/AccentColorPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { DialogButton, Field, Millennium, pluginSelf } from "millennium-lib"
import { useState } from "react";
import { DispatchSystemColors } from "../patcher/SystemColors";
import { settingsClasses } from "../classes";
import { locale } from "../locales";

const RenderAccentColorPicker = () => {
const [colorState, setColorState] = useState(pluginSelf.systemColors.accent.substring(0, 7));

const UpdateAllWindows = () => {
// @ts-ignore
g_PopupManager.m_mapPopups.data_.forEach((element: any) => {
element.value_.m_popup.window.document.querySelectorAll("#SystemAccentColorInject").forEach((element: any) => {
element.innerText = pluginSelf.systemColor
})
})
}

const UpdateColor = (hexColor: string) => {
setColorState(hexColor)

Millennium.callServerMethod("cfg.change_accent_color", { new_color: hexColor }).then((result: any) => {
DispatchSystemColors(JSON.parse(result));
UpdateAllWindows();
})
}

const ResetColor = () => {
Millennium.callServerMethod("cfg.reset_accent_color").then((result: any) => {
DispatchSystemColors(JSON.parse(result));
setColorState(pluginSelf.systemColors.accent.substring(0, 7));
UpdateAllWindows();
})
}

return (<>
<style>
{`.DialogBody { margin-bottom: 48px; }
input.colorPicker { margin-left: 10px !important; border: unset !important; min-width: 38px; width: 38px !important; height: 38px; !important; background: transparent; padding: unset !important; }`}
</style>

<Field
label={locale.themePanelCustomAccentColor}
description={locale.themePanelCustomAccentColorDescription}
>
{<DialogButton className={settingsClasses.SettingsDialogButton} onClick={ResetColor}>Reset</DialogButton>}
<input type="color" className="colorPicker" name="colorPicker" value={colorState} onChange={(event) => UpdateColor(event.target.value)}/>
</Field>
</>)
}

export { RenderAccentColorPicker }
7 changes: 7 additions & 0 deletions assets/src/tabs/Themes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import { ComboItem, ThemeItem } from '../types'
import { SetupAboutRenderer } from '../custom_components/AboutTheme'
import { locale } from '../locales'
import { ConnectionFailed } from '../custom_components/ConnectionFailed'
import { settingsClasses } from '../classes'
import { DispatchSystemColors } from '../patcher/SystemColors'
import { DOMModifier } from '../patcher/Dispatch'
import { RenderAccentColorPicker } from './AccentColorPicker'

const Localize = (token: string): string =>
// @ts-ignore
Expand Down Expand Up @@ -265,6 +269,9 @@ const ThemeViewModal: React.FC = () => {
<Toggle value={cssState} onChange={onStyleToggle} />
)}
</Field>

<RenderAccentColorPicker />

</DialogBody>
</>
)
Expand Down

0 comments on commit a850936

Please sign in to comment.