layout-switcher-package/layoutSwitcher.nim

169 lines
6.1 KiB
Nim

from std/os import symlinkExists, getConfigDir, `/`
from std/envvars import existsEnv, getEnv, delEnv, putEnv
from std/options import Option, some, get, isNone
from std/strutils import toLowerAscii, split
import owlkettle, sharedModule
const
dataDir = "/usr/share/tromjaro-layout-switcher"
iconsDir = dataDir / "icons"
profilesDir = dataDir / "profiles"
# GTK CSS for changing the default icon size and shape of our layout buttons
gtkCSS = """
.layout-button > image {
-gtk-icon-transform: scale(19);
}
.layout-button {
min-height: 200px;
min-width: 300px;
}"""
appID = "com.tromjaro.LayoutSwitcher"
layoutsGrid = [
[ "Windows-Like (default)", "MacOS-Like (experimental)", "MX-Like" ],
[ "Gnome-Like", "Unity-Like (experimental)", "TopX-Like" ]
]
proc enableTopBarIntegration(): bool =
if not isGlobalMenuEnabled():
# Enable global menu
let exitCode = runCommand("/usr/bin/pkexec", ["/usr/bin/toggle-global-menu", "enable"])
case exitCode
of 0:
discard
of 126:
return false
of 127:
sendNotification(appID, "Layout Switcher", "Authentication failed!")
return false
else:
sendNotification(appID, "Layout Switcher", "Failed to enable global menu!")
return false
# Hide window borders when maximized
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/borderless_maximize", "--create", "--type=bool", "--set=true"])
# Hide top bar of windows when maximized
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/titleless_maximize", "--create", "--type=bool", "--set=true"])
# Put window buttons on left side
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/button_layout", "--create", "--type=string", "--set=CMH|"])
return true
proc disableTopBarIntegration(): bool =
if isGlobalMenuEnabled():
# Disable global menu
let exitCode = runCommand("/usr/bin/pkexec", ["/usr/bin/toggle-global-menu", "disable"])
case exitCode
of 0:
discard
of 126:
return false
of 127:
sendNotification(appID, "Layout Switcher", "Authentication failed!")
else:
sendNotification(appID, "Layout Switcher", "Failed to disable global menu!")
return false
# Hide window borders when maximized
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/borderless_maximize", "--create", "--type=bool", "--set=true"])
# Don't hide top bar of windows when maximized
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/titleless_maximize", "--create", "--type=bool", "--set=false"])
# Put window buttons on right side
discard runCommand("/usr/bin/xfconf-query",
args=["--channel=xfwm4", "--property=/general/button_layout", "--create", "--type=string", "--set=|HMC"])
return true
var
oldConfigDir: Option[string]
configDirChanged: bool
# Prevent loading GTK theme from ~/.config/gtk-4.0 directory when it is a symlink
if symlinkExists(getConfigDir() / "gtk-4.0"):
if existsEnv("XDG_CONFIG_HOME"):
oldConfigDir = some(getEnv("XDG_CONFIG_HOME"))
putEnv("XDG_CONFIG_HOME", "/dev/null")
configDirChanged = true
viewable App:
loading: bool
hooks:
build:
# Reset the user's XDG_CONFIG_HOME variable back to what it was before
if configDirChanged == true:
if oldConfigDir.isNone():
delEnv("XDG_CONFIG_HOME")
else:
putEnv("XDG_CONFIG_HOME", get(oldConfigDir))
proc enableLayout(args: tuple[layoutName: string, app: AppState]) {.thread.} =
let
layoutName = args.layoutName
app = args.app
defer:
app.loading = false
app.redrawFromThread()
case layoutName
of "Windows-Like", "MX-Like", "Gnome-Like", "TopX-Like":
if not disableTopBarIntegration():
return
of "Unity-Like", "MacOS-Like":
if not enableTopBarIntegration():
return
else:
return
# Load the layout profile
discard runCommand("/usr/bin/xfce4-panel-profiles", ["load", profilesDir / layoutName & ".tar.bz2"])
# Restart mate-hud
if runCommand("/usr/bin/killall", ["mate-hud"]) == 0:
discard runCommand("/usr/bin/setsid", ["-f", "/usr/lib/mate-hud/mate-hud"])
sendNotification(appID, "Layout Switcher", layoutName & " layout was enabled",
icon = iconsDir / layoutName.toLowerAscii() & "-layout.png")
var thread: Thread[(string, AppState)]
method view(app: AppState): Widget =
result = gui:
Window:
title = "TROMjaro Layout Switcher"
# Shrink window to the smallest size
defaultSize = (0, 0)
iconName = "tromjaro-layout-switcher"
if app.loading:
Box(orient = OrientY, margin = 70):
Spinner(spinning = true)
Label(text = "Loading your layout, please wait...") {.expand: false.}
else:
Box(orient = OrientY, margin = 7, spacing = 5):
Box(orient = OrientX):
Label {.hAlign: AlignEnd.}:
text = "Please use the"
LinkButton {.expand: false.}:
text = "Panel Profiles"
proc clicked() =
discard runCommand("/usr/bin/setsid", ["-f", "/usr/bin/xfce4-panel-profiles"])
Label {.hAlign: AlignStart.}:
text = "to save your current configuration in case you did any manual changes, else you may lose them."
Label:
text = "Changing to or from any layout that has global menu will require your admin password."
for row in layoutsGrid:
Box(orient = OrientX, spacing = 5):
for tooltip in row:
let layoutName = tooltip.split(' ', 1)[0]
Button {.vAlign: AlignCenter, hAlign: AlignCenter.}:
icon = layoutName.toLowerAscii() & "-layout"
tooltip = tooltip
style = [ButtonFlat, StyleClass("layout-button")]
proc clicked() =
app.loading = true
createThread(thread, enableLayout, (layoutName, app))
brew(appID, gui(App()), icons=[iconsDir], stylesheets=[newStylesheet(gtkCSS)])
if running(thread):
joinThread(thread)