diff --git a/themeSwitcher.nim b/themeSwitcher.nim index 466bd3e..a02eba7 100644 --- a/themeSwitcher.nim +++ b/themeSwitcher.nim @@ -1,10 +1,9 @@ -from std/osproc import execProcess, ProcessOption, startProcess, waitForExit, close from std/os import symlinkExists, getConfigDir, walkDirs, `/` from std/envvars import existsEnv, getEnv, delEnv, putEnv -from std/strutils import split, endsWith, contains from std/options import Option, some, get, isNone from std/strformat import fmt -import owlkettle +import pkg/owlkettle +import ./themes const # Set the directory path where icons are stored @@ -25,35 +24,21 @@ const padding: 0.4em; border-radius: 50%; } - -#.accent-button.suggested-action { -# outline-color: black; -# outline-width: 0.05em; -# outline-offset: -0.45em; -}""" - appID = "com.tromjaro.ThemeSwitcher" - appLogo = "tromjaro-theme-switcher" - themeShades = ["Light", "", "Dark"] - themeStyles = ["Default", "Nord", "Dracula", "Catppuccin", "Everforest", "Gruvbox"] - accentColors = ["Blue", "Red", "Purple", "Pink", "Teal", "Green", "Yellow", "Orange", "Grey"] +""" var iconsDirs: seq[string] for dir in walkDirs(accentIconsDir / "*"): iconsDirs.add(dir) -type Theme = tuple[shade: string, style: string, color: string] - # Function prototypes -proc getCurrentTheme(): Theme proc makeThemeButton(shadeName: string, styleName: string): Widget proc makeAccentButton(accentColor: string): Widget -proc runCommand(command: string, args: openArray[string]): bool -proc setIconTheme(iconName: string) -proc enableDarkPanels(): bool -proc setTheme(themeName: string) +proc accentButtons(): Widget +proc themeButtons(): Widget +proc mainGUI(): Widget -# Highlight current theme button +# Keep track of current theme button var currentTheme = getCurrentTheme() var @@ -68,6 +53,7 @@ if symlinkExists(getConfigDir() / "gtk-4.0"): configDirChanged = true # Display the GTK-4 GUI using owlkettle + viewable App: hooks: build: @@ -79,106 +65,19 @@ viewable App: putEnv("XDG_CONFIG_HOME", get(oldConfigDir)) method view(app: AppState): Widget = - result = gui: - Window: - title = "TROMjaro Theme Switcher" - # Shrink window to the smallest size - defaultSize = (1, 1) - iconName = appLogo - # Vertical box - Box(orient = OrientY, margin = 13, spacing = 10): - Label: - useMarkup=true - text="STYLE" - # Vertical box - Box(orient = OrientY, spacing = 5): - for shadeName in themeShades: - # Horizontal box - Box(orient = OrientX, spacing = 5): - for styleName in themeStyles: - insert(makeThemeButton(shadeName, styleName)) {.vAlign: AlignCenter, hAlign: AlignCenter.} - Separator(margin = Margin(top: 5)) {.vAlign: AlignCenter.} - Label: - useMarkup=true - text="ACCENT COLOR" - # Horizontal box - Box(orient = OrientX, spacing = 3): - for accentColor in accentColors: - insert(makeAccentButton(accentColor)) {.vAlign: AlignCenter, hAlign: AlignCenter.} - Separator(margin = Margin(top: 12)) {.vAlign: AlignCenter.} - # Vertical box - Box(orient = OrientY): - Label: - text="A Theme Switcher for TROMjaro's default theme-set (Colloid) and icon-set (Zafiro)." - Label {.vAlign: AlignStart.}: - useMarkup = true - text="NOTE: Some apps need to be reopened for the theme to be applied." + result = mainGUI() brew(appID, gui(App()), icons=iconsDirs, stylesheets=[newStylesheet(gtkCSS)]) # Function declarations -proc getCurrentTheme(): Theme = - let currentThemeString = execProcess("/usr/bin/xfconf-query", - args=["--channel=xsettings", "--property=/Net/ThemeName"], - options={})[0 .. ^2] - - let words = currentThemeString.split('-') - if words[0] != "Colloid": - return - var currentTheme: Theme = ("", "Default", "Blue") - - case len(words): - of 1: discard - - of 2: - let word1 = words[1] - if word1 in accentColors: - currentTheme.color = word1 - elif word1 in themeShades: - currentTheme.shade = word1 - elif word1 in themeStyles: - currentTheme.style = word1 - else: - return - - of 3: - let - word1 = words[1] - word2 = words[2] - if (word1 in accentColors) and (word2 in themeShades): - currentTheme.color = word1 - currentTheme.shade = word2 - elif (word1 in themeShades) and (word2 in themeStyles): - currentTheme.shade = word1 - currentTheme.style = word2 - else: - return - - of 4: - let - word1 = words[1] - word2 = words[2] - word3 = words[3] - if (word1 in accentColors) and (word2 in themeShades) and (word3 in themeStyles): - currentTheme.color = word1 - currentTheme.shade = word2 - currentTheme.style = word3 - else: - return - - else: - return - - result = currentTheme - proc makeThemeButton(shadeName: string, styleName: string): Widget = let shade = if shadeName == "": "" else: fmt"-{shadeName}" style = if styleName == "Default": "" else: fmt"-{styleName}" color = if currentTheme.color in ["Blue", ""]: "" else: fmt"-{currentTheme.color}" - theme = fmt"Colloid{color}{shade}{style}" + themeString = fmt"Colloid{color}{shade}{style}" result = gui: Button: @@ -191,7 +90,7 @@ proc makeThemeButton(shadeName: string, styleName: string): Widget = [StyleClass("style-button"), ButtonFlat] proc clicked() = - setTheme(theme) + setTheme(themeString) # Highlight this button currentTheme.shade = shadeName currentTheme.style = styleName @@ -203,7 +102,7 @@ proc makeAccentButton(accentColor: string): Widget = shade = if currentTheme.shade == "": "" else: fmt"-{currentTheme.shade}" style = if currentTheme.style in ["Default", ""]: "" else: fmt"-{currentTheme.style}" color = if accentColor == "Blue": "" else: fmt"-{accentColor}" - theme = fmt"Colloid{color}{shade}{style}" + themeString = fmt"Colloid{color}{shade}{style}" result = gui: Button: @@ -215,48 +114,56 @@ proc makeAccentButton(accentColor: string): Widget = [StyleClass("accent-button"), ButtonFlat] proc clicked() = - setTheme(theme) + setTheme(themeString) # Highlight this button currentTheme.color = accentColor if currentTheme.style == "": currentTheme.style = "Default" -proc runCommand(command: string, args: openArray[string]): bool = - ## This will run a command with the given args and return true upon success - let process = startProcess(command, args=args, options={poParentStreams}) - result = process.waitForExit() == 0 - process.close() +proc themeButtons(): Widget = + gui: + # Vertical box + Box(orient = OrientY, spacing = 5): + for shadeName in themeShades: + # Horizontal box + Box(orient = OrientX, spacing = 5): + for styleName in themeStyles: + let themeButton = makeThemeButton(shadeName, styleName) + insert(themeButton) {.vAlign: AlignCenter, hAlign: AlignCenter.} -proc setIconTheme(iconName: string) = - # Change icon theme in XFCE - discard runCommand("/usr/bin/xfconf-query", - args=["--channel=xsettings", "--property=/Net/IconThemeName", "--create", "--type=string", "--set", iconName]) +proc accentButtons(): Widget = + gui: + # Horizontal box + Box(orient = OrientX, spacing = 3): + for accentColor in accentColors: + let accentButton = makeAccentButton(accentColor) + insert(accentButton) {.vAlign: AlignCenter, hAlign: AlignCenter.} -proc enableDarkPanels(): bool = - # Return if dark panels is already enabled - if execProcess("/usr/bin/xfconf-query", - args=["--channel=xfce4-panel", "--property=/panels/dark-mode"], options={}) == "true\n": - return - # Try to enable it and return true upon success - result = runCommand("/usr/bin/xfconf-query", - args=["--channel=xfce4-panel", "--property=/panels/dark-mode", "--create", "--type=bool", "--set=true"]) - -proc setTheme(themeName: string) = - - var panelNotification: string - - # Set the icon theme and panel color according to the chosen theme - if themeName.endsWith("-Dark") or themeName.contains("-Dark-"): - setIconTheme("zafiro-dark") - else: - setIconTheme("zafiro") - if enableDarkPanels(): - panelNotification = " with dark panels" - - # Change the main theme to the chosen one - discard runCommand("/usr/bin/xfconf-query", - args=["--channel=xsettings", "--property=/Net/ThemeName", "--create", "--type=string", "--set", themeName]) - - # Send notification about the theme change - sendNotification(appID, "Theme Switcher", fmt"{themeName} theme{panelNotification} is enabled", - icon=appLogo) +proc mainGUI(): Widget = + gui: + Window: + title = "TROMjaro Theme Switcher" + # Shrink window to the smallest size + defaultSize = (1, 1) + iconName = appLogo + # Vertical box + Box(orient = OrientY, margin = 13, spacing = 10): + Label: + useMarkup=true + text="STYLE" + # Theme buttons + insert(themeButtons()) + Separator(margin = Margin(top: 5)) {.vAlign: AlignCenter.} + Label: + useMarkup=true + text="ACCENT COLOR" + # Accent buttons + insert(accentButtons()) + Separator(margin = Margin(top: 12)) {.vAlign: AlignCenter.} + # Vertical box + Box(orient = OrientY): + Label: + text="A Theme Switcher for TROMjaro's default theme-set (Colloid) and icon-set (Zafiro)." + Label {.vAlign: AlignStart.}: + useMarkup = true + text="NOTE: Some apps need to be reopened for the theme to be applied." diff --git a/themes.nim b/themes.nim new file mode 100644 index 0000000..6dd741e --- /dev/null +++ b/themes.nim @@ -0,0 +1,111 @@ +from std/osproc import execProcess, ProcessOption, startProcess, waitForExit, close +from std/strutils import split, endsWith, contains +from std/strformat import fmt +import pkg/owlkettle + +type Theme = tuple[color, shade, style: string] + +const + themeShades* = ["Light", "", "Dark"] + themeStyles* = ["Default", "Nord", "Dracula", "Catppuccin", "Everforest", "Gruvbox"] + accentColors* = ["Blue", "Red", "Purple", "Pink", "Teal", "Green", "Yellow", "Orange", "Grey"] + appID* = "com.tromjaro.ThemeSwitcher" + appLogo* = "tromjaro-theme-switcher" + +proc getCurrentTheme*(): Theme = + let currentThemeString = execProcess("/usr/bin/xfconf-query", + args=["--channel=xsettings", "--property=/Net/ThemeName"], + options={})[0 .. ^2] + + # Split the theme string into words + let words = currentThemeString.split('-') + if words[0] != "Colloid": + return + var currentTheme: Theme = (color: "Blue", shade: "", style: "Default") + + case len(words): + of 1: discard + + # Match against Colloid-Color, Colloid-Shade, Colloid-Style + of 2: + let word1 = words[1] + if word1 in accentColors: + currentTheme.color = word1 + elif word1 in themeShades: + currentTheme.shade = word1 + elif word1 in themeStyles: + currentTheme.style = word1 + else: + return + + # Match against Colloid-Color-Shade, Colloid-Shade-Style + of 3: + let + word1 = words[1] + word2 = words[2] + if (word1 in accentColors) and (word2 in themeShades): + currentTheme.color = word1 + currentTheme.shade = word2 + elif (word1 in themeShades) and (word2 in themeStyles): + currentTheme.shade = word1 + currentTheme.style = word2 + else: + return + + # Match against Colloid-Color-Shade-Style + of 4: + let + word1 = words[1] + word2 = words[2] + word3 = words[3] + if (word1 in accentColors) and (word2 in themeShades) and (word3 in themeStyles): + currentTheme.color = word1 + currentTheme.shade = word2 + currentTheme.style = word3 + else: + return + + else: + return + + return currentTheme + +proc runCommand(command: string, args: openArray[string]): bool = + ## This will run a command with the given args and return true upon success + let process = startProcess(command, args=args, options={poParentStreams}) + result = process.waitForExit() == 0 + process.close() + +proc setIconTheme(iconName: string) = + # Change icon theme in XFCE + discard runCommand("/usr/bin/xfconf-query", + args=["--channel=xsettings", "--property=/Net/IconThemeName", "--create", "--type=string", "--set", iconName]) + +proc enableDarkPanels(): bool = + # Return if dark panels is already enabled + if execProcess("/usr/bin/xfconf-query", + args=["--channel=xfce4-panel", "--property=/panels/dark-mode"], options={}) == "true\n": + return + # Try to enable it and return true upon success + result = runCommand("/usr/bin/xfconf-query", + args=["--channel=xfce4-panel", "--property=/panels/dark-mode", "--create", "--type=bool", "--set=true"]) + +proc setTheme*(themeName: string) = + + var panelNotification: string + + # Set the icon theme and panel color according to the chosen theme + if themeName.endsWith("-Dark") or themeName.contains("-Dark-"): + setIconTheme("zafiro-dark") + else: + setIconTheme("zafiro") + if enableDarkPanels(): + panelNotification = " with dark panels" + + # Change the main theme to the chosen one + discard runCommand("/usr/bin/xfconf-query", + args=["--channel=xsettings", "--property=/Net/ThemeName", "--create", "--type=string", "--set", themeName]) + + # Send notification about the theme change + sendNotification(appID, "Theme Switcher", fmt"{themeName} theme{panelNotification} is enabled", + icon=appLogo)